|
| 1 | +from rdflib.namespace import RDF |
| 2 | +from rdflib.term import BNode |
| 3 | +from rdflib import URIRef |
| 4 | +from random import randint |
| 5 | + |
| 6 | +__all__ = ["Container", "Bag", "Seq", "Alt", "NoElementException"] |
| 7 | + |
| 8 | + |
| 9 | +class Container(object): |
| 10 | + """A class for constructing RDF containers, as per https://www.w3.org/TR/rdf11-mt/#rdf-containers |
| 11 | +
|
| 12 | + Basic usage, creating a ``Bag`` and adding to it:: |
| 13 | +
|
| 14 | + >>> from rdflib import Graph, BNode, Literal, Bag |
| 15 | + >>> g = Graph() |
| 16 | + >>> b = Bag(g, BNode(), [Literal("One"), Literal("Two"), Literal("Three")]) |
| 17 | + >>> print(g.serialize(format="turtle").decode()) |
| 18 | + @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . |
| 19 | + <BLANKLINE> |
| 20 | + [] a rdf:Bag ; |
| 21 | + rdf:_1 "One" ; |
| 22 | + rdf:_2 "Two" ; |
| 23 | + rdf:_3 "Three" . |
| 24 | + <BLANKLINE> |
| 25 | + <BLANKLINE> |
| 26 | +
|
| 27 | + >>> # print out an item using an index reference |
| 28 | + >>> print(b[2]) |
| 29 | + Two |
| 30 | +
|
| 31 | + >>> # add a new item |
| 32 | + >>> b.append(Literal("Hello")) |
| 33 | + >>> print(g.serialize(format="turtle").decode()) |
| 34 | + @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . |
| 35 | + <BLANKLINE> |
| 36 | + [] a rdf:Bag ; |
| 37 | + rdf:_1 "One" ; |
| 38 | + rdf:_2 "Two" ; |
| 39 | + rdf:_3 "Three" ; |
| 40 | + rdf:_4 "Hello" . |
| 41 | + <BLANKLINE> |
| 42 | + <BLANKLINE> |
| 43 | +
|
| 44 | + """ |
| 45 | + |
| 46 | + def __init__(self, graph, uri, seq=[], rtype="Bag"): |
| 47 | + """Creates a Container |
| 48 | +
|
| 49 | + :param graph: a Graph instance |
| 50 | + :param uri: URI or Blank Node of the Container |
| 51 | + :param seq: the elements of the Container |
| 52 | + :param rtype: the type of Container, one of "Bag", "Seq" or "Alt" |
| 53 | + """ |
| 54 | + |
| 55 | + self.graph = graph |
| 56 | + self.uri = uri or BNode() |
| 57 | + self._len = 0 |
| 58 | + self._rtype = rtype # rdf:Bag or rdf:Seq or rdf:Alt |
| 59 | + |
| 60 | + self.append_multiple(seq) |
| 61 | + |
| 62 | + # adding triple corresponding to container type |
| 63 | + self.graph.add((self.uri, RDF.type, RDF[self._rtype])) |
| 64 | + |
| 65 | + def n3(self): |
| 66 | + |
| 67 | + items = [] |
| 68 | + for i in range(len(self)): |
| 69 | + |
| 70 | + v = self[i + 1] |
| 71 | + items.append(v) |
| 72 | + |
| 73 | + return "( %s )" % " ".join([a.n3() for a in items]) |
| 74 | + |
| 75 | + def _get_container(self): |
| 76 | + """Returns the URI of the container""" |
| 77 | + |
| 78 | + return self.uri |
| 79 | + |
| 80 | + def __len__(self): |
| 81 | + """Number of items in container""" |
| 82 | + |
| 83 | + return self._len |
| 84 | + |
| 85 | + def type_of_conatiner(self): |
| 86 | + return self._rtype |
| 87 | + |
| 88 | + def index(self, item): |
| 89 | + """Returns the 1-based numerical index of the item in the container""" |
| 90 | + |
| 91 | + pred = self.graph.predicates(self.uri, item) |
| 92 | + if not pred: |
| 93 | + raise ValueError("%s is not in %s" % (item, "container")) |
| 94 | + LI_INDEX = URIRef(str(RDF) + "_") |
| 95 | + |
| 96 | + i = None |
| 97 | + for p in pred: |
| 98 | + i = int(p.replace(LI_INDEX, "")) |
| 99 | + return i |
| 100 | + |
| 101 | + def __getitem__(self, key): |
| 102 | + """Returns item of the container at index key""" |
| 103 | + |
| 104 | + c = self._get_container() |
| 105 | + |
| 106 | + assert isinstance(key, int) |
| 107 | + elem_uri = str(RDF) + "_" + str(key) |
| 108 | + if key <= 0 or key > len(self): |
| 109 | + raise KeyError(key) |
| 110 | + v = self.graph.value(c, URIRef(elem_uri)) |
| 111 | + if v: |
| 112 | + return v |
| 113 | + else: |
| 114 | + raise KeyError(key) |
| 115 | + |
| 116 | + def __setitem__(self, key, value): |
| 117 | + """Sets the item at index key or predicate rdf:_key of the container to value""" |
| 118 | + |
| 119 | + assert isinstance(key, int) |
| 120 | + |
| 121 | + c = self._get_container() |
| 122 | + elem_uri = str(RDF) + "_" + str(key) |
| 123 | + if key <= 0 or key > len(self): |
| 124 | + raise KeyError(key) |
| 125 | + |
| 126 | + self.graph.set((c, URIRef(elem_uri), value)) |
| 127 | + |
| 128 | + def __delitem__(self, key): |
| 129 | + """Removing the item with index key or predicate rdf:_key""" |
| 130 | + |
| 131 | + assert isinstance(key, int) |
| 132 | + if key <= 0 or key > len(self): |
| 133 | + raise KeyError(key) |
| 134 | + |
| 135 | + graph = self.graph |
| 136 | + container = self.uri |
| 137 | + elem_uri = str(RDF) + "_" + str(key) |
| 138 | + graph.remove((container, URIRef(elem_uri), None)) |
| 139 | + for j in range(key + 1, len(self) + 1): |
| 140 | + elem_uri = str(RDF) + "_" + str(j) |
| 141 | + v = graph.value(container, URIRef(elem_uri)) |
| 142 | + graph.remove((container, URIRef(elem_uri), v)) |
| 143 | + elem_uri = str(RDF) + "_" + str(j - 1) |
| 144 | + graph.add((container, URIRef(elem_uri), v)) |
| 145 | + |
| 146 | + self._len -= 1 |
| 147 | + |
| 148 | + def items(self): |
| 149 | + """Returns a list of all items in the container""" |
| 150 | + |
| 151 | + l_ = [] |
| 152 | + container = self.uri |
| 153 | + i = 1 |
| 154 | + while True: |
| 155 | + elem_uri = str(RDF) + "_" + str(i) |
| 156 | + |
| 157 | + if (container, URIRef(elem_uri), None) in self.graph: |
| 158 | + i += 1 |
| 159 | + l_.append(self.graph.value(container, URIRef(elem_uri))) |
| 160 | + else: |
| 161 | + break |
| 162 | + return l_ |
| 163 | + |
| 164 | + def end(self): # |
| 165 | + |
| 166 | + # find end index (1-based) of container |
| 167 | + |
| 168 | + container = self.uri |
| 169 | + i = 1 |
| 170 | + while True: |
| 171 | + elem_uri = str(RDF) + "_" + str(i) |
| 172 | + |
| 173 | + if (container, URIRef(elem_uri), None) in self.graph: |
| 174 | + i += 1 |
| 175 | + else: |
| 176 | + return i - 1 |
| 177 | + |
| 178 | + def append(self, item): |
| 179 | + """Adding item to the end of the container""" |
| 180 | + |
| 181 | + end = self.end() |
| 182 | + elem_uri = str(RDF) + "_" + str(end + 1) |
| 183 | + container = self.uri |
| 184 | + self.graph.add((container, URIRef(elem_uri), item)) |
| 185 | + self._len += 1 |
| 186 | + |
| 187 | + def append_multiple(self, other): |
| 188 | + """Adding multiple elements to the container to the end which are in python list other""" |
| 189 | + |
| 190 | + end = self.end() # it should return the last index |
| 191 | + |
| 192 | + container = self.uri |
| 193 | + for item in other: |
| 194 | + |
| 195 | + end += 1 |
| 196 | + self._len += 1 |
| 197 | + elem_uri = str(RDF) + "_" + str(end) |
| 198 | + self.graph.add((container, URIRef(elem_uri), item)) |
| 199 | + |
| 200 | + def clear(self): |
| 201 | + """Removing all elements from the container""" |
| 202 | + |
| 203 | + container = self.uri |
| 204 | + graph = self.graph |
| 205 | + i = 1 |
| 206 | + while True: |
| 207 | + elem_uri = str(RDF) + "_" + str(i) |
| 208 | + if (container, URIRef(elem_uri), None) in self.graph: |
| 209 | + graph.remove((container, URIRef(elem_uri), None)) |
| 210 | + i += 1 |
| 211 | + else: |
| 212 | + break |
| 213 | + self._len = 0 |
| 214 | + |
| 215 | + |
| 216 | +class Bag(Container): |
| 217 | + """Unordered container (no preference order of elements)""" |
| 218 | + |
| 219 | + def __init__(self, graph, uri, seq=[]): |
| 220 | + Container.__init__(self, graph, uri, seq, "Bag") |
| 221 | + |
| 222 | + |
| 223 | +class Alt(Container): |
| 224 | + def __init__(self, graph, uri, seq=[]): |
| 225 | + Container.__init__(self, graph, uri, seq, "Alt") |
| 226 | + |
| 227 | + def anyone(self): |
| 228 | + if len(self) == 0: |
| 229 | + raise NoElementException() |
| 230 | + else: |
| 231 | + p = randint(1, len(self)) |
| 232 | + item = self.__getitem__(p) |
| 233 | + return item |
| 234 | + |
| 235 | + |
| 236 | +class Seq(Container): |
| 237 | + def __init__(self, graph, uri, seq=[]): |
| 238 | + Container.__init__(self, graph, uri, seq, "Seq") |
| 239 | + |
| 240 | + def add_at_position(self, pos, item): |
| 241 | + assert isinstance(pos, int) |
| 242 | + if pos <= 0 or pos > len(self) + 1: |
| 243 | + raise ValueError("Invalid Position for inserting element in rdf:Seq") |
| 244 | + |
| 245 | + if pos == len(self) + 1: |
| 246 | + self.append(item) |
| 247 | + else: |
| 248 | + for j in range(len(self), pos - 1, -1): |
| 249 | + container = self._get_container() |
| 250 | + elem_uri = str(RDF) + "_" + str(j) |
| 251 | + v = self.graph.value(container, URIRef(elem_uri)) |
| 252 | + self.graph.remove((container, URIRef(elem_uri), v)) |
| 253 | + elem_uri = str(RDF) + "_" + str(j + 1) |
| 254 | + self.graph.add((container, URIRef(elem_uri), v)) |
| 255 | + elem_uri_pos = str(RDF) + "_" + str(pos) |
| 256 | + self.graph.add((container, URIRef(elem_uri_pos), item)) |
| 257 | + self._len += 1 |
| 258 | + |
| 259 | + |
| 260 | +class NoElementException(Exception): |
| 261 | + def __init__(self, message="rdf:Alt Container is empty"): |
| 262 | + self.message = message |
| 263 | + |
| 264 | + def __str__(self): |
| 265 | + return self.message |
0 commit comments