Package RDFClosure :: Module Closure
[hide private]
[frames] | no frames]

Source Code for Module RDFClosure.Closure

  1  #!/d/Bin/Python/python.exe 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  """ 
  5  The generic superclasses for various rule based semantics and the possible extensions. 
  6   
  7  @requires: U{RDFLib<http://rdflib.net>}, 2.2.2. 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  """ 
 15  $Id: Closure.py,v 1.17 2009/09/20 11:45:30 ivan Exp $ $Date: 2009/09/20 11:45:30 $ 
 16  """ 
 17   
 18  __author__  = 'Ivan Herman' 
 19  __contact__ = 'Ivan Herman, ivan@w3.org' 
 20  __license__ = u'W3C® SOFTWARE NOTICE AND LICENSE, http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231' 
 21   
 22  from rdflib.BNode               import BNode 
 23  from rdflib.Literal             import Literal as rdflibLiteral 
 24  from rdflib.Namespace   import Namespace 
 25  from rdflib.RDF                 import RDFNS as ns_rdf 
 26  from rdflib.RDF                 import type 
 27   
 28  from RDFClosure.Literals        import LiteralProxies 
 29   
 30  debugGlobal             = False 
 31  offlineGeneration       = False 
 32   
 33  ###################################################################################################### 
 34   
35 -class Core :
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} occurence 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 - def __init__(self, graph, axioms, daxioms, rdfs = False) :
60 """ 61 @param graph: the RDF graph to be extended 62 @type graph: rdflib.Graph 63 @param axioms: whether axioms should be added or not 64 @type axioms: boolean 65 @param daxioms: whether datatype axioms should be added or not 66 @type daxioms: boolean 67 @param rdfs: whether RDFS inference is also done (used in subclassed only) 68 @type rdfs: boolean 69 """ 70 self._debug = debugGlobal 71 72 # Calculate the maximum 'n' value for the '_i' type predicates (see Horst's paper) 73 n = 1; 74 maxnum = 0 75 cont = True 76 while cont : 77 cont = False 78 predicate = ns_rdf[("_%d" % n)] 79 for (s,p,o) in graph.triples((None,predicate,None)) : 80 # there is at least one if we got here 81 maxnum = n 82 n += 1 83 cont = True 84 self.IMaxNum = maxnum 85 86 self.graph = graph 87 self.axioms = axioms 88 self.daxioms = daxioms 89 90 self.rdfs = rdfs 91 92 self.error_messages = [] 93 self.empty_stored_triples()
94
95 - def add_error(self,message) :
96 """ 97 Add an error message 98 @param message: error message 99 @type message: string 100 """ 101 if message not in self.error_messages : 102 self.error_messages.append(message)
103
104 - def pre_process(self) :
105 """ 106 Do some pre-processing step. This method before anything else in the closure. By default, this method is empty, subclasses 107 can add content to it by overriding it. 108 """ 109 pass
110
111 - def post_process(self) :
112 """ 113 Do some post-processing step. This method when all processing is done, but before handling possible 114 errors (ie, the method can add its own error messages). By default, this method is empty, subclasses 115 can add content to it by overriding it. 116 """ 117 pass
118
119 - def rules(self,t,cycle_num) :
120 """ 121 The core processing cycles through every tuple in the graph and dispatches it to the various methods implementing 122 a specific group of rules. By default, this method raises an exception; indeed, subclasses 123 I{must} add content to by overriding it. 124 @param t: one triple on which to apply the rules 125 @type t: tuple 126 @param cycle_num: which cycle are we in, starting with 1. This value is forwarded to all local rules; it is also used 127 locally to collect the bnodes in the graph. 128 """ 129 raise Exception("This method should not be called directly; subclasses should override it")
130
131 - def add_axioms(self) :
132 """ 133 Add axioms. 134 This is only a placeholder and raises an exception by default; subclasses I{must} fill this with real content 135 """ 136 raise Exception("This method should not be called directly; subclasses should override it")
137
138 - def add_d_axioms(self) :
139 """ 140 Add d axioms. 141 This is only a placeholder and raises an exception by default; subclasses I{must} fill this with real content 142 """ 143 raise Exception("This method should not be called directly; subclasses should override it")
144
145 - def one_time_rules(self) :
146 """ 147 This is only a placeholder; subclasses should fill this with real content. By default, it is just an empty call. 148 This set of rules is invoked only once and not in a cycle. 149 """ 150 pass
151
152 - def get_literal_value(self, node) :
153 """ 154 Return the literal value corresponding to a Literal node. Used in error messages. 155 @param node: literal node 156 @return: the literal value itself 157 """ 158 try : 159 return self.literal_proxies.bnode_to_lit[node].lex 160 except : 161 return "????"
162
163 - def empty_stored_triples(self) :
164 """ 165 Empty the internal store for triples 166 """ 167 self.added_triples = set()
168
169 - def flush_stored_triples(self) :
170 """ 171 Send the stored triples to the graph, and empty the container 172 """ 173 for t in self.added_triples : self.graph.add(t) 174 self.empty_stored_triples()
175
176 - def store_triple(self, t) :
177 """ 178 In contrast to its name, this does not yet add anything to the graph itself, it just stores the tuple in an 179 L{internal set<Core.added_triples>}. (It is important for this to be a set: some of the rules in the various closures may 180 generate the same tuples several times.) Before adding the tuple to the set, the method checks whether 181 the tuple is in the final graph already (if yes, it is not added to the set). 182 183 The set itself is emptied at the start of every processing cycle; the triples are then effectively added to the 184 graph at the end of such a cycle. If the set is 185 actually empty at that point, this means that the cycle has not added any new triple, and the full processing can stop. 186 187 @param t: the triple to be added to the graph, unless it is already there 188 @type t: a 3-element tuple of (s,p,o) 189 """ 190 (s,p,o) = t 191 if not( isinstance(s, rdflibLiteral) or isinstance(p, rdflibLiteral) ) and t not in self.graph : 192 if self._debug or offlineGeneration : print t 193 self.added_triples.add(t)
194
195 - def closure(self) :
196 """ 197 Generate the closure the graph. This is the real 'core'. 198 199 The processing rules store new triples via the L{separate method<store_triple>} which stores 200 them in the L{added_triples<added_triples>} array. If that array is emtpy at the end of a cycle, 201 it means that the whole process can be stopped. 202 203 If required, the relevant axiomatic triples are added to the graph before processing in cycles. Similarly 204 the exchange of literals against bnodes is also done in this step (and restored after all cycles are over). 205 """ 206 self.pre_process() 207 208 # Handling the axiomatic triples. In general, this means adding all tuples in the list that 209 # forwarded, and those include RDF or RDFS. In both cases the relevant parts of the container axioms should also 210 # be added. 211 if self.axioms : 212 self.add_axioms() 213 214 # Create the bnode proxy structure 215 self.literal_proxies = LiteralProxies(self.graph) 216 217 # Add the datatype axioms, if needed (note that this makes use of the literal proxies, the order of the call is important! 218 if self.daxioms : 219 self.add_d_axioms() 220 221 self.flush_stored_triples() 222 223 # Get first the 'one-time rules', ie, those that do not need an extra round in cycles down the line 224 self.one_time_rules() 225 self.flush_stored_triples() 226 227 # Go cyclically through all rules until no change happens 228 new_cycle = True 229 cycle_num = 0 230 error_messages = [] 231 while new_cycle : 232 # yes, there was a change, let us go again 233 cycle_num += 1 234 235 # DEBUG: print the cycle number out 236 if self._debug: print "----- Cycle #:%d" % cycle_num 237 238 # go through all rules, and collect the replies (to see whether any change has been done) 239 # the new triples to be added are collected separately not to interfere with 240 # the current graph yet 241 self.empty_stored_triples() 242 243 # Execute all the rules; these might fill up the added triples array 244 for t in self.graph : 245 self.rules(t, cycle_num) 246 247 # Add the tuples to the graph (if necessary, that is). If any new triple has been generated, a new cycle 248 # will be necessary... 249 new_cycle = len(self.added_triples) > 0 250 251 for t in self.added_triples : self.graph.add(t) 252 253 # All done, but we should restore the literals from their proxies 254 self.literal_proxies.restore() 255 256 self.post_process() 257 self.flush_stored_triples() 258 259 # Add possible error messages 260 if self.error_messages : 261 # I am not sure this is the right vocabulary to use for this purpose, but I haven't found anything! 262 # I could, of course, come up with my own, but I am not sure that would be kosher... 263 ERRNS = Namespace("http://www.daml.org/2002/03/agents/agent-ont#") 264 self.graph.bind("err","http://www.daml.org/2002/03/agents/agent-ont#") 265 for m in self.error_messages : 266 message = BNode() 267 self.graph.add((message,type,ERRNS['ErrorMessage'])) 268 self.graph.add((message,ERRNS['error'],rdflibLiteral(m)))
269