# -*- Mode: Perl -*- # # Node.pm - Redland Perl RDF Node module # # Copyright (C) 2000-2005 David Beckett - http://www.dajobe.org/ # Copyright (C) 2000-2005 University of Bristol - http://www.bristol.ac.uk/ # # This package is Free Software and part of Redland http://librdf.org/ # # It is licensed under the following three licenses as alternatives: # 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version # 2. GNU General Public License (GPL) V2 or any newer version # 3. Apache License, V2.0 or any newer version # # You may not use this file except in compliance with at least one of # the above three licenses. # # See LICENSE.html or LICENSE.txt at the top of this package for the # full license terms. # # # package RDF::Redland::Node; use strict; use Encode; use vars qw($Type_Resource $Type_Property $Type_Literal $Type_Statement $Type_Blank); # FIXME: Should be the same as values of librdf_node_type enum in rdf_node.h # and mechanically kept in sync. $Type_Resource = 1; $Type_Literal = 2; $Type_Blank = 4; # FIXME: Needs to also match documentation near sub type =pod =head1 NAME RDF::Redland::Node - Redland RDF Node (RDF Resource, Property, Literal) Class =head1 SYNOPSIS use RDF::Redland; my $node1=new RDF::Redland::Node("Hello, World!"); my $node2=new RDF::Redland::Node($uri); # $uri is an RDF::Redland::URI my $node3=$node2->clone; my $node4=new RDF::Redland::URINode("http://example.com/"); my $node5=new RDF::Redland::LiteralNode("Hello, World!"); my $node6=new RDF::Redland::XMLLiteral("content"); my $node7=new RDF::Redland::BlankNode("genid1"); # alternate more verbose ways: my $node4=RDF::Redland::Node->new_from_uri("http://example.com/"); my $node5=RDF::Redland::Node->new_literal("Hello, World!"); my $node6=RDF::Redland::Node->new_xml_literal("content"); my $node7=RDF::Redland::Node->new_from_blank_identifier("genid1"); ... print $node4->uri->as_string,"\n"; # Using RDF::Redland::URI::as_string print $node5->literal_value_as_latin1,"\n"; =head1 DESCRIPTION This class represents RDF URIs, literals and blank nodes in the RDF graph. =cut ###################################################################### =pod =head1 CONSTRUCTORS =over =item new [STRING | URI | NODE] Create a new URI node, literal node or copy an existing node. If a literal I is given, make a plain literal node. If a the argument is of type I (perl URI or RDF::Redland::URI), make a resource node. Otherwise if the argument is an RDF::Redland::Node I, copy it. =cut # CONSTRUCTOR # (main) sub new ($;$) { my($proto,$arg)=@_; my $class = ref($proto) || $proto; my $self = {}; if($arg) { if(my $arg_class=ref($arg)) { # Try several classes if(UNIVERSAL::isa($arg, 'RDF::Redland::Node')) { return $arg->clone; } elsif(UNIVERSAL::isa($arg, 'RDF::Redland::URI')) { $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_uri_string($RDF::Redland::World->{WORLD},$arg->as_string); } elsif (UNIVERSAL::isa($arg, 'URI')) { $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_uri_string($RDF::Redland::World->{WORLD},$arg->as_string); } else { die "RDF::Redland::Node::new - Cannot make a node from an object of class $arg_class\n"; } } else { # Not a class $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_typed_literal($RDF::Redland::World->{WORLD},$arg,'',undef); } } else { $self->{NODE}=&RDF::Redland::CORE::librdf_new_node($RDF::Redland::World->{WORLD}); } return undef if !$self->{NODE}; bless ($self, $class); return $self; } sub new_from_uri_string ($$) { my($proto,$uri_string)=@_; my $class = ref($proto) || $proto; my $self = {}; die "RDF::Redland::Node::new_from_uri_string - Cannot create node from empty URI\n" unless $uri_string; $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_uri_string($RDF::Redland::World->{WORLD},$uri_string); return undef if !$self->{NODE}; bless ($self, $class); return $self; } =item new_from_uri URI Create a new URI node. I can be either a RDF::Redland::URI object, a perl URI class or a literal string. An alternative is: new RDF::Redland::URINode("http://example.org/"); =cut sub new_from_uri ($$) { my($proto,$arg)=@_; my $class = ref($proto) || $proto; my $self = {}; if(my $class=ref $arg) { if(UNIVERSAL::isa($arg, 'RDF::Redland::URI')) { $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_uri($RDF::Redland::World->{WORLD},$arg->{URI}); } elsif (UNIVERSAL::isa($arg, 'URI')) { $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_uri_string($RDF::Redland::World->{WORLD},$arg->as_string); } else { die "RDF::Redland::Node::new_from_uri - Cannot make a Node from an object of class $class\n"; } } else { $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_uri_string($RDF::Redland::World->{WORLD},$arg); } return undef if !$self->{NODE}; bless ($self, $class); return $self; } sub new_from_literal ($$$$) { my($proto,$string,$xml_language,$is_wf_xml)=@_; my $class = ref($proto) || $proto; my $self = {}; $is_wf_xml=($is_wf_xml ? 1 : 0); $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_literal($RDF::Redland::World->{WORLD},$string,$xml_language,$is_wf_xml); return undef if !$self->{NODE}; bless ($self, $class); return $self; } =item new_literal STRING [DATATYPE [XML_LANGUAGE]] Create a new literal node for a literal value I. Optional datatype URI I (RDF::Redland::URI, perl URI or string) and language (xml:lang attribute) I may also be given. An alternative is: new RDF::Redland::LiteralNode("Hello, World!"); new RDF::Redland::LiteralNode("Bonjour monde!", undef, "fr"); =cut sub new_literal ($$;$$) { my($proto,$string,$dt,$xml_language)=@_; my $class = ref($proto) || $proto; my $self = {}; my $dt_uri=undef; if(defined $dt) { if(UNIVERSAL::isa($dt, 'RDF::Redland::URI')) { # nop } elsif (UNIVERSAL::isa($dt, 'URI')) { $dt=RDF::Redland::URI->new($dt->as_string); } else { $dt=RDF::Redland::URI->new($dt); } $dt_uri=$dt->{URI}; } $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_typed_literal($RDF::Redland::World->{WORLD},$string,$xml_language,$dt_uri); return undef if !$self->{NODE}; bless ($self, $class); return $self; } =item new_xml_literal STRING Create a new XML datatyped literal node for the XML in I. An alternative is: new RDF::Redland::XMLLiteral("content"); =cut sub new_xml_literal ($$) { my($proto,$string)=@_; return $proto->new_from_literal($string,'',1); } =item new_from_blank_identifier IDENTIFIER Create a new blank node with blank node identifier I. An alternative is: new RDF::Redland::BlankNode("id"); =cut sub new_from_blank_identifier ($;$) { my($proto,$identifier)=@_; my $class = ref($proto) || $proto; my $self = {}; $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_blank_identifier($RDF::Redland::World->{WORLD},$identifier); return undef if !$self->{NODE}; bless ($self, $class); return $self; } =item clone Copy a RDF::Redland::Node. =cut sub clone ($) { my($node)=@_; my $class = ref($node); my $self = {}; if(!$class || $class ne 'RDF::Redland::Node') { die "RDF::Redland::Node::clone - Cannot copy a node object not of class RDF::Redland::Node\n"; } $self->{NODE}=&RDF::Redland::CORE::librdf_new_node_from_node($node->{NODE}); return undef if !$self->{NODE}; bless ($self, $class); return $self; } sub new_from_node ($$) { my($proto,$node)=@_; return $node->clone; } # internal constructor to build an object from a node created # by librdf e.g. from the result of a iterator->next operation # It always makes a new Redland Node sub _new_from_object ($$;$) { my($proto,$object,$do_not_copy)=@_; return undef if !$object; my $class = ref($proto) || $proto; my $self = {}; $self->{NODE}=$do_not_copy ? $object : &RDF::Redland::CORE::librdf_new_node_from_node($object); bless ($self, $class); return $self; } =pod =back =cut # DESTRUCTOR sub DESTROY ($) { my $self=shift; warn "RDF::Redland::Node DESTROY $self" if $RDF::Redland::Debug; if($self->{NODE}) { &RDF::Redland::CORE::librdf_free_node($self->{NODE}); } warn "RDF::Redland::Node DESTROY done\n" if $RDF::Redland::Debug; } =head1 METHODS =over =item uri Get the current URI of the node as an RDF::Redland::URI object. =cut sub uri ($) { my $obj=&RDF::Redland::CORE::librdf_node_get_uri(shift->{NODE}); return $obj ? RDF::Redland::URI->_new_from_object($obj) : undef; } =item blank_identifier Get the current blank identifier of the node =cut sub blank_identifier ($) { return &RDF::Redland::CORE::librdf_node_get_blank_identifier(shift->{NODE}); } =item type Get the node type. It is recommended to use the is_resource, is_literal or is_blank methods in preference to this (both simpler and quicker). The current list of types that are supported are: $RDF::Redland::Node::Type_Resource $RDF::Redland::Node::Type_Literal $RDF::Redland::Node::Type_Blank Example: if ($node->type == $RDF::Redland::Node::Type_Resource) { print "Node is a resource with URI ", $node->uri->as_string, "\n"; } else { ... } =cut sub type ($) { return &RDF::Redland::CORE::librdf_node_get_type(shift->{NODE}); } =item is_resource Return true if node is a resource (with a URI) =cut sub is_resource($) { return &RDF::Redland::CORE::librdf_node_is_resource(shift->{NODE}); } =item is_literal Return true if node is a literal =cut sub is_literal($) { return &RDF::Redland::CORE::librdf_node_is_literal(shift->{NODE}); } =item is_blank Return true if node is a blank nodeID =cut sub is_blank($) { return &RDF::Redland::CORE::librdf_node_is_blank(shift->{NODE}); } =item literal_value Get the node literal value string as UTF-8 (when the node is of type $RDF::Redland::Node::Type_Literal) =cut sub literal_value ($) { return decode_utf8(&RDF::Redland::CORE::librdf_node_get_literal_value(shift->{NODE})); } =item literal_value_as_latin1 Get the node literal value string converted from UTF-8 to ISO Latin-1 (when the node is of type $RDF::Redland::Node::Type_Literal) =cut sub literal_value_as_latin1 ($) { &RDF::Redland::CORE::librdf_node_get_literal_value_as_latin1(shift->{NODE}); } =item literal_value_language Get the node literal XML language (when the node is of type $RDF::Redland::Node::Type_Literal) or undef if not present. =cut sub literal_value_language ($) { &RDF::Redland::CORE::librdf_node_get_literal_value_language(shift->{NODE}); } =item literal_value_is_wf_xml Return non 0 if the literal string is well formed XML (when the node is of type $RDF::Redland::Node::Type_Literal). =cut sub literal_value_is_wf_xml ($) { &RDF::Redland::CORE::librdf_node_get_literal_value_is_wf_xml(shift->{NODE}); } =item literal_datatype Return the RDF::Redland::URI of the literal datatype or undef if it is not a datatype. =cut sub literal_datatype($) { my $self=shift; my $uri=&RDF::Redland::CORE::librdf_node_get_literal_value_datatype_uri($self->{NODE}); return $uri ? RDF::Redland::URI->new(&RDF::Redland::CORE::librdf_uri_to_string($uri)) : undef; } =item as_string Return the RDF::Redland::Node formatted as a string (UTF-8 encoded). =cut sub as_string ($) { my $v = decode_utf8(&RDF::Redland::CORE::librdf_node_to_string(shift->{NODE})); return $v; } =item equals NODE Return non zero if this node is equal to NODE =cut sub equals ($$) { my($self,$node)=@_; &RDF::Redland::CORE::librdf_node_equals($self->{NODE}, $node->{NODE}); } # Ensure the thing given is a Redland node, promoting it if possible # from other perl objects. sub _ensure ($) { my $node=shift; if(UNIVERSAL::isa($node, 'RDF::Redland::Node')) { $node=&RDF::Redland::CORE::librdf_new_node_from_node($node->{NODE}); } elsif(UNIVERSAL::isa($node, 'RDF::Redland::URI')) { $node=&RDF::Redland::CORE::librdf_new_node_from_uri($RDF::Redland::World->{WORLD},$node->{URI}); } elsif (UNIVERSAL::isa($node, 'URI')) { $node=&RDF::Redland::CORE::librdf_new_node_from_uri_string($RDF::Redland::World->{WORLD},$node->as_string); } else { $node=undef; } return $node; } =pod =back =head1 OLDER METHODS =over =item new_from_literal STRING XML_LANGUAGE IS_WF Create a new RDF::Redland::Node object for a literal value I with XML language (xml:lang attribute) I and if content is well formed XML, when I is non 0. I is optional can can be set to undef. This method remains but using new_literal is preferred. Instead, for plain literals use: $node=new RDF::Redland::Node("blah") =item new_from_typed_literal STRING [DATATYPE [XML_LANGUAGE]] Renamed to new_literal with same arguments. =item new_from_uri_string URI_STRING Create a new RDF::Redland::Node object for a resource with URI I. It is equivalent to use the shorter: $a=new RDF::Redland::Node->new_from_uri($uri_string) =item new_from_node NODE Create a new RDF::Redland::Node object from existing RDF::Redland::Node I (copy constructor). It is equivalent to use: $new_node=$old_node->clone =back =head1 SEE ALSO L =head1 AUTHOR Dave Beckett - http://www.dajobe.org/ =cut 1;