import sys from collections import defaultdict import os import rdflib from rdflib.plugins.sparql import prepareQuery import json # Work around Python 2 where a 'big integer' automatically becomes a long if sys.version > '3': # pragma: no cover integer_types = (int,) primitive_types = (int, float, str, bool) else: # pragma: no cover integer_types = (int, long) primitive_types = (int, long, float, str, bool, unicode) complex_primitives = frozenset(["if", "while", "assign", "call", "break", "continue", "return","resolve","access", "constant", "input", "output", "declare", "global"]) def instance_to_string(value): return value["value"] def string_to_instance(value): return {'value': value} class ModelverseState(object): def __init__(self, bootfile = None): self.graph = rdflib.Graph() self.mv = rdflib.Namespace("http://modelverse.mv/#") self.graph.bind("MV", self.mv) self.free_id = -1 self.prepared_queries = { "read_value": """ SELECT ?value WHERE { ?var1 MV:hasValue ?value . } """, "read_outgoing": """ SELECT ?link WHERE { ?link MV:hasSource ?var1 . } """, "read_incoming": """ SELECT ?link WHERE { ?link MV:hasTarget ?var1 . } """, "read_edge": """ SELECT ?source ?target WHERE { ?var1 MV:hasSource ?source ; MV:hasTarget ?target . } """, "read_dict_keys": """ SELECT ?key WHERE { ?main_edge MV:hasSource ?var1 . ?attr_edge MV:hasSource ?main_edge ; MV:hasTarget ?key . } """, "read_dict_node": """ SELECT ?value_node WHERE { ?main_edge MV:hasSource ?var1 ; MV:hasTarget ?value_node . ?attr_edge MV:hasSource ?main_edge ; MV:hasTarget ?var2 . } """, "read_dict_node_edge": """ SELECT ?main_edge WHERE { ?main_edge MV:hasSource ?var1 . ?attr_edge MV:hasSource ?main_edge ; MV:hasTarget ?var2 . } """, "delete_node": """ SELECT ?edge WHERE { { ?edge MV:hasTarget ?var1 . } UNION { ?edge MV:hasSource ?var1 . } } """, "delete_edge": """ SELECT ?edge WHERE { { ?edge MV:hasTarget ?var1 . } UNION { ?edge MV:hasSource ?var1 . } } """, } for k, v in self.prepared_queries.iteritems(): self.prepared_queries[k] = prepareQuery(self.prepared_queries[k], initNs={"MV": self.mv}) self.root = self.parse(bootfile) def parse(self, filename): symbols = {} def resolve(symb): try: return int(symb) except: if symb[0] == "?": derefs = symb[1:].split("/") v, _ = self.read_dict(symbols["root"], "__hierarchy") for deref in derefs: v, _ = self.read_dict(v, deref) return v else: return symbols[symb] with open(filename, 'r') as f: for line in f: element_type, constructor = line.split(None, 1) name, values = constructor.split("(", 1) name = name.split()[0] values, _ = values.rsplit(")", 1) if element_type == "Node": if values == "": symbols[name] = self.create_node() else: value = values if value in complex_primitives: value = string_to_instance(value) else: value = eval(value) symbols[name] = self.create_nodevalue(value) elif element_type == "Edge": values = [v.split()[0] for v in values.split(",")] symbols[name] = self.create_edge(resolve(values[0]), resolve(values[1])) else: raise Exception("Unknown element type: %s" % element_type) return symbols["root"] def read_root(self): return self.root def create_node(self): self.free_id += 1 return rdflib.URIRef("http://modelverse.mv/#%s" % self.free_id) def create_edge(self, source, target): if not isinstance(source, rdflib.URIRef): return None elif not isinstance(target, rdflib.URIRef): return None self.free_id += 1 edge = rdflib.URIRef("http://modelverse.mv/#%s" % self.free_id) self.graph.add((edge, self.mv.hasSource, source)) self.graph.add((edge, self.mv.hasTarget, target)) return edge def is_valid_datavalue(self, value): if isinstance(value, dict): if "value" in value and value["value"] in complex_primitives: return True else: return False elif not isinstance(value, primitive_types): return False elif isinstance(value, integer_types) and not (-2**63 <= value <= 2**64 - 1): return False return True def create_nodevalue(self, value): if not self.is_valid_datavalue(value): return None self.free_id += 1 node = rdflib.URIRef("http://modelverse.mv/#%s" % self.free_id) self.graph.add((node, self.mv.hasValue, rdflib.Literal(json.dumps(value)))) return node def create_dict(self, source, data, destination): if not isinstance(source, rdflib.URIRef): return None if not isinstance(destination, rdflib.URIRef): return None if not self.is_valid_datavalue(data): return None n = self.create_nodevalue(data)[0] e = self.create_edge(source, destination)[0] self.create_edge(e, n) return None def read_value(self, node): if not isinstance(node, rdflib.URIRef): return None result = self.graph.query(self.prepared_queries["read_value"], initBindings={"var1": node}) if len(result) == 0: return None return json.loads(list(result)[0][0]) def read_outgoing(self, elem): if not isinstance(elem, rdflib.URIRef): return None result = self.graph.query(self.prepared_queries["read_outgoing"], initBindings={"var1": elem}) return [i[0] for i in result] def read_incoming(self, elem): if not isinstance(elem, rdflib.URIRef): return None result = self.graph.query(self.prepared_queries["read_incoming"], initBindings={"var1": elem}) return [i[0] for i in result] def read_edge(self, edge): result = self.graph.query(self.prepared_queries["read_edge"], initBindings={"var1": edge}) if len(result) == 0: return [None, None] else: return [list(result)[0][0], list(result)[0][1]] def read_dict(self, node, value): if not isinstance(node, rdflib.URIRef): return None if not self.is_valid_datavalue(value): return None q = """ SELECT ?value_node WHERE { ?main_edge MV:hasSource <%s> ; MV:hasTarget ?value_node . ?attr_edge MV:hasSource ?main_edge ; MV:hasTarget ?attr_node . ?attr_node MV:hasValue '%s' . } """ % (node, json.dumps(value)) result = self.graph.query(q) if len(result) == 0: return None if len(result) != 1: raise Exception("Error!") return list(result)[0][0] def read_dict_keys(self, node): if not isinstance(node, rdflib.URIRef): return None result = self.graph.query(self.prepared_queries["read_dict_keys"], initBindings={"var1": node}) return [i[0] for i in result] def read_dict_edge(self, node, value): if not isinstance(node, rdflib.URIRef): return None if not self.is_valid_datavalue(value): return None result = self.graph.query( """ SELECT ?main_edge WHERE { ?main_edge MV:hasSource <%s> ; MV:hasTarget ?value_node . ?attr_edge MV:hasSource ?main_edge ; MV:hasTarget ?attr_node . ?attr_node MV:hasValue '%s' . } """ % (node, json.dumps(value))) if len(result) == 0: return None return list(result)[0][0] def read_dict_node(self, node, value_node): if not isinstance(node, rdflib.URIRef): return None result = self.graph.query(self.prepared_queries["read_dict_node"], initBindings={"var1": node, "var2": value_node}) if len(result) == 0: return None return list(result)[0][0] def read_dict_node_edge(self, node, value_node): if not isinstance(node, rdflib.URIRef): return None result = self.graph.query(self.prepared_queries["read_dict_node_edge"], initBindings={"var1": node, "var2": value_node}) if len(result) == 0: return None return list(result)[0][0] def read_reverse_dict(self, node, value): if not isinstance(node, rdflib.URIRef): return None if not self.is_valid_datavalue(value): return None result = self.graph.query( """ SELECT ?source_node WHERE { ?main_edge MV:hasTarget <%s> ; MV:hasSource ?source_node . ?attr_edge MV:hasSource ?main_edge ; MV:hasTarget ?value_node . ?value_node MV:hasValue '%s' . } """ % (node, json.dumps(value))) return [i[0] for i in result] def delete_node(self, node): if node == self.root: return None if not isinstance(node, rdflib.URIRef): return None # Remove its value if it exists self.graph.remove((node, None, None)) # Get all edges connecting this result = self.graph.query(self.prepared_queries["delete_node"], initBindings={"var1": node}) # ... and remove them print("Got related nodes: " + str(len(result))) for e in result: self.delete_edge(e[0]) return None def delete_edge(self, edge): if not isinstance(edge, rdflib.URIRef): return None # Remove its links self.graph.remove((edge, None, None)) # Get all edges connecting this result = self.graph.query(self.prepared_queries["delete_edge"], initBindings={"var1": edge}) # ... and remove them for e in result: self.delete_edge(e[0]) return None def garbage_collect(self): pass def purge(self): pass