state.py 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. # coding: utf-8
  2. import importlib
  3. import typing
  4. from _elementtree import Element
  5. from lxml import etree
  6. from synergine2.config import Config
  7. from synergine2.log import get_logger
  8. from synergine2.simulation import Subject
  9. from opencombat.exception import StateLoadError
  10. from opencombat.simulation.base import TileStrategySimulation
  11. from opencombat.util import get_class_from_string_path
  12. class State(object):
  13. def __init__(
  14. self,
  15. config: Config,
  16. state_root: Element,
  17. simulation: TileStrategySimulation,
  18. ) -> None:
  19. self._config = config
  20. self._state_root = state_root
  21. self._subjects = None # type: typing.List[Subject]
  22. self._simulation = simulation
  23. @property
  24. def subjects(self) -> typing.List[Subject]:
  25. if self._subjects is None:
  26. self._subjects = self._get_subjects()
  27. return self._subjects
  28. def _get_subjects(self) -> typing.List[Subject]:
  29. subjects = []
  30. subject_elements = self._state_root.find('subjects').findall('subject')
  31. for subject_element in subject_elements:
  32. subject_class_path = subject_element.find('type').text
  33. subject_class = get_class_from_string_path(
  34. self._config,
  35. subject_class_path,
  36. )
  37. subject = subject_class(self._config, self._simulation)
  38. # TODO BS 2018-06-13: Fill subject with property
  39. subjects.append(subject)
  40. return subjects
  41. class StateLoader(object):
  42. def __init__(
  43. self,
  44. config: Config,
  45. simulation: TileStrategySimulation,
  46. ) -> None:
  47. self._logger = get_logger('StateLoader', config)
  48. self._config = config
  49. self._simulation = simulation
  50. def get_state(
  51. self,
  52. state_file_path: str,
  53. ) -> State:
  54. return State(
  55. self._config,
  56. self._validate_and_return_state_element(state_file_path),
  57. self._simulation,
  58. )
  59. def _validate_and_return_state_element(
  60. self,
  61. state_file_path: str,
  62. ) -> Element:
  63. # open and read schema file
  64. schema_file_path = self._config.get(
  65. 'global.state_schema',
  66. 'opencombat/state.xsd',
  67. )
  68. with open(schema_file_path, 'r') as schema_file:
  69. schema_to_check = schema_file.read()
  70. # open and read xml file
  71. with open(state_file_path, 'r') as xml_file:
  72. xml_to_check = xml_file.read()
  73. xmlschema_doc = etree.fromstring(schema_to_check.encode('utf-8'))
  74. xmlschema = etree.XMLSchema(xmlschema_doc)
  75. try:
  76. doc = etree.fromstring(xml_to_check.encode('utf-8'))
  77. # check for file IO error
  78. except IOError as exc:
  79. self._logger.error(exc)
  80. raise StateLoadError('Invalid File "{}": {}'.format(
  81. state_file_path,
  82. str(exc),
  83. ))
  84. # check for XML syntax errors
  85. except etree.XMLSyntaxError as exc:
  86. self._logger.error(exc)
  87. raise StateLoadError('XML Syntax Error in "{}": {}'.format(
  88. state_file_path,
  89. str(exc.error_log),
  90. ))
  91. except Exception as exc:
  92. self._logger.error(exc)
  93. raise StateLoadError('Unknown error with "{}": {}'.format(
  94. state_file_path,
  95. str(exc),
  96. ))
  97. # validate against schema
  98. try:
  99. xmlschema.assertValid(doc)
  100. except etree.DocumentInvalid as exc:
  101. self._logger.error(exc)
  102. raise StateLoadError(
  103. 'Schema validation error with "{}": {}'.format(
  104. state_file_path,
  105. str(exc),
  106. )
  107. )
  108. except Exception as exc:
  109. self._logger.error(exc)
  110. raise StateLoadError(
  111. 'Unknown validation error with "{}": {}'.format(
  112. state_file_path,
  113. str(exc),
  114. )
  115. )
  116. return doc
  117. class StateLoaderBuilder(object):
  118. def __init__(
  119. self,
  120. config: Config,
  121. simulation: TileStrategySimulation,
  122. ) -> None:
  123. self._logger = get_logger('StateLoader', config)
  124. self._config = config
  125. self._simulation = simulation
  126. def get_state_loader(
  127. self,
  128. ) -> StateLoader:
  129. class_address = self._config.resolve(
  130. 'global.state_loader',
  131. 'opencombat.state.StateLoader',
  132. )
  133. state_loader_class = get_class_from_string_path(
  134. self._config,
  135. class_address,
  136. )
  137. return state_loader_class(
  138. self._config,
  139. self._simulation,
  140. )