1
2
3
4 """
5 The generic superclasses for various rule based semantics and the possible extensions.
6
7 @requires: U{RDFLib<https://github.com/RDFLib/rdflib>}, 4.0.0 and higher
8 @license: This software is available for use under the U{W3C Software License<http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231>}
9 @organization: U{World Wide Web Consortium<http://www.w3.org>}
10 @author: U{Ivan Herman<a href="http://www.w3.org/People/Ivan/">}
11
12 """
13
14 __author__ = 'Ivan Herman'
15 __contact__ = 'Ivan Herman, ivan@w3.org'
16 __license__ = u'W3C® SOFTWARE NOTICE AND LICENSE, http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231'
17
18 import rdflib
19 from rdflib import BNode
20 from rdflib import Literal as rdflibLiteral
21 from rdflib import Namespace
22
23
24 from RDFClosure.RDFS import RDFNS as ns_rdf
25 from RDFClosure.RDFS import type
26
27 from RDFClosure.Literals import LiteralProxies
28
29 debugGlobal = False
30 offlineGeneration = False
31
32
33
34
36 """Core of the semantics management, dealing with the RDFS and other Semantic triples. The only
37 reason to have it in a separate class is for an easier maintainability.
38
39 This is a common superclass only. In the present module, it is subclassed by
40 a L{RDFS Closure<RDFClosure.RDFSClosure.RDFS_Semantics>} class and a L{OWL RL Closure<RDFClosure.OWLRL.OWLRL_Semantics>} classes.
41 There are some methods that are implemented in the subclasses only, ie, this class cannot be used by itself!
42
43 @ivar IMaxNum: maximal index of C{rdf:_i} occurrence in the graph
44 @ivar literal_proxies: L{Literal Proxies with BNodes<RDFClosure.Literals.LiteralProxies>} for the graph
45 @type literal_proxies: L{LiteralProxies<RDFClosure.Literals.LiteralProxies>}
46 @ivar graph: the real graph
47 @type graph: rdflib.Graph
48 @ivar axioms: whether axioms should be added or not
49 @type axioms: boolean
50 @ivar daxioms: whether datatype axioms should be added or not
51 @type daxioms: boolean
52 @ivar added_triples: triples added to the graph, conceptually, during one processing cycle
53 @type added_triples: set of triples
54 @ivar error_messages: error messages (typically inconsistency messages in OWL RL) found during processing. These are added to the final graph at the very end as separate BNodes with error messages
55 @type error_messages: array of strings
56 @ivar rdfs: whether RDFS inference is also done (used in subclassed only)
57 @type rdfs: boolean
58 """
59
60 - def __init__(self, graph, axioms, daxioms, rdfs=False):
61 """
62 @param graph: the RDF graph to be extended
63 @type graph: rdflib.Graph
64 @param axioms: whether axioms should be added or not
65 @type axioms: boolean
66 @param daxioms: whether datatype axioms should be added or not
67 @type daxioms: boolean
68 @param rdfs: whether RDFS inference is also done (used in subclassed only)
69 @type rdfs: boolean
70 """
71 self._debug = debugGlobal
72
73
74 n = 1
75 maxnum = 0
76 cont = True
77 while cont:
78 cont = False
79 predicate = ns_rdf[("_%d" % n)]
80 for (s, p, o) in graph.triples((None, predicate, None)):
81
82 maxnum = n
83 n += 1
84 cont = True
85 self.IMaxNum = maxnum
86
87 self.graph = graph
88 self.axioms = axioms
89 self.daxioms = daxioms
90
91 self.rdfs = rdfs
92
93 self.error_messages = []
94 self.empty_stored_triples()
95
97 """
98 Add an error message
99 @param message: error message
100 @type message: string
101 """
102 if message not in self.error_messages:
103 self.error_messages.append(message)
104
106 """
107 Do some pre-processing step. This method before anything else in the closure. By default, this method is empty, subclasses
108 can add content to it by overriding it.
109 """
110 pass
111
112 - def post_process(self):
113 """
114 Do some post-processing step. This method when all processing is done, but before handling possible
115 errors (ie, the method can add its own error messages). By default, this method is empty, subclasses
116 can add content to it by overriding it.
117 """
118 pass
119
120 - def rules(self,t,cycle_num):
121 """
122 The core processing cycles through every tuple in the graph and dispatches it to the various methods implementing
123 a specific group of rules. By default, this method raises an exception; indeed, subclasses
124 I{must} add content to by overriding it.
125 @param t: one triple on which to apply the rules
126 @type t: tuple
127 @param cycle_num: which cycle are we in, starting with 1. This value is forwarded to all local rules; it is also used
128 locally to collect the bnodes in the graph.
129 """
130 raise Exception("This method should not be called directly; subclasses should override it")
131
133 """
134 Add axioms.
135 This is only a placeholder and raises an exception by default; subclasses I{must} fill this with real content
136 """
137 raise Exception("This method should not be called directly; subclasses should override it")
138
140 """
141 Add d axioms.
142 This is only a placeholder and raises an exception by default; subclasses I{must} fill this with real content
143 """
144 raise Exception("This method should not be called directly; subclasses should override it")
145
147 """
148 This is only a placeholder; subclasses should fill this with real content. By default, it is just an empty call.
149 This set of rules is invoked only once and not in a cycle.
150 """
151 pass
152
153
155 """
156 Return the literal value corresponding to a Literal node. Used in error messages.
157 @param node: literal node
158 @return: the literal value itself
159 """
160 try:
161 return self.literal_proxies.bnode_to_lit[node].lex
162 except:
163 return "????"
164
165
167 """
168 Empty the internal store for triples
169 """
170 self.added_triples = set()
171
173 """
174 Send the stored triples to the graph, and empty the container
175 """
176 for t in self.added_triples:
177 self.graph.add(t)
178 self.empty_stored_triples()
179
181 """
182 In contrast to its name, this does not yet add anything to the graph itself, it just stores the tuple in an
183 L{internal set<Core.added_triples>}. (It is important for this to be a set: some of the rules in the various closures may
184 generate the same tuples several times.) Before adding the tuple to the set, the method checks whether
185 the tuple is in the final graph already (if yes, it is not added to the set).
186
187 The set itself is emptied at the start of every processing cycle; the triples are then effectively added to the
188 graph at the end of such a cycle. If the set is
189 actually empty at that point, this means that the cycle has not added any new triple, and the full processing can stop.
190
191 @param t: the triple to be added to the graph, unless it is already there
192 @type t: a 3-element tuple of (s,p,o)
193 """
194 (s, p, o) = t
195 if not(isinstance(s, rdflibLiteral) or isinstance(p, rdflibLiteral)) and t not in self.graph:
196 if self._debug or offlineGeneration:
197 print t
198 self.added_triples.add(t)
199
200
202 """
203 Generate the closure the graph. This is the real 'core'.
204
205 The processing rules store new triples via the L{separate method<store_triple>} which stores
206 them in the L{added_triples<added_triples>} array. If that array is emtpy at the end of a cycle,
207 it means that the whole process can be stopped.
208
209 If required, the relevant axiomatic triples are added to the graph before processing in cycles. Similarly
210 the exchange of literals against bnodes is also done in this step (and restored after all cycles are over).
211 """
212 self.pre_process()
213
214
215
216
217 if self.axioms:
218 self.add_axioms()
219
220
221 self.literal_proxies = LiteralProxies(self.graph, self)
222
223
224 if self.daxioms:
225 self.add_d_axioms()
226
227 self.flush_stored_triples()
228
229
230 self.one_time_rules()
231 self.flush_stored_triples()
232
233
234 new_cycle = True
235 cycle_num = 0
236 while new_cycle:
237
238 cycle_num += 1
239
240
241 if self._debug:
242 print "----- Cycle #:%d" % cycle_num
243
244
245
246
247 self.empty_stored_triples()
248
249
250 for t in self.graph:
251 self.rules(t, cycle_num)
252
253
254
255 new_cycle = len(self.added_triples) > 0
256
257 for t in self.added_triples:
258 self.graph.add(t)
259
260
261 self.literal_proxies.restore()
262
263 self.post_process()
264 self.flush_stored_triples()
265
266
267 if self.error_messages:
268
269
270 ERRNS = Namespace("http://www.daml.org/2002/03/agents/agent-ont#")
271 self.graph.bind("err","http://www.daml.org/2002/03/agents/agent-ont#")
272 for m in self.error_messages:
273 message = BNode()
274 self.graph.add((message, type, ERRNS['ErrorMessage']))
275 self.graph.add((message, ERRNS['error'], rdflibLiteral(m)))
276