|
| 1 | +import sys |
| 2 | +import mvdxml_expression |
| 3 | + |
| 4 | +from xml.dom.minidom import parse, Element |
| 5 | + |
| 6 | +import sparql |
| 7 | + |
| 8 | +class rule(object): |
| 9 | + """ |
| 10 | + A class for representing an mvdXML EntityRule or AttributeRule |
| 11 | + """ |
| 12 | + |
| 13 | + def __init__(self, tag, attribute, nodes, bind=None, optional=False): |
| 14 | + self.tag, self.attribute, self.nodes, self.bind = tag, attribute, nodes, bind |
| 15 | + self.optional = optional |
| 16 | + |
| 17 | + def to_string(self, indent=0): |
| 18 | + return "%s%s%s[%s](%s%s)%s" % ("\n" if indent else "", " "*indent, self.tag, self.attribute, "".join(n.to_string(indent+2) for n in self.nodes), ("\n" + " "*indent) if len(self.nodes) else "", (" -> %s" % self.bind) if self.bind else "") |
| 19 | + |
| 20 | + def __repr__(self): |
| 21 | + return self.to_string() |
| 22 | + |
| 23 | +class template(object): |
| 24 | + """ |
| 25 | + Representation of an mvdXML template |
| 26 | + """ |
| 27 | + |
| 28 | + def __init__(self, concept, root, params=None, rules=None): |
| 29 | + self.concept, self.root, self.params = concept, root, params |
| 30 | + self.rules = rules or [] |
| 31 | + self.entity = str(root.attributes['applicableEntity'].value) |
| 32 | + self.name = root.attributes['name'].value |
| 33 | + |
| 34 | + def bind(self, params): |
| 35 | + return template(self.concept, self.root, params, self.rules) |
| 36 | + |
| 37 | + def parse(self): |
| 38 | + for rules in self.root.childNodes: |
| 39 | + if not isinstance(rules, Element): continue |
| 40 | + |
| 41 | + for r in rules.childNodes: |
| 42 | + if not isinstance(r, Element): continue |
| 43 | + self.rules.append(self.parse_rule(r)) |
| 44 | + |
| 45 | + |
| 46 | + def traverse(self, fn, root=None, with_parents=False): |
| 47 | + def _(n, p=root, ps=[root]): |
| 48 | + if with_parents: |
| 49 | + close = fn(rule=n, parents=ps) |
| 50 | + else: |
| 51 | + close = fn(rule=n, parent=p) |
| 52 | + |
| 53 | + for s in n.nodes: |
| 54 | + _(s, n, ps + [n]) |
| 55 | + |
| 56 | + if close: |
| 57 | + close() |
| 58 | + |
| 59 | + for r in self.rules: |
| 60 | + _(r) |
| 61 | + |
| 62 | + def parse_rule(self, root): |
| 63 | + def visit(node, prefix=""): |
| 64 | + r = None |
| 65 | + n = node |
| 66 | + nm = None |
| 67 | + p = prefix |
| 68 | + optional=False |
| 69 | + |
| 70 | + if node.tagName == "AttributeRule": |
| 71 | + r = node.attributes["AttributeName"].value |
| 72 | + try: |
| 73 | + nm = node.attributes["RuleID"].value |
| 74 | + except: |
| 75 | + # without binding, it's wrapped in a SPARQL OPTIONAL {} clause |
| 76 | + # Aim is to insert this clause once as high in the stack as possible |
| 77 | + # All topmost attribute rules are optional anyway as in the binding requirements on existence is specified |
| 78 | + |
| 79 | + def child_has_ruleid_or_prefix(node): |
| 80 | + if type(node).__name__ == "Element": |
| 81 | + if "RuleID" in node.attributes or "IdPrefix" in node.attributes: |
| 82 | + return True |
| 83 | + for n in node.childNodes: |
| 84 | + if child_has_ruleid_or_prefix(n): return True |
| 85 | + |
| 86 | + optional = node.parentNode.tagName == "Rules" or not child_has_ruleid_or_prefix(node) |
| 87 | + elif node.tagName == "EntityRule": |
| 88 | + r = node.attributes["EntityName"].value |
| 89 | + elif node.tagName == "References": |
| 90 | + ref = node.getElementsByTagName("Template")[0].attributes['ref'].value |
| 91 | + n = self.concept.template(ref).root |
| 92 | + try: p = p + node.attributes["IdPrefix"].value |
| 93 | + except: pass |
| 94 | + |
| 95 | + def _(n): |
| 96 | + for subnode in n.childNodes: |
| 97 | + if not isinstance(subnode, Element): continue |
| 98 | + for x in visit(subnode, p): yield x |
| 99 | + |
| 100 | + if r: |
| 101 | + yield rule(node.tagName, r, list(_(n)), (p + nm) if nm else nm, optional=optional) |
| 102 | + else: |
| 103 | + for subnode in n.childNodes: |
| 104 | + if not isinstance(subnode, Element): continue |
| 105 | + for x in visit(subnode, p): yield x |
| 106 | + |
| 107 | + return list(visit(root))[0] |
| 108 | + |
| 109 | +class concept_or_applicability(object): |
| 110 | + """ |
| 111 | + Representation of either a mvdXML Concept or the Applicability node. Basically a structure |
| 112 | + for the hierarchical TemplateRule |
| 113 | + """ |
| 114 | + |
| 115 | + def __init__(self, root, c): |
| 116 | + self.root = root |
| 117 | + self.concept_node = c |
| 118 | + try: |
| 119 | + self.name = c.attributes["name"].value |
| 120 | + except: |
| 121 | + # probably applicability and not concept |
| 122 | + self.name = "Applicability" |
| 123 | + |
| 124 | + def template(self, id = None): |
| 125 | + if id is None: |
| 126 | + id = self.concept_node.getElementsByTagName("Template")[0].attributes['ref'].value |
| 127 | + |
| 128 | + for node in self.root.dom.getElementsByTagName("ConceptTemplate"): |
| 129 | + if node.attributes["uuid"].value == id: |
| 130 | + t = template(self, node) |
| 131 | + t.parse() |
| 132 | + t_with_rules = t.bind(self.rules()) |
| 133 | + return t_with_rules |
| 134 | + |
| 135 | + |
| 136 | + def rules(self): |
| 137 | + # Get the top most TemplateRule and traverse |
| 138 | + rules = self.concept_node.getElementsByTagName("TemplateRules")[0] |
| 139 | + |
| 140 | + def visit(rules): |
| 141 | + def _(): |
| 142 | + for i, r in enumerate([c for c in rules.childNodes if isinstance(c, Element)]): |
| 143 | + if i: |
| 144 | + yield rules.attributes["operator"].value |
| 145 | + if r.tagName == "TemplateRules": |
| 146 | + yield visit(r) |
| 147 | + elif r.tagName == "TemplateRule": |
| 148 | + yield mvdxml_expression.parse(r.attributes["Parameters"].value) |
| 149 | + else: |
| 150 | + raise Exception() |
| 151 | + |
| 152 | + return list(_()) |
| 153 | + |
| 154 | + return visit(rules) |
| 155 | + |
| 156 | +class concept_root(object): |
| 157 | + def __init__(self, dom, root): |
| 158 | + self.dom, self.root = dom, root |
| 159 | + self.name = root.attributes['name'].value |
| 160 | + self.entity = str(root.attributes['applicableRootEntity'].value) |
| 161 | + |
| 162 | + def applicability(self): |
| 163 | + return concept_or_applicability(self, self.root.getElementsByTagName("Applicability")[0]) |
| 164 | + |
| 165 | + def concepts(self): |
| 166 | + for c in self.root.getElementsByTagName("Concept"): |
| 167 | + yield concept_or_applicability(self, c) |
| 168 | + |
| 169 | + @staticmethod |
| 170 | + def parse(fn): |
| 171 | + dom = parse(fn) |
| 172 | + try: |
| 173 | + root = dom.getElementsByTagName("ConceptRoot")[0] |
| 174 | + CR = concept_root(dom, root) |
| 175 | + return CR |
| 176 | + except: |
| 177 | + root = dom.getElementsByTagName("ConceptTemplate")[0] |
| 178 | + t = template(None, root) |
| 179 | + t.parse() |
| 180 | + return t |
| 181 | + |
| 182 | + |
| 183 | +if __name__ == "__main__": |
| 184 | + |
| 185 | + if len(sys.argv) == 3: |
| 186 | + ttlfn, mvdfn = sys.argv[1:] |
| 187 | + sparql.derive_prefix(ttlfn) |
| 188 | + ttlfn = sparql.infer_subtypes(ttlfn) |
| 189 | + MVD = concept_root.parse(mvdfn) |
| 190 | + sparql.executor.run(MVD, mvdfn, ttlfn) |
| 191 | + |
| 192 | + else: |
| 193 | + mvdfn = sys.argv[1] |
| 194 | + MVD = concept_root.parse(mvdfn) |
| 195 | + |
| 196 | + def dump(rule, parents): |
| 197 | + print(" " * len(parents), rule.tag, rule.attribute) |
| 198 | + |
| 199 | + for c in MVD.concepts(): |
| 200 | + print(c.name) |
| 201 | + print() |
| 202 | + |
| 203 | + t = c.template() |
| 204 | + print("RootEntity", t.entity) |
| 205 | + t.traverse(dump, with_parents=True) |
| 206 | + print(" ".join(map(str, t.params))) |
| 207 | + |
| 208 | + print() |
0 commit comments