File Coverage

blib/lib/RDF/Trine/Node.pm
Criterion Covered Total %
statement 110 125 88.0
branch 40 52 76.9
condition 10 18 55.5
subroutine 21 24 87.5
pod 13 13 100.0
total 194 232 83.6


line stmt bran cond sub pod time code
1             # RDF::Trine::Node
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Trine::Node - Base class for RDF Nodes
7              
8             =head1 VERSION
9              
10             This document describes RDF::Trine::Node version 1.018
11              
12             =cut
13              
14             package RDF::Trine::Node;
15              
16 68     68   398 use strict;
  68         135  
  68         1696  
17 68     68   317 use warnings;
  68         130  
  68         1747  
18 68     68   318 no warnings 'redefine';
  68         134  
  68         5025  
19              
20             our ($VERSION, @ISA, @EXPORT_OK);
21             BEGIN {
22 68     68   238 $VERSION = '1.018';
23            
24 68         262 require Exporter;
25 68         599 @ISA = qw(Exporter);
26 68         1506 @EXPORT_OK = qw(ntriples_escape);
27             }
28              
29 68     68   332 use Scalar::Util qw(blessed refaddr);
  68         152  
  68         3018  
30              
31 68     68   21939 use RDF::Trine::Node::Nil;
  68         163  
  68         2420  
32 68     68   21658 use RDF::Trine::Node::Blank;
  68         161  
  68         2634  
33 68     68   24867 use RDF::Trine::Node::Literal;
  68         230  
  68         4047  
34 68     68   26012 use RDF::Trine::Node::Resource;
  68         235  
  68         3690  
35 68     68   23812 use RDF::Trine::Node::Variable;
  68         183  
  68         85737  
