324 lines
11 KiB
Python
324 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...")
|
|
# _instances_also_awaiting_sugar = []
|
|
|
|
for inst in self._instances_awaiting_sugar:
|
|
print("applying sugar from '" + inst["from_file_name"] + "'")
|
|
self.load_instance(inst)
|
|
print("\n")
|
|
|
|
# do recursively until we're all out of instances awaiting sugar
|
|
# if len(self._instances_awaiting_sugar) > 0:
|
|
# self.apply_sugar()
|
|
|
|
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"]
|
|
if not entities is None:
|
|
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 assign_relationship_value(self, instanceId, rel, rel_iid, relationshipValue):
|
|
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
|
|
#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(relationshipValue)
|
|
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, rel_iid, relinst_iid)
|
|
else:
|
|
if "globalIdentifier" in relationshipValue:
|
|
globalIdentifier = Guid(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, 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 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 relationshipValue is None:
|
|
continue
|
|
|
|
if isinstance(relationshipValue, list):
|
|
# multiple instances
|
|
for v in relationshipValue:
|
|
self.assign_relationship_value(instanceId, rel, Guid(self.manager.expand_entity_references(rel_iid)), v)
|
|
|
|
else:
|
|
self.assign_relationship_value(instanceId, rel, Guid(self.manager.expand_entity_references(rel_iid)), relationshipValue)
|
|
|
|
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
|
|
instanceId = Guid(self.manager.expand_entity_references(elem["instance"]))
|
|
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
|
|
print("WARNING: class hack used for instanceId " + instanceId.get_value())
|
|
|
|
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 |