1
2
3 """
4 Separate module to handle literals.
5
6 The issue is that pure literals cannot appear in subject position according to the current rules on RDF. That means that
7 different types of conclusions cannot properly finish. The present trick is trying to get around the problem as follows:
8
9 1. For all literals in the graph a bnode is created. The module does not do a full D entailment but just relies on RDFLib's ability to recognize identical literals
10 2. All those bnodes get a type Literal
11 3. All triples with literals are exchanged against a triple with the associated bnode
12
13 The inferences are then calculated with the modified Graph. At the end of the process, the above steps are done backwards: for all triples where
14 a bnode representing a literal appear in object position, a triple is generated; however, all triples where the bnode appears in a
15 subject position are removed from the final graph.
16
17
18 @requires: U{RDFLib<https://github.com/RDFLib/rdflib>}, 4.0.0 and higher
19 @license: This software is available for use under the U{W3C Software License<http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231>}
20 @organization: U{World Wide Web Consortium<http://www.w3.org>}
21 @author: U{Ivan Herman<a href="http://www.w3.org/People/Ivan/">}
22
23 """
24
25 __author__ = 'Ivan Herman'
26 __contact__ = 'Ivan Herman, ivan@w3.org'
27 __license__ = u'W3C® SOFTWARE NOTICE AND LICENSE, http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231'
28
29 from rdflib import BNode
30 from rdflib import Literal as rdflibLiteral
31 from rdflib.namespace import XSD as ns_xsd
32
33 from .RDFS import type
34 from .RDFS import Literal
35 from .DatatypeHandling import AltXSDToPYTHON
36
37
39 """This class serves as a wrapper around rdflib's Literal, by changing the equality function to a strict
40 identity of datatypes and lexical values.
41
42 On the other hand, to implement, eg, OWL RL's datatype rules, a should be able to generate
43 an 'a sameAs b' triple, ie, the distinction should be kept. Hence this class that overrides the equality,
44 and then can be used as a key in a Python dictionary.
45 """
46
47
49 self.lit = lit
50 self.lex = str(lit)
51 self.dt = lit.datatype
52 self.lang = lit.language
53 self.value = lit.value
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
77 """Compare to literal structure instances for equality. Here equality means in the sense of datatype values
78 @return: comparison result
79 @rtype: boolean
80 """
81 try:
82 return self.lit == other.lit
83 except:
84
85 return False
86
88 if other is None :
89 return False
90 else :
91 return self.lex == other.lex and self.dt == other.dt and self.lang == other.lang
92
94 return not self.__eq__(other)
95
97 if self.dt is not None :
98 return hash(self.lit) ^ hash(self.dt)
99 else :
100 return hash(self.lit)
101
103 retval = ""
104 retval += "lexical value: %s; " % self.lex
105 retval += "datatype: %s; " % self.dt
106 retval += "language tag: %s; " % self.lang
107 return retval
108
109
111
113 """
114 @param graph: the graph to be modified
115 """
116 self.lit_to_bnode = {}
117 self.bnode_to_lit = {}
118 self.graph = graph
119
120 to_be_removed = []
121 to_be_added = []
122 for t in self.graph :
123 (subj, pred, obj) = t
124
125 if isinstance(obj, rdflibLiteral):
126
127 if obj.datatype:
128 try:
129 AltXSDToPYTHON[obj.datatype](str(obj))
130 except ValueError:
131 closure.add_error("Lexical value of the literal '%s' does not match its datatype (%s)" % (str(obj), obj.datatype))
132
133
134 if t not in to_be_removed:
135 to_be_removed.append(t)
136
137 obj_st = _LiteralStructure(obj)
138 found = False
139 for l in self.lit_to_bnode.keys() :
140 if obj_st.lex == l.lex and obj_st.dt == l.dt and obj_st.lang == l.lang :
141 t1 = (subj, pred, self.lit_to_bnode[l])
142 to_be_added.append(t1)
143 found = True
144 break
145 if not found:
146
147 bn = BNode()
148
149 self.lit_to_bnode[obj_st] = bn
150 self.bnode_to_lit[bn] = obj_st
151
152 to_be_added.append((subj, pred, bn))
153 to_be_added.append((bn, type, Literal))
154
155
156 if obj_st.dt is None and obj_st.lang is None:
157 newLit = rdflibLiteral(obj_st.lex, datatype=ns_xsd["string"])
158 new_obj_st = _LiteralStructure(newLit)
159 new_obj_st.dt = ns_xsd["string"]
160 bn2 = BNode()
161 self.lit_to_bnode[new_obj_st] = bn2
162 self.bnode_to_lit[bn2] = new_obj_st
163 to_be_added.append((subj, pred, bn2))
164 to_be_added.append((bn2, type, Literal))
165 elif obj_st.dt == ns_xsd["string"]:
166 newLit = rdflibLiteral(obj_st.lex, datatype=None)
167 new_obj_st = _LiteralStructure(newLit)
168
169 new_obj_st.dt = None
170 bn2 = BNode()
171 self.lit_to_bnode[new_obj_st] = bn2
172 self.bnode_to_lit[bn2] = new_obj_st
173 to_be_added.append((subj, pred, bn2))
174 to_be_added.append((bn2, type, Literal))
175
176
177 self._massageGraph(to_be_removed, to_be_added)
178
180 """
181 This method is to be invoked at the end of the forward chain processing. It restores literals (whenever possible)
182 to their original self...
183 """
184 to_be_removed = []
185 to_be_added = []
186 for t in self.graph :
187 (subj, pred, obj) = t
188
189 if subj in self.bnode_to_lit :
190
191
192
193 if t not in to_be_removed:
194 to_be_removed.append(t)
195 elif obj in self.bnode_to_lit:
196
197 if t not in to_be_removed:
198 to_be_removed.append(t)
199
200
201 lit = self.bnode_to_lit[obj].lit
202 if lit.datatype is not None and lit.datatype == ns_xsd["string"]:
203 lit = rdflibLiteral(str(lit))
204 to_be_added.append((subj, pred, lit))
205
206
207 self._massageGraph(to_be_removed, to_be_added)
208
210 """
211 Perform the removal and addition actions on the graph
212 @param to_be_removed: list of tuples to be removed
213 @param to_be_added : list of tuples to be added
214 """
215 for t in to_be_removed:
216 self.graph.remove(t)
217 for t in to_be_added:
218 self.graph.add(t)
219