36              
37              
38             =head1 FUNCTIONS
39              
40             =over 4
41              
42             =item C<< ntriples_escape ( $value ) >>
43              
44             Returns the passed string value with special characters (control characters,
45             Unicode, etc.) escaped, suitable for printing inside an N-Triples or Turtle
46             encoded literal.
47              
48             =cut
49              
50             sub ntriples_escape {
51 0     0 1 0 my $class = __PACKAGE__;
52 0         0 return $class->_unicode_escape( @_ );
53             }
54              
55             =back
56              
57             =head1 METHODS
58              
59             =over 4
60              
61             =item C<< is_node >>
62              
63             Returns true if this object is a RDF node, false otherwise.
64              
65             =cut
66              
67             sub is_node {
68 1     1 1 4 my $self = shift;
69 1   33     22 return (blessed($self) and $self->isa('RDF::Trine::Node'));
70             }
71              
72             =item C<< is_nil >>
73              
74             Returns true if this object is the nil-valued node.
75              
76             =cut
77              
78             sub is_nil {
79 3411     3411 1 5666 my $self = shift;
80 3411   33     34040 return (blessed($self) and $self->isa('RDF::Trine::Node::Nil'));
81             }
82              
83             =item C<< is_blank >>
84              
85             Returns true if this RDF node is a blank node, false otherwise.
86              
87             =cut
88              
89             sub is_blank {
90 1098     1098 1 1965 my $self = shift;
91 1098   66     9289 return (blessed($self) and $self->isa('RDF::Trine::Node::Blank'));
92             }
93              
94             =item C<< is_resource >>
95              
96             Returns true if this RDF node is a resource, false otherwise.
97              
98             =cut
99              
100             sub is_resource {
101 1111     1111 1 2035 my $self = shift;
102 1111   66     7413 return (blessed($self) and $self->isa('RDF::Trine::Node::Resource'));
103             }
104              
105             =item C<< is_literal >>
106              
107             Returns true if this RDF node is a literal, false otherwise.
108              
109             =cut
110              
111             sub is_literal {
112 292     292 1 454 my $self = shift;
113 292   66     2937 return (blessed($self) and $self->isa('RDF::Trine::Node::Literal'));
114             }
115              
116             =item C<< is_variable >>
117              
118             Returns true if this RDF node is a variable, false otherwise.
119              
120             =cut
121              
122             sub is_variable {
123 12009     12009 1 18359 my $self = shift;
124 12009   66     88815 return (blessed($self) and $self->isa('RDF::Trine::Node::Variable'));
125             }
126              
127             =item C<< as_string >>
128              
129             Returns the node in a string form.
130              
131             =cut
132              
133             sub as_string {
134 11147     11147 1 16657 my $self = shift;
135 11147 50       35388 Carp::confess unless ($self->can('sse'));
136 11147         27553 return $self->sse;
137             }
138              
139             =item C<< as_ntriples >>
140              
141             Returns the node in a string form suitable for NTriples serialization.
142              
143             =cut
144              
145             sub as_ntriples {
146 0     0 1 0 return $_[0]->sse;
147             }
148              
149             =item C<< sse >>
150              
151             Returns the SSE serialization of the node.
152              
153             =cut
154              
155             =item C<< equal ( $node ) >>
156              
157             Returns true if the two nodes are equal, false otherwise.
158              
159             =cut
160              
161             sub equal {
162 0     0 1 0 my $self = shift;
163 0         0 my $node = shift;
164 0 0       0 return 0 unless (blessed($node));
165 0         0 return (refaddr($self) == refaddr($node));
166             }
167              
168             =item C<< compare ( $node_a, $node_b ) >>
169              
170             Returns -1, 0, or 1 if $node_a sorts less than, equal to, or greater than
171             $node_b in the defined SPARQL ordering, respectively. This function may be
172             used as the function argument to C<<sort>>.
173              
174             =cut
175              
176             my %order = (
177             NIL => 0,
178             BLANK => 1,
179             URI => 2,
180             LITERAL => 3,
181             );
182             sub compare {
183 2369     2369 1 3399 my $a = shift;
184 2369         3269 my $b = shift;
185 2369 100       6516 return -1 unless blessed($a);
186 2368 50       6168 return 1 unless blessed($b);
187            
188             # (Lowest) no value assigned to the variable or expression in this solution.
189             # Blank nodes
190             # IRIs
191             # RDF literals (plain < xsd:string)
192 2368         5730 my $at = $a->type;
193 2368         5026 my $bt = $b->type;
194 2368 100       4849 if ($a->type ne $b->type) {
195 254         460 my $an = $order{ $at };
196 254         418 my $bn = $order{ $bt };
197 254         616 return ($an <=> $bn);
198             } else {
199 2114         5162 return $a->_compare( $b );
200             }
201             }
202              
203             =item C<< as_hashref >>
204              
205             Returns a hashref representing the node in an RDF/JSON-like manner.
206              
207             See C<< as_hashref >> at L<RDF::Trine::Model> for full documentation of the
208             hashref format.
209              
210             =cut
211              
212             sub as_hashref {
213 6     6 1 12 my $self = shift;
214 6         11 my $o = {};
215 6 100       23 if ($self->isa('RDF::Trine::Node::Literal')) {
216 4         11 $o->{'type'} = 'literal';
217 4         12 $o->{'value'} = $self->literal_value;
218 4 100       17 $o->{'lang'} = $self->literal_value_language
219             if $self->has_language;
220 4 50       11 $o->{'datatype'} = $self->literal_datatype
221             if $self->has_datatype;
222             } else {
223 2 50       12 $o->{'type'} = $self->isa('RDF::Trine::Node::Blank') ? 'bnode' : 'uri';
224 2 50       14 $o->{'value'} = $self->isa('RDF::Trine::Node::Blank') ?
225             ('_:'.$self->blank_identifier) :
226             $self->uri ;
227             }
228 6         19 return $o;
229             }
230              
231             =item C<< from_sse ( $string, $context ) >>
232              
233             Parses the supplied SSE-encoded string and returns a RDF::Trine::Node object.
234              
235             =cut
236              
237             my $r_PN_CHARS_BASE = qr/([A-Z]|[a-z]|[\x{00C0}-\x{00D6}]|[\x{00D8}-\x{00F6}]|[\x{00F8}-\x{02FF}]|[\x{0370}-\x{037D}]|[\x{037F}-\x{1FFF}]|[\x{200C}-\x{200D}]|[\x{2070}-\x{218F}]|[\x{2C00}-\x{2FEF}]|[\x{3001}-\x{D7FF}]|[\x{F900}-\x{FDCF}]|[\x{FDF0}-\x{FFFD}]|[\x{10000}-\x{EFFFF}])/;
238             my $r_PN_CHARS_U = qr/(_|${r_PN_CHARS_BASE})/;
239             my $r_VARNAME = qr/((${r_PN_CHARS_U}|[0-9])(${r_PN_CHARS_U}|[0-9]|\x{00B7}|[\x{0300}-\x{036F}]|[\x{203F}-\x{2040}])*)/;
240             sub from_sse {
241 10     10 1 2941 my $class = shift;
242 10         17 my $context = $_[1];
243 10         19 $_ = $_[0];
244 10 100       153 if (my ($iri) = m/^<([^>]+)>/o) {
    100          
    100          
    50          
    50          
    50          
245 5         19 s/^<([^>]+)>\s*//;
246 5         18 return RDF::Trine::Node::Resource->new( $iri );
247             } elsif (my ($lit) = m/^"(([^"\\]+|\\([\\"nt]))+)"/o) {
248 3         6 my @args;
249 3         11 s/^"(([^"\\]+|\\([\\"nt]))+)"//;
250 3 100       17 if (my ($lang) = m/[@](\S+)/) {
    100          
251 1         5 s/[@](\S+)\s*//;
252 1         3 $args[0] = $lang;
253             } elsif (m/^\Q^^\E/) {
254 1         4 s/^\Q^^\E//;
255 1         7 my ($dt) = $class->from_sse( $_, $context );
256 1         4 $args[1] = $dt->uri_value;
257             }
258 3         6 $lit =~ s/\\(.)/eval "\"\\$1\""/ge;
  0         0  
259 3         13 return RDF::Trine::Node::Literal->new( $lit, @args );
260             } elsif (my ($id1) = m/^[(]([^)]+)[)]/) {
261 1         5 s/^[(]([^)]+)[)]\s*//;
262 1         4 return RDF::Trine::Node::Blank->new( $id1 );
263             } elsif (my ($id2) = m/^_:(\S+)/) {
264 0         0 s/^_:(\S+)\s*//;
265 0         0 return RDF::Trine::Node::Blank->new( $id2 );
266             } elsif (my ($v) = m/^[?](${r_VARNAME})/) {
267 0         0 s/^[?](${r_VARNAME})\s*//;
268 0         0 return RDF::Trine::Node::Variable->new( $v );
269             } elsif (my ($pn, $ln) = m/^(\S*):(\S*)/o) {
270 1 50       5 if ($pn eq '') {
271 0         0 $pn = '__DEFAULT__';
272             }
273 1 50       4 if (my $ns = $context->{namespaces}{ $pn }) {
274 1         4 s/^(\S+):(\S+)\s*//;
275 1         6 return RDF::Trine::Node::Resource->new( join('', $ns, $ln) );
276             } else {
277 0         0 throw RDF::Trine::Error -text => "No such namespace '$pn' while parsing SSE QName: >>$_<<";
278             }
279             } else {
280 0         0 throw RDF::Trine::Error -text => "Cannot parse SSE node from SSE string: >>$_<<";
281             }
282             }
283              
284             sub _unicode_escape {
285 6945     6945   10510 my $self = shift;
286 6945         11113 my $str = shift;
287            
288 6945 100       23008 if ($str =~ /\A[^\\\n\t\r"\x{10000}-\x{10ffff}\x{7f}-\x{ffff}\x{00}-\x{08}\x{0b}-\x{0c}\x{0e}-\x{1f}]*\z/sm) {
289             # hot path - no special characters to escape, just printable ascii
290 6364         15984 return $str;
291             } else {
292             # slow path - escape all the special characters
293 581         1088 my $rslt = '';
294 581         1648 while (length($str)) {
295 8538 100       20960 if (my ($ascii) = $str =~ /^([A-Za-z0-9 \t]+)/) {
296 2506         3958 $rslt .= $ascii;
297 2506         7193 substr($str, 0, length($ascii)) = '';
298             } else {
299 6032         12342 my $utf8 = substr($str,0,1,'');
300 6032 100       20116 if ($utf8 eq '\\') {
    100          
    100          
    100          
301 77         173 $rslt .= '\\\\';
302             } elsif ($utf8 =~ /^[\x{10000}-\x{10ffff}]$/) {
303 366         1349 $rslt .= sprintf('\\U%08X', ord($utf8));
304             } elsif ($utf8 =~ /^[\x7f-\x{ffff}]$/) {
305             # $rslt = '\\u'.uc(unpack('H4', $uchar->utf16be)) . $rslt;
306 924         3324 $rslt .= sprintf('\\u%04X', ord($utf8));
307             } elsif ($utf8 =~ /^[\x00-\x08\x0b-\x0c\x0e-\x1f]$/) {
308 604         2116 $rslt .= sprintf('\\u%04X', ord($utf8));
309             } else {
310 4061         9662 $rslt .= $utf8;
311             }
312             }
313             }
314             # $rslt =~ s/\\/\\\\/g;
315 581         1604 $rslt =~ s/\n/\\n/g;
316 581         1170 $rslt =~ s/\t/\\t/g;
317 581         1036 $rslt =~ s/\r/\\r/g;
318 581         1176 $rslt =~ s/"/\\"/g;
319 581         1778 return $rslt;
320             }
321             }
322              
323             1;
324              
325             __END__
326              
327             =back
328              
329             =head1 BUGS
330              
331             Please report any bugs or feature requests to through the GitHub web interface
332             at L<https://github.com/kasei/perlrdf/issues>.
333              
334             =head1 AUTHOR
335              
336             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
337              
338             =head1 COPYRIGHT
339              
340             Copyright (c) 2006-2012 Gregory Todd Williams. This
341             program is free software; you can redistribute it and/or modify it under
342             the same terms as Perl itself.
343              
344             =cut