# $Id: N3Node.py,v 1.7 2004/03/26 21:26:23 graham Exp $ # # RDF node class for use with N3 parser # #--------+---------+---------+---------+---------+---------+---------+---------+ # # Copyright (c) 2002, G. KLYNE # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #--------+---------+---------+---------+---------+---------+---------+---------+ # $Source: /file/cvsdev/PythonN3/N3Node.py,v $ # $Author: graham $ # $Revision: 1.7 $ $Date: 2004/03/26 21:26:23 $ #--------+---------+---------+---------+---------+---------+---------+---------+ # 1 2 3 4 5 6 7 8 #import os #import re import sys import string import StringIO #--------+---------+---------+---------+---------+---------+---------+---------+ # Define N3Node package global values NameStartChar = string.letters+"_" # characters to start a name NameChar = NameStartChar+string.digits # characters to start a name #--------+---------+---------+---------+---------+---------+---------+---------+ # N3Node - represents any node in a graph: # URI-labelled, string-labelled, blank # Formula nodes are represented by adding a list of statements # (This formula implementation is flexible to allow experimentation) # class N3Node: """ Represents a graph node. """ # Define class values next = 0 # Helper function: node identifier generator def GenSym( self ): N3Node.next = N3Node.next + 1 return "_:n" + str(N3Node.next) # Helper function: return length of namespace prefix in URI # If URI doesn't end with local name, return length of URI. def Split( self, URI ): s = len(URI) i = s - 1 while URI[i] in NameChar: if URI[i] in NameStartChar: s = i i = i - 1 return s # Node constructor def __init__( self, uri=None, ns=None, name=None, lit=None, extid=None ): """ Construct a node object: N3Node() - creates a blank node N3Node( uri=URI ) - creates a URI-labelled node N3Node( ns=URI, name=name ) - creates a URI-labelled node N3Node( lit=literal ) - creates a literal node N3Node( name=name ) - creates a named query node Any constructor may also specify extid=string to specify an external node identifier value. This string takes precedence over the locally assigned identifier for determining node equivalence; it is provided for linking unlabelled nodes to Jena database nodes. """ self.URI = None self.ns = None self.name = None self.lit = None self.stmts = None self.extId = extid self.reversed = 0 self.ident = self.GenSym() if uri: i = self.Split( uri ) self.URI = uri self.ns = uri[:i] self.name = uri[i:] elif ns: self.URI = ns + name self.ns = ns self.name = name elif lit: self.lit = lit else: self.name = name ####if extid: #### self.debug( "extid: "+extid ) #### self.debug( "node: "+self.getNodeName({}) ) return def __eq__( self, Node ): """ Compare for equality with another node. Nodes are equal if they have the same URI, same string value, same extId, or same internal identifier. Otherwise they are different. """ if Node: if self.URI or Node.URI: return self.URI == Node.URI if self.lit or Node.lit: return self.lit == Node.lit if self.extId or Node.extId: return self.extId == Node.extId return self.ident == Node.ident return 0 def __ne__( self, Node ): """ Compare for inequality with another node. (Complement of __eq__ above.) """ return not self.__eq__( Node ) def __str__( self ): if self.URI: return "<" + str(self.URI) + ">" if self.lit: return `self.lit` if self.ident: if self.name: return self.ident+"("+self.name+")" if self.extId: return self.ident+"(ext:"+self.extId+")" return self.ident return "<***unknown node***>" def __repr__( self ): return ( str(self) ) def isURI( self ): """ Return true if node is labelled with a URI """ return self.URI != None def isLiteral( self ): """ Return true if node is labelled with a literal string """ return self.lit != None def isBlank( self ): """ Return true if node is unlabelled """ return ( self.URI == None ) and ( self.lit == None ) def isVariable( self ): """ Return true if node is a query variable """ return ( self.URI == None ) and ( self.name != None ) def isFormula( self ): """ Return true if node is a formula node """ return self.stmts != None def isResource( self ): """ Return true if node is a URI or blank node (not a literal) """ return self.isURI() or self.isBlank() def setReversed( self, reversed ): """ Flag this as a property with reversed subject/object ( is of ). """ self.reversed = reversed return def isReversed( self ): """ Return true if node is a property with reversed subject/object ( is of ) """ return self.reversed def getURI( self ): """ Return URI for node, or None if not URI-labelled """ return self.URI def getNamespace( self ): """ Return namespace name for node, or None if not available """ return self.ns def getLocalName( self ): """ Return local name for node, or None if not available """ return self.name def getString( self ): """ Return literal string label for node, or None if not a literal node """ return self.lit def getIdent( self ): """ Return unique identifier string for node (_:xxxx) or URI. """ return self.URI or self.ident def setExtId( self, ExtId ): """ Set external identifier string. This is applied when an anonymous node is inserted into an external RDF store, so that future references to the current node can match the externally stored node. """ self.extId = ExtId return def getExtId( self ): """ Return external identifier string, or None. """ return self.extId def getVariableName( self ): """ Return variable name string if node is a query variable, otherwise None """ if self.URI == None: return self.name return None def getQName( self, PrefixTable ): """ Return QName for the current node using the supplied prefix table, or None """ ns = self.getNamespace() pre = None for p in PrefixTable.items(): if p[1] == ns: pre = p[0] return pre + ":" + self.getLocalName() # No prefix: return None return None def getNodeName( self, PrefixTable ): """ Return convenient description for the current node, using the supplied prefix table: QName, string or blank node id """ if self.isURI(): return self.getQName( PrefixTable ) or "<" + self.getURI() + ">" elif self.isLiteral(): return `self.getString()` elif self.isBlank(): if self.name: return self.ident+"("+self.name+")" if self.extId: return self.ident+"(ext:"+self.extId+")" return self.ident else: return "<***Unknown node type***>" def makeFormula( self ): """ Make node into a formula. """ if self.stmts == None: self.stmts = [] return def addStatement( self, Statement ): """ Add statement to formula """ if self.stmts == None: self.stmts = [] self.stmts.append( Statement ) return def getStatements( self ): """ Return list of statements in formula, or None """ return self.stmts def debug( self, msg ): sys.stdout.write( "N3Node: "+msg+"\n" ) return # End of N3Node #--------+---------+---------+---------+---------+---------+---------+---------+ # # Stand-alone test code follows: # if __name__ == '__main__': sys.stdout.write( "Node tests\n" ) Node1 = N3Node() sys.stdout.write( "Node 1: "+str(Node1)+"\n" ) Node2 = N3Node( uri='http://example.org/base/local' ) sys.stdout.write( "Node 2: "+str(Node2)+"\n" ) Node3 = N3Node( ns='http://example.org/base/', name='local' ) sys.stdout.write( "Node 3: "+str(Node3)+"\n" ) Node4 = N3Node( lit='a string' ) sys.stdout.write( "Node 4: "+str(Node4)+"\n" ) Node5 = N3Node( uri='http://example.org/base/1local' ) sys.stdout.write( "Node 5: "+str(Node5)+"\n" ) if Node1 == Node1: sys.stdout.write( "Node1 == Node1 (OK)\n" ) else: sys.stdout.write( "Node1 != Node1 (Error)\n" ) if Node2 == Node3 and Node3 == Node2: sys.stdout.write( "Node2 == Node3 (OK)\n" ) else: sys.stdout.write( "Node2 != Node3 (Error)\n" ) if ( ( Node2.getNamespace() == 'http://example.org/base/' ) and ( Node2.getLocalName() == 'local' ) ): sys.stdout.write( "Node2 namespace '%s', local '%s' (OK)\n" % ( Node2.getNamespace(), Node2.getLocalName() ) ) else: sys.stdout.write( "Node2 namespace '%s', local '%s' (error)\n" % ( Node2.getNamespace(), Node2.getLocalName() ) ) if ( ( Node5.getNamespace() == 'http://example.org/base/1' ) and ( Node5.getLocalName() == 'local' ) ): sys.stdout.write( "Node5 namespace '%s', local '%s' (OK)\n" % ( Node5.getNamespace(), Node5.getLocalName() ) ) else: sys.stdout.write( "Node5 namespace '%s', local '%s' (error)\n" % ( Node5.getNamespace(), Node5.getLocalName() ) ) # Tests with external id Node11 = N3Node( extid='abcdefghi' ) sys.stdout.write( "Node 11: "+str(Node11)+"\n" ) Node12 = N3Node( uri='http://example.org/base/local', extid='abcdefghi' ) sys.stdout.write( "Node 12: "+str(Node12)+"\n" ) Node13 = N3Node( extid='abcdefghi' ) sys.stdout.write( "Node 13: "+str(Node13)+"\n" ) Node14 = N3Node( uri='http://example.org/base/local', extid='xyzzy' ) sys.stdout.write( "Node 14: "+str(Node14)+"\n" ) Node15 = N3Node( extid='xyzzy' ) sys.stdout.write( "Node 15: "+str(Node15)+"\n" ) if Node11 == Node11: sys.stdout.write( "Node11 == Node11 (OK)\n" ) else: sys.stdout.write( "Node11 != Node11 (Error)\n" ) if Node12 == Node12: sys.stdout.write( "Node12 == Node12 (OK)\n" ) else: sys.stdout.write( "Node12 != Node12 (Error)\n" ) if Node13 == Node13: sys.stdout.write( "Node13 == Node13 (OK)\n" ) else: sys.stdout.write( "Node13 != Node13 (Error)\n" ) if Node14 == Node14: sys.stdout.write( "Node14 == Node14 (OK)\n" ) else: sys.stdout.write( "Node14 != Node14 (Error)\n" ) if Node15 == Node15: sys.stdout.write( "Node15 == Node15 (OK)\n" ) else: sys.stdout.write( "Node15 != Node15 (Error)\n" ) if Node11 == Node1 or Node1 == Node11: sys.stdout.write( "Node11 == Node1 (Error)\n" ) else: sys.stdout.write( "Node11 != Node1 (OK)\n" ) if Node11 == Node12 or Node12 == Node11: sys.stdout.write( "Node11 == Node12 (Error)\n" ) else: sys.stdout.write( "Node11 != Node12 (OK)\n" ) if Node11 == Node13 and Node13 == Node11: sys.stdout.write( "Node11 == Node13 (OK)\n" ) else: sys.stdout.write( "Node11 != Node13 (Error)\n" ) if Node11 == Node14 or Node14 == Node11: sys.stdout.write( "Node11 == Node14 (Error)\n" ) else: sys.stdout.write( "Node11 != Node14 (OK)\n" ) if Node11 == Node15 or Node15 == Node11: sys.stdout.write( "Node11 == Node15 (Error)\n" ) else: sys.stdout.write( "Node11 != Node15 (OK)\n" ) if Node12 == Node14 and Node14 == Node12: sys.stdout.write( "Node12 == Node14 (OK)\n" ) else: sys.stdout.write( "Node12 != Node14 (Error)\n" ) if Node12 == Node15 or Node15 == Node12: sys.stdout.write( "Node12 == Node15 (Error)\n" ) else: sys.stdout.write( "Node12 != Node15 (OK)\n" ) if Node13 == Node15 or Node15 == Node13: sys.stdout.write( "Node13 == Node15 (Error)\n" ) else: sys.stdout.write( "Node13 != Node15 (OK)\n" ) if Node14 == Node15 or Node15 == Node14: sys.stdout.write( "Node14 == Node15 (Error)\n" ) else: sys.stdout.write( "Node14 != Node15 (OK)\n" ) sys.stdout.write( "Exiting\n" ) #--------+---------+---------+---------+---------+---------+---------+---------+ # # $Log: N3Node.py,v $ # Revision 1.7 2004/03/26 21:26:23 graham # Bug-fixes imn report generation software to support new header # field registry document generation. Also added some generic # variable binding options to N3GenReport. # # Revision 1.6 2002/05/14 15:18:44 graham # Initial verion of N3ModelJenaMem.py, which # works with a Jena memory-based RDF store # # Revision 1.5 2002/05/01 15:43:48 graham # Cleanup small details # # Revision 1.4 2002/04/25 19:00:13 graham # Update CVS keyword fields # # Revision 1.3 2002/04/25 18:53:26 graham # Add copyright and disclaimer notices # # Revision 1.2 2002/04/23 17:09:57 graham # Basic query and report generation works # # Revision 1.1 2002/04/19 22:07:39 graham # Separated into smaller modules; basic N3Model functions working # # Revision 1.3 2002/04/18 07:39:42 graham # Added TODO # # Revision 1.2 2002/04/17 23:41:31 graham # Basic N3 parser is working #