switch from dom to xmlschema

as suggested by @Moult, xmlschema better fits the purpose than xml.dom.minidom
This commit is contained in:
ArturTomczak
2021-06-10 15:19:23 +02:00
parent 4017097f19
commit 8c9ab98697
+44 -39
View File
@@ -1,7 +1,6 @@
import operator import operator
import ifcopenshell.util.element import ifcopenshell.util.element
from xml.dom.minidom import parse
from xmlschema import XMLSchema from xmlschema import XMLSchema
@@ -53,10 +52,16 @@ class facet(metaclass=meta_facet):
self.node = node self.node = node
def __getattr__(self, k): def __getattr__(self, k):
try:
v = self.node.getElementsByTagName(k)[0] if k in self.node:
except IndexError: v = self.node[k]
else:
v = None v = None
return v
#TODO implement restrictons
"""
if v: if v:
elems = [n for n in v.childNodes if n.nodeType == n.ELEMENT_NODE] elems = [n for n in v.childNodes if n.nodeType == n.ELEMENT_NODE]
if elems: if elems:
@@ -65,6 +70,7 @@ class facet(metaclass=meta_facet):
return v.firstChild.nodeValue.strip() return v.firstChild.nodeValue.strip()
else: else:
return None return None
"""
def __iter__(self): def __iter__(self):
for k in self.parameters: for k in self.parameters:
@@ -90,10 +96,16 @@ class entity(facet):
if self.predefinedtype and hasattr(inst, "PredefinedType"): if self.predefinedtype and hasattr(inst, "PredefinedType"):
# logger.debug("Testing if entity predefinedtype '%s' == '%s'", inst.PredefinedType, self.predefinedtype) # logger.debug("Testing if entity predefinedtype '%s' == '%s'", inst.PredefinedType, self.predefinedtype)
self.message = "an entity name '%(name)s' of predefined type '%(predefinedtype)s'" self.message = "an entity name '%(name)s' of predefined type '%(predefinedtype)s'"
return facet_evaluation(inst.is_a(self.name) and inst.PredefinedType == self.predefinedtype, self.message % {"name": inst.is_a(), "predefinedtype": inst.PredefinedType}) return facet_evaluation(
inst.is_a(self.name) and inst.PredefinedType == self.predefinedtype,
self.message % {"name": inst.is_a(), "predefinedtype": inst.PredefinedType}
)
else: else:
self.message = "an entity name '%(name)s'" self.message = "an entity name '%(name)s'"
return facet_evaluation(inst.is_a(self.name), self.message % {"name": inst.is_a()}) return facet_evaluation(
inst.is_a(self.name),
self.message % {"name": inst.is_a()}
)
class classification(facet): class classification(facet):
@@ -124,8 +136,6 @@ class property(facet):
""" """
parameters = ["name", "propertyset", "value"] parameters = ["name", "propertyset", "value"]
# import pdb;pdb.set_trace()
message = "a property '%(name)s' in '%(propertyset)s' with value '%(value)s'" message = "a property '%(name)s' in '%(propertyset)s' with value '%(value)s'"
def __call__(self, inst, logger): def __call__(self, inst, logger):
@@ -212,19 +222,24 @@ class restriction:
self.options = [] self.options = []
self.type = [] self.type = []
for n in node.childNodes: #TODO implement restrictions
if n.nodeType == n.ELEMENT_NODE and n.tagName.endswith("enumeration"): # for n in node.childNodes:
self.options.append(n.getAttribute("value")) # if n.nodeType == n.ELEMENT_NODE and n.tagName.endswith("enumeration"):
self.type = "enumeration" # self.options.append(n.getAttribute("value"))
elif n.nodeType == n.ELEMENT_NODE and (n.tagName.endswith("Inclusive") or n.tagName.endswith("Exclusive")): # self.type = "enumeration"
self.options.append(n.getAttribute("value")) # elif n.nodeType == n.ELEMENT_NODE and (n.tagName.endswith("Inclusive") or n.tagName.endswith("Exclusive")):
self.type = "bounds" # self.options.append(n.getAttribute("value"))
elif n.nodeType == n.ELEMENT_NODE and n.tagName.endswith("length"): # self.type = "bounds"
self.options.append(n.getAttribute("value")) # elif n.nodeType == n.ELEMENT_NODE and n.tagName.endswith("length"):
self.type = "length" # self.options.append(n.getAttribute("value"))
elif n.nodeType == n.ELEMENT_NODE and n.tagName.endswith("pattern"): # self.type = "length"
self.options.append(n.getAttribute("value")) # elif n.nodeType == n.ELEMENT_NODE and n.tagName.endswith("pattern"):
self.type = "pattern" # self.options.append(n.getAttribute("value"))
# self.type = "pattern"
#TODO add min/maxLength
#TODO add fractionDigits
#TODO add totalDigits
#TODO add whiteSpace
def __eq__(self, other): def __eq__(self, other):
return other in self.options return other in self.options
@@ -248,27 +263,22 @@ class specification:
def __init__(self, node): def __init__(self, node):
def parse_rules(node): def parse_rules(node):
children = [n for n in node.childNodes if n.nodeType == n.ELEMENT_NODE] children = [node[n][0] for n in node]
names = map(operator.attrgetter("tagName"), children) names = [n for n in node]
classes = map(meta_facet.facets.__getitem__, names) classes = map(meta_facet.facets.__getitem__, names)
return [cls(n) for cls, n in zip(classes, children)] return [cls(n) for cls, n in zip(classes, children)]
phrases = [n for n in node.childNodes if n.nodeType == n.ELEMENT_NODE] self.applicability = boolean_and(parse_rules(node['applicability']))
self.requirements = boolean_and(parse_rules(node['requirements']))
len(phrases) == 2 or error("expected two child nodes for <specification>")
phrases[0].tagName == "applicability" or error("expected <applicability>")
phrases[1].tagName == "requirements" or error("expected <requirements>")
self.applicability, self.requirements = (boolean_and(parse_rules(phrase)) for phrase in phrases)
def __call__(self, inst, logger): def __call__(self, inst, logger):
if self.applicability(inst, logger): if self.applicability(inst, logger):
valid = self.requirements(inst, logger) valid = self.requirements(inst, logger)
if valid: if valid:
logger.info({'guid':inst.GlobalId, 'result':valid.success,'sentence':str(self) + "\n'" + inst.Name + "' (id:" + inst.GlobalId + ") has " + str(valid) + " so is compliant"}) logger.info({'guid':inst.GlobalId, 'result':valid.success,'sentence':str(self) + "\n" + inst.is_a() + " '" + str(inst.Name) + "' (#" + str(inst.id()) + ") has " + str(valid) + " so is compliant"})
else: else:
logger.error({'guid':inst.GlobalId, 'result':valid.success, 'sentence':str(self) + "\n'" + inst.Name + "' (id:" + inst.GlobalId + ") has " + str(valid) + " so is not compliant"}) logger.error({'guid':inst.GlobalId, 'result':valid.success, 'sentence':str(self) + "\n" + inst.is_a() + " '" + str(inst.Name) + "' (#" + str(inst.id()) + ") has " + str(valid) + " so is not compliant"})
def __str__(self): def __str__(self):
return "Given an instance with %(applicability)s\nWe expect %(requirements)s" % self.__dict__ return "Given an instance with %(applicability)s\nWe expect %(requirements)s" % self.__dict__
@@ -283,13 +293,8 @@ class ids:
ids_schema = XMLSchema("http://standards.buildingsmart.org/IDS/ids.xsd") ids_schema = XMLSchema("http://standards.buildingsmart.org/IDS/ids.xsd")
ids_schema.validate(fn) ids_schema.validate(fn)
dom = parse(fn) ids = ids_schema.to_dict(fn)
ids = dom.childNodes[0] self.specifications = [specification(s) for s in ids['specification']]
ids.tagName == "ids" or error("expected <ids>")
self.specifications = [
specification(n) for n in ids.childNodes if n.nodeType == n.ELEMENT_NODE and n.tagName == "specification"
]
def validate(self, ifc_file, logger): def validate(self, ifc_file, logger):
for spec in self.specifications: for spec in self.specifications: