# coding: utf-8 import importlib import typing from _elementtree import Element from lxml import etree from synergine2.config import Config from synergine2.log import get_logger from synergine2.simulation import Subject from opencombat.exception import StateLoadError from opencombat.simulation.base import TileStrategySimulation from opencombat.util import get_class_from_string_path class State(object): def __init__( self, config: Config, state_root: Element, simulation: TileStrategySimulation, ) -> None: self._config = config self._state_root = state_root self._subjects = None # type: typing.List[Subject] self._simulation = simulation @property def subjects(self) -> typing.List[Subject]: if self._subjects is None: self._subjects = self._get_subjects() return self._subjects def _get_subjects(self) -> typing.List[Subject]: subjects = [] subject_elements = self._state_root.find('subjects').findall('subject') for subject_element in subject_elements: subject_class_path = subject_element.find('type').text subject_class = get_class_from_string_path( self._config, subject_class_path, ) subject = subject_class(self._config, self._simulation) # TODO BS 2018-06-13: Fill subject with property subjects.append(subject) return subjects class StateLoader(object): def __init__( self, config: Config, simulation: TileStrategySimulation, ) -> None: self._logger = get_logger('StateLoader', config) self._config = config self._simulation = simulation def get_state( self, state_file_path: str, ) -> State: return State( self._config, self._validate_and_return_state_element(state_file_path), self._simulation, ) def _validate_and_return_state_element( self, state_file_path: str, ) -> Element: # open and read schema file schema_file_path = self._config.get( 'global.state_schema', 'opencombat/state.xsd', ) with open(schema_file_path, 'r') as schema_file: schema_to_check = schema_file.read() # open and read xml file with open(state_file_path, 'r') as xml_file: xml_to_check = xml_file.read() xmlschema_doc = etree.fromstring(schema_to_check.encode('utf-8')) xmlschema = etree.XMLSchema(xmlschema_doc) try: doc = etree.fromstring(xml_to_check.encode('utf-8')) # check for file IO error except IOError as exc: self._logger.error(exc) raise StateLoadError('Invalid File "{}": {}'.format( state_file_path, str(exc), )) # check for XML syntax errors except etree.XMLSyntaxError as exc: self._logger.error(exc) raise StateLoadError('XML Syntax Error in "{}": {}'.format( state_file_path, str(exc.error_log), )) except Exception as exc: self._logger.error(exc) raise StateLoadError('Unknown error with "{}": {}'.format( state_file_path, str(exc), )) # validate against schema try: xmlschema.assertValid(doc) except etree.DocumentInvalid as exc: self._logger.error(exc) raise StateLoadError( 'Schema validation error with "{}": {}'.format( state_file_path, str(exc), ) ) except Exception as exc: self._logger.error(exc) raise StateLoadError( 'Unknown validation error with "{}": {}'.format( state_file_path, str(exc), ) ) return doc class StateLoaderBuilder(object): def __init__( self, config: Config, simulation: TileStrategySimulation, ) -> None: self._logger = get_logger('StateLoader', config) self._config = config self._simulation = simulation def get_state_loader( self, ) -> StateLoader: class_address = self._config.resolve( 'global.state_loader', 'opencombat.state.StateLoader', ) state_loader_class = get_class_from_string_path( self._config, class_address, ) return state_loader_class( self._config, self._simulation, )