container.py 7.9 KB

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