# $Id: Context.pm,v 1.25 2001/02/26 10:02:12 aigan Exp $ -*-perl-*- package RDF::Service::Context; #===================================================================== # # DESCRIPTION # All resources exists in a context. This is the context. # # AUTHOR # Jonas Liljegren # # COPYRIGHT # Copyright (C) 2000-2001 Jonas Liljegren. All Rights Reserved. # # This module is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # #===================================================================== use strict; use vars qw( $AUTOLOAD ); use RDF::Service::Dispatcher qw( go ); use RDF::Service::Constants qw( :all ); use RDF::Service::Cache qw( interfaces uri2id list_prefixes get_unique_id id2uri debug debug_start debug_end $DEBUG expire time_string $Level validate_context ); use Data::Dumper; use Carp qw( confess cluck croak); sub new { my( $proto, $node, $context, $wmodel, $session ) = @_; # This constructor shouls only be called from get(), which # could be called from find_node or create_node. my $class = ref($proto) || $proto; my $self = bless [], $class; my $history = []; if( ref($proto) ) { $context ||= $proto->[CONTEXT]; $node ||= $proto->[NODE]; # The same node in another context? $wmodel ||= $proto->[WMODEL]; $session ||= $proto->[SESSION]; $history = $proto->[HISTORY]; } unless( ref($history) eq 'ARRAY' ) { confess "HOLY SHIT!!!"; } # TODO: Maby perform a deep copy of the context. At least copy # each key-value pair. $self->[NODE] = $node or die; $self->[CONTEXT] = $context or die "No context supplied"; $self->[WMODEL] = $wmodel; $self->[SESSION] = $session; $self->[MEMORY] = {}; $self->[HISTORY] = $history; # debug " Session is $session\n", 2; if( $node->[RUNLEVEL] ) { $wmodel or confess "No WMODEL supplied by $proto\n"; $session or confess "No session supplied by $proto\n"; unless( ref $session eq "RDF::Service::Resource" ) { confess "Holy shit: $session!!!"; } } return $self; } sub AUTOLOAD { # The substr depends on the package length # $AUTOLOAD = substr($AUTOLOAD, 23); return if $AUTOLOAD eq 'DESTROY'; debug "AUTOLOAD $AUTOLOAD\n", 2; # warn "*** Level = $Level\n"; ### DEBUG # warn "*** DEBUG = $DEBUG\n\n"; # Expand abbrevations only for the app if( $Level == 0 ) { my $self = shift; debug " Checking for abbrevation\n", 2; # debug " Self is $self->[NODE][URISTR]\n", 2; # debug " Session is $self->[SESSION][URISTR]\n", 2; # debug "Abbrevations:\n".Dumper($self->[SESSION][ABBREV])."\n", 2; if( substr($AUTOLOAD, 0, 4) eq 'rev_') { if( my $x = $self->[SESSION][ABBREV]{substr($AUTOLOAD, 4)} ) { return $self->arc_subj($x, @_); } else { debug " Non found for ".substr($AUTOLOAD, 4)."\n", 2; return &RDF::Service::Dispatcher::go($self, $AUTOLOAD, @_); } } else { if( my $x = $self->[SESSION][ABBREV]{$AUTOLOAD} ) { return $self->arc_obj($x, @_); } else { debug " Non found for $AUTOLOAD\n", 2; return &RDF::Service::Dispatcher::go($self, $AUTOLOAD, @_); } } } return &RDF::Service::Dispatcher::go(shift, $AUTOLOAD, @_); } sub name { my( $self ) = @_; return $self->[NODE][NAME]; # not guaranteed to be defined } sub uri { # This is always defined $_[0]->[NODE][URISTR]; } sub model { my( $self ) = @_; die "not implemented" if $_[0]->[NODE][MULTI]; my $model_res = $_[0]->new( $_[0]->[NODE][MODEL] ); if( $DEBUG ) { unless( $model_res->is_a( NS_LS.'#Model' ) ) { die "The model is not a model"; } } return $model_res; # TODO: Should return a selection of models } sub get { unless( $_[1] ) { return get_context_by_id( $_[0], uri2id(NS_LD."#".&get_unique_id), $_[2], 1 ); } return get_context_by_id( $_[0], uri2id($_[1]), $_[2] ); } sub get_context_by_id { my( $self, $id , $ids, $local ) = @_; # # $local guarantees that no info about the resource exists in the # interfaces. Only fetch dynamic properties. # TODO: First look for the object in the cache my $node = $self->[NODE]; $ids ||= $node->[IDS]; $local ||= 0; if( $DEBUG ) { confess "IDS undefined" unless defined $ids; unless( ref $RDF::Service::Cache::node->{$ids} ) { $node->[RUNLEVEL] and confess "IDS $ids not initialized\n"; } confess "id not defined" unless $id; } my $obj = $RDF::Service::Cache::node->{$ids}{ $id }; unless( $obj ) { # Create an uninitialized object. Any request for the objects # properties will initialize the object with the interfaces. $obj = $node->new_by_id($id); $RDF::Service::Cache::node->{$ids}{ $id } = $obj; } $obj->[LOCAL] = $local; if( $DEBUG ) { unless( $self->[WMODEL] or $obj->[URISTR] eq NS_LD.'#The_Base_Model' ) { confess "No WMODEL found for $node->[URISTR] "; } unless( ref $obj eq "RDF::Service::Resource" ) { my $uri = id2uri( $id ); confess "Cached $uri ($id) corrupt"; } } return $self->new( $obj ); } sub get_node { unless( $_[1] ) { return get_node_by_id( $_[0], uri2id(NS_LD."#".&get_unique_id), $_[2], 1 ); } return get_node_by_id( $_[0], uri2id($_[1]), $_[2] ); } sub get_node_by_id { my( $self, $id, $ids, $local ) = @_; $ids ||= $self->[NODE][IDS]; $local ||= 0; my $obj = $RDF::Service::Cache::node->{$ids}{ $id }; unless( $obj ) { # Create an uninitialized object. Any request for the objects # properties will initialize the object with the interfaces. $obj = $self->[NODE]->new(undef, $id, $ids); $RDF::Service::Cache::node->{$ids}{ $id } = $obj; } $obj->[LOCAL] = $local; if( $DEBUG ) { unless( $self->[WMODEL] or $obj->[URISTR] eq NS_LD.'#The_Base_Model' ) { confess "No WMODEL found for $self->[NODE][URISTR] "; } unless( ref $obj eq "RDF::Service::Resource" ) { my $uri = id2uri( $id ); confess "Cached $uri ($id) corrupt"; } } return $obj; } sub get_model { my( $self, $uri ) = @_; debug_start("get_model", ' ', $self); # Does not work. Must include creation time # Not short for get->set([NS_LS.'Model']) # $self->get($uri)->set([NS_LS.'Model']); die "No uri specified" unless $uri; debug " ( $uri )\n", 2; my $obj = $self->find_node( $uri ); if( $obj ) { debug "Model existing: $uri\n", 1; # Is this a model? unless( $obj->is_a(NS_LS.'#Model') ) { die "$obj->[NODE][URISTR] is not a model\n". $obj->types_as_string; } # setting WMODEL $obj->[WMODEL] = $obj; } else { debug "Model not existing. Creating it: $uri\n", 1; # create_model sets WMODEL $obj = $self->create_model( $uri ); } debug_end("get_model", ' ', $self); return $obj; } sub is_a { my( $self, $class ) = @_; $self->[NODE][TYPE_ALL] or $self->init_types; $self->[NODE][TYPE_ALL] ||= 1; return $self->is_known_as_a( $class ); } sub could_be_a { my( $self, $class ) = @_; return 1 unless $self->[NODE][TYPE_ALL]; return $self->is_known_as_a( $class ); } sub is_known_as_a { my( $self, $class ) = @_; $class = $self->get( $class ) unless ref $class; if( defined $self->[NODE][TYPE]{$class->[NODE][ID]} ) { return 1; } else { return 0; } } sub exist_pred { my( $self, $pred ) = @_; my $pred_id; if( ref $pred ) { $pred_id = $pred->[NODE][ID]; } else { $pred_id = uri2id( $pred ); } if( $self->[NODE][REV_SUBJ]{$pred_id} ) { return 1; } else { return 0; } } sub type_orderd_list { my( $self, $i, $point ) = @_; # TODO: This should (as all the other methods) be cached and # dependencies registred. die "Not implemented" if $point; my $node = $self->[NODE]; debug_start("type_orderd_list", ' ', $self); # NOTE: Same type of check as in get_level() # # We can't call level() for the resources used to define level() # if( $node->[URISTR] =~ /^(@{[NS_RDF]}|@{[NS_RDFS]}|@{[NS_LS]})/o ) { # Uses $Schema in Constants. Can only be scalar ref. my $type_uri_ref = $Schema->{$self->[NODE][URISTR]}{NS_RDF.'type'}; # TODO: include more classes debug "Returning simplified list\n", 2; debug_end("type_orderd_list", ' ', $self); return( [$self->get( $$type_uri_ref ), $self->get(NS_RDFS.'Resource')] ); } # Do we have to have all types to list the *present* defined types? # $node->[TYPE_ALL] or $self->init_types; my @types = (); my %included; # Keep track of included types # Sorting algorithm based on FMTYEWTK, sort, code 3.5 # my @ot= map $self->get_context_by_id($_), keys %{$node->[TYPE]}; my $level = $self->get( NS_LS.'#level' ); my @tl = map $_->arc_obj_value($level), @ot; foreach my $type (@ot[ sort {$tl[$b] <=> $tl[$a] } 0..$#ot ]) { # Check that at least one model defines the type. Can we # assume that the existence of the type (in the hash tree) # implies the existence of at least one model (in the hash # treee) and that the existence of a model implies that that # model has the value 1, meaning that the model states the # type? Yes. Assume that. push @types, $type unless $included{$type->[NODE][ID]}; $included{$type->[NODE][ID]}++; } debug_end("type_orderd_list", ' ', $self); return( \@types ); } # The alternative selectors: # # arc subj arcs # arc_obj subj arcs objs # arc_obj_list subj arcs objs list # select_arc container subj arcs # select_arc_obj container subj arcs objs # type subj types # select_type container subj types # rev_arc obj arcs # arc_subj obj arcs subjs # select_rev_arc container obj arcs # select_arc_subj container obj arcs subjs # rev_type type objs # select_rev_type container types objs # li container res # rev_li res container # select container res # rev_select res container sub type { my( $self, $point ) = @_; die "Not implemented" if $point; debug_start("type", ' ', $self); $self->[NODE][TYPE_ALL] or $self->init_types; # TODO: Insert the query in the selection, rather than the query # result die "Not implemented"; # Should give a list of type nodes my $selection = $self->declare_selection( ); debug_end("rev_type", ' ', $self); return( $selection ); } sub rev_type { my( $self, $point ) = @_; die "Not implemented" if $point; debug_start("rev_type", ' ', $self); $self->[NODE][REV_TYPE_ALL] or $self->init_rev_types; # TODO: Insert the query in the selection, rather than the query # result my %subjs = (); foreach my $subj_id ( keys %{$self->[NODE][REV_TYPE]} ) { # This includes types from all models foreach my $model_id ( keys %{$self->[NODE][REV_TYPE]{$subj_id}}) { if( $self->[NODE][REV_TYPE]{$subj_id}{$model_id} ) { $subjs{$subj_id} = $self->get_node_by_id( $subj_id ); } } } my $selection = $self->declare_selection( [values %subjs] ); debug_end("rev_type", ' ', $self); return( $selection ); } sub arc { my( $self, $point ) = @_; debug_start( "arc", ' ', $self ); unless( ref $point ) { unless( defined $point ) { # TODO: allow partially initialized props $self->[NODE][REV_SUBJ_ALL] or $self->init_rev_subjs; # TODO: Insert the query in the selection, rather than the # query result # my $arcs = []; foreach my $pred_id ( keys %{$self->[NODE][REV_SUBJ]} ) { foreach my $arc_node ( @{$self->[NODE][REV_SUBJ]{$pred_id}} ) { push @$arcs, $arc_node; } } my $selection = $self->declare_selection( $arcs ); debug_end("arc", ' ', $self); return $selection; } $point = $self->get( $point ); } # Take action depending on $point # if( ref $point eq 'RDF::Service::Context' ) # property { debug " ( $point->[NODE][URISTR] )\n", 1; unless( $self->[NODE][REV_SUBJ]{$point->[NODE][ID]} or $self->[NODE][REV_SUBJ_ALL] ) { $self->init_rev_subjs( [$point->[NODE]] ); } # TODO: Insert the query in the selection, rather than the # query result # my $arcs = []; foreach my $arc_node ( @{$self->[NODE][REV_SUBJ]{$point->[NODE][ID]}} ) { push @$arcs, $arc_node; } my $selection = $self->declare_selection( $arcs ); debug_end("arc", ' ', $self); return $selection; } else { die "not implemented"; } } sub arc_subj { my( $self, $point ) = @_; # Default $point to be a property resource # unless( ref $point ) { unless( defined $point ) { die "Not implemented"; } $point = $self->get( $point ); } debug_start( "arc_subj", ' ', $self ); debug " ( $point->[NODE][URISTR] )\n", 1; # Take action depending on $point # if( ref $point eq 'RDF::Service::Context' ) # property { $self->init_rev_objs( $point ) unless defined $self->[NODE][REV_OBJ]{$point->[NODE][ID]}; # TODO: Insert the query in the selection, rather than the # query result # my $subjs = []; if( $self->[NODE][REV_OBJ]{$point->[NODE][ID]} ) { foreach my $arc_node ( @{$self->[NODE][REV_OBJ]{$point->[NODE][ID]}} ) { push @$subjs, $arc_node->[SUBJ]; } } my $selection = $self->declare_selection( $subjs ); debug_end("arc_subj", ' ', $self); return $selection; } else { die "not implemented"; } die "What???"; } sub arc_pred { my( $self, $point ) = @_; debug_start( "arc_pred", ' ', $self ); if( not defined $point ) { $self->[NODE][REV_SUBJ_ALL] or $self->init_rev_subjs; # TODO: Insert the query in the selection, rather than the # query result # my $preds = []; foreach my $pred_id ( keys %{$self->[NODE][REV_SUBJ]} ) { push @$preds, $self->get_node_by_id($pred_id); } my $selection = $self->declare_selection( $preds ); debug_end("arc_pred", ' ', $self); return $selection; } else { die "not implemented"; } die "What???"; } sub arc_obj { my( $self, $point ) = @_; # Default $point to be a property resource # unless( ref $point ) { unless( defined $point ) { warn "*** Failed\n"; warn "*** Called $self->[NODE][URISTR] with ( $point )\n"; croak "arc_obj ( $self->[NODE][URISTR] ) without point not implemented"; } $point = $self->get( $point ); } debug_start( "arc_obj", ' ', $self ); debug " ( $point->[NODE][URISTR] )\n", 1; # Take action depending on $point # if( ref $point eq 'RDF::Service::Context' ) # property { unless( $self->[NODE][REV_SUBJ]{$point->[NODE][ID]} or $self->[NODE][REV_SUBJ_ALL] ) { $self->init_rev_subjs( [$point->[NODE]] ); } unless( defined $self->[NODE][REV_SUBJ]{$point->[NODE][ID]} ) { debug "Resource doesn't have any $point->[NODE][URISTR]!\n", 2; debug_end("arc_obj", ' ', $self); return $self->declare_selection( [] ); } # TODO: Insert the query in the selection, rather than the # query result # my $objs = []; debug "Result:\n", 3; foreach my $arc_node ( @{$self->[NODE][REV_SUBJ]{$point->[NODE][ID]}} ) { push @$objs, $arc_node->[OBJ]; debug " $arc_node->[OBJ][URISTR]\n", 3; } my $selection = $self->declare_selection( $objs ); debug_end("arc_obj", ' ', $self); return $selection; } else { die "not implemented"; } die "What???"; } sub arc_obj_list { my( $self, $point ) = @_; # Default $point to be a property resource # unless( ref $point ) { unless( defined $point ) { die "Not implemented"; } $point = $self->get( $point ); } debug_start( "arc_obj_list", ' ', $self ); debug " ( $point->[NODE][URISTR] )\n", 1; # Take action depending on $point # if( ref $point eq 'RDF::Service::Context' ) # property { unless( $self->[NODE][REV_SUBJ]{$point->[NODE][ID]} or $self->[NODE][REV_SUBJ_ALL] ) { $self->init_rev_subjs( [$point->[NODE]] ); } unless( defined $self->[NODE][REV_SUBJ]{$point->[NODE][ID]} ) { debug " found nothing\n", 2; debug_end("arc_obj_list", ' ', $self); return []; } my $objs = []; foreach my $arc_node ( @{$self->[NODE][REV_SUBJ]{$point->[NODE][ID]}} ) { my $obj = $self->new( $arc_node )->obj; debug " found $obj->[NODE][URISTR]\n", 2; push @$objs, $obj; } debug_end("arc_obj_list", ' ', $self); return $objs; } else { die "not implemented"; } die "What???"; } sub arc_obj_value { my( $self, $point ) = @_; # Default $point to be a property resource # unless( ref $point ) { unless( defined $point ) { die "Not implemented"; } $point = $self->get( $point ); } debug_start( "arc_obj_value", ' ', $self ); debug " ( $point->[NODE][URISTR] )\n", 1; # Take action depending on $point # if( ref $point eq 'RDF::Service::Context' ) # property { unless( $self->[NODE][REV_SUBJ]{$point->[NODE][ID]} or $self->[NODE][REV_SUBJ_ALL] ) { $self->init_rev_subjs( [$point->[NODE]] ); } my $props = $self->[NODE][REV_SUBJ]{$point->[NODE][ID]}; unless( defined $props ) { # No property defined debug "Returning undef\n", 1; debug_end("arc_obj_value", ' ', $self); return undef; } if( $props->[1] ) { warn "More than one property found\n"; foreach my $prop ( @$props ) { # warn "\t $prop\n"; if( $prop->[OBJ][VALUE] ) { warn "\tvalue: ${$prop->[OBJ][VALUE]}\n"; } else { warn "\tWe expected $prop->[OBJ][URISTR] to be a literal\n"; warn "\t\tBut VALUE is undefined!\n"; } } confess; } if( my $valref = $props->[0][OBJ][VALUE] ) { if( $DEBUG >= 2 ) { my $val = substr($$valref, 0, 40); debug "Returning '$$valref'\n"; } debug_end("arc_obj_value", ' ', $self); return $$valref; } else { die "Poperty has no value\n"; } } else { die "not implemented"; } die "What???"; } sub selector { die "not imlemented"; my $point; if( not defined $point ) # Return all arcs { } elsif( ref $point eq 'ARRAY' ) # Return ORed elements { } elsif( ref $point eq 'HASH' ) # Return ANDed elements { } elsif( ref $point eq 'RDF::Service::Context' ) { } else { die "Malformed entry"; } } sub set { my( $self, $types, $props ) = @_; # This is practicaly the same as declare_self. set() updates the # data in the interfaces. debug_start("set", ' ', $self); # Should each type and property only be saved in the first best # interface and not saved in the following interfaces? Yes! # # The types and props taken by one interface must be marked so # that the next interface doesn't handle them. This could be done # by modifying the arguments $types and $props to exclude those # that has been taken care of. $self->set_types( $types, 1); $self->set_props( $props, 1); $self->store; debug_end("set", ' ', $self); return $self; } sub set_types { my( $self, $types, $trim, $solid ) = @_; # # Remove existing types not mentioned if $trim debug_start("set_types", ' ', $self); my $node = $self->[NODE]; my $model = $self->[WMODEL][NODE]; $solid ||= 0; if( $DEBUG ) { ref $model eq "RDF::Service::Resource" or confess "Bad model ($model)"; } $node->[TYPE_ALL] or $self->init_types; my @add_types; my %del_types; foreach my $type ( @{$self->type_orderd_list} ) { if( $node->[TYPE]{$type->[NODE][ID]}{$model->[ID]} ) { $del_types{$type->[NODE][ID]} = $type; } } foreach my $type ( @$types ) { $type = $self->get( $type ) unless ref $type; if ( $del_types{ $type->[NODE][ID] } ) { delete $del_types{ $type->[NODE][ID] }; } else { push @add_types, $type; } } if ( @add_types ) { # Will only add each type in one interface $self->declare_add_types( [@add_types], undef, $solid ); } if ( $trim and %del_types ) { # Will delete types from all interfaces $self->declare_del_types( [values %del_types] ); $self->remove_types( [values %del_types] ); # Was: unless local_changes } unless( $solid ) { $node->[TYPE_ALL] = 1; } debug_end("set_types", ' ', $self); return $self; } sub set_props { my( $self, $props, $trim, $solid ) = @_; # # Remove existing props not mentioned if $trim. Add props not yet # existing. Only operate within WMODEL. # # $props->{$pred_uri => [ $obj ] } # # Pass scalar ref objs through; NO, can't do that! debug_start("set_props", ' ', $self); my $node = $self->[NODE]; my $model = $self->[WMODEL][NODE]; $solid ||= 0; $node->[REV_SUBJ_ALL] or $self->init_rev_subjs; my %add_props; # $add_props->{$pred_id}[$obj] my %del_props; # $del_props->{$pred_id}{$obj_id => $arc} # This will hold present properties in the model # that does not exist in the new set of # properties. Start by adding all present properties and remove # the ones that exist in the new property list. foreach my $arc ( @{$self->arc->list} ) { if( $arc->[NODE][MODEL][ID] == $model->[ID] ) { $del_props{$arc->[NODE][PRED][ID]}{$arc->[NODE][OBJ][ID]} = $arc; } } # Iterate through the submitted props, by pred and obj debug "Setting up adds/removes\n",2; foreach my $pred_key ( keys %$props ) { my $pred_id; # Checks for abbrevations if( my $x = $self->[SESSION][ABBREV]{$pred_key} ) { debug "abbrev for $x->[NODE][URISTR]\n", 2; $pred_id = $x->[NODE][ID]; } else { $pred_id = uri2id($pred_key); } debug " Checking $pred_key\n", 2; # Normalize obj part to a list ref unless( ref $props->{$pred_key} eq 'ARRAY' ) { $props->{$pred_key} = [$props->{$pred_key}]; } # handle implicit properties # # TODO: Support URIs for implicit properties # foreach my $obj ( @{ $props->{$pred_key} } ) { # Is the object a literal? if( not ref $obj ) { if( $DEBUG ) { unless( $obj =~ /^http/ ) { confess "The uri '$obj' seem to be a literal"; } } $obj = $self->get( $obj ); } elsif( ref $obj eq 'SCALAR' ) { debug " Creating literal '$$obj'\n", 1; # Warning. Previouslu wrote this as \$obj. It took me # over a day to find this and realize that the # reasigning of $obj would overwrite the created # literal value, creating a self-reference # $obj = $self->declare_literal($obj); $obj->[NODE][SOLID] = $solid; debug "Changing SOLID to $solid for $obj->[NODE][URISTR] ". "IDS $obj->[NODE][IDS]\n", 3; # The SOLID is set to $solid. If this literal shouldn't be # saved in any interface, the base interface should # take care of the literal. The storage of the # literal should be done via the arc that points at # it. It should suffice to handle the arc and at that # time declare the literal solid in case it is saved # together with the arc. } elsif( ref $obj ne "RDF::Service::Context" ) { die "Each obj should be context obj, scalar ref or res uri"; } if( $DEBUG ) { if( $obj->[NODE][VALUE] ) { unless( ref($obj->[NODE][VALUE]) eq 'SCALAR') { confess "Bad value for $obj->[NODE][URISTR] ( ". ref($obj->[NODE][VALUE])." ne 'SCALAR' )"; } } } # TODO: Maby check if the values is the same even if the # literal URIs differ? No, use the same URI if this # matters! debug " Object $obj->[NODE][URISTR]\n", 2; # Does this resource already have the arc? if( $del_props{$pred_id}{$obj->[NODE][ID]} ) { # Keep this property debug " Keep object\n", 2; delete $del_props{$pred_id}{$obj->[NODE][ID]}; } else { # Add this property debug " Add object\n", 2; push @{$add_props{$pred_id}}, $obj; } } } if( %del_props ) { # Will delete props from all interfaces foreach my $pred_id ( keys %del_props ) { if( $DEBUG ) { my $pred_uri = &id2uri($pred_id); debug "Del checking $pred_uri\n", 1; } # Remove the pred objs if other pred objs was specified, # or if $trim is set, in case we remove everything not # specified # if( $DEBUG >= 2 ) { unless( $trim ) { debug " Skipping delcheck\n"; } my $nokeys = keys %{ $del_props{$pred_id} }; debug " $nokeys objs found\n"; } next unless $trim; # next unless $trim or $add_props{$pred_id}; Wrong, right? foreach my $obj_id ( keys %{ $del_props{$pred_id} } ) { debug " Deleting node ". $del_props{$pred_id}{$obj_id}[NODE][URISTR]."\n", 2; $del_props{$pred_id}{$obj_id}->delete_node(); } } } if( %add_props ) { # Will only add each prop in one interface foreach my $pred_id ( keys %add_props ) { foreach my $obj ( @{ $add_props{$pred_id} } ) { $self->declare_add_prop( $self->get_context_by_id($pred_id), $obj, undef, undef, $solid); } } } debug_end("set_props", ' ', $self); return $self; } sub create_literal ### DEPRECATED { my( $self, $uristr, $lit_str_ref ) = @_; die "deprecated"; debug_start("create_literal", ' ', $self); debug " Creating ($$lit_str_ref)\n", 1; if( $uristr ) { # NOTE: Copied from create_model # This should validate the uri. If this interface can't # create the URI, it will either return "try next interface" # or "failed", depending on why. # For now: Just allow models in the local namespace unless( $uristr =~ /@{[NS_LD]}/o ) { die "Invalid namespace for literal"; } } my $literal = $self->declare_literal( $lit_str_ref, $uristr ); # Change SOLID to 0 instead of storing the node directly # $literal->[NODE][SOLID] = 0; #$literal->store_node; if( $DEBUG ) { if( $literal->[NODE][VALUE] ) { debug "Checking literal value\n"; unless( ref($literal->[NODE][VALUE]) eq 'SCALAR') { confess "Bad value for $literal->[NODE][URISTR] ( ". ref($literal->[NODE][VALUE])." ne 'SCALAR' )"; } else { # warn Dumper $literal->[NODE][VALUE]; } } } debug_end("create_literal", ' ', $self); # Return the literal object # return( $literal ); } sub set_literal { my( $self, $lit_str_ref ) = @_; debug_start("set_literal", ' ', $self); debug " Change to ($$lit_str_ref)\n", 1; # TODO: make sure you have the right to update this literal! $self->declare_literal( $lit_str_ref, $self, ); $self->store_node; debug_end("set_literal", ' ', $self); } sub set_label { my( $self, $val ) = @_; die "deprecated"; # For simplicity, we only use one label. Intended for system # administration. if( ref $val eq 'ARRAY' ) { $val = $val->[0]; } if( ref $val eq 'RDF::Service::Context' ) { # $val = $val->[NODE][VALUE]; } if( ref $val eq 'SCALAR' ) { $val = $$val; } # $self->[NODE][LABEL] = $val || ''; } sub types_as_string { my( $self ) = @_; # # die $self->uri."--::".Dumper($self->[TYPES]); my $result = ""; my $type_ref = $self->[NODE][TYPE]; foreach my $type_id ( sort keys %{$type_ref} ) { my $type_uristr = id2uri( $type_id ); $result .= "t $type_uristr\n"; foreach my $model_id ( sort keys %{$type_ref->{$type_id}} ) { my $model_uristr = id2uri( $model_id ); my $solid = $type_ref->{$type_id}{$model_id} - 1; $result .= " m $model_uristr SOLID $solid\n"; } } return $result; } sub to_string { my( $self ) = @_; # Old! my $str = ""; no strict 'refs'; $str.="TYPES\t: ". $self->types_as_string ."\n"; foreach my $attrib (qw( IDS URISTR ID NAME LABEL PREFIX MODULE_NAME )) { $self->[NODE][&{$attrib}] and $str.="$attrib\t:". $self->[NODE][&{$attrib}] ."\n"; } foreach my $attrib (qw( NS MODEL ALIASFOR LANG PRED SUBJ OBJ )) { # my $dd = Data::Dumper->new([$self->[&{$attrib}]]); # $str.=Dumper($dd->Values)."\n\n\n"; # $self->[&{$attrib}] and $str.="$attrib\t:".Dumper($self->[&{$attrib}])."\n"; $self->[NODE][&{$attrib}] and $str.="$attrib\t:". ($self->[NODE][&{$attrib}][URISTR]||"no value")."\n"; } return $str; } ######################################################## # # Wrapper methods for the interfaces # # &RDF::Service::Dispatcher::go(shift, $AUTOLOAD, @_); # TODO: Should not mark that ALL data has been initialized if # init_types was called from a function in the process of adding new # types. This could be done with a memory of previous calls in the # CONTEXT. # NB! Expect all initiated data to be solid! # NB! Set ALL after a call to init, unless thare are additionall data # to be set. sub init_types { my( $self ) = shift; debug "GO init_types\n", 3; go($self, 'init_types', @_) unless $self->[NODE][LOCAL]; # go($self, 'init_dyntypes', @_); $self->[NODE][TYPE_ALL] ||= 1; } sub init_rev_types { my( $self ) = shift; debug "GO init_rev_types\n", 3; go($self, 'init_rev_types', @_) unless $self->[NODE][LOCAL]; # go($self, 'init_rev_dyntypes', @_); $self->[NODE][REV_TYPE_ALL] ||= 1; } sub init_rev_subjs { my( $self, $wanted ) = @_; # # $wanted is an array ref of predicate nodes # TODO: Should $wanted be array of contexts instead? # TODO: Only initlialize wanted predicates # HERE $wanted ? debug "GO init_rev_subjs\n", 3; my $node = $self->[NODE]; go($self, 'init_rev_subjs', $wanted) unless $node->[LOCAL]; $wanted ||= []; # Use DYNTABLE debug "..DYNTABLE for $node->[URISTR]\n", 2; # TODO: This raised for me the question of what the model for the # dynamic properties should be. Who is stating the statement? # It's a combination of: # # - The model of the program code # - The model of all data used as input # # My previous thought, if I remember, was to create a custom model # for the specific combination, and put the data about the # involved models as metadata for that model. # # I think that the agent property of the model will be used as a # base for trust. For dynamic properties, a group could be used # as agent. The first time the group is encounterd, we could # check each agent. But then we could trust the group. That # would mean that the agent would be a bag of agents. # # Should every dynamic property be placed in a unique model? Or # should we group properties that has identical metadata, maby # within the same session? But the session doesn't have a clear # connection to the dynamic properties, other than maby as the # model for a part of the source data. Ah, thats it! A new # session results in a new source model and a new dynamic model. my $dyn_model = $self->get(NS_LD.'#The_Dynamic_Model'); $dyn_model->[WMODEL] = $dyn_model; # # TODO: $dyn_model->[NODE][MODEL] should be The_Base_Model ### <<<--- HERE !!! # # TODO: # 1. Only initialize the wanted dynprops # 2. merge duplicate props DYNLOOP: foreach my $pred_uristr ( keys %{$node->[DYNTABLE]} ) { # Also used in Dispatcher for method calls # my $call_key = "$pred_uristr $self->[NODE] @$wanted"; foreach( @{$self->[HISTORY]} ) { if( $_ eq $call_key ) { debug "Recursive call '$call_key' skipped", 2; last DYNLOOP; } } # warn "---<<< called $call_key >>>---\n"; push @{$self->[HISTORY]}, $call_key; my $coderef = $node->[DYNTABLE]{$pred_uristr}; my $pred = $self->get($pred_uristr); for( my $i=0; $i<= $#$coderef; $i++ ) { debug_start( $pred_uristr, $i, $self ); debug "..Calling dynprop $coderef->[$i][1][URISTR]\n", 2; # The second parameter is the interface object my( $res ) = &{$coderef->[$i][0]}($self, $coderef->[$i][1], $wanted); debug_end( $pred_uristr, $i, $self ); unless( ref $res eq 'ARRAY' ) { confess"Malformed return value from $pred_uristr"; } ### <--- HERE !!! # It could be that the DYNPROP initialization, in a higher # level adds the prop. In that, case, we should not do it # again, but just continue. Check if the specific # property is defined. (See declare_arc()) # unless( $self->[NODE][REV_SUBJ]{$pred->[NODE][ID]} ) { # Add the properties in a special model foreach my $obj ( @$res ) { $dyn_model->declare_arc( $pred, $self, $obj, undef, undef, 1 ); } } } # Falling back one step (not one LEVEL) pop @{$self->[HISTORY]}; } debug "..DYNTABLE done\n", 2; $node->[REV_SUBJ_ALL] ||= 1; } sub init_rev_objs { my( $self ) = shift; debug "GO init_rev_objs\n", 3; go($self, 'init_rev_objs', @_) unless $self->[NODE][LOCAL]; # TODO: Same as init_rev_subjs, but reverse $self->[NODE][REV_OBJ_ALL] ||= 1; } # # TODO: init_rev_preds # sub store_types { my( $self, @args ) = @_; debug_start("store_types", ' ', $self ); if( $DEBUG >= 4 ) { debug "HISTORY\n"; foreach( @{$self->[HISTORY]} ) { debug " $_\n"; } } my $node = $self->[NODE]; $node->[TYPE_ALL] or $self->init_types(); # Check if all types are solid # my $unsaved = 0; CHECK: foreach my $type_id ( keys %{$node->[TYPE]} ) { my $type = $self->get_context_by_id( $type_id ); foreach my $model_id ( keys %{$node->[TYPE]{$type_id}} ) { my $model = $self->get_context_by_id( $model_id )->[NODE]; unless( $node->[TYPE]{$type_id}{$model_id} == 2 ) { $unsaved ++; last CHECK; } } } if( $unsaved ) { debug "GO store_types\n", 3; if( go($self, 'store_types', @args) and $node->[TYPE_ALL] ) { $node->[TYPE_ALL] = 2; } } else { $node->[TYPE_ALL] = 2; } debug_end("store_types", ' ', $self); } sub store_props { my( $self, @args ) = @_; my $node = $self->[NODE]; $node->[REV_SUBJ_ALL] or $self->init_rev_subjs(); debug "GO store_props\n", 3; if( go($self, 'store_props', @args) and $node->[REV_SUBJ_ALL] ) { $node->[REV_SUBJ_ALL] = 2; } } sub store { my( $self ) = @_; debug_start("store", ' ', $self); # TODO: Reset all other IDS caches (for this Resource) my $node = $self->[NODE]; # The order of checking for a node is: # # 1. props # 2. types # 3. node # 4. model # # The order is significant, since the store_props and store_types # methods can delegate storage of specific data to the node or # model. # Save props unless they are solid unless( defined $node->[REV_SUBJ_ALL] and $node->[REV_SUBJ_ALL] == 2 ) { $self->store_props; } # Save types unless they are solid unless( defined $node->[TYPE_ALL] and $node->[TYPE_ALL] == 2 ) { $self->store_types; } unless( $node->[SOLID] ) { if( $DEBUG ) { if( $node->[VALUE] ) { debug( "Node NOT solid: ${$node->[VALUE]}\n" ); } } $self->store_node; } else { if( $DEBUG ) { if( $node->[VALUE] ) { debug( "Node SOLID: ${$node->[VALUE]}\n" ); } } } # Also store the model if( $node->[MODEL] ) { debug " Is the model ($node->[URISTR]) solid?\n", 2; $self->get_context_by_id($node->[MODEL][ID])->store unless $node->[MODEL][SOLID]; } # Only saves props. Not rev props. debug_end("store", ' ', $self); return( 1 ); } ###################################################################### # # Declaration methods should only be called from interfaces. # sub declare_del_types { my( $self, $types ) = @_; debug_start("declare_del_types", ' ', $self); my $node_type = $self->[NODE][TYPE]; my $model_id = $self->[WMODEL][NODE][ID]; my $id = $self->[NODE][ID]; my @ids = (); if( defined $types ) { @ids = map $_->[NODE][ID], @$types; } else { @ids = keys %$node_type; } foreach my $class_id ( @ids ) { my $class_node = $self->get_node_by_id($class_id); debug " Checking $class_node->[URISTR]\n", 2; unless( delete $node_type->{$class_id}{$model_id} ) { debug " Type defined in another model:\n", 2; foreach my $other_model_id ( keys %{$node_type->{$class_id}} ) { my $other_model_node = $self->get_node_by_id($other_model_id); debug " $other_model_node->[URISTR]\n", 2; } next; } my $class_rev_type = $class_node->[REV_TYPE]; debug " Removing rev_type node\n", 2; if( $DEBUG ) { unless( $class_rev_type->{$id} ) { unless( $class_node->[URISTR] eq NS_RDFS.'Resource' ) { die " There was no rev_type to remove!!\n"; } } } delete $class_rev_type->{$id}{$model_id}; delete $node_type->{$class_id} unless keys %{$node_type->{$class_id}}; delete $class_rev_type->{$id} unless keys %{$class_rev_type->{$id}}; } debug_end("declare_del_types", ' ', $self); } sub declare_del_rev_types { my( $self, $res ) = @_; debug_start("declare_del_rev_types", ' ', $self); my $class_rev_type = $self->[NODE][REV_TYPE]; my $model_id = $self->[WMODEL][NODE][ID]; my $id = $self->[NODE][ID]; my @ids = (); if( defined $res ) { @ids = map $_->[NODE][ID], @$res; } else { @ids = keys %$class_rev_type; } foreach my $res_id ( @ids ) { my $class_node = $self->get_node_by_id($res_id); debug " Checking $class_node->[URISTR]\n", 2; unless( delete $class_rev_type->{$res_id}{$model_id} ) { next; } my $class_type = $class_node->[TYPE]; debug " Removing type node\n", 2; if( $DEBUG ) { unless( $class_type->{$id} ) { die " There was no type to remove!!\n"; } } delete $class_type->{$id}{$model_id}; delete $class_rev_type->{$res_id} unless keys %{$class_rev_type->{$res_id}}; delete $class_type->{$id} unless keys %{$class_type->{$id}}; } debug_end("declare_del_rev_types", ' ', $self); } sub declare_literal { my( $self, $lit_str_ref, $lit, $types, $props, $model ) = @_; # # - $model is a resource object # - $lit (uri or node or undef) # - $lref will be a scalar ref # - $types is ref to array of type objects or undef # - $props is hash ref with remaining properties or undef # - $model is working model node die "not implemented" if $types or $props; # TODO: CHECK that this resource realy is SOLID by default? debug_start("declare_literal", ' ', $self ); my $local = 0; # $lit can be node or uristr # unless( ref $lit ) { unless( defined $lit ) { $lit = NS_LD."/literal/". &get_unique_id; $local = 1; } $lit = $self->get( $lit ); } if( $DEBUG ) { debug " ( $$lit_str_ref )\n", 1; ref $lit_str_ref eq 'SCALAR' or die "Value must be a scalar reference"; if( $$lit_str_ref =~ /^RDF/ ) { warn "*****"; confess "Value is $$lit_str_ref"; } } # TODO: Set value as property if value differ among models my $lit_node = $lit->[NODE]; $model ||= $self->[WMODEL][NODE]; $lit_node->[VALUE] = $lit_str_ref; $lit_node->[MODEL] = $model; $lit_node->[NAME] = 'Literal'; $lit_node->[LOCAL] = 1; $model->[REV_MODEL]{$lit_node->[ID]} = $lit_node; $lit->declare_add_types([NS_RDFS.'Literal'], $model, 1 ); debug_end("declare_literal", ' ', $self); return $lit; } sub declare_selection { my( $self, $content, $selection ) = @_; debug_start("declare_selection", ' ', $self); if( $DEBUG ) { confess unless ref $content; my @con_uristr = (); debug " Selection consists of:\n", 2; foreach my $res ( @$content ) { confess "$res no Resource" unless ref $res eq "RDF::Service::Resource"; debug " $res->[URISTR]\n", 2; } } $content ||= []; my $local = 0; my $model = $self->[WMODEL][NODE] or die "$self->[NODE][URISTR] doesn't have a defined model"; unless( ref $selection ) { unless( defined $selection ) { $selection = NS_LD.'/selection/'.&get_unique_id; $local = 1; } $selection = $self->get( $selection ); } # warn "*** Selection is $selection->[NODE][URISTR]\n"; my $selection_node = $selection->[NODE]; $selection_node->[MODEL] = $model; $selection_node->[SELECTION] = $content; $selection_node->[NAME] = 'Selection'; $selection_node->[LOCAL] = $local; # TODO: Only add if this is an addition $model->[REV_MODEL]{$selection_node->[ID]} = $selection_node; $selection->declare_add_types( [NS_LS.'#Selection'] ); debug_end("declare_selection", ' ', $self); return $selection; } sub declare_node { my( $self, $uri, $types, $props ); die "Not done"; } sub declare_self ### DEPRECATED { my( $self, $types, $props ) = @_; die "deprecared"; # This is practicaly the same as set. declare_self does not store # the changes in the interfaces debug_start("declare_self", ' ', $self ); # Should each type and property only be saved in the first best # interface and not saved in the following interfaces? Yes! # # The types and props taken by one interface must be marked so # that the next interface doesn't handle them. This could be done # by modifying the arguments $types and $props to exclude those # that has been taken care of. $self->set_types( $types, 0, 1 ); $self->set_props( $props, 0, 1 ); debug_end("declare_self", ' ', $self); return $self; } sub declare_add_types { my( $self, $types, $model, $solid ) = @_; debug_start("declare_add_types", ' ', $self ); # TODO: Should it be model instead of types? # TODO: type(Resource) should be added by base init_types # The types will be listed in order from the most specific to the # most general. rdfs:Resource will allways be last. Insert # implicit items according to subClassOf. my $node = $self->[NODE]; $model ||= $self->[WMODEL][NODE]; $model = $self->get_node($model) unless ref $model; $solid ||= 0; if( $DEBUG ) { croak "Invalid solid value: $solid" if $solid > 1; croak "types must be a list ref" unless ref $types; croak "Bad model: $model" unless ref $model eq "RDF::Service::Resource"; confess "Bad node: $node" unless ref $node eq "RDF::Service::Resource"; debug " in model $model->[URISTR] IDS $model->[IDS]\n"; } my $model_id = $model->[ID]; foreach my $type ( @$types ) { # This should update the $types listref # $type = $self->get( $type ) unless ref $type; # Duplicate types in the same model will merge # # SOLID = 2, NONSOLID = 1 # $node->[TYPE]{$type->[NODE][ID]}{$model_id} = 1 + $solid; $type->[NODE][REV_TYPE]{$node->[ID]}{$model_id} = 1 + $solid; if( $DEBUG ) { debug(" T $type->[NODE][URISTR] ". "(IDS $type->[NODE][IDS] )\n", 2); if( $type->[NODE][MODEL] ) { debug(" Model of type is ". $type->[NODE][MODEL][URISTR] . " IDS $type->[NODE][MODEL][IDS]\n", 2); } } } # TODO: Only set this if one type was added # # NB! The model include this node in REV_MODEL if it self or any # of its types belongs to the model. But the node includes the # model only if int's internal data belongs to that model. # $model->[REV_MODEL]{$node->[ID]} = $node; unless( $solid ) { # Node type no longer solid. (Unsaved types) # $node->[TYPE_ALL] = 1 if $node->[TYPE_ALL]; } # TODO: Separate the dynamic types to a separate init_types debug "Adding subtypes\n", 2; # Add the implicit types for $node. This is done in a second loop # in order to resolv cyclic dependencies. # TODO: Check that this generates the right result. # my $subClassOf = $self->get(NS_RDFS.'subClassOf'); foreach my $type ( @$types ) { # $types has previously (in this function) been converted from # URISTR to res # NB!!! Special handling of some basic classes in order to # avoid cyclic dependencies # my $type_node = $type->[NODE]; next if $type_node->[URISTR] eq NS_RDFS.'Literal'; next if $type_node->[URISTR] eq NS_RDFS.'Class'; next if $type_node->[URISTR] eq NS_RDFS.'Resource'; next if $type_node->[URISTR] eq NS_RDF.'Statement'; # next if $type_node->[URISTR] eq NS_LS.'#Selection'; debug " for $type_node->[URISTR]\n", 2; # The class init_rev_subjs creates implicit subClassOf for # second and nth stage super classes. We only have to iterate # through the subClassOf properties of the type. # foreach my $sc ( @{$type->arc_obj_list($subClassOf)} ) { # Special handling of Resource. Added below next if $sc->[NODE][URISTR] eq NS_RDFS.'Resource'; debug " T $sc->[NODE][URISTR]\n", 2; # These are SOLID, since they are dynamic # TODO: What should the model be? # $node->[TYPE]{$sc->[NODE][ID]}{$model_id} = 2; $sc->[NODE][REV_TYPE]{$node->[ID]}{$model_id} = 2; # These types are dependent on the subClasOf statements } } # Add RDFS:Resource, in case not done yet # $node->[TYPE]{&uri2id(NS_RDFS.'Resource')}{uri2id(NS_RDFS)} = 2; # The jumptable must be redone now! if( $node->[JUMPTABLE] ) { debug "Resetting jumptable and DYNTABLE for ". "$node->[URISTR]: $node->[JTK]\n", 1; $node->[JTK] = '--resetted--'; undef $node->[JUMPTABLE]; # TODO: Should we reset the dyntable?!? # undef $node->[DYNTABLE]; } debug_end("declare_add_types", ' ', $self); return 1; } sub declare_add_static_literal { my( $subj, $pred, $lit_str, $arc_uristr ) = @_; # # $lit_str is a scalar ref # # The URI of a static literal represents what the value # represents. That is; the abstract property. It will never # change. (The literal static/dynamic type info is not stored) # TODO: find the literal... die "Not implemented"; # $arc_uristr ||= $model.'#'.get_unique_id(); # my $arc_id = uri2id( $arc_uristr ); # push @{ $subj->[PROPS]{$pred->[ID]} }, [$obj->[ID], # $arc_id, # $model->[ID], # ]; # return $arc_uristr; } sub declare_add_dynamic_literal { my( $subj, $pred, $lit_str_ref, $lit_uristr, $arc_uristr, $model ) = @_; # # $lit_str is a scalar ref # # The URI of a dynamic literal represents the property for the # specific subject. The literal changes content as the subjects # property changes. (The literal static/dynamic type info is not # stored) # TODO: Make this deprecated? Use declare_add_prop! die "deprecated!"; debug_start("declare_add_dynamic_literal", ' ', $subj ); croak "Invalid subj" unless ref $subj; croak "No subj model" unless ref $subj->[NODE][MODEL]; $pred = $subj->get( $pred ) unless ref $pred; $model ||= $subj->[WMODEL][NODE]; $arc_uristr ||= NS_LD."/literal/".get_unique_id(); # TODO: This is a implicit object. It's URI should be based on the # subject URI # my $lit = $subj->declare_literal( $lit_str_ref, $lit_uristr, undef, undef, $model, ); my $arc = $subj->declare_add_prop( $pred, $lit, $arc_uristr, $model ); debug_end("declare_add_dynamic_literal", ' ', $subj); return $arc; } sub declare_add_prop { my( $subj, $pred, $obj, $arc_uristr, $model, $solid ) = @_; $model ||= $subj->[WMODEL][NODE]; $solid ||= 0; my $arc = $subj->declare_arc( $pred, $subj, $obj, $arc_uristr, $model, $solid ); return $arc; } sub declare_arc { my( $self, $pred, $subj, $obj, $uristr, $model, $solid ) = @_; # It *could* be that we have two diffrent arcs with the same URI, # if they comes from diffrent models. The common case is that the # arcs with the same URI are identical. The PRED, SUBJ, OBJ slots # are used for the common case. # # TODO: Use explicit properties if the models differs. # # All models says the same thing unless the properties are # explicit. # A defined [REV_SUBJ] only means that some props has been # defined. It doesn't mean that ALL props has been defined. # An existing prop key with an undef value means that we know that # the prop doesn't exist. But a look for a nonexisting prop sould # (for now) trigger a complete initialization and set the complete # key. If the prop key is defined, ALL the valus will be there. # (Maby we will change this to make position 0 in the list to hold # special data. Or maby change the value to be another hash.) # The concept of "complete list" depends on other selection. # Diffrent selections will have diffrent lists. Every such # selection will be saved separately from the [REV_SUBJ] list. # It's existence guarantee that the list is complete. # TODO: Merge duplicate properties (with same obj or objs with # same value) frome same model !!! debug_start("declare_arc", ' ', $self); $subj = $self->get($subj) unless ref $subj; $model ||= $self->[WMODEL][NODE]; $solid ||= 0; my $local = 0; # Checks for abbrevations unless( ref $pred ) { if( my $x = $self->[SESSION][ABBREV]{$pred} ) { debug "......abbrev for $x->[NODE][URISTR]\n", 2; $pred = $x; } else { $pred = $self->get($pred); } } # handle implicit properties # # TODO: Support URIs for implicit properties # if( ref $obj eq 'SCALAR' ) { # The literal should by default be solid $obj = $self->declare_literal( $obj ); $obj->[NODE][SOLID] = $solid; # # We should not have to do anything to REV_SUBJ_ALL, et # al, right? debug "Changing SOLID to $solid for $obj->[NODE][URISTR] ". "IDS $obj->[NODE][IDS]\n", 3; } elsif( not ref $obj ) { $obj = $self->get( $obj ); } if( $DEBUG ) { if( $obj->[NODE][VALUE] ) { unless( ref($obj->[NODE][VALUE]) eq 'SCALAR') { confess "Bad value for $obj->[NODE][URISTR] ( ". ref($obj->[NODE][VALUE])." ne 'SCALAR' )"; } } } if( $uristr ) # arc could be changed { # TODO: Check that tha agent owns the namespace # For now: Just allow models in the local namespace my $ns_l = NS_LD; unless( $uristr =~ /$ns_l/ ) { confess "Invalid namespace for literal: $uristr"; } } else # The arc is created { # Who will know anything about this arc? There could be # statements about it later, but not now. $uristr = NS_LD."/arc/". &get_unique_id; $local = 1; # TODO: Call a miniversion of add_types that knows that no other # types has been added. We should not require the setting of # types and props to initialize itself. The initialization # should be done here. } my $arc = $self->get( $uristr ); my $arc_node = $arc->[NODE]; $model or die "*** No WMODEL for arc $arc_node->[URISTR]\n"; $arc_node->[IDS] or die "*** No IDS for arc $arc_node->[URISTR]\n"; if( $DEBUG ) { unless( ref( $model ) eq "RDF::Service::Resource" ) { confess "Bad model: $model"; } debug " P $pred->[NODE][URISTR]\n", 1; debug " S $subj->[NODE][URISTR]\n", 1; debug " O $obj->[NODE][URISTR]\n", 1; debug " M $model->[URISTR]\n", 1; debug " A $arc->[NODE][URISTR]\n", 1; } $arc_node->[PRED] = $pred->[NODE]; $arc_node->[SUBJ] = $subj->[NODE]; $arc_node->[OBJ] = $obj->[NODE]; $arc_node->[MODEL] = $model; $arc_node->[LOCAL] = $local; push @{ $subj->[NODE][REV_SUBJ]{$pred->[NODE][ID]} }, $arc_node; push @{ $obj->[NODE][REV_OBJ]{$pred->[NODE][ID]} }, $arc_node; push @{ $pred->[NODE][REV_PRED] }, $arc_node; $model->[REV_MODEL]{$arc_node->[ID]} = $arc_node; if( $solid ) { debug "Changing SOLID to 1 for $arc_node->[URISTR] ". "IDS $arc_node->[IDS]\n", 3; $arc_node->[SOLID] = 1; } else { debug "Changing SOLID to 0 for $arc_node->[URISTR] ". "IDS $arc_node->[IDS]\n", 3; $arc_node->[SOLID] = 0; $subj->[NODE][REV_SUBJ_ALL] = 1 if $subj->[NODE][REV_SUBJ_ALL]; $obj->[NODE][REV_OBJ_ALL] = 1 if $subj->[NODE][REV_OBJ_ALL]; } # TODO: declare_self should only be used if a existing arc is # changed. New arc should not call declare_self since that forces # an deep initialization of itself. $arc->declare_add_types( [NS_RDF.'Statement'], NS_RDF, 1 ); debug_end("declare_arc", ' ', $self); return $arc; } 1; __END__