2024-01-02 00:17:29 -05:00

312 lines
11 KiB
Python

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