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.017
11              
12             =cut
13              
14             package RDF::Trine::Node;
15              
16 68     68   412 use strict;
  68         144  
  68         1725  
17 68     68   328 use warnings;
  68         133  
  68         1927  
18 68     68   318 no warnings 'redefine';
  68         138  
  68         5121  
19              
20             our ($VERSION, @ISA, @EXPORT_OK);
21             BEGIN {
22 68     68   232 $VERSION = '1.017';
23            
24 68         292 require Exporter;
25 68         621 @ISA = qw(Exporter);
26 68         1436 @EXPORT_OK = qw(ntriples_escape);
27             }
28              
29 68     68   357 use Scalar::Util qw(blessed refaddr);
  68         147  
  68         3133  
30              
31 68     68   22826 use RDF::Trine::Node::Nil;
  68         169  
  68         2594  
32 68     68   23268 use RDF::Trine::Node::Blank;
  68         157  
  68         2557  
33 68     68   24995 use RDF::Trine::Node::Literal;
  68         221  
  68         4322  
34 68     68   27396 use RDF::Trine::Node::Resource;
  68         264  
  68         3855  
35 68     68   24821 use RDF::Trine::Node::Variable;
  68         192  
  68         86057  
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 3 my $self = shift;
69 1   33     18 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 5537 my $self = shift;
80 3411   33     32069 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 1843 my $self = shift;
91 1098   66     8890 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 2073 my $self = shift;
102 1111   66     6972 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 480 my $self = shift;
113 292   66     4096 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 18788 my $self = shift;
124 12009   66     92014 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 18120 my $self = shift;
135 11147 50       40037 Carp::confess unless ($self->can('sse'));
136 11147         27881 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 2365     2365 1 3872 my $a = shift;
184 2365         3622 my $b = shift;
185 2365 100       7949 return -1 unless blessed($a);
186 2364 50       7145 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 2364         6610 my $at = $a->type;
193 2364         5752 my $bt = $b->type;
194 2364 100       5993 if ($a->type ne $b->type) {
195 256         549 my $an = $order{ $at };
196 256         448 my $bn = $order{ $bt };
197 256         661 return ($an <=> $bn);
198             } else {
199 2108         6256 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 10 my $self = shift;
214 6         12 my $o = {};
215 6 100       22 if ($self->isa('RDF::Trine::Node::Literal')) {
216 4         10 $o->{'type'} = 'literal';
217 4         13 $o->{'value'} = $self->literal_value;
218 4 100       12 $o->{'lang'} = $self->literal_value_language
219             if $self->has_language;
220 4 50       12 $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       17 $o->{'value'} = $self->isa('RDF::Trine::Node::Blank') ?
225             ('_:'.$self->blank_identifier) :
226             $self->uri ;
227             }
228 6         20 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 2320 my $class = shift;
242 10         23 my $context = $_[1];
243 10         18 $_ = $_[0];
244 10 100       191 if (my ($iri) = m/^<([^>]+)>/o) {
    100          
    100          
    50          
    50          
    50          
245 5         25 s/^<([^>]+)>\s*//;
246 5         22 return RDF::Trine::Node::Resource->new( $iri );
247             } elsif (my ($lit) = m/^"(([^"\\]+|\\([\\"nt]))+)"/o) {
248 3         6 my @args;
249 3         13 s/^"(([^"\\]+|\\([\\"nt]))+)"//;
250 3 100       17 if (my ($lang) = m/[@](\S+)/) {
    100          
251 1         4 s/[@](\S+)\s*//;
252 1         4 $args[0] = $lang;
253             } elsif (m/^\Q^^\E/) {
254 1         4 s/^\Q^^\E//;
255 1         8 my ($dt) = $class->from_sse( $_, $context );
256 1         5 $args[1] = $dt->uri_value;
257             }
258 3         7 $lit =~ s/\\(.)/eval "\"\\$1\""/ge;
  0         0  
259 3         12 return RDF::Trine::Node::Literal->new( $lit, @args );
260             } elsif (my ($id1) = m/^[(]([^)]+)[)]/) {
261 1         5 s/^[(]([^)]+)[)]\s*//;
262 1         5 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       6 if ($pn eq '') {
271 0         0 $pn = '__DEFAULT__';
272             }
273 1 50       5 if (my $ns = $context->{namespaces}{ $pn }) {
274 1         5 s/^(\S+):(\S+)\s*//;
275 1         7 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 6947     6947   11524 my $self = shift;
286 6947         11345 my $str = shift;
287            
288 6947 100       24973 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 6366         17386 return $str;
291             } else {
292             # slow path - escape all the special characters
293 581         1082 my $rslt = '';
294 581         1617 while (length($str)) {
295 8538 100       21028 if (my ($ascii) = $str =~ /^([A-Za-z0-9 \t]+)/) {
296 2506         4046 $rslt .= $ascii;
297 2506         7548 substr($str, 0, length($ascii)) = '';
298             } else {
299 6032         12084 my $utf8 = substr($str,0,1,'');
300 6032 100       19717 if ($utf8 eq '\\') {
    100          
    100          
    100          
301 77         180 $rslt .= '\\\\';
302             } elsif ($utf8 =~ /^[\x{10000}-\x{10ffff}]$/) {
303 366         1200 $rslt .= sprintf('\\U%08X', ord($utf8));
304             } elsif ($utf8 =~ /^[\x7f-\x{ffff}]$/) {
305             # $rslt = '\\u'.uc(unpack('H4', $uchar->utf16be)) . $rslt;
306 924         3094 $rslt .= sprintf('\\u%04X', ord($utf8));
307             } elsif ($utf8 =~ /^[\x00-\x08\x0b-\x0c\x0e-\x1f]$/) {
308 604         1893 $rslt .= sprintf('\\u%04X', ord($utf8));
309             } else {
310 4061         9883 $rslt .= $utf8;
311             }
312             }
313             }
314             # $rslt =~ s/\\/\\\\/g;
315 581         1645 $rslt =~ s/\n/\\n/g;
316 581         1236 $rslt =~ s/\t/\\t/g;
317 581         1056 $rslt =~ s/\r/\\r/g;
318 581         1302 $rslt =~ s/"/\\"/g;
319 581         1758 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