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

Source Code for Module RDFClosure.Literals

  1  # -*- coding: utf-8 -*- 
  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 entailement 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<http://rdflib.net>}, 2.2.2. 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  """ 
 26  $Id: Literals.py,v 1.13 2009/09/18 07:25:00 ivan Exp $ $Date: 2009/09/18 07:25:00 $ 
 27  """ 
 28   
 29  __author__  = 'Ivan Herman' 
 30  __contact__ = 'Ivan Herman, ivan@w3.org' 
 31  __license__ = u'W3C® SOFTWARE NOTICE AND LICENSE, http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231' 
 32   
 33  from rdflib.BNode               import BNode 
 34  from rdflib.Literal             import Literal as rdflibLiteral 
 35  from rdflib.Literal     import _XSD_NS as ns_xsd 
 36  from rdflib.RDF             import type 
 37  from rdflib.RDFS            import Literal 
 38   
 39  #from RDFClosure.OWLDatatypes   import Rational 
 40  from RDFClosure.OWL                             import OWLNS 
 41   
42 -class _LiteralStructure :
43 """This class serves as a wrapper around rdflib's Literal, by changing the equality function to a strict 44 identity of datatypes and lexical values. 45 46 On the other hand, to implement, eg, OWL RL's datatype rules, a should be able to generate 47 an 'a sameAs b' triple, ie, the distinction should be kept. Hence this class that overrides the equality, 48 and then can be used as a key in a Python dictionary 49 """ 50
51 - def __init__(self, lit) :
52 self.lit = lit 53 self.lex = str(lit) 54 self.dt = lit.datatype 55 self.lang = lit.language 56 self.value = self.lit._cmp_value
57 58 # def compare_value(self, other ) : 59 # """Compare to literal structure instances for equality. Here equality means in the sense of datatype values 60 # @return: comparison result 61 # @rtype: boolean 62 # """ 63 # try: 64 # if self.dt == OWLNS["rational"] and other.dt == OWLNS["rational"] : 65 # return self.value == other.value 66 # elif self.dt == OWLNS["rational"] and other.dt != OWLNS["rational"] : 67 # l = rdflibLiteral(float(self.lit._cmp_value)) 68 # return l == other.lit 69 # elif self.dt != OWLNS["rational"] and other.dt == OWLNS["rational"] : 70 # l = rdflibLiteral(float(other.lit._cmp_value)) 71 # return self.lit == l 72 # else : 73 # return self.lit == other.lit 74 # except: 75 # # There might be conversion problems... 76 # return False 77
78 - def compare_value(self, other ) :
79 """Compare to literal structure instances for equality. Here equality means in the sense of datatype values 80 @return: comparison result 81 @rtype: boolean 82 """ 83 try: 84 return self.lit == other.lit 85 except: 86 # There might be conversion problems... 87 return False
88
89 - def __eq__(self, other) :
90 if other == None : 91 retval = False 92 else : 93 retval = self.lex == other.lex and self.dt == other.dt and self.lang == other.lang 94 return retval
95
96 - def __ne__(self,other) :
97 return not self.__eq__(other)
98
99 - def __hash__(self) :
100 if self.dt != None : 101 return hash(self.lit) ^ hash(self.dt) 102 else : 103 return hash(self.lit)
104
105 - def __repr__(self) :
106 retval = "" 107 retval += "lexical value: %s; " % self.lex 108 retval += "datatype: %s; " % self.dt 109 retval += "language tag: %s; " % self.lang 110 return retval
111
112 -class LiteralProxies :
113 - def __init__(self,graph) :
114 """ 115 @param graph: the graph to be modified 116 """ 117 self.lit_to_bnode = {} 118 self.bnode_to_lit = {} 119 self.graph = graph 120 121 to_be_removed = [] 122 to_be_added = [] 123 for t in self.graph : 124 (subj,pred,obj) = t 125 # This is supposed to be a "proper" graph, so only the obj may be a literal 126 if isinstance(obj,rdflibLiteral) : 127 # In any case, this should be removed: 128 if t not in to_be_removed : to_be_removed.append(t) 129 # Check if a BNode has already been associated with that literal 130 obj_st = _LiteralStructure(obj) 131 found = False 132 for l in self.lit_to_bnode.keys() : 133 if obj_st.lex == l.lex and obj_st.dt == l.dt and obj_st.lang == l.lang : 134 t1 = (subj, pred, self.lit_to_bnode[l]) 135 to_be_added.append(t1) 136 found = True 137 break 138 if found == False : 139 # the bnode has to be created 140 bn = BNode() 141 # store this in the internal administration 142 self.lit_to_bnode[obj_st] = bn 143 self.bnode_to_lit[bn] = obj_st 144 # modify the graph 145 to_be_added.append((subj,pred,bn)) 146 to_be_added.append((bn,type,Literal)) 147 # Furthermore: a plain literal should be identified with a corresponding xsd:string and vice versa, 148 # cf, RDFS Semantics document 149 if obj_st.dt == None and obj_st.lang == None : 150 newLit = rdflibLiteral(obj_st.lex,datatype = ns_xsd["string"]) 151 new_obj_st = _LiteralStructure(newLit) 152 new_obj_st.dt = ns_xsd["string"] 153 bn2 = BNode() 154 self.lit_to_bnode[new_obj_st] = bn2 155 self.bnode_to_lit[bn2] = new_obj_st 156 to_be_added.append((subj,pred,bn2)) 157 to_be_added.append((bn2,type,Literal)) 158 elif obj_st.dt == ns_xsd["string"] : 159 newLit = rdflibLiteral(obj_st.lex,datatype = None) 160 new_obj_st = _LiteralStructure(obj) 161 new_obj_st.dt = None 162 bn2 = BNode() 163 self.lit_to_bnode[new_obj_st] = bn2 164 self.bnode_to_lit[bn2] = new_obj_st 165 to_be_added.append((subj,pred,bn2)) 166 to_be_added.append((bn2,type,Literal)) 167 168 # Do the real modifications 169 self._massageGraph(to_be_removed,to_be_added)
170
171 - def restore(self) :
172 """ 173 This method is to be invoked at the end of the forward chain processing. It restores literals (whenever possible) 174 to their original self... 175 """ 176 to_be_removed = [] 177 to_be_added = [] 178 for t in self.graph : 179 (subj,pred,obj) = t 180 # The two cases, namely when the literal appears in subject or object positions, should be treated differently 181 if subj in self.bnode_to_lit : 182 # well... there may be to cases here: either this is the original tuple stating that 183 # this bnode is a literal, or it is the result of an inference. In both cases, the tuple must 184 # be removed from the result without any further action 185 if t not in to_be_removed : to_be_removed.append(t) 186 elif obj in self.bnode_to_lit : 187 # This is where the exchange should take place: put back the real literal into the graph, removing the proxy one 188 if t not in to_be_removed : to_be_removed.append(t) 189 t1 = (subj,pred,self.bnode_to_lit[obj].lit) 190 to_be_added.append(t1) 191 192 # Do the real modifications 193 self._massageGraph(to_be_removed,to_be_added)
194
195 - def _massageGraph(self,to_be_removed,to_be_added) :
196 """ 197 Perform the removal and addition actions on the graph 198 @param to_be_removed: list of tuples to be removed 199 @param to_be_added : list of tuples to be added 200 """ 201 for t in to_be_removed : self.graph.remove(t) 202 for t in to_be_added : self.graph.add(t)
203