| 116 | | 124 | |
|---|
| 117 | def search (self, uri, object = None, context = None): | 125 | def search (self, uri, object = None, context = None): |
|---|
| 118 | """search() accepts a predicate uri and an optional object uri or | 126 | """search() accepts a predicate uri and an optional object uri or |
|---|
| 119 | literal. An optional context may be provided as well.""" | 127 | literal. An optional context may be provided as well.""" |
|---|
| 120 | | 128 | |
|---|
| 121 | """First, search() ensures that the URI it is trying to fetch is | 129 | """First, search() ensures that the URI it is trying to fetch is |
|---|
| 122 | encapsulated in a Redland object.""" | 130 | encapsulated in a Redland object.""" |
|---|
| 123 | | 131 | |
|---|
| 124 | if uri is not None and type(uri) is not RDF.Node: | 132 | if uri is not None and type(uri) is not RDF.Node: |
|---|
| 125 | uri = RDF.Uri(uri) | 133 | uri = RDF.Uri(uri) |
|---|
| 126 | | 134 | |
|---|
| 127 | """With the optional object uri supplied, search() calls fetch() | 135 | """With the optional object uri supplied, search() calls fetch() |
|---|
| 128 | with the predicate and object (and optional context) supplied.""" | 136 | with the predicate and object (and optional context) supplied.""" |
|---|
| 129 | | 137 | |
|---|
| 130 | if object is not None: | 138 | if object is not None: |
|---|
| 131 | ids = self.model.get_sources_context( uri,self.node_ify(object) ) | 139 | ids = self.model.get_sources_context( uri,self.node_ify(object) ) |
|---|
| 132 | return Iterator(self, ids, context) | 140 | return Iterator(self, ids, context) |
|---|
| 133 | | 141 | |
|---|
| 134 | else: | 142 | else: |
|---|
| 135 | """Otherwise it find all statements matching that predicate.""" | 143 | """Otherwise it find all statements matching that predicate.""" |
|---|
| 136 | stats = self.model.find_statements_context( | 144 | stats = self.model.find_statements_context( |
|---|
| 137 | RDF.Statement(None,uri,None) ) | 145 | RDF.Statement(None,uri,None) ) |
|---|
| 138 | return Iterator(self, stats, context) | 146 | return Iterator(self, stats, context) |
|---|
| 139 | | 147 | |
|---|
| 140 | def node_ify(self,object): | 148 | def node_ify(self,object): |
|---|
| 141 | if type(object) == str: | 149 | if type(object) == str: |
|---|
| 142 | s = is_uri.search(object) | 150 | s = is_uri.search(object) |
|---|
| 143 | if s is not None: | 151 | if s is not None: |
|---|
| 144 | return RDF.Uri(object) | 152 | return RDF.Uri(object) |
|---|
| 145 | else: | 153 | else: |
|---|
| 146 | return RDF.Node(object) | 154 | return RDF.Node(object) |
|---|
| 147 | elif isinstance(object, Object): | 155 | elif isinstance(object, Object): |
|---|
| 148 | return object.uri() | 156 | return object.uri() |
|---|
| 149 | return object | 157 | return object |
|---|
| 150 | | 158 | |
|---|
| 151 | def instantiate (self, uri, context = None): | 159 | def instantiate (self, uri, context = None): |
|---|
| 152 | """instantiate() handles the details of creating an honest-to-goodness | 160 | """instantiate() handles the details of creating an honest-to-goodness |
|---|
| 153 | Python object from a set of database statements, which involves a lot of | 161 | Python object from a set of database statements, which involves a lot of |
|---|
| 154 | monkey work.""" | 162 | monkey work.""" |
|---|
| 155 | | 163 | |
|---|
| 156 | obj = None | 164 | obj = None |
|---|
| 157 | | 165 | |
|---|
| 158 | """We can be passed an RDF.Node representing a URI, or an RDF.Uri, or a | 166 | """We can be passed an RDF.Node representing a URI, or an RDF.Uri, or a |
|---|
| 159 | string which we convert to an RDF.Uri""" | 167 | string which we convert to an RDF.Uri""" |
|---|
| 160 | | 168 | |
|---|
| 161 | if type(uri) is not RDF.Node: | 169 | if type(uri) is not RDF.Node: |
|---|
| 162 | uri = RDF.Uri(uri) | 170 | uri = RDF.Uri(uri) |
|---|
| 163 | | 171 | |
|---|
| 164 | """instantiate() checks the database to see if the object represented | 172 | """instantiate() checks the database to see if the object represented |
|---|
| 165 | by the URI has an rdf:type, so that it can search for the matching | 173 | by the URI has an rdf:type, so that it can search for the matching |
|---|
| 166 | code module.""" | 174 | code module.""" |
|---|
| 167 | | 175 | |
|---|
| 168 | objtype = self.model.get_target(uri, rdf.type) | 176 | objtype = self.model.get_target(uri, rdf.type) |
|---|
| 169 | | 177 | |
|---|
| 170 | if objtype is not None: | 178 | if objtype is not None: |
|---|
| 171 | """If so, we look at our qname map to reverse engineer | 179 | """If so, we look at our qname map to reverse engineer |
|---|
| 172 | the qualified name of the rdf:type of the object.""" | 180 | the qualified name of the rdf:type of the object.""" |
|---|
| 173 | module_name = "" | 181 | module_name = "" |
|---|
| 174 | target_class = "" | 182 | target_class = "" |
|---|
| 175 | match = uri_split.match( str(objtype.uri) ) | 183 | match = uri_split.match( str(objtype.uri) ) |
|---|
| 176 | if match: | 184 | if match: |
|---|
| 177 | prefix = match.group(1) | 185 | prefix = match.group(1) |
|---|
| 178 | target_class = match.group(2) | 186 | target_class = match.group(2) |
|---|
| 179 | if self.qname.has_key(prefix): | 187 | if self.qname.has_key(prefix): |
|---|
| 180 | module_name = str(self.qname[prefix]) | 188 | module_name = str(self.qname[prefix]) |
|---|
| 181 | | 189 | |
|---|
| 182 | if module_name: | 190 | if module_name: |
|---|
| 183 | try: | 191 | try: |
|---|
| 184 | """Attempt to load the code module matching the rdf:type's | 192 | """Attempt to load the code module matching the rdf:type's |
|---|
| 185 | qname and instantiate an object from that module, if it | 193 | qname and instantiate an object from that module, if it |
|---|
| 186 | exists.""" | 194 | exists.""" |
|---|
| 187 | | 195 | |
|---|
| 188 | ### this only gets the top-level module, alas | 196 | ### this only gets the top-level module, alas |
|---|
| 189 | module_name = self.module_ns + "." + module_name | 197 | module_name = self.module_ns + "." + module_name |
|---|
| 190 | module = __import__(module_name) | 198 | module = __import__(module_name) |
|---|
| 191 | | 199 | |
|---|
| 192 | ### as recommended by | 200 | ### as recommended by |
|---|
| 193 | ### http://docs.python.org/lib/built-in-funcs.html | 201 | ### http://docs.python.org/lib/built-in-funcs.html |
|---|
| 194 | components = module_name.split('.') | 202 | components = module_name.split('.') |
|---|
| 195 | for comp in components[1:]: | 203 | for comp in components[1:]: |
|---|
| 196 | module = getattr(module, comp) | 204 | module = getattr(module, comp) |
|---|
| 197 | | 205 | |
|---|
| 198 | ### now that we've gotten the module object, get the | 206 | ### now that we've gotten the module object, get the |
|---|
| 199 | ### class out. | 207 | ### class out. |
|---|
| 200 | module = getattr(module, target_class) | 208 | module = getattr(module, target_class) |
|---|
| 201 | | 209 | |
|---|
| 202 | ### this instantiates the object itself. | 210 | ### this instantiates the object itself. |
|---|
| 203 | obj = module(self, uri, context) | 211 | obj = module(self, uri, context) |
|---|
| 204 | except (ImportError, AttributeError): | 213 | except (ImportError, AttributeError): |
|---|
| 205 | pass | 214 | pass |
|---|
| 206 | except: | 215 | except: |
|---|
| 207 | raise | 216 | raise |
|---|
| 208 | | 217 | |
|---|
| 209 | """If the object doesn't have an rdf:type or there isn't a code module | 218 | """If the object doesn't have an rdf:type or there isn't a code module |
|---|
| 210 | matching that rdf:type's qname, then instantiate a bare rdfobj.Object | 219 | matching that rdf:type's qname, then instantiate a bare rdfobj.Object |
|---|
| 211 | from the database using the given URI.""" | 220 | from the database using the given URI.""" |
|---|
| 212 | | 221 | |
|---|
| 213 | if obj is None: | 222 | if obj is None: |
|---|
| 214 | obj = Object(self, uri, context) | 223 | obj = Object(self, uri, context) |
|---|
| 215 | | 224 | |
|---|
| 216 | return obj | 225 | return obj |
|---|
| 217 | | 226 | |
|---|
| 218 | def create (self, type, uri = None, context = None): | 227 | def create (self, type, uri = None, context = None): |
|---|
| 219 | """create() makes a new object with the specified type and (optional) | 228 | """create() makes a new object with the specified type and (optional) |
|---|
| 220 | URI. If no URI is provided, a bnode ID is generated.""" | 229 | URI. If no URI is provided, a bnode ID is generated.""" |
|---|
| 221 | | 230 | |
|---|
| 222 | if uri is None: # then it's a blank node | 231 | if uri is None: # then it's a blank node |
|---|
| 223 | uri = RDF.Uri("_id:%08x%04x" % (time(), randint(0, 1 << 16))) | 232 | uri = RDF.Uri("_id:%08x%04x" % (time(), randint(0, 1 << 16))) |
|---|
| 224 | else: | 233 | else: |
|---|
| 225 | uri = RDF.Uri(uri) | 234 | uri = RDF.Uri(uri) |
|---|
| 226 | | 235 | |
|---|
| 227 | """First, we seed the object's rdf:type, and then we do the usual | 236 | """First, we seed the object's rdf:type, and then we do the usual |
|---|
| 228 | instantiation via instantiate().""" | 237 | instantiation via instantiate().""" |
|---|
| 229 | if self.context: context = self.context | 238 | if self.context: context = self.context |
|---|
| 230 | rdf_type = RDF.Statement( uri, rdf.type, RDF.Uri(type) ) | 239 | rdf_type = RDF.Statement( uri, rdf.type, RDF.Uri(type) ) |
|---|
| 231 | self.model.append( rdf_type, context ) | 240 | self.model.append( rdf_type, context ) |
|---|
| 232 | return self.instantiate( uri, context ) | 241 | return self.instantiate( uri, context ) |
|---|
| 233 | | 242 | |
|---|
| 234 | def remove (self, uri): | 243 | def remove (self, uri): |
|---|
| 235 | removeable = [] | 244 | removeable = [] |
|---|
| 236 | node = self.node_ify(uri) | 245 | node = self.node_ify(uri) |
|---|
| 237 | model = self.model | 246 | model = self.model |
|---|
| 238 | context = None | 247 | context = None |
|---|
| 239 | #if self.context: context = self.context | 248 | #if self.context: context = self.context |
|---|
| 240 | repudiate_source = RDF.Statement( node, None, None ) | 249 | repudiate_source = RDF.Statement( node, None, None ) |
|---|
| 241 | for fact in model.find_statements( repudiate_source ):#, context ): | 250 | for fact in model.find_statements( repudiate_source ):#, context ): |
|---|
| 242 | removeable.append(fact) | 251 | removeable.append(fact) |
|---|
| 243 | | 252 | |
|---|
| 244 | repudiate_target = RDF.Statement( None, None, node ) | 253 | repudiate_target = RDF.Statement( None, None, node ) |
|---|
| 245 | for fact in model.find_statements( repudiate_target):#, context ): | 254 | for fact in model.find_statements( repudiate_target):#, context ): |
|---|
| 246 | removeable.append(fact) | 255 | removeable.append(fact) |
|---|
| 247 | | 256 | |
|---|
| 248 | for fact in removeable: | 257 | for fact in removeable: |
|---|
| 249 | del model[fact]#, context] | 258 | del model[fact]#, context] |
|---|
| 250 | | 259 | |
|---|
| 251 | def _abbreviate_uri (self, format, match): | 260 | def _abbreviate_uri (self, format, match): |
|---|
| 252 | prefix = match.group(1) | 261 | prefix = match.group(1) |
|---|
| 253 | if self.qname.has_key(prefix): | 262 | if self.qname.has_key(prefix): |
|---|
| 254 | return format % self.qname[prefix] | 263 | return format % self.qname[prefix] |
|---|
| 255 | else: | 264 | else: |
|---|
| 256 | return prefix | 265 | return prefix |
|---|
| 257 | | 266 | |
|---|
| 258 | def abbreviate (self, uri, as_xml_ns = False, as_xml_entity = False): | 267 | def abbreviate (self, uri, as_xml_ns = False, as_xml_entity = False): |
|---|
| 259 | repl = None | 268 | repl = None |
|---|
| 260 | if as_xml_entity: | 269 | if as_xml_entity: |
|---|
| 261 | repl = "&%s;" | 270 | repl = "&%s;" |
|---|
| 262 | elif as_xml_ns: | 271 | elif as_xml_ns: |
|---|
| 263 | repl = "%s:" | 272 | repl = "%s:" |
|---|
| 264 | else: | 273 | else: |
|---|
| 265 | repl = "%s." | 274 | repl = "%s." |
|---|
| 266 | callback = lambda match: self._abbreviate_uri(repl, match) | 275 | callback = lambda match: self._abbreviate_uri(repl, match) |
|---|
| 267 | return re.sub( self.match_ns, callback, str(uri) ); | 276 | return re.sub( self.match_ns, callback, str(uri) ); |
|---|
| 268 | | 277 | |
|---|
| 269 | def dump (self, iter = None): | 278 | def dump (self, iter = None): |
|---|
| 270 | if iter is None: | 279 | if iter is None: |
|---|
| 271 | iter = self | 280 | iter = self |
|---|
| 272 | | 281 | |
|---|
| 273 | entities = [""] | 282 | entities = [""] |
|---|
| 274 | xmlns = [""] | 283 | xmlns = [""] |
|---|
| 275 | for qname, ns in self.ns.iteritems(): | 284 | for qname, ns in self.ns.iteritems(): |
|---|
| 276 | entities.append( '<!ENTITY %s "%s" >' % (qname, ns) ) | 285 | entities.append( '<!ENTITY %s "%s" >' % (qname, ns) ) |
|---|
| 277 | xmlns.append( 'xmlns:%s="%s"' % (qname, ns) ) | 286 | xmlns.append( 'xmlns:%s="%s"' % (qname, ns) ) |
|---|
| 278 | | 287 | |
|---|
| 279 | out = '<?xml version="1.0"?>\n<!DOCTYPE rdf:RDF [' | 288 | out = '<?xml version="1.0"?>\n<!DOCTYPE rdf:RDF [' |
|---|
| 280 | out += "\n ".join(entities) + "\n]>\n" | 289 | out += "\n ".join(entities) + "\n]>\n" |
|---|
| 281 | out += '<rdf:RDF xmlns="' + \ | 290 | out += '<rdf:RDF xmlns="' + \ |
|---|
| 282 | Schema_Namespaces["rdf"] + '"' + \ | 291 | Schema_Namespaces["rdf"] + '"' + \ |
|---|
| 283 | '\n '.join(xmlns) + '>\n' | 292 | '\n '.join(xmlns) + '>\n' |
|---|
| 284 | | 293 | |
|---|
| 285 | for obj in iter: | 294 | for obj in iter: |
|---|
| 286 | out += obj.dump() | 295 | out += obj.dump() |
|---|
| 287 | | 296 | |
|---|
| 288 | out += "</rdf:RDF>\n" | 297 | out += "</rdf:RDF>\n" |
|---|
| 289 | return out | 298 | return out |
|---|
| 290 | | 299 | |
|---|
| 291 | def data(self): | 300 | def data(self): |
|---|
| 292 | return self.__dict__['__factory'] | 301 | return self.__dict__['__factory'] |
|---|
| 293 | | 302 | |
|---|
| 294 | class Iterator: | 303 | class Iterator: |
|---|
| 295 | """This is typically a query result from the database. It gives you a | 304 | """This is typically a query result from the database. It gives you a |
|---|
| 296 | sequence of nodes matching a query, one by one.""" | 305 | sequence of nodes matching a query, one by one.""" |
|---|
| 297 | | 306 | |
|---|
| 298 | def __init__ (self, model, nodes, context = None): | 307 | def __init__ (self, model, nodes, context = None): |
|---|
| 299 | | 308 | |
|---|
| 300 | """__init__ is supplied a model, a Redland iterator or sequence, | 309 | """__init__ is supplied a model, a Redland iterator or sequence, |
|---|
| 301 | and an optional context. This object is really a wrapper around the | 310 | and an optional context. This object is really a wrapper around the |
|---|
| 302 | Redland iterator.""" | 311 | Redland iterator.""" |
|---|
| 303 | | 312 | |
|---|
| 304 | self.model = model | 313 | self.model = model |
|---|
| 305 | self.context = context | 314 | self.context = context |
|---|
| 306 | if type(nodes) is RDF.Stream: | 315 | if type(nodes) is RDF.Stream: |
|---|
| 307 | self.nodes = RDF.StreamIter(nodes) | 316 | self.nodes = RDF.StreamIter(nodes) |
|---|
| 308 | else: | 317 | else: |
|---|
| 309 | self.nodes = nodes | 318 | self.nodes = nodes |
|---|
| 310 | | 319 | |
|---|
| 311 | def next_node (self): | 320 | def next_node (self): |
|---|
| 312 | """next_node() jumps the hoops between RDF.Iterator and RDF.Sequence.""" | 321 | """next_node() jumps the hoops between RDF.Iterator and RDF.Sequence.""" |
|---|
| 313 | node = context = None | 322 | node = context = None |
|---|
| 314 | | 323 | |
|---|
| 315 | node = self.nodes.next() | 324 | node = self.nodes.next() |
|---|
| 316 | | 325 | |
|---|
| 317 | if type(node) is not tuple: | 326 | if type(node) is not tuple: |
|---|
| 318 | return (node, None) | 327 | return (node, None) |
|---|
| 319 | else: | 328 | else: |
|---|
| 320 | return node | 329 | return node |
|---|
| 321 | | 330 | |
|---|
| 322 | def __iter__ (self): | 331 | def __iter__ (self): |
|---|
| 323 | return self | 332 | return self |
|---|
| 324 | | 333 | |
|---|
| 325 | def next (self): | 334 | def next (self): |
|---|
| 326 | while True: | 335 | while True: |
|---|
| 327 | node, context = self.next_node() | 336 | node, context = self.next_node() |
|---|
| 328 | | 337 | |
|---|
| 329 | """Items are filtered until they match a given context, if one is | 338 | """Items are filtered until they match a given context, if one is |
|---|
| 330 | provided.""" | 339 | provided.""" |
|---|
| 331 | if self.context is not None and context != self.context: | 340 | if self.context is not None and context != self.context: |
|---|
| 332 | continue | 341 | continue |
|---|
| 333 | | 342 | |
|---|
| 334 | if node is None: | 343 | if node is None: |
|---|
| 335 | raise StopIteration | 344 | raise StopIteration |
|---|
| 336 | | 345 | |
|---|
| 337 | """If our list contains a sequence of statements, we only want the | 346 | """If our list contains a sequence of statements, we only want the |
|---|
| 338 | subject uri of each statement.""" | 347 | subject uri of each statement.""" |
|---|
| 339 | | 348 | |
|---|
| 340 | if type(node) is RDF.Statement: | 349 | if type(node) is RDF.Statement: |
|---|
| 341 | node = node.subject | 350 | node = node.subject |
|---|
| 342 | | 351 | |
|---|
| 343 | """We instantiate an object from the statements which contain this | 352 | """We instantiate an object from the statements which contain this |
|---|
| 344 | subject uri.""" | 353 | subject uri.""" |
|---|
| 345 | | 354 | |
|---|
| 346 | obj = self.model.instantiate(node) | 355 | obj = self.model.instantiate(node) |
|---|
| 347 | return obj | 356 | return obj |
|---|
| 348 | | 357 | |
|---|
| 349 | def first (self): | 358 | def first (self): |
|---|
| 350 | object = None | 359 | object = None |
|---|
| 351 | try: | 360 | try: |
|---|
| 352 | object = self.next() | 361 | object = self.next() |
|---|
| 353 | except StopIteration: | 362 | except StopIteration: |
|---|
| 354 | object = None | 363 | object = None |
|---|
| 355 | return object | 364 | return object |
|---|
| 356 | | 365 | |
|---|
| 357 | def list (self): | 366 | def list (self): |
|---|
| 358 | return [object for object in self] | 367 | return [object for object in self] |
|---|
| 359 | | 368 | |
|---|
| 360 | def filter (self, prop, value = None): | 369 | def filter (self, prop, value = None): |
|---|
| 361 | return Filter(self, prop, value) | 370 | return Filter(self, prop, value) |
|---|
| 362 | | 371 | |
|---|
| 363 | class Filter (Iterator): | 372 | class Filter (Iterator): |
|---|
| 364 | def __init__ (self, iter, prop, value = None): | 373 | def __init__ (self, iter, prop, value = None): |
|---|
| 365 | self.prop = prop | 374 | self.prop = prop |
|---|
| 366 | self.value = value | 375 | self.value = value |
|---|
| 367 | self.iter = iter | 376 | self.iter = iter |
|---|
| 368 | | 377 | |
|---|
| 369 | def next (self): | 378 | def next (self): |
|---|
| 370 | while True: # StopIteration breaks this loop | 379 | while True: # StopIteration breaks this loop |
|---|
| 371 | obj = self.iter.next() | 380 | obj = self.iter.next() |
|---|
| 372 | if self.value is None: | 381 | if self.value is None: |
|---|
| 373 | if obj[self.prop] is not None: | 382 | if obj[self.prop] is not None: |
|---|
| 374 | return obj | 383 | return obj |
|---|
| 375 | elif self.prop is None: | 384 | elif self.prop is None: |
|---|
| 376 | for st in obj: | 385 | for st in obj: |
|---|
| 377 | if str(st.object) == self.value: | 386 | if str(st.object) == self.value: |
|---|
| 378 | return obj | 387 | return obj |
|---|
| 379 | elif str(obj[self.prop]) == str(self.value): | 388 | elif str(obj[self.prop]) == str(self.value): |
|---|
| 380 | return obj | 389 | return obj |
|---|
| 381 | | 390 | |
|---|
| 382 | class Namespace: | 391 | class Namespace: |
|---|
| 383 | """The rdfobj.Namespace class provides RDF namespace magic.""" | 392 | """The rdfobj.Namespace class provides RDF namespace magic.""" |
|---|
| 384 | | 393 | |
|---|
| 385 | def __init__ (self, uri): | 394 | def __init__ (self, uri): |
|---|
| 386 | """__init__() takes the URI prefix of the RDF namespace and creates | 395 | """__init__() takes the URI prefix of the RDF namespace and creates |
|---|
| 387 | a uri() method on the Namespace object that returns that URI prefix.""" | 396 | a uri() method on the Namespace object that returns that URI prefix.""" |
|---|
| 388 | | 397 | |
|---|
| 389 | self.uri = lambda x = None: uri | 398 | self.uri = lambda x = None: uri |
|---|
| 390 | | 399 | |
|---|
| 391 | def __getitem__ (self, property): | 400 | def __getitem__ (self, property): |
|---|
| 392 | """Any properties fetched from the Namespace object return a full RDF | 401 | """Any properties fetched from the Namespace object return a full RDF |
|---|
| 393 | URI for any requested property.""" | 402 | URI for any requested property.""" |
|---|
| 394 | | 403 | |
|---|
| 395 | return RDF.Uri(self.uri() + str(property)) | 404 | return RDF.Uri(self.uri() + str(property)) |
|---|
| 396 | | 405 | |
|---|
| 397 | def __getattr__ (self, property): | 406 | def __getattr__ (self, property): |
|---|
| 398 | """Any properties fetched from the Namespace object return a full RDF | 407 | """Any properties fetched from the Namespace object return a full RDF |
|---|
| 399 | URI for any requested property.""" | 408 | URI for any requested property.""" |
|---|
| 400 | | 409 | |
|---|
| 401 | return self.__getitem__(property) | 410 | return self.__getitem__(property) |
|---|
| 402 | | 411 | |
|---|
| 403 | def __str__ (self): | 412 | def __str__ (self): |
|---|
| 404 | """Namespace objects also conveniently stringify to their URI prefix.""" | 413 | """Namespace objects also conveniently stringify to their URI prefix.""" |
|---|
| 405 | | 414 | |
|---|
| 406 | return str(self.uri()) | 415 | return str(self.uri()) |
|---|
| 407 | | 416 | |
|---|
| 408 | class Object (dict): | 417 | class Object (dict): |
|---|
| 409 | """The rdfobj.Object class provides a working base class for all RDF | 418 | """The rdfobj.Object class provides a working base class for all RDF |
|---|
| 410 | objects. rdfobj.Object inherits from dict.""" | 419 | objects. rdfobj.Object inherits from dict.""" |
|---|
| 411 | | 420 | |
|---|
| 412 | def __init__ (self, model, uri, context = None): | 421 | def __init__ (self, model, uri, context = None): |
|---|
| 413 | """__init__() takes a reference to the object's rdfobj.Model, and | 422 | """__init__() takes a reference to the object's rdfobj.Model, and |
|---|
| 414 | to its own RDF.Node or URI.""" | 423 | to its own RDF.Node or URI.""" |
|---|
| 415 | | 424 | |
|---|
| 416 | dict.__init__(self) | 425 | dict.__init__(self) |
|---|
| 417 | self.__dict__['__context'] = context | 426 | self.__dict__['__context'] = context |
|---|
| 418 | self.__dict__['__factory'] = model | 427 | self.__dict__['__factory'] = model |
|---|
| 419 | self.__dict__['__node'] = uri | 428 | self.__dict__['__node'] = uri |
|---|
| 420 | | 429 | |
|---|
| 421 | def __to_uri (self, key): | 430 | def __to_uri (self, key): |
|---|
| 422 | factory = self.__dict__['__factory'] | 431 | factory = self.__dict__['__factory'] |
|---|
| 423 | if type(key) is str and key.find("_") != -1: | 432 | if type(key) is str and key.find("_") != -1: |
|---|
| 424 | """If the attribute name contains an underscore, extract the qname | 433 | """If the attribute name contains an underscore, extract the qname |
|---|
| 425 | and property, and look up the qname in the parent model's qname map.""" | 434 | and property, and look up the qname in the parent model's qname map.""" |
|---|
| 426 | | 435 | |
|---|
| 427 | (qname, property) = key.split("_", 1) | 436 | (qname, property) = key.split("_", 1) |
|---|
| 428 | | 437 | |
|---|
| 429 | if factory.ns.has_key(qname): | 438 | if factory.ns.has_key(qname): |
|---|
| 430 | """If the qname is present, formulate the URI of the RDF property | 439 | """If the qname is present, formulate the URI of the RDF property |
|---|
| 431 | and return the value from the internal dict, if present, otherwise, | 440 | and return the value from the internal dict, if present, otherwise, |
|---|
| 432 | return None.""" | 441 | return None.""" |
|---|
| 433 | | 442 | |
|---|
| 434 | key = str( factory.ns[qname] ) + property | 443 | key = str( factory.ns[qname] ) + property |
|---|
| 435 | | 444 | |
|---|
| 436 | if type(key) is not RDF.Node: | 445 | if type(key) is not RDF.Node: |
|---|
| 437 | key = factory.node_ify(key) | 446 | key = factory.node_ify(key) |
|---|
| 438 | | 447 | |
|---|
| 439 | return key | 448 | return key |
|---|
| 440 | | 449 | |
|---|
| 441 | def __getattr__ (self, key): | 450 | def __getattr__ (self, key): |
|---|
| 442 | """Attributes on rdfobj.Object are either built-in or take the form | 451 | """Attributes on rdfobj.Object are either built-in or take the form |
|---|
| 443 | {qname}_{property}.""" | 452 | {qname}_{property}.""" |
|---|
| 444 | | 453 | |
|---|
| 445 | if self.__dict__.has_key(key): | 454 | if self.__dict__.has_key(key): |
|---|
| 446 | """If the attribute name is built-in, | 455 | """If the attribute name is built-in, |
|---|
| 447 | then return the underlying Python attribute.""" | 456 | then return the underlying Python attribute.""" |
|---|
| 448 | return self.__dict__[key] | 457 | return self.__dict__[key] |
|---|
| 449 | elif isinstance(key, RDF.Uri) or key.find("_") > 0: | 458 | elif isinstance(key, RDF.Uri) or key.find("_") > 0: |
|---|
| 450 | return self[key] | 459 | return self[key] |
|---|
| 451 | else: | 460 | else: |
|---|
| 452 | raise AttributeError, \ | 461 | raise AttributeError, \ |
|---|
| 453 | "rdfobj.Object instance has no attribute '%s'" % key | 462 | "rdfobj.Object instance has no attribute '%s'" % key |
|---|
| 454 | | 463 | |
|---|
| 455 | def __setattr__ (self, key, value): | 464 | def __setattr__ (self, key, value): |
|---|
| 456 | """The same rules apply when setting an attribute on an rdfobj.Object.""" | 465 | """The same rules apply when setting an attribute on an rdfobj.Object.""" |
|---|
| 457 | if key.find("_") <= 0 or self.__dict__.has_key(key): | 466 | if key.find("_") <= 0 or self.__dict__.has_key(key): |
|---|
| 458 | """If the attribute name starts with an underscore, or doesn't | 467 | """If the attribute name starts with an underscore, or doesn't |
|---|
| 459 | contain one at all, or is built-in, then set the underlying Python | 468 | contain one at all, or is built-in, then set the underlying Python |
|---|
| 460 | attribute.""" | 469 | attribute.""" |
|---|
| 461 | self.__dict__[key] = value | 470 | self.__dict__[key] = value |
|---|
| 462 | else: | 471 | else: |
|---|
| 463 | self[key] = value | 472 | self[key] = value |
|---|
| 464 | | 473 | |
|---|
| 465 | return value | 474 | return value |
|---|
| 466 | | 475 | |
|---|
| 467 | def __iter__ (self): | 476 | def __iter__ (self): |
|---|
| 468 | """When used as an iterator, rdfobj.Object returns an iterator of | 477 | """When used as an iterator, rdfobj.Object returns an iterator of |
|---|
| 469 | statements about that object.""" | 478 | statements about that object.""" |
|---|
| 470 | | 479 | |
|---|
| 471 | context = self.__dict__["__context"] | 480 | context = self.__dict__["__context"] |
|---|
| 472 | model = self.__dict__["__factory"].model | 481 | model = self.__dict__["__factory"].model |
|---|
| 473 | node = self.__dict__["__node"] | 482 | node = self.__dict__["__node"] |
|---|
| 474 | | 483 | |
|---|
| 475 | o = model.find_statements(RDF.Statement(node, None, None), context) | 484 | o = model.find_statements(RDF.Statement(node, None, None), context) |
|---|
| 476 | return RDF.StreamIter(o) | 485 | return RDF.StreamIter(o) |
|---|
| 477 | | 486 | |
|---|
| 478 | def __getitem__ (self, key): | 487 | def __getitem__ (self, key): |
|---|
| 479 | """rdfobj.Object can be used as a dict. Keys are expected to be full | 488 | """rdfobj.Object can be used as a dict. Keys are expected to be full |
|---|
| 480 | RDF predicate URIs.""" | 489 | RDF predicate URIs.""" |
|---|
| 481 | values = self.list(key) | 490 | values = self.list(key) |
|---|
| 482 | if values: | 491 | if values: |
|---|
| 483 | return values[0] | 492 | return values[0] |
|---|
| 484 | else: | 493 | else: |
|---|
| 485 | return None | 494 | return None |
|---|
| 486 | | 495 | |
|---|
| 487 | def list (self, key): | 496 | def list (self, key): |
|---|
| 488 | factory = self.__dict__["__factory"] | 497 | factory = self.__dict__["__factory"] |
|---|
| 489 | context = self.__dict__["__context"] | 498 | context = self.__dict__["__context"] |
|---|
| 490 | node = self.__dict__["__node"] | 499 | node = self.__dict__["__node"] |
|---|
| 491 | | 500 | |
|---|
| 492 | key = self.__to_uri(key) | 501 | key = self.__to_uri(key) |
|---|
| 493 | | 502 | |
|---|
| 494 | """If the rdfobj.Object dict contains a URI for that key, | 503 | """If the rdfobj.Object dict contains a URI for that key, |
|---|
| 495 | then the rdfobj.Object corresponding to that URI is returned.""" | 504 | then the rdfobj.Object corresponding to that URI is returned.""" |
|---|
| 496 | values = [] | 505 | values = [] |
|---|
| 497 | for val, ctxt in factory.model.get_targets_context( node, key ): | 506 | for val, ctxt in factory.model.get_targets_context( node, key ): |
|---|
| 498 | if context is None or context == ctxt: | 507 | if context is None or context == ctxt: |
|---|
| 499 | if not val.is_literal(): | 508 | if not val.is_literal(): |
|---|
| 500 | # print "### FETCH %s (%s)" % (val, type(val)) | 509 | # print "### FETCH %s (%s)" % (val, type(val)) |
|---|
| 501 | val = factory.fetch( val, context ) | 510 | val = factory.fetch( val, context ) |
|---|
| 502 | else: | 511 | else: |
|---|
| 503 | val = str(val) | 512 | val = str(val) |
|---|
| 504 | values.append(val) | 513 | values.append(val) |
|---|
| 505 | return values | 514 | return values |
|---|
| 506 | | 515 | |
|---|
| 507 | def __setitem__ (self, key, values): | 516 | def __setitem__ (self, key, values): |
|---|
| 508 | """Setting items in the rdfobj.Object dict works in a similar fashion. | 517 | """Setting items in the rdfobj.Object dict works in a similar fashion. |
|---|
| 509 | URIs are stored in place of objects (due to object stringification).""" | 518 | URIs are stored in place of objects (due to object stringification).""" |
|---|
| 510 | | 519 | |
|---|
| 511 | context = self.__dict__["__context"] | 520 | context = self.__dict__["__context"] |
|---|
| 512 | node = self.__dict__["__node"] | 521 | node = self.__dict__["__node"] |
|---|
| 513 | factory = self.__dict__["__factory"] | 522 | factory = self.__dict__["__factory"] |
|---|
| 514 | model = factory.model | 523 | model = factory.model |
|---|
| 515 | | 524 | |
|---|
| 516 | if key is None: | 525 | if key is None: |
|---|
| 517 | raise ValueError, "rdfobj.Object keys must not be None" | 526 | raise ValueError, "rdfobj.Object keys must not be None" |
|---|
| 518 | key = self.__to_uri(key) | 527 | key = self.__to_uri(key) |
|---|
| 519 | | 528 | |
|---|
| 520 | # First remove old value | 529 | # First remove old value |
|---|
| 521 | # Setting a value of None has the same effect as del... | 530 | # Setting a value of None has the same effect as del... |
|---|
| 522 | del self[key] | 531 | del self[key] |
|---|
| 523 | if values is None: return None | 532 | if values is None: return None |
|---|
| 524 | | 533 | |
|---|
| 525 | if not type(values) is list: | 534 | if not type(values) is list: |
|---|
| 526 | values = [values] | 535 | values = [values] |
|---|
| 527 | | 536 | |
|---|
| 528 | facts = [] | 537 | facts = [] |
|---|
| 529 | for val in values: | 538 | for val in values: |
|---|
| 530 | if type(val) is Object: | 539 | if type(val) is Object: |
|---|
| 531 | val = val.uri() | 540 | val = val.uri() |
|---|
| 532 | else: | 541 | else: |
|---|
| 533 | val = factory.node_ify(val) | 542 | val = factory.node_ify(val) |
|---|
| 534 | fact = RDF.Statement( node, key, val ) | 543 | fact = RDF.Statement( node, key, val ) |
|---|
| 535 | model.append( fact, context ) | 544 | model.append( fact, context ) |
|---|
| 536 | facts.append( fact ) | 545 | facts.append( fact ) |
|---|
| 537 | | 546 | |
|---|
| 538 | return facts | 547 | return facts |
|---|
| 539 | | 548 | |
|---|
| 540 | def __delitem__ (self, key): | 549 | def __delitem__ (self, key): |
|---|
| 541 | """Items can be removed from the rdfobj.Object as you'd expect. The | 550 | """Items can be removed from the rdfobj.Object as you'd expect. The |
|---|
| 542 | process is two-staged to keep Redland from segfaulting.""" | 551 | process is two-staged to keep Redland from segfaulting.""" |
|---|
| 543 | | 552 | |
|---|
| 544 | context = self.__dict__["__context"] | 553 | context = self.__dict__["__context"] |
|---|
| 545 | model = self.__dict__["__factory"].model | 554 | model = self.__dict__["__factory"].model |
|---|
| 546 | node = self.__dict__["__node"] | 555 | node = self.__dict__["__node"] |
|---|
| 547 | | 556 | |
|---|
| 548 | key = self.__to_uri(key) | 557 | key = self.__to_uri(key) |
|---|
| 549 | | 558 | |
|---|
| 550 | removeable = [] | 559 | removeable = [] |
|---|
| 551 | repudiate = RDF.Statement( node, key, None ) | 560 | repudiate = RDF.Statement( node, key, None ) |
|---|
| 552 | for fact in model.find_statements( repudiate, context ): | 561 | for fact in model.find_statements( repudiate, context ): |
|---|
| 553 | removeable.append(fact) | 562 | removeable.append(fact) |
|---|
| 554 | | 563 | |
|---|
| 555 | for fact in removeable: | 564 | for fact in removeable: |
|---|
| 556 | del model[fact, context] | 565 | del model[fact, context] |
|---|
| 557 | | 566 | |
|---|
| 558 | def __repr__ (self): | 567 | def __repr__ (self): |
|---|
| 559 | """rdfobj.Objects stringify as their URI.""" | 568 | """rdfobj.Objects stringify as their URI.""" |
|---|
| 560 | return str(self.uri()) | 569 | return str(self.uri()) |
|---|
| 561 | | 570 | |
|---|
| 562 | def uri (self): | 571 | def uri (self): |
|---|
| 563 | """The object URI can be found out from the uri() method.""" | 572 | """The object URI can be found out from the uri() method.""" |
|---|
| 564 | node = self.__dict__["__node"] | 573 | node = self.__dict__["__node"] |
|---|
| 565 | if type(node) is RDF.Uri: | 574 | if type(node) is RDF.Uri: |
|---|
| 566 | return node | 575 | return node |
|---|
| 567 | elif node.is_resource(): | 576 | elif node.is_resource(): |
|---|
| 568 | return node.uri | 577 | return node.uri |
|---|