from .LibraryParser import LibraryParser from ..Guid import Guid from .MochaSyntaxError import MochaSyntaxError class YAMLLibraryParser (LibraryParser): def __init__(self, manager): LibraryParser.__init__(self, manager) self.yamlIds = dict() self.templates = dict() # these two are special reserved tagNames # self.yamlIds["entityDefinitions"] = None # self.yamlIds["instance"] = { } self.manager = manager self._instances_awaiting_sugar = [] def apply_sugar(self): print("applying syntactic sugar to remaining instances...") for inst in self._instances_awaiting_sugar: print("applying sugar from '" + inst["from_file_name"] + "'") self.load_instance(inst) print("\n") def load_library(self, elem): print("loading library from elem") def load_tenant(self, elem): print("loading tenant from elem") def find_custom_tag_name(self, elem) -> (str, Guid): # return tuple (tagname, value) if "instance" in elem: return ("instance", Guid(self.manager.expand_entity_references(elem["instance"]))) for key in self.yamlIds: if key in elem: k = str(key) v = Guid(self.manager.expand_entity_references(elem[key])) return (k, v) return (None, None) def load_entity_definitions_from_file(self, filename): print("loading entity defs from " + filename) import yaml with open(filename, 'r') as file: content = yaml.safe_load_all(file) for doc in content: for elem in doc: if "entityDefinitions" in elem: entities = elem["entityDefinitions"] for entity in entities: for key in entity: self.manager.register_entity_reference(key, entity[key]) def load_instance_with_template(self, elem, template): (parentCreatorKey, parentInstanceId) = self.find_custom_tag_name(template) (creatorKey, instanceId) = self.find_custom_tag_name(elem) self.apply_template(elem, template, instanceId) def apply_template(self, elem, template, instanceId): if "value" in elem: print("VALUE found in ELEM:") print(elem["value"]) if "attributes" in template: attrs = template["attributes"] if isinstance(attrs, list): for attr in attrs: if "customTagName" in attr: customTagName = attr["customTagName"] if customTagName in elem: attrinst = Guid(self.manager.expand_entity_references(attr["instance"])) attrval = elem[customTagName] self.manager.set_attribute_value(instanceId, attrinst, attrval) if "relationships" in template: rels = template["relationships"] for rel in rels: if "instance" in rel: rel_iid = rel["instance"] # the globalId of the relationship instance else: print("no relationship instance specified for relationship sugar") continue if "customTagName" in rel: customTagName = rel["customTagName"] if customTagName in elem: relationshipValue = elem[customTagName] if isinstance(relationshipValue, list): # multiple instances for v in relationshipValue: #FIXME: this 'instance' isn't always the tag name, this can be subject to customTagName as well!and should be processed accordingly (relinst_key, relinst_iid) = self.find_custom_tag_name(v) if relinst_key is not None: # for example, 'relinst_key' is 'elementContent' and 'relinst_iid' is '{8ECA14A4...}' if rel_iid is not None: print("found customTagName '" + str(relinst_key) + "' with value '" + str(relinst_iid.get_value()) + "'") # relval = Guid(self.manager.expand_entity_references(elem[customTagName])) # we need to create the new instance and assign relationship self.manager.assign_relationship(instanceId, Guid(self.manager.expand_entity_references(rel_iid)), relinst_iid) else: print("HELLO!!! NO RELATIONSHIP SUGAR!!! CREATE THE INSTANCE!!!") else: if (isinstance(relationshipValue, str)): # single instance, this should be a GUID or entityref # we should only need to refer to existing instance in this case relationshipValueInst = Guid(self.manager.expand_entity_references(relationshipValue)) if "instance" in rel: self.manager.assign_relationship(instanceId, Guid(self.manager.expand_entity_references(rel["instance"])), relationshipValueInst) else: print("no relationship instance specified for relationship sugar") else: # dynamically created instance if "globalIdentifier" in relationshipValue: globalIdentifier = relationshipValue["globalIdentifier"] else: globalIdentifier = Guid.create() print("creating new instance for relationship '%s' with globalid '%s'" % (rel_iid, globalIdentifier.get_value())) if "customTagNameCreatesInstanceOf" in rel: createsInstanceOf = Guid(self.manager.expand_entity_references(rel["customTagNameCreatesInstanceOf"])) # create the new instance self.manager.add_instance(createsInstanceOf, globalIdentifier, None, None) # assign relationships self.manager.assign_relationship(instanceId, Guid(self.manager.expand_entity_references(rel_iid)), globalIdentifier) # self.manager.assign_relationship(globalIdentifier, relationshipInstanceId, instanceId) # FIXME: apply relationships from the parent class template if createsInstanceOf.get_value() in self.templates: print("applying template for createsInstanceOf '%s'" % (createsInstanceOf.get_value())) createsInstanceOfTemplate = self.templates[createsInstanceOf.get_value()] self.apply_template(relationshipValue, createsInstanceOfTemplate, globalIdentifier) else: print("no template registered for createsInstanceOf '%s'" % (createsInstanceOf.get_value())) def get_instance_sugar_for_elem(self, elem): if "instance" in elem: return (elem["instance"], "instance", None) else: for key in self.yamlIds: # no 'instance', see if we if key in elem: template = self.yamlIds[key] templateKey = key return (elem[templateKey], templateKey, template) return (None, None, None) # should never get here def load_instance(self, elem, elemParent = None): # 'class' and 'relationship' are both at the 'same level' # so we can't define 'relationship' without first defining 'class' # we could try to keep looping as long as we're still getting "unknown tag name" # (instanceID is None), but we could get stuck that way... # for now, let's just forward define important classes (e.g. IDC_Relationship) # all of these problems could be avoided by simply # LOADING EVERYTHING INTO MEMORY (like the .NET version does) # and only committing to the DB at the very end # this will resolve problems where we can't find instances, or use syntactic sugar (globalIdentifier, templateKey, template) = self.get_instance_sugar_for_elem(elem) print ("globalIdentifier = " + str(globalIdentifier) + ", templateKey = " + str(templateKey)) if template is None: # no sugar pass customTagName = None if "customTagName" in elem: customTagName = elem["customTagName"] self.yamlIds[customTagName] = elem print("registering customTagName '" + customTagName + "'") if "registerForTemplate" in elem: if elem["registerForTemplate"] == True: self.templates[self.manager.expand_entity_references(globalIdentifier)] = elem print("registering elem for template") classId = None classIndex = None if template is None and elemParent is not None: template = elemParent if template is not None: if "instance" in template: classId = Guid(self.manager.expand_entity_references(template["instance"])) else: for key in self.yamlIds: if key in template: classId = Guid(self.manager.expand_entity_references(template[key])) break if "index" in template: classIndex = template["index"] instanceId = None creatorKey = None if "instance" in elem: if "templateOnly" in elem and elem["templateOnly"] == True: instanceId = None else: instanceId = Guid(self.manager.expand_entity_references(elem["instance"])) creatorKey = 'instance' else: for key in self.yamlIds: if key in elem: instanceId = Guid(self.manager.expand_entity_references(elem[key])) creatorKey = key break index = None if "index" in elem: index = elem["index"] # else: # print("index not found for element") # print(elem) if classId is None and instanceId is not None: classId = instanceId # HACK HACK if instanceId is not None: self.manager.add_instance(classId, instanceId, classIndex, index) if creatorKey == 'class': #FIXME: remove this special-case behavior print("creating class " + instanceId.get_value()) # assign relationship `Instance.for Class` self.manager.assign_relationship(instanceId, Guid('494D5A6D04BE477B8763E3F57D0DD8C8'), Guid('B9C9B9B7AD8A4CBDAA6BE05784630B6B')) # assign relationship `Class.has Instance` self.manager.assign_relationship(Guid('B9C9B9B7AD8A4CBDAA6BE05784630B6B'), Guid('7EB41D3C2AE9488483A4E59441BCAEFB'), instanceId) if "instances" in elem: classInsts = elem["instances"] for inst in classInsts: self.load_instance(inst, elem) else: print("creating instance " + instanceId.get_value() + " (of class " + classId.get_value() + ")") # assign relationship `Instance.for Class` self.manager.assign_relationship(instanceId, Guid('494D5A6D04BE477B8763E3F57D0DD8C8'), classId) # assign relationship `Class.has Instance` self.manager.assign_relationship(classId, Guid('7EB41D3C2AE9488483A4E59441BCAEFB'), instanceId) # self.manager.create_instance_of(classId, instanceId, classIndex, index) if templateKey is not None: if template is not None: print("using template (" + templateKey + ")") #print(template) self.load_instance_with_template(elem, template) else: #print("ignoring '" + templateKey + "'") pass def load_instances_from_file(self, filename): print("loading instances from " + filename) import yaml with open(filename, 'r') as file: content = yaml.safe_load_all(file) for doc in content: for elem in doc: if "entityDefinitions" in elem: continue elif "library" in elem: self.load_library(filename, elem) elif "tenant" in elem: self.load_tenant(filename, elem) else: raise MochaSyntaxError("neither 'library' nor 'tenant' top level element found") def load_library(self, filename, elem): if "instances" in elem: instances = elem["instances"] for elem1 in instances: if "instance" in elem1: # this is an instance definition (no sugar), so just load it self.load_instance(elem1) else: # this is a syntactic sugar definition, so store it for later use elem1["from_file_name"] = filename self._instances_awaiting_sugar.append(elem1) def load_tenant(self, filename, elem): pass