From triplesoup-commits-return-61-apmail-incubator-triplesoup-commits-archive=incubator.apache.org@incubator.apache.org Fri Apr 13 08:57:57 2007 Return-Path: Delivered-To: apmail-incubator-triplesoup-commits-archive@locus.apache.org Received: (qmail 40263 invoked from network); 13 Apr 2007 08:57:44 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 13 Apr 2007 08:57:44 -0000 Received: (qmail 71310 invoked by uid 500); 13 Apr 2007 08:57:50 -0000 Delivered-To: apmail-incubator-triplesoup-commits-archive@incubator.apache.org Received: (qmail 71271 invoked by uid 500); 13 Apr 2007 08:57:50 -0000 Mailing-List: contact triplesoup-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: triplesoup-dev@incubator.apache.org Delivered-To: mailing list triplesoup-commits@incubator.apache.org Received: (qmail 71260 invoked by uid 99); 13 Apr 2007 08:57:49 -0000 Received: from herse.apache.org (HELO herse.apache.org) (140.211.11.133) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 13 Apr 2007 01:57:49 -0700 X-ASF-Spam-Status: No, hits=-99.5 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 13 Apr 2007 01:57:30 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id D66361A9874; Fri, 13 Apr 2007 01:56:43 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r528394 [13/35] - in /incubator/triplesoup/donations/TRIPLES-3-RDFStore: ./ dbms/ dbms/client/ dbms/client/t/ dbms/dbmsproxy/ dbms/deamon/ dbms/doc/ dbms/include/ dbms/libdbms/ dbms/utils/ doc/ include/ lib/ lib/DBD/ lib/RDFStore/ lib/RDFSt... Date: Fri, 13 Apr 2007 08:56:16 -0000 To: triplesoup-commits@incubator.apache.org From: leosimons@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20070413085643.D66361A9874@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/DBMS.pm URL: http://svn.apache.org/viewvc/incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/DBMS.pm?view=auto&rev=528394 ============================================================================== --- incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/DBMS.pm (added) +++ incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/DBMS.pm Fri Apr 13 01:56:01 2007 @@ -0,0 +1,167 @@ +# * +# * Copyright (c) 2000-2006 Alberto Reggiori +# * Dirk-Willem van Gulik +# * +# * NOTICE +# * +# * This product is distributed under a BSD/ASF like license as described in the 'LICENSE' +# * file you should have received together with this source code. If you did not get a +# * a copy of such a license agreement you can pick up one at: +# * +# * http://rdfstore.sourceforge.net/LICENSE +# * +# * +# * DBMS.pm -- Perl 5 interface to DBMS sockets +# * +# * +=NAME DBMS + +=head1 NAME + +DBMS - Perl5 access to a dbms server. + +=head1 SYNOPSIS + + use DBMS ; + $x=tie %hash, 'DBMS', $type,$name; + + # Use the %hash array. + $hash{ aap } = noot; + foreach $k (keys(%hash)) { + print "$k - $hash{ $k }\n"; + }; + # and destroy.. + undef $x; + +=head1 DESCRIPTION + +B is a module which allows Perl programs to make use of the +facilities provided by the dbms server. The dbms server is a small +server with a bit of glue to the Berkeley DB (> 1.85 < 2.x) and some code +to listen to a few sockets. + +=head1 DETAILS + +As the devil is in the details... this module supports three +functions which are not part of the normal tie interface; +atomic counter increment, atomic counter decrement and atomic list retrival. + +The increment and decrement functions increments or decrement a counter before it returns +a value. Thus a null or undef value can safely be taken as +an error. + +=head2 EXAMPLE + + use DBMS ; + $x=tie %hash, 'DBMS', $type,$name + or die "Could not ty to $name: $!"; + + $hash{ counter } = 0; + + $my_id = $x->inc( 'counter' ) + or die "Oi"; + + $my_id = $x->dec( 'counter' ) + or die "Oi oi"; + + # and these are not quite implemented yet.. + # + @keys = $x->keys(); + @values = $x->values(); + @all = $x->hash(); + +=head1 VERSION + +$Id: DBMS.pm,v 1.5 2006/06/19 10:10:24 areggiori Exp $ + +=head1 AVAILABILITY + +Well, ah hmmm. For ParlEuNet at the beginning but now for the whole CPAN community. + +=head1 BUGS + +Memory management not fully checked. Some speed issues, I.e. only +about 100 TPS. No proper use of $! and $@, i.e. it will just croak, +carp or return an undef. And there is no automagic retry should you +loose the connection. + +=head1 Author + +Dirk-Willem van Gulik and Alberto Reggiori + +=head1 SEE ALSO + +L, L L L. + +=cut + +package DBMS; + +$E_NOSUCHDATABASE = 1011; + +use strict; +use vars qw($VERSION @ISA $AUTOLOAD); + +use RDFStore; # load the underlying C code in RDFStore.xs because it is all in one module file + +use Carp; +use Tie::Hash; +use AutoLoader; +@ISA = qw(Tie::Hash); + +$VERSION = "1.7"; + +# some inlin-ed h2ph macros - need to be in-sync with dbms/include/dbms.h +eval("sub DBMS::EVENT_RECONNECT () { 0; }") unless defined(&DBMS::EVENT_RECONNECT); +eval("sub DBMS::EVENT_WAITING () { 1; }") unless defined(&DBMS::EVENT_WAITING); +eval("sub DBMS::XSMODE_DEFAULT () { 0; }") unless defined(&DBMS::XSMODE_DEFAULT); +eval("sub DBMS::XSMODE_RDONLY () { 1; }") unless defined(&DBMS::XSMODE_RDONLY); +eval("sub DBMS::XSMODE_RDWR () { 2; }") unless defined(&DBMS::XSMODE_RDWR); +eval("sub DBMS::XSMODE_CREAT () { 3; }") unless defined(&DBMS::XSMODE_CREAT); +eval("sub DBMS::XSMODE_DROP () { 4; }") unless defined(&DBMS::XSMODE_DROP); + + +# B-tree comparinson functions - see include/rdfstore_flat_store.h for definitions +eval("sub DBMS::BT_COMP_INT () { 7000; }") unless defined(&DBMS::BT_COMP_INT); +eval("sub DBMS::BT_COMP_DOUBLE () { 7001; }") unless defined(&DBMS::BT_COMP_DOUBLE); +eval("sub DBMS::BT_COMP_DATE () { 7002; }") unless defined(&DBMS::BT_COMP_DATE); + +sub inc { + my($class,$key)=@_; + return $class->INC($key); +}; + +sub dec { + my($class,$key)=@_; + return $class->DEC($key); +}; + +sub isalive { + my($class,$key)=@_; + return $class->PING($key); +}; + +sub drop { + my($class,$key)=@_; + return $class->DROP($key); +}; + +sub AUTOLOAD { + my($constname); + ($constname = $AUTOLOAD) =~ s/.*:://; + my $val = constant($constname, @_ ? $_[0] : 0); + if ($! != 0) { + if ($! =~ /Invalid/) { + $AutoLoader::AUTOLOAD = $AUTOLOAD; + goto &AutoLoader::AUTOLOAD; + } + else { + Carp::croak("Your vendor has not defined DBMS macro $constname, used"); + } + } + eval "sub $AUTOLOAD { $val }"; + goto &$AUTOLOAD; +} + +1; +__END__ Propchange: incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/DBMS.pm ------------------------------------------------------------------------------ svn:executable = * Added: incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Digest/Digestable.pm URL: http://svn.apache.org/viewvc/incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Digest/Digestable.pm?view=auto&rev=528394 ============================================================================== --- incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Digest/Digestable.pm (added) +++ incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Digest/Digestable.pm Fri Apr 13 01:56:01 2007 @@ -0,0 +1,50 @@ +# * +# * Copyright (c) 2000-2004 Alberto Reggiori +# * Dirk-Willem van Gulik +# * +# * NOTICE +# * +# * This product is distributed under a BSD/ASF like license as described in the 'LICENSE' +# * file you should have received together with this source code. If you did not get a +# * a copy of such a license agreement you can pick up one at: +# * +# * http://rdfstore.sourceforge.net/LICENSE +# * +# * Changes: +# * version 0.1 - Wed May 23 18:16:29 CEST 2001 +# * + +package RDFStore::Digest::Digestable; +{ +use vars qw ($VERSION); +use strict; + +$VERSION = '0.1'; + +sub new { + bless {} , shift; +}; + +sub getDigest { +}; + +1; +}; + +__END__ + +=head1 NAME + +RDFStore::Digest::Digestable - implementation of the Digestable RDF API + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +=head1 SEE ALSO + +Digest(3) + +=head1 AUTHOR + + Alberto Reggiori Added: incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Literal.pm URL: http://svn.apache.org/viewvc/incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Literal.pm?view=auto&rev=528394 ============================================================================== --- incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Literal.pm (added) +++ incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Literal.pm Fri Apr 13 01:56:01 2007 @@ -0,0 +1,143 @@ +# * +# * Copyright (c) 2000-2006 Alberto Reggiori +# * Dirk-Willem van Gulik +# * +# * NOTICE +# * +# * This product is distributed under a BSD/ASF like license as described in the 'LICENSE' +# * file you should have received together with this source code. If you did not get a +# * a copy of such a license agreement you can pick up one at: +# * +# * http://rdfstore.sourceforge.net/LICENSE +# * +# * Changes: +# * version 0.1 - 2000/11/03 at 04:30 CEST +# * version 0.2 +# * - modified new() equals(), getLabel() methods accordingly to rdf-api-2000-10-30 +# * - modified toString() +# * version 0.3 +# * - fixed bugs when checking references/pointers (defined and ref() ) +# * version 0.4 +# * - updated accordingly to rdf-api-2001-01-19 +# * - modified getLabel() and getURI() to return a lebel even if the Literal is a BLOB (using Storable) +# * - updated equals() method to make a real comparison of BLOBs using Storable module +# * version 0.41 +# * - added getDigest() to generate the digest using quotes and the label +# * version 0.42 +# * - updated accordingly to new RDFStore API +# * - removed BLOB support +# * + +package RDFStore::Literal; +{ +use vars qw ($VERSION); +use strict; + +use Carp; + +$VERSION = '0.42'; + +use Carp; +use RDFStore; # load the underlying C code in RDFStore.xs because it is all in one module file +use RDFStore::RDFNode; + +sub equals { + return 0 + unless(defined $_[1]); + + return 0 + if ( ref($_[1]) =~ /^(SCALAR|ARRAY|HASH|CODE|REF|GLOB|LVALUE)/ ); #see perldoc perlfunc ref() + + my $label1 = $_[0]->getLabel(); + my $label2; + if( ($_[1]) && + (ref($_[1])) && + ($_[1]->isa("RDFStore::Literal")) ) { + $label2 = $_[1]->getLabel(); + } else { + $label2 = $_[1]; + }; + + return ($label1 eq $label2) ? 1 : 0; + }; + +1; +}; + +__END__ + +=head1 NAME + +RDFStore::Literal - An RDF Literal Node implementation + +=head1 SYNOPSIS + + use RDFStore::Literal; + my $literal = new RDFStore::Literal('Tim Berners-Lee'); + my $literal1 = new RDFStore::Literal('Today is a sunny day, again :)'); + + print $literal->toString." is "; + print "not" + unless $literal->equals($literal1); + print " equal to ".$literal1->toString."\n"; + +=head1 DESCRIPTION + +An RDF Literal Node implementation using Storable(3). A Literal object can either contain plain (utf8) strings. Such an implementation allows to create really generic RDF statements about Perl data-structures or objects for example. Generally an RDFStore::Literal can be thought like an atomic perl scalar. +XML well-formed literal values are supported simply by storing the resulting utf8 bytes into a perl scalar; none methods are being provided tomanage literals as XML (e.g. SAX2 events and stuff like that) + +=head1 METHODS + +=over 4 + +=item new ( LITERAL ) + +This is a class method, the constructor for RDFStore::Literal. The only parameter passed is either a plain perl scalar (LITERAL) + +=item getParseType + +Return the parseType of the RDF Literal; possible values are I or I (B). + +=item getLang + +Return the language of the RDF Literal eventually coming from xml:lang attribute on parsing. + +=item getDataType + +Return the RDFStore::Resource representing the XMLSchema data type of the RDF Literal. + +=item getLabel + +Return the literal text of the node. + +=item getDigest + +Return a Cryptographic Digest (SHA-1 by default) of the RDF Literal; the actual digest message is guranteed to be different for URI representing RDF Resources or RDF Literals. + +=item equals + +Compare two literals. + +=head1 SEE ALSO + +RDFStore::RDFNode(3) + +=head1 ABOUT RDF + + http://www.w3.org/TR/rdf-primer/ + + http://www.w3.org/TR/rdf-mt + + http://www.w3.org/TR/rdf-syntax-grammar/ + + http://www.w3.org/TR/rdf-schema/ + + http://www.w3.org/TR/1999/REC-rdf-syntax-19990222 (obsolete) + +=head1 BUGS + +The language of the literal as recently specified by the RDF Core WG is not supported and the typed literals are not implemented; the latter is due mainly because perl is an untyped language and perhaps such data-typing abstractions should fit in a higher level application specific API. + +=head1 AUTHOR + + Alberto Reggiori Added: incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Model.pm URL: http://svn.apache.org/viewvc/incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Model.pm?view=auto&rev=528394 ============================================================================== --- incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Model.pm (added) +++ incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/Model.pm Fri Apr 13 01:56:01 2007 @@ -0,0 +1,1494 @@ +# * +# * Copyright (c) 2000-2006 Alberto Reggiori +# * Dirk-Willem van Gulik +# * +# * NOTICE +# * +# * This product is distributed under a BSD/ASF like license as described in the 'LICENSE' +# * file you should have received together with this source code. If you did not get a +# * a copy of such a license agreement you can pick up one at: +# * +# * http://rdfstore.sourceforge.net/LICENSE +# * +# * Changes: +# * version 0.1 - 2000/11/03 at 04:30 CEST +# * version 0.2 +# * - fixed bug in new() to check if triples is HASH ref when passed by user +# * - fixed bug in find() do avoid to return instances of SetModel (see SchemaModel.pm also) +# * Now result sets are put in an object(model) of the the same type - see find() +# * - modified add() remove() clone() duplicate() and added toString() makePrivate() +# * getNamespace() getLocalName() methods accordingly to rdf-api-2000-10-30 +# * - modifed new(), duplicate(), clone() and find() to support cloned models +# * Due the fact that Data::MagicTie does not support the clone method, when +# * either the triples or the index are duplicated (or cloned) the user could +# * specify on which HASH(es) (tied or not) to store the results (see duplicate()) +# * - modified find() to manage normal Models and indexed Models differently +# * - added optional indirect indexing to find() i.e. the FindIndex stores just digested keys +# * and not the full BLOB; fetch from an index then require an additional look up in triples +# * version 0.3 +# * - fixed bug in find(). Check the type of $t before using methods on it +# * - added toStrawmanRDF() to serialise the model in strawman RDF for RDFStore::Parser::OpenHealth +# * - fixed bug in create() +# * - fixed bugs when checking references/pointers (defined and ref() ) +# * - modified updateDigest() method accordingly to rdf-api-2000-11-13 +# * version 0.31 +# * - commented out isEmpty() check in find() due to DBMS(3) efficency problems +# * - fixed bug in add() when adding statements with a Literal value +# * - updated toStrawmanRDF() method +# * - modifed add() to avoid update of existing statements +# * version 0.4 +# * - modifed add() to return undef if the triples exists already in the database +# * - changed way to return undef in subroutines +# * - renamed triples hash to store +# * - adapted to use the new Data::MagicTie interface +# * - complete re-design of the indexing and storage method +# * - added getOptions() method +# * - Devon Smith changed getDigestBytes() to generate digests and hashes +# * that match Stanford java ones exactly +# * - added inheritance from RDFStore::Digest::Digestable +# * - removed RDFStore::Resource inheritance +# * version 0.41 +# * - updated _getLookupValue() and _getValuesFromLookup() to consider negative hashcodes +# * version 0.42 +# * - complete redesign of the indexing method up to free-text search on literals +# * - added tied array iterator RDFStore::Model::Statements to allow fetching results one by one +# * - modified find() to allow a 4th paramater to make free-text search over literals +# * version 0.43 +# * - brand new design now using the faster C/XS RDFStore(3) module....finally :) +# * - updated methods to avoid a full copy of statements across when the model is shared if possible +# * - added basic support for statements grouping - see setContext(), getContext() and resetContext() +# * - zapped toStrawmanRDF() method +# * - added serialize() method to generally dump a model/graph to a string or filehanlde +# * - added isConnected() and isRemote() methods +# * - added unite(), subtract(), intersect(), complement() and exor() methods +# * - re-added RDFStore::Resource inheritance +# * - added getParser(), getReader(), getSerializer() and getWriter() methods +# * version 0.44 +# * - updated search() method call to use new XS code interface (hash ref) +# * - added ifModifiedSince() method +# * + +package RDFStore::Model; +{ +use vars qw ($VERSION); +use strict; + +$VERSION = '0.44'; + +use Carp; + +use RDFStore; +use RDFStore::Digest::Digestable; +use RDFStore::Literal; +use RDFStore::Resource; +use RDFStore::Object; +use RDFStore::Statement; +use RDFStore::NodeFactory; +use RDFStore::Parser::SiRPAC; +use RDFStore::Parser::NTriples; +use RDFStore::Serializer::RDFXML; +use RDFStore::Serializer::NTriples; +use RDFStore::Util::Digest; + +@RDFStore::Model::ISA = qw( RDFStore::Resource RDFStore::Digest::Digestable ); + +sub new { + my ($pkg,%params) = @_; + + my $self = {}; + + # first operation creates lookup table + $self->{nodeFactory}=( (exists $params{nodeFactory}) && + (defined $params{nodeFactory}) && + (ref($params{nodeFactory})) && + ($params{nodeFactory}->isa("RDFStore::NodeFactory")) ) ? + $params{nodeFactory} : new RDFStore::NodeFactory(); + + $self->{options} = \%params; + + #store + my @params = (); + + if ( (exists $params{Name}) && + (defined $params{Name}) ) { + push @params, $params{Name}; + } else { + push @params,undef; + }; + if ( (exists $params{Mode}) && + (defined $params{Mode}) ) { + push @params, ($params{Mode} eq 'r') ? 1 : 0; + } else { + push @params,0; + }; + if ( (exists $params{FreeText}) && + (defined $params{FreeText}) ) { + push @params, ($params{FreeText} =~ /(1|on|yes|true)/i) ? 1 : 0; + } else { + push @params,0; + }; + if ( (exists $params{Sync}) && + (defined $params{Sync}) ) { + push @params, int($params{Sync}); + } else { + push @params,0; + }; + if ( ( (exists $params{Host}) && + (defined $params{Host}) ) || + ( (exists $params{Port}) && + (defined $params{Port}) ) ) { + push @params, 1; + } else { + push @params, 0; + }; + if ( (exists $params{Host}) && + (defined $params{Host}) ) { + push @params, $params{Host}; + } else { + push @params,undef; + }; + if ( (exists $params{Port}) && + (defined $params{Port}) ) { + push @params, $params{Port}; + } else { + push @params,undef; + }; + + $self->{'rdfstore'} = new RDFStore( @params ); + + die "Cannot connect rdfstore" + unless( (defined $self->{rdfstore}) && + (ref($self->{rdfstore})) && + ($self->{rdfstore}->isa("RDFStore")) ); + + $self->{'rdfstore_params'} = \%params; + + bless $self,$pkg; + + return $self; +}; + +# set a context for the statements (i.e. each asserted statement will get such a context automatically) +# NOTE: this stuff I can not still understand how could be related to reification/logic/inference but it should... +sub setContext { + my ($class,$context)=@_; + + $class->{rdfstore}->set_context( $context ); + }; + +# reset the context for the statements (i.e. each asserted statement will be in a *empty* context after calling this method) +sub resetContext { + my ($class)=@_; + + $class->{rdfstore}->reset_context; + }; + +#return actual defined context of the model +sub getContext { + my ($class)=@_; + + my $ctx = $class->{rdfstore}->get_context; + + return + unless($ctx); + + return ($ctx->isbNode) ? $class->{nodeFactory}->createAnonymousResource($ctx->toString) : $class->{nodeFactory}->createResource($ctx->toString); + }; + +# return model options +sub getOptions { + return %{$_[0]->{'options'}}; + }; + +sub isAnonymous { + return 0; + }; + +sub getNamespace { + return undef; + }; + +sub getLocalName { + return $_[0]->getURI(); + }; + +sub toString { + return "Model[".$_[0]->getSourceURI()."]"; + }; + +# Set a base URI for the model +sub setSourceURI { + $_[0]->{rdfstore}->set_source_uri( ( (ref($_[1])) && ($_[1]->isa("RDFStore::Resource")) ) ? $_[1]->toString : $_[1] ); + # we shuld probably set it as default context eventually but I am not sure.... + }; + +# Returns current base URI for the model +sub getSourceURI { + $_[0]->{rdfstore}->get_source_uri; + }; + +# model access methods + +# return the number of triples in the model +sub size { + if( (exists $_[0]->{Shared}) && + (defined $_[0]->{Shared}) ) { + if(exists $_[0]->{query_iterator}) { + return $_[0]->{query_iterator}->size; + } else { + return $_[0]->{Shared}->size; + }; + }; + + $_[0]->{rdfstore}->size; + }; + +# check whether or not the model is empty +sub isEmpty { + if( (exists $_[0]->{Shared}) && + (defined $_[0]->{Shared}) ) { + if(exists $_[0]->{query_iterator}) { + return ( $_[0]->{query_iterator}->size > 0 ) ? 0 : 1; + } else { + return $_[0]->{Shared}->isEmpty; + }; + }; + $_[0]->{rdfstore}->is_empty; + }; + +sub isConnected { + if( (exists $_[0]->{Shared}) && + (defined $_[0]->{Shared}) ) { + return $_[0]->{Shared}->isConnected; + }; + $_[0]->{rdfstore}->is_connected; + }; + +sub isRemote { + if( (exists $_[0]->{Shared}) && + (defined $_[0]->{Shared}) ) { + return $_[0]->{Shared}->isRemote; + }; + $_[0]->{rdfstore}->is_remote; + }; + +sub ifModifiedSince { + if( (exists $_[0]->{Shared}) && + (defined $_[0]->{Shared}) ) { + return $_[0]->{Shared}->ifModifiedSince( $_[1] ); + }; + + &RDFStore::if_modified_since( $_[0]->{'rdfstore_params'}->{'Name'}, $_[1] ); + }; + +# return an instance of RDFStore::Model::Iterator +sub elements { + my ($class) = @_; + + if( (exists $class->{Shared}) && + (defined $class->{Shared}) ) { + if(exists $class->{query_iterator}) { + if( ($class->{query_iterator}->size > 0 ) && + (defined $class->{query}) && + (ref($class->{query})=~/ARRAY/) ) { + delete($class->{query}); + }; + # iterator over result set + return RDFStore::Model::Iterator->new( $class->getNodeFactory, + $class->{query_iterator} ); + } else { + return $class->{Shared}->elements; + }; + } else { + # normal iterator over the whole model + return RDFStore::Model::Iterator->new( $class->getNodeFactory, + $class->{rdfstore}->elements ); + }; + }; + +sub namespaces { + my ($class) = @_; + + my %ns_table=(); + + # must scan the whole database of course :-( + my $itr = $class->elements; + while ( my $p = $itr->each_predicate ) { + my $ns_uri = $p->getNamespace; + + next + unless(defined $ns_uri); + + $ns_table{ $ns_uri } = 1 + unless(exists $ns_table{ $ns_uri }); + }; + + return keys %ns_table; + }; + +# tests if the model contains a given statement +sub contains { + return 0 + unless( (defined $_[1]) && + (ref($_[1])) && + ($_[1]->isa("RDFStore::Statement")) ); + + croak "Statement context '".$_[2]."' is not instance of RDFStore::Resource" + unless( (not(defined $_[2])) || + ( (defined $_[2]) && + (ref($_[2])) && + ($_[2]->isa('RDFStore::Resource')) ) ); + + my $context; + if(defined $_[2]) { + $context = $_[2]; + } else { + $context = $_[1]->context + if($_[1]->context); + }; + + if( (exists $_[0]->{Shared}) && + (defined $_[0]->{Shared}) ) { + if(exists $_[0]->{query_iterator}) { + return 0 + if($_[0]->{query_iterator}->size <= 0); #got an empty query + + # simply use the iterator we got from last query to quickly (??) check whether or not the st_id exists in the rdfstore; efficient, really?? + return $_[0]->{query_iterator}->contains( $_[1], undef, undef, $context ); + } else { + return ( $_[0]->find( $_[1]->subject, $_[1]->predicate, $_[1]->object, $context )->elements->size == 1 ); + }; + }; + + return $_[0]->{rdfstore}->contains( $_[1], undef, undef, $context ); + }; + +# Model manipulation: add, remove, find +# +# NOTE: it is not really safe here - we might need to lock all DBs, add statement, unlock and return (TXP) :) +# +# Adds a new triple to the model +sub add { + my ($class, $subject,$predicate,$object,$context) = @_; + + croak "Subject or Statement '".$subject."' is either not instance of RDFStore::Statement or RDFStore::Resource" + unless( (defined $subject) && + (ref($subject)) && + ( ($subject->isa('RDFStore::Resource')) || + ($subject->isa('RDFStore::Statement')) ) ); + croak "Predicate '".$predicate."' is not instance of RDFStore::Resource" + unless( (not(defined $predicate)) || + ( (defined $predicate) && + (ref($predicate)) && + ($predicate->isa('RDFStore::Resource')) ) ); + croak "Object '".$object."' is not instance of RDFStore::RDFNode" + unless( (not(defined $object)) || + ( ( (defined $object) && + (ref($object)) && + ($object->isa('RDFStore::RDFNode'))) || + ( (defined $object) && + ($object !~ m/^\s+$/) ) ) ); + + croak "Statement context '".$context."' is not instance of RDFStore::Resource" + unless( (not(defined $context)) || + ( (defined $context) && + (ref($context)) && + ($context->isa('RDFStore::Resource')) ) ); + + if( (defined $subject) && + (ref($subject)) && + ($subject->isa("RDFStore::Statement")) && + (not(defined $predicate)) && + (not(defined $object)) ) { + $context = $subject->context + unless(defined $context); + ($subject,$predicate,$object) = ($subject->subject, $subject->predicate, $subject->object); + } elsif( (defined $object) && + (!(ref($object))) ) { + $object = $class->{nodeFactory}->createLiteral($object); + }; + + if( (exists $class->{Shared}) && + (defined $class->{Shared}) ) { + if(exists $class->{query_iterator}) { + # simply use the iterator we got from last query to quickly (??) check whether or not the st_id exists in the rdfstore + # this should save the expensive _copyOnWrite() below eventually + return 0 #is it working also for empty queries???! + if ( $class->{query_iterator}->contains( $subject, $predicate, $object, $context ) ); + }; + # copy across stuff if necessary + $class->_copyOnWrite(); + }; + + my $status = $_[0]->{rdfstore}->insert( $subject, $predicate, $object, $context ); + + $class->updateDigest($subject,$predicate,$object,$context); #add context to updateDigest() as well??? + + return $status; + }; + +sub updateDigest { + delete $_[0]->{digest}; + + #return + # unless(defined $_[0]->{digest}); + # see http://nestroy.wi-inf.uni-essen.de/rdf/sum_rdf_api/#K31 + #my $digest = $_[1]->getDigest(); + #RDFStore::Util::Digest::xor($_[0]->getDigest(),$digest->getDigest()); + }; + +# Removes the triple from the model +# NOTE: it is not really safe here - we might need to lock all DBs, del statement, unlock and return (TXP) :) +sub remove { + croak "Statement '".$_[1]."' is not instance of RDFStore::Statement" + unless( (defined $_[1]) && + (ref($_[1])) && + ($_[1]->isa('RDFStore::Statement')) ); + + croak "Statement context '".$_[2]."' is not instance of RDFStore::Resource" + unless( (not(defined $_[2])) || + ( (defined $_[2]) && + (ref($_[2])) && + ($_[2]->isa('RDFStore::Resource')) ) ); + + my $context; + if(defined $_[2]) { + $context = $_[2]; + } else { + $context = $_[1]->context + if($_[1]->context); + }; + + # copy across stuff if necessary + $_[0]->_copyOnWrite() + if( (exists $_[0]->{Shared}) && + (defined $_[0]->{Shared}) ); + + my $status = $_[0]->{rdfstore}->remove( $_[1], undef, undef, $context ); + + $_[0]->updateDigest($_[1]->subject, $_[1]->predicate, $_[1]->object, $context); + + return $status; + }; + +sub isMutable { + return 1; + }; + +# General method to search for triples. +# null input for any parameter will match anything. +# Example: $result = $m->find( undef, $RDFStore::Vocabulary::RDF::type, new RDFStore::Resource("http://...#MyClass"), [ context, words_operator, @words ] ); +# finds all instances in the model +sub find { + my ($class) = shift; + my ($subject,$predicate,$object,$context,$words_operator,@words) = @_; + + croak "Subject '".$subject."' is not instance of RDFStore::Resource" + unless( (not(defined $subject)) || + ( (defined $subject) && + (ref($subject)) && + ($subject->isa('RDFStore::Resource')) ) ); + croak "Predicate '".$predicate."' is not instance of RDFStore::Resource" + unless( (not(defined $predicate)) || + ( (defined $predicate) && + (ref($predicate)) && + ($predicate->isa('RDFStore::Resource')) ) ); + croak "Object '".$object."' is not instance of RDFStore::RDFNode" + unless( (not(defined $object)) || + ( (defined $object) && + (ref($object)) && + ($object->isa('RDFStore::RDFNode')) ) ); + + croak "Statement context '".$context."' is not instance of RDFStore::Resource" + unless( (not(defined $context)) || + ( (defined $context) && + (ref($context)) && + ($context->isa('RDFStore::Resource')) ) ); + + # e.g. $class->find($subject,$predicate,$object)->find(....) and so on + # NOTE: we are trying to improve this by avoiding DB operations using shared_ids of statements/properties..... + if( (exists $class->{Shared}) && + (defined $class->{Shared}) ) { + $class->{Shared}->{sharing_query_iterator} = $class->{query_iterator}->duplicate + if(exists $class->{query_iterator}); + return $class->{Shared}->find($subject,$predicate,$object,$context,$words_operator,@words); + }; + + my @query = @_; + $class->{query} = \@query; + + # we have the same problem like in Pen - a result set must be a model/collection :-) + my $res = $class->create(); #EMPTY MODEL + + #skip 2 FETCHES for the moment - efficency + #return $res + # if($class->{rdfstore}->is_empty()); + + #share IDs till first write operation such as add() or remove() on query result model + # NOTE: sharing avoid add() full-blown statements to the result model + $res->{Shared}=$class; + + $res->setContext( $context ) #correct ???!!??? + if(defined $context); + + if( (not(defined $subject)) && + (not(defined $predicate)) && + (not(defined $object)) && + (not(defined $context)) && + ($#words < 0) ) { + # does NOT work for shared models yet!!!! - note: the duplicate() above could have fixed it :) + if ( exists $class->{sharing_query_iterator}) { + $res->{query_iterator} = $class->{sharing_query_iterator}; + delete $class->{sharing_query_iterator}; + + return $res; + } else { + my $d = $class->duplicate(); + #$d->setContext( $context ) #correct???!?? + # if(defined $context); #impossible + return $d; + }; + }; + + #all non-words operators are set to 0=OR - will need 1=AND for real RDQL query + my $query = { 'search_type' => 0, #default triple-pattern search + "s" => [], + "s_op" => "or", + "p" => [], + "p_op" => "or", + "o" => [], + "o_op" => "or", + "c" => [], + "c_op" => "or", + "xml:lang" => [], + "xml:lang_op" => "or", + "rdf:datatype" => [], + "rdf:datatype_op" => "or" + }; + + my @qq=(); + if($subject) { + push @{$query->{'s'}}, $subject; + }; + if($predicate) { + push @{$query->{'p'}}, $predicate; + }; + if($object) { + push @{$query->{'o'}}, $object; + #still need to add xml:lang and rdf:datatype for passed object here... + }; + if($context) { + push @{$query->{'c'}}, $context; + }; + $query->{'words_op'} = ( (defined $words_operator) && + ($words_operator =~ /(and|&|1)/i) ) ? 'and' : + ( (defined $words_operator) && + ($words_operator =~ /(not|~|2)/i) ) ? 'not' : 'or' ; + + push @{$query->{'words'}}, @words; + + my $iterator = $class->{rdfstore}->search( $query ); + + if ( exists $class->{sharing_query_iterator}) { + # intersect/diff the two iterators + $res->{query_iterator} = $class->{sharing_query_iterator}->intersect( $iterator ); + delete $class->{sharing_query_iterator}; + } else { + $res->{query_iterator} = $iterator; + }; + + return $res; + }; + +sub fetch_object { + my ($class,$resource,$context) = @_; + + croak "Resource '".$resource."' is not instance of RDFStore::Resource" + unless( (defined $resource) && + (ref($resource)) && + ($resource->isa('RDFStore::Resource')) ); + + croak "Context '".$context."' is not instance of RDFStore::Resource" + unless( (not(defined $context)) || + ( (defined $context) && + (ref($context)) && + ($context->isa('RDFStore::Resource')) ) ); + + return + if( $resource->isbNode ); + + # we have the same problem like in Pen - a result set must be a model/collection :-) + my $res = $class->create(); #EMPTY MODEL + + #share IDs till first write operation such as add() or remove() on query result model + # NOTE: sharing avoid add() full-blown statements to the result model + $res->{Shared}=$class; + + $res->setContext( $context ) #correct ???!!??? + if(defined $context); + +#print "FETCH --> ".$resource->toString."\n"; + $res->{query_iterator} = $class->{rdfstore}->fetch_object( ($resource->isa("RDFStore::Object")) ? $resource->{'rdf_object'} : $resource, $context ); + + return $res; + }; + +sub getResource { + my ($class,$resource) = @_; + + my $object = new RDFStore::Object( $resource ); + $object->load( $class->fetch_object( $class->getNodeFactory->createResource($resource) ) ); + + return $object; #return a new in-memory RDF object (and relative model) + }; + +# clone the model - So due that copy is expensive we use sharing :) +sub duplicate { + my ($class) = @_; + + return $class->{Shared}->duplicate + if( (exists $class->{Shared}) && + (defined $class->{Shared}) ); + + my $new = $class->create(); + + # return a model that shares store and lookup with this model + # delegate read operations till first write operation such as add() or remove() + # NOTE: sharing avoid to copy right the way the whole original model that could be very large :) + # This trick allows to chain nicely find() methods + $new->{Shared} = $class; + + # set default context if any was set + my $sg = $class->getContext; + $new->setContext( $sg ) + if(defined $sg); + return $new; +}; + +# Creates in-memory empty model with the same options but Sync +sub create { + my($class) = shift; + + my $self = ref($class); + my $new = $self->new(); #we also get empty RDFStore(3) + + return $new; + }; + +sub getNodeFactory { + return $_[0]->{nodeFactory}; + }; + +sub getLabel { + return $_[0]->getURI; + }; + +sub getURI { + if($_[0]->isEmpty()) { + return $_[0]->{nodeFactory}->createUniqueResource()->toString(); + } else { + return "urn:rdf:". + &RDFStore::Util::Digest::getDigestAlgorithm()."-". + unpack("H*", $_[0]->getDigest() ); + }; + }; + +sub getDigest { + unless ( defined $_[0]->{digest} ) { + sub digest_sorter { + my @a1 = unpack "c*",$a->getDigest(); + my @b1 = unpack "c*",$b->getDigest(); + my $i; + for ($i=0; $i < $#a1 +1; $i++) { + return $a1[$i] - $b1[$i] unless ord $a1[$i] == ord $b1[$i]; + }; + return 0; + }; + my $t; + my $digest_bytes; + my ($el) = $_[0]->elements; + my @sts = (); + my $ss; + for ( $ss = $el->first; + $el->hasnext; + $ss = $el->next ) { + push @sts, $ss; + }; + for $t ( sort digest_sorter @sts ){ #this still fetches all statements in-memory :( + $digest_bytes .= $t->getDigest(); + }; + $_[0]->{digest} = RDFStore::Util::Digest::computeDigest($digest_bytes); + }; + return $_[0]->{digest}; + }; + +#set operations on RDFStore::Model using RDFStore::Iterator (mostly efficient in-memory) +sub intersect { + my ($class,$other) = @_; + + return + unless($other); + + croak "Model '".$other."' is not instance of RDFStore::Model" + unless( (defined $other) && (ref($other)) && + ($other->isa('RDFStore::Model')) ); + + croak "Models can not be intersected" + unless( ( $class->{Shared} == $class->{Shared} ) || + ( $class->{rdfstore} == $other->{rdfstore} ) ); + + my $res = $class->create(); #EMPTY MODEL in-memory + + $res->{Shared} = $class; # share storage (the other is sharing it anyway by definition :-) + + my $iter = $class->elements->intersect( $other->elements ); # that easy :) + + return + unless($iter); + + $res->{query_iterator} = $iter->{iterator}; + + # set default context if any was set + my $sg = $class->getContext; + $res->setContext( $sg ) + if(defined $sg); + + return $res; + }; + +sub subtract { + my ($class,$other) = @_; + + return + unless($other); + + croak "Model '".$other."' is not instance of RDFStore::Model" + unless( (defined $other) && (ref($other)) && + ($other->isa('RDFStore::Model')) ); + + croak "Models can not be subtracted" + unless( ( $class->{Shared} == $class->{Shared} ) || + ( $class->{rdfstore} == $other->{rdfstore} ) ); + + my $res = $class->create(); #EMPTY MODEL in-memory + + $res->{Shared} = $class; # share storage (the other is sharing it anyway by definition :-) + + my $iter = $class->elements->subtract( $other->elements ); + + return + unless($iter); + + $res->{query_iterator} = $iter->{iterator}; + + # set default context if any was set + my $sg = $class->getContext; + $res->setContext( $sg ) + if(defined $sg); + + return $res; + }; + +sub unite { + my ($class,$other) = @_; + + return + unless($other); + + croak "Model '".$other."' is not instance of RDFStore::Model" + unless( (defined $other) && (ref($other)) && + ($other->isa('RDFStore::Model')) ); + + croak "Models can not be united" + unless( ( $class->{Shared} == $class->{Shared} ) || + ( $class->{rdfstore} == $other->{rdfstore} ) ); + + my $res = $class->create(); #EMPTY MODEL in-memory + + $res->{Shared} = $class; # share storage (the other is sharing it anyway by definition :-) + + my $iter = $class->elements->unite( $other->elements ); + + return + unless($iter); + + $res->{query_iterator} = $iter->{iterator}; + + # set default context if any was set + my $sg = $class->getContext; + $res->setContext( $sg ) + if(defined $sg); + + return $res; + }; + +sub complement { + my ($class) = @_; + + my $res = $class->create(); #EMPTY MODEL in-memory + + $res->{Shared} = $class; # share storage (the other is sharing it anyway by definition :-) + + my $iter = $class->elements->complement; + + return + unless($iter); + + $res->{query_iterator} = $iter->{iterator}; + + # set default context if any was set + my $sg = $class->getContext; + $res->setContext( $sg ) + if(defined $sg); + + return $res; + }; + +sub exor { + my ($class,$other) = @_; + + return + unless($other); + + croak "Model '".$other."' is not instance of RDFStore::Model" + unless( (defined $other) && (ref($other)) && + ($other->isa('RDFStore::Model')) ); + + croak "EXOR can not be performed between the two given models" + unless( ( $class->{Shared} == $class->{Shared} ) || + ( $class->{rdfstore} == $other->{rdfstore} ) ); + + my $res = $class->create(); #EMPTY MODEL in-memory + + $res->{Shared} = $class; # share storage (the other is sharing it anyway by definition :-) + + my $iter = $class->elements->exor( $other->elements ); # that easy :) + + return + unless($iter); + + $res->{query_iterator} = $iter->{iterator}; + + # set default context if any was set + my $sg = $class->getContext; + $res->setContext( $sg ) + if(defined $sg); + + return $res; + }; + +#serialize the model/graph as string or to a filehandle using a specific syntax ("RDF/XML", "N-Triples") +sub serialize { + my ($class, $fh, $syntax, $namespaces, $base ) = @_; + + my $serializer; + if( (! $syntax ) || + ( $syntax =~ m#RDF/XML#i) ) { + $serializer = new RDFStore::Serializer::RDFXML; + } elsif( $syntax =~ m/N-Triples/i) { + $serializer = new RDFStore::Serializer::NTriples; + } else { + croak "Unknown serialization syntax '$syntax'"; + }; + + return + unless($serializer); + + return $serializer->write( $class, $fh, $namespaces, $base ); +}; + +sub getSerializer { + my ($class) = shift; + + $class->getWriter(@_); + }; + +sub getWriter { + my ($class, $syntax) = @_; + + my $serializer; + if( (! $syntax ) || + ( $syntax =~ m#RDF/XML#i) ) { + $serializer = new RDFStore::Serializer::RDFXML; + } elsif( $syntax =~ m/N-Triples/i) { + $serializer = new RDFStore::Serializer::NTriples; + } else { + croak "Unknown serialization syntax '$syntax'"; + }; + + $serializer->{'model'} = $class; #not sure is correct - perhaps we really need to spell it out in write() method + + return $serializer; + }; + +sub getParser { + my ($class) = shift; + + $class->getReader(@_); + }; + +sub getReader { + my ($class, $syntax) = @_; + + $class->{'GenidNumber'} = 0 + unless(exists $class->{'GenidNumber'}); + + my $parser; + if( (! $syntax ) || + ( $syntax =~ m#RDF/XML#i) ) { + $parser = new RDFStore::Parser::SiRPAC( + ErrorContext => 3, + Style => 'RDFStore::Parser::Styles::RDFStore::Model', + NodeFactory => $class->getNodeFactory, + Source => ($class->getSourceURI ) ? $class->getSourceURI : undef, + GenidNumber => $class->{'GenidNumber'}, + 'style_options' => { 'store_options' => { 'sourceModel' => $class } } ); + } elsif( $syntax =~ m/N-Triples/i) { + $parser = new RDFStore::Parser::NTriples( + ErrorContext => 3, + Style => 'RDFStore::Parser::Styles::RDFStore::Model', + NodeFactory => $class->getNodeFactory, + Source => ($class->getSourceURI ) ? $class->getSourceURI : undef, + GenidNumber => $class->{'GenidNumber'}, + 'style_options' => { 'store_options' => { 'sourceModel' => $class } } ); + } else { + croak "Unknown RDF syntax '$syntax'"; + }; + + return $parser; + }; + +# Copy shared statements across; we do not set any context for them here bacause the add() below will do it eventually using the default context. Shared/virtual models are there for efficiency +# only and can be only generated by a find() or duplicate(); in the former case the context eventually is the context of the query while in the latter is the contexnt of the model (default one). +# Those two other methods are setting/copying the right default context if necessary +sub _copyOnWrite { + my($class) = @_; + + return + unless( (exists $class->{Shared}) && + (defined $class->{Shared}) ); + +#print "Copying stuff across:\n"; + + my ($shares) = $class->elements; + + #forget about being a query model if necessary :) + delete($class->{query}); + if(exists $class->{query_iterator}) { + delete($class->{query_iterator}); + }; + + #break the sharing + delete($class->{Shared}); + + my $ss; + for ( $ss = $shares->first; + $shares->hasnext; + $ss = $shares->next ) { + #print "\tcopying('".$ss->toString."')\n"; + $class->add($ss); # what about context here in the cross-copy??? + }; + +#print "\nDONE!\n"; +}; + +# simple front-end to RDFStore::Iterator using a the given nodeFactory +package RDFStore::Model::Iterator; + +use vars qw ( $VERSION ); +use strict; + +$VERSION = '0.1'; + +sub new { + my ($pkg,$factory,$iterator) = @_; + + return + unless( (defined $iterator) && + (ref($iterator)) && + ($iterator->isa("RDFStore::Iterator")) && + (defined $factory) && + (ref($factory)) && + ($factory->isa("RDFStore::NodeFactory")) ); + + return bless { + factory => $factory, + iterator => $iterator + },$pkg; + }; + +sub size { + return $_[0]->{iterator}->size; + }; + +sub duplicate { + return $_[0]->{iterator}->duplicate; + }; + +sub hasnext { + return $_[0]->{iterator}->hasnext; + }; + +sub remove { + return $_[0]->{iterator}->remove; + }; + +sub intersect { + return + unless( (defined $_[1]) && + (ref($_[1])) && + ($_[1]->isa("RDFStore::Model::Iterator")) ); + + return new RDFStore::Model::Iterator( $_[0]->{factory}, + $_[0]->{iterator}->intersect( $_[1]->{iterator} ) ); + }; + +sub unite { + return + unless( (defined $_[1]) && + (ref($_[1])) && + ($_[1]->isa("RDFStore::Model::Iterator")) ); + + return new RDFStore::Model::Iterator( $_[0]->{factory}, + $_[0]->{iterator}->unite( $_[1]->{iterator} ) ); + }; + +sub subtract { + return + unless( (defined $_[1]) && + (ref($_[1])) && + ($_[1]->isa("RDFStore::Model::Iterator")) ); + + return new RDFStore::Model::Iterator( $_[0]->{factory}, + $_[0]->{iterator}->subtract( $_[1]->{iterator} ) ); + }; + +sub complement { + return + unless( (defined $_[1]) && + (ref($_[1])) && + ($_[1]->isa("RDFStore::Model::Iterator")) ); + + return new RDFStore::Model::Iterator( $_[0]->{factory}, + $_[0]->{iterator}->complement( $_[1]->{iterator} ) ); + }; + +sub exor { + return + unless( (defined $_[1]) && + (ref($_[1])) && + ($_[1]->isa("RDFStore::Model::Iterator")) ); + + return new RDFStore::Model::Iterator( $_[0]->{factory}, + $_[0]->{iterator}->exor( $_[1]->{iterator} ) ); + }; + +sub next { + my ($st) = $_[0]->{iterator}->next; + + return + unless($st); + + return $_[0]->{factory}->createStatement( + ( $st->subject->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->subject->toString ) : + $_[0]->{factory}->createResource( $st->subject->toString ), + ( $st->predicate->isbNode ) ? # I know that is not possible but we allow it anyway ;-/ + $_[0]->{factory}->createAnonymousResource( $st->predicate->toString ) : + $_[0]->{factory}->createResource( $st->predicate->toString ), + ( $st->object->isa("RDFStore::Literal") ) ? + $_[0]->{factory}->createLiteral( $st->object->getLabel, + $st->object->getParseType, + $st->object->getLang, + $st->object->getDataType ) : + ( $st->object->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->object->toString ) : + $_[0]->{factory}->createResource( $st->object->toString ), + ( $st->context ) ? ( $st->context->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->context->toString ) : + $_[0]->{factory}->createResource( $st->context->toString ) : undef ); + }; + +sub next_subject { + my ($n) = $_[0]->{iterator}->next_subject; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub next_predicate { + my ($n) = $_[0]->{iterator}->next_predicate; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub next_object { + my ($n) = $_[0]->{iterator}->next_object; + + return + unless($n); + + return ( $n->isa("RDFStore::Literal") ) ? + $_[0]->{factory}->createLiteral( $n->getLabel, + $n->getParseType, + $n->getLang, + $n->getDataType ) : + ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub next_context { + my ($n) = $_[0]->{iterator}->next_context; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub current { + my ($st) = $_[0]->{iterator}->current; + + return + unless($st); + + return $_[0]->{factory}->createStatement( + ( $st->subject->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->subject->toString ) : + $_[0]->{factory}->createResource( $st->subject->toString ), + ( $st->predicate->isbNode ) ? # I know that is not possible but we allow it anyway ;-/ + $_[0]->{factory}->createAnonymousResource( $st->predicate->toString ) : + $_[0]->{factory}->createResource( $st->predicate->toString ), + ( $st->object->isa("RDFStore::Literal") ) ? + $_[0]->{factory}->createLiteral( $st->object->getLabel, + $st->object->getParseType, + $st->object->getLang, + $st->object->getDataType ) : + ( $st->object->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->object->toString ) : + $_[0]->{factory}->createResource( $st->object->toString ), + ( $st->context ) ? ( $st->context->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->context->toString ) : + $_[0]->{factory}->createResource( $st->context->toString ) : undef ); + }; + +sub current_subject { + my ($n) = $_[0]->{iterator}->current_subject; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub current_predicate { + my ($n) = $_[0]->{iterator}->current_predicate; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub current_object { + my ($n) = $_[0]->{iterator}->current_object; + + return + unless($n); + + return ( $n->isa("RDFStore::Literal") ) ? + $_[0]->{factory}->createLiteral( $n->getLabel, + $n->getParseType, + $n->getLang, + $n->getDataType ) : + ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub current_context { + my ($n) = $_[0]->{iterator}->current_context; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub first { + my ($st) = $_[0]->{iterator}->first; + + return + unless($st); + + return $_[0]->{factory}->createStatement( + ( $st->subject->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->subject->toString ) : + $_[0]->{factory}->createResource( $st->subject->toString ), + ( $st->predicate->isbNode ) ? # I know that is not possible but we allow it anyway ;-/ + $_[0]->{factory}->createAnonymousResource( $st->predicate->toString ) : + $_[0]->{factory}->createResource( $st->predicate->toString ), + ( $st->object->isa("RDFStore::Literal") ) ? + $_[0]->{factory}->createLiteral( $st->object->getLabel, + $st->object->getParseType, + $st->object->getLang, + $st->object->getDataType ) : + ( $st->object->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->object->toString ) : + $_[0]->{factory}->createResource( $st->object->toString ), + ( $st->context ) ? ( $st->context->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->context->toString ) : + $_[0]->{factory}->createResource( $st->context->toString ) : undef ); + }; + +sub first_subject { + my ($n) = $_[0]->{iterator}->first_subject; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub first_predicate { + my ($n) = $_[0]->{iterator}->first_predicate; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub first_object { + my ($n) = $_[0]->{iterator}->first_object; + + return + unless($n); + + return ( $n->isa("RDFStore::Literal") ) ? + $_[0]->{factory}->createLiteral( $n->getLabel, + $n->getParseType, + $n->getLang, + $n->getDataType ) : + ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub first_context { + my ($n) = $_[0]->{iterator}->first_context; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub each { + my ($st) = $_[0]->{iterator}->each; + + return + unless($st); + + return $_[0]->{factory}->createStatement( + ( $st->subject->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->subject->toString ) : + $_[0]->{factory}->createResource( $st->subject->toString ), + ( $st->predicate->isbNode ) ? # I know that is not possible but we allow it anyway ;-/ + $_[0]->{factory}->createAnonymousResource( $st->predicate->toString ) : + $_[0]->{factory}->createResource( $st->predicate->toString ), + ( $st->object->isa("RDFStore::Literal") ) ? + $_[0]->{factory}->createLiteral( $st->object->getLabel, + $st->object->getParseType, + $st->object->getLang, + $st->object->getDataType ) : + ( $st->object->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->object->toString ) : + $_[0]->{factory}->createResource( $st->object->toString ), + ( $st->context ) ? ( $st->context->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $st->context->toString ) : + $_[0]->{factory}->createResource( $st->context->toString ) : undef ); + }; + +sub each_subject { + my ($n) = $_[0]->{iterator}->each_subject; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub each_predicate { + my ($n) = $_[0]->{iterator}->each_predicate; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub each_object { + my ($n) = $_[0]->{iterator}->each_object; + + return + unless($n); + + return ( $n->isa("RDFStore::Literal") ) ? + $_[0]->{factory}->createLiteral( $n->getLabel, + $n->getParseType, + $n->getLang, + $n->getDataType ) : + ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +sub each_context { + my ($n) = $_[0]->{iterator}->each_context; + + return + unless($n); + + return ( $n->isbNode ) ? + $_[0]->{factory}->createAnonymousResource( $n->toString ) : + $_[0]->{factory}->createResource( $n->toString ); + }; + +1; +}; + +__END__ + +=head1 NAME + +RDFStore::Model - An implementation of the Model RDF API using tied hashes and implementing free-text search on literals + +=head1 SYNOPSIS + + use RDFStore::NodeFactory; + my $factory= new RDFStore::NodeFactory(); + my $statement = $factory->createStatement( + $factory->createResource('http://perl.org'), + $factory->createResource('http://iscool.org/schema/1.0/','label'), + $factory->createLiteral('Cool Web site') + ); + my $statement1 = $factory->createStatement( + $factory->createResource("http://www.altavista.com"), + $factory->createResource("http://pen.jrc.it/schema/1.0/','author'), + $factory->createLiteral("Who? :-)") + ); + + my $statement2 = $factory->createStatement( + $factory->createUniqueResource(), + $factory->createUniqueResource(), + $factory->createLiteral("") + ); + + use RDFStore::Model; + my $model = new RDFStore::Model( Name => 'store', FreeText => 1 ); + + $model->add($statement); + $model->add($statement1); + $model->add($statement2); + my $model1 = $model->duplicate(); + + print $model1->getDigest->equals( $model1->getDigest ); + print $model1->getDigest->hashCode; + + my $found = $model->find($statement2->subject,undef,undef); + my $found1 = $model->find(undef,undef,undef,undef,'Cool'); #free-text search on literals :) + + #get Statements + foreach ( @{$found->elements} ) { + print $_->getLabel(),"\n"; + }; + + #or faster + my $fetch; + foreach ( @{$found->elements} ) { + my $fetch=$_; #avoid too many fetches from RDFStore::Model::Statements + print $fetch->getLabel(),"\n"; + }; + + #or + my($statements)=$found1->elements; + for ( 0..$#{$statements} ) { + print $statements->[$_]->getLabel(),"\n"; + }; + + #get RDFNodes + foreach ( keys %{$found->elements}) { + print $found->elements->{$_}->getLabel(),"\n"; + }; + + # set operations + my $set = new RDFStore::Model( Name => 'setmodel' ); + + $set=$set->interset($other_model); + $set=$set->unite($other_model); + $set=$set->subtract($other_model); + +=head1 DESCRIPTION + +An RDFStore::Model implementation using RDFStore(3) to store triplets. + +=head1 CONSTRUCTORS + +The following methods construct/tie RDFStore::Model storages and objects: + +=item $model = new RDFStore::Model( %whateveryoulikeit ); + +Create an new RDFStore::Model object and tie up the RDFStore(3). The %whateveryoulikeit hash contains a set of configuration options about how and where store actual data. + +Possible additional options are the following: + +=over 4 + +=item Name + +This is a label used to identify a B storage by name. It might correspond to a physical file system directory containing the indexes DBs. By default if no B option is given the storage is assumed to be B (e.g. RDFStore::Storage::find method return result sets as in-memory models by default unless specified differently). For local persistent storages a directory named liek this option is created in the current working directory with mode 0666) + +=item Sync + +Sync the RDFStore::Model with the underling Data::MagciTie GDS after each add() or remove(). + +=item FreeText + +Enable free text searching on literals over a model (see B) + +=head1 SEE ALSO + +Digest(3) RDFStore(3) RDFStore::Digest::Digestable(3) RDFStore::Digest(3) RDFStore::RDFNode(3) RDFStore::Resource(3) + +=head1 AUTHOR + + Alberto Reggiori Added: incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/NodeFactory.pm URL: http://svn.apache.org/viewvc/incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/NodeFactory.pm?view=auto&rev=528394 ============================================================================== --- incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/NodeFactory.pm (added) +++ incubator/triplesoup/donations/TRIPLES-3-RDFStore/lib/RDFStore/NodeFactory.pm Fri Apr 13 01:56:01 2007 @@ -0,0 +1,314 @@ +# * +# * Copyright (c) 2000-2006 Alberto Reggiori +# * Dirk-Willem van Gulik +# * +# * NOTICE +# * +# * This product is distributed under a BSD/ASF like license as described in the 'LICENSE' +# * file you should have received together with this source code. If you did not get a +# * a copy of such a license agreement you can pick up one at: +# * +# * http://rdfstore.sourceforge.net/LICENSE +# * +# * Changes: +# * version 0.1 - 2000/11/03 at 04:30 CEST +# * version 0.2 +# * - modified createResource() method accordingly to rdf-api-2000-10-30 +# * version 0.4 +# * - changed way to return undef in subroutines +# * - implemented createOrdinal() +# * version 0.41 +# * - added anonymous resource support via createAnonymousResource() and createbNode() - see also RDFStore::Resource(3) +# * - added statements reification support via createReifiedStatement() - see also RDFStore::Statement(3) +# * - updated accordingly to new RDFStore API +# * - added createNTriple() method +# * version 0.42 +# * - fixed bNodes identifers generation +# * + +package RDFStore::NodeFactory; +{ +use vars qw ($VERSION); +use strict; + +$VERSION = '0.42'; + +use Carp; +use RDFStore::Literal; +use RDFStore::Resource; +use RDFStore::Statement; +use RDFStore::Util::Digest; + +sub new { + my ( $pkg, $nodesCounter, $bnodesCounter, $timestamp, $rand_seed ) = @_; + + $nodesCounter = 0 + unless($nodesCounter); + $bnodesCounter = 0 + unless($bnodesCounter); + $timestamp = time() + unless($timestamp); + $rand_seed = unpack("H*", rand()) + unless($rand_seed); + + bless { + 'nodesCounter' => $nodesCounter, + 'bnodesCounter' => $bnodesCounter, + 'timestamp' => $timestamp, + 'rand_seed' => $rand_seed + }, $pkg; + }; + +# Creates a resource from a URI or from namespace and local name +sub createResource { + if(defined $_[2]) { + return new RDFStore::Resource($_[1],$_[2]) or + return; + } else { + return ($_[1]) ? new RDFStore::Resource($_[1]) : undef; + }; + }; + +sub createAnonymousResource { + if(defined $_[1]) { + return new RDFStore::Resource($_[1], undef, 1); + } else { + # try to generate system/run wide unique ID i.e. 'S' + unpack("H*", rand()) + 'P' + $$ + 'T' + time() + 'N' + GenidNumber + return new RDFStore::Resource( + 'rdfnodeIDgenidrdfstore' . + 'S'.$_[0]->{'rand_seed'} . + 'P'. $$. + 'T'. $_[0]->{'timestamp'} . + 'N'. $_[0]->{bnodesCounter}++, undef, 1 ); + }; + }; + +sub createbNode { + my ($class) = shift; + + return $class->createAnonymousResource(@_); + }; + +sub createLiteral { + my ($class) = shift; + + return new RDFStore::Literal(@_); + }; + +sub createStatement { + return ( (defined $_[1]) && + (defined $_[2]) && + (defined $_[3]) ) ? new RDFStore::Statement($_[1], $_[2], $_[3], (defined $_[4]) ? $_[4] : undef ) : + undef; + }; + +sub createReifiedStatement { + return ( (defined $_[1]) && + (defined $_[2]) && + (defined $_[3]) ) ? + new RDFStore::Statement( $_[1], $_[2], $_[3], + ( (defined $_[4]) ? $_[4] : undef ), 1, + ( (defined $_[5]) ? $_[5] : undef ) ) : undef; + }; + +# Creates a resource with a unique ID +sub createUniqueResource { + # try to generate system/run wide unique ID i.e. 'S' + unpack("H*", rand()) + 'P' + $$ + 'T' + time() + 'N' + IdNumber + return new RDFStore::Resource( + 'rdfresourcerdfstore' . + 'S'.$_[0]->{'rand_seed'} . + 'P'. $$. + 'T'. $_[0]->{'timestamp'} . + 'N'. $_[0]->{nodesCounter}++ ); + }; + +# Creates an ordinal property (rdf:li, rdf:_N) +sub createOrdinal { + my ($class,$i) = @_; + + if($i < 1) { + croak "Attempt to construct invalid ordinal resource"; + } else { + return $class->createResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "_" . $i); + }; + }; + +# see http://www.w3.org/TR/rdf-testcases/#ntriples and http://robustai.net/sailor/grammar/Quads.html +sub createNTriple { + my ($class, $ntriple) = @_; + + # some basic parsing - see http://aspn.activestate.com/ASPN/Mail/Message/787168 + chomp( $ntriple ); + $ntriple =~ s/^[\x20\x09]+//; # remove leading white space + $ntriple =~ s/[\x20\x09]+$//; # remove trailing white space + + if ($ntriple =~ m/[^\x20-\x7e\x0d\x0a\x09]/) { + warn 'Invalid character(s) found'; + return undef; + }; + + unless ($ntriple =~ s/\.$//) { + warn 'Syntax error: missing trailing full stop'; + return undef; + }; + + my ($subject, $predicate, $object, $context ); + + # parse subject + if ($ntriple =~ s/^<([^>]*)>[\x20\x09]+//) { + # uriref + $subject = $class->createResource( $1 ); + } elsif ($ntriple =~ s/^_:([A-Za-z][A-Za-z0-9]*)[\x20\x09]+//) { + # bNode + $subject = $class->createbNode( $1 ); + } else { + warn 'Syntax error in token'; + return undef; + }; + + # parse predicate + if ($ntriple =~ s/^<([^>]*)>[\x20\x09]+//) { + # uriref + $predicate = $class->createResource( $1 ); + } else { + warn 'Syntax error in token'; + return undef; + }; + + # parse object + if ($ntriple =~ s/^<([^>]*)>[\x20\x09]+//) { + # uriref + $object = $class->createResource( $1 ); + } elsif ($ntriple =~ s/^_:([A-Za-z][A-Za-z0-9]*)[\x20\x09]+//) { + # bNode + $object = $class->createbNode( $1 ); + } elsif ($ntriple =~ s/"([^"]*)"\@([a-z0-9]+(-[a-z0-9]+)?)\^\^<([^>]*)>[\x20\x09]+//) { + # literal + if ( $4 eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral' ) { + #parseType='Literal' + $object = $class->createLiteral( $1, 1, $2, $4 ); + } else { + $object = $class->createLiteral( $1, undef, $2, $4 ); + }; + } elsif ($ntriple =~ s/"([^"]*)"\^\^<([^>]*)>[\x20\x09]+//) { + # literal + if ( $2 eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral' ) { + #parseType='Literal' + $object = $class->createLiteral( $1, 1, undef, $2 ); + } else { + $object = $class->createLiteral( $1, undef, undef, $2 ); + }; + } elsif ($ntriple =~ s/"([^"]*)"\@([a-z0-9]+(-[a-z0-9]+)?)[\x20\x09]+//) { + # literal + $object = $class->createLiteral( $1, undef, $2 ); + } elsif ($ntriple =~ s/"([^"]*)"[\x20\x09]+//) { + # literal + $object = $class->createLiteral( $1 ); + } else { + warn 'Syntax error in token'; + return undef; + }; + + if ( length($ntriple) ) { + # parse context (Quads actually see http://robustai.net/sailor/grammar/Quads.html) + if ($ntriple =~ s/^<([^>]*)>[\x20\x09]+//) { + # uriref + $context = $class->createResource( $1 ); + } elsif ($ntriple =~ s/^_:([A-Za-z][A-Za-z0-9]*)[\x20\x09]+//) { + # bNode + $context = $class->createbNode( $1 ); + } else { + warn 'Trash found after token'; # should really say 'Syntax error in token' :-) + return undef; + }; + }; + + return $class->createStatement( $subject, $predicate, $object, $context ); +}; + +1; +}; + +__END__ + +=head1 NAME + +RDFStore::NodeFactory - An RDF node factory implementation + +=head1 SYNOPSIS + + use RDFStore::NodeFactory; + my $factory = new RDFStore::NodeFactory(); + my $statement = $factory->createStatement( + $factory->createResource("http://pen.com"), + $factory->createResource("http://purl.org/schema/1.0#author"), + $factory->createLiteral("Peter Pan") + ); + my $reified_statement = $factory->createReifiedStatement( + $factory->createResource("http://pen.com"), + $factory->createResource("http://purl.org/schema/1.0#author"), + $factory->createLiteral("Lady Oscar") + ); + + +=head1 DESCRIPTION + +An RDFStore::NodeFactory implementation using RDFStore::RDFNode, RDFStore::Resource and RDFStore::Literal + +=head1 METHODS + +=over 4 + +=item new + +This is a class method, the constructor for RDFStore::NodeFactory. + +=item createResource( LOCALNAME_NAMESPACE [, LOCALNAME ] ) + +Create a new RDFStore::Resource. If the method is called with a single perl scalar as parameter a new RDF Resource is created with the string passed as indentifier (LOCALNAME); a fully qualified RDF resource can be constructed by invoching the constructor with B paramter s where the former is the NAMESPACE and the latter is the LOCALNAME. By RDF definition we assume that B. If LOCALNAME is a perl reference the new Resource is flagged as anonymous-resource or bNode. + +bNodes can also be created using the B or B methods below + +=item createAnonymousResource( LOCALNAME_NAMESPACE [, LOCALNAME ] ) + +Create a new anonymous RDFStore::Resource like in the B method above but the method is setting the RDFStore::Resource(3) internal bNode flag. + +=item createbNode( LOCALNAME_NAMESPACE [, LOCALNAME ] ) + +Create a new anonymous RDFStore::Resource like in the B method above but the method is setting the RDFStore::Resource(3) internal bNode flag. + +=item createLiteral( LITERAL ) + +Create a new RDFStore::Literal. The only parameter passed is either a plain perl scalar (LITERAL) - see RDFStore::Literal(3) + +=item createStatement( SUBJECT, PREDICATE, OBJECT ) + +Create a new RDFStore::Statement. SUBJECT and PREDICATE must be two RDFStore::Resource while OBJECT is RDFStore::RDFNode + +=item createUniqueResource + +Creates a new RDFStore::Resource with a unique ID using a random seed. + +=item createOrdinal( INTEGER ) + +Creates a new RDFStore::Resource ordinal property (rdf:li, rdf:_N). The only parameter INTEGER is the scalar number to set the property to. + +=head1 ABOUT RDF + + http://www.w3.org/TR/rdf-primer/ + + http://www.w3.org/TR/rdf-mt + + http://www.w3.org/TR/rdf-syntax-grammar/ + + http://www.w3.org/TR/rdf-schema/ + + http://www.w3.org/TR/1999/REC-rdf-syntax-19990222 (obsolete) + +=head1 SEE ALSO + +RDFStore::RDFNode(3) RDFStore::Resource(3) RDFStore::Literal(3) RDFStore(3) + +=head1 AUTHOR + + Alberto Reggiori