File Coverage

blib/lib/RDF/Trine/Parser/NTriples.pm
Criterion Covered Total %
statement 175 205 85.3
branch 60 84 71.4
condition 7 9 77.7
subroutine 24 25 96.0
pod 4 4 100.0
total 270 327 82.5


line stmt bran cond sub pod time code
1             # RDF::Trine::Parser::NTriples
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Trine::Parser::NTriples - N-Triples Parser
7              
8             =head1 VERSION
9              
10             This document describes RDF::Trine::Parser::NTriples version 1.017
11              
12             =head1 SYNOPSIS
13              
14             use RDF::Trine::Parser;
15             my $parser = RDF::Trine::Parser->new( 'ntriples' );
16             $parser->parse_into_model( $base_uri, $data, $model );
17              
18             =head1 DESCRIPTION
19              
20             ...
21              
22             =head1 METHODS
23              
24             Beyond the methods documented below, this class inherits methods from the
25             L<RDF::Trine::Parser> class.
26              
27             =over 4
28              
29             =cut
30              
31             package RDF::Trine::Parser::NTriples;
32              
33 68     68   440 use strict;
  68         163  
  68         1854  
34 68     68   369 use warnings;
  68         149  
  68         1752  
35 68     68   345 use utf8;
  68         150  
  68         567  
36              
37 68     68   1701 use base qw(RDF::Trine::Parser);
  68         177  
  68         5662  
38              
39 68     68   429 use Carp;
  68         154  
  68         3739  
40 68     68   392 use Encode qw(decode);
  68         163  
  68         2631  
41 68     68   395 use Data::Dumper;
  68         170  
  68         2639  
42 68     68   383 use Log::Log4perl;
  68         159  
  68         632  
43 68     68   4410 use Scalar::Util qw(blessed reftype);
  68         166  
  68         3078  
44              
45 68     68   405 use RDF::Trine qw(literal);
  68         161  
  68         2520  
46 68     68   398 use RDF::Trine::Node;
  68         167  
  68         1997  
47 68     68   372 use RDF::Trine::Statement;
  68         167  
  68         1579  
48 68     68   339 use RDF::Trine::Error qw(:try);
  68         151  
  68         371  
49              
50             ######################################################################
51              
52             our ($VERSION);
53             BEGIN {
54 68     68   13453 $VERSION = '1.017';
55 68         202 $RDF::Trine::Parser::parser_names{ 'ntriples' } = __PACKAGE__;
56 68         186 foreach my $ext (qw(nt)) {
57 68         238 $RDF::Trine::Parser::file_extensions{ $ext } = __PACKAGE__;
58             }
59 68         144 my $class = __PACKAGE__;
60 68         173 $RDF::Trine::Parser::canonical_media_types{ $class } = 'text/plain';
61 68         159 foreach my $type (qw(text/plain)) {
62 68         212 $RDF::Trine::Parser::media_types{ $type } = __PACKAGE__;
63             }
64 68         13442 $RDF::Trine::Parser::format_uris{ 'http://www.w3.org/ns/formats/N-Triples' } = __PACKAGE__;
65             }
66              
67             ######################################################################
68              
69             =item C<< new >>
70              
71             =cut
72              
73             sub new {
74 394     394 1 173137 my $class = shift;
75 394         1654 my $self = bless( {@_}, $class);
76 394         1263 return $self;
77             }
78              
79             =item C<< parse_into_model ( $base_uri, $data, $model [, context => $context] ) >>
80              
81             Parses the bytes in C<< $data >>.
82             For each RDF statement parsed, will call C<< $model->add_statement( $statement ) >>.
83              
84             =item C<< parse_file_into_model ( $base_uri, $fh, $model [, context => $context] ) >>
85              
86             Parses all data read from the filehandle C<< $fh >>.
87             For each RDF statement parsed, will call C<< $model->add_statement( $statement ) >>.
88              
89             =cut
90              
91              
92             =item C<< parse ( $base_uri, $rdf, \&handler ) >>
93              
94             =cut
95              
96             sub parse {
97 24     24 1 98 my $self = shift;
98 24         39 my $base = shift;
99 24         42 my $string = shift;
100 24         41 my $handler = shift;
101 24     4   498 open( my $fh, '<:encoding(UTF-8)', \$string );
  4     4   26  
  4         9  
  4         26  
  4         2791  
  4         9  
  4         18  
102 24         4809 return $self->parse_file( $base, $fh, $handler );
103             }
104              
105             =item C<< parse_node ( $string, $base ) >>
106              
107             Returns the RDF::Trine::Node object corresponding to the node whose N-Triples
108             serialization is found at the beginning of C<< $string >>.
109              
110             =cut
111              
112             sub parse_node {
113 0     0 1 0 my $self = shift;
114 0         0 my $string = shift;
115 0         0 my $uri = shift;
116 0         0 my $n = $self->_eat_node( $uri, 0, $string );
117 0         0 return $n;
118             }
119              
120             =item C<< parse_file ( $base, $fh, \&handler ) >>
121              
122             =cut
123              
124             sub parse_file {
125 412     412 1 1048 my $self = shift;
126 412         899 my $base = shift;
127 412         972 my $fh = shift;
128 412         911 my $handler = shift;
129            
130 412 50       1722 unless (ref($fh)) {
131 0         0 my $filename = $fh;
132 0         0 undef $fh;
133 0 0       0 open( $fh, '<:encoding(UTF-8)', $filename ) or throw RDF::Trine::Error::ParserError -text => $!;
134             }
135            
136 412         934 my $lineno = 0;
137 68     68   489 no warnings 'uninitialized';
  68         153  
  68         109096  
138 412         8828 while (defined(my $line = <$fh>)) {
139 3870         19537 LINE:
140             ($line, my @extra) = split(/\r\n|\r|\n/, $line, 2);
141 3870         7046 $lineno++;
142            
143 3870 100 100     23742 next unless (defined($line) and length($line));
144 3019 100       9286 next unless ($line =~ /\S/);
145 3017         5737 chomp($line);
146 3017         9134 $line =~ s/^\s*//;
147 3017         18589 $line =~ s/\s*$//;
148 3017 100       12647 next if ($line =~ /^#/);
149            
150 750         1711 my @nodes = ();
151             try {
152 750     750   17924 while (my $n = $self->_eat_node( $base, $lineno, $line )) {
153 2257         4573 push(@nodes, $n);
154 2257         11712 $line =~ s/^\s*//;
155             }
156 750         6172 };
157 750         13629 $line =~ s/^\s//g;
158 750 50       2396 unless ($line eq '.') {
159             # Carp::cluck 'N-Triples parser failed: ' . Dumper(\@nodes, $line);
160 0         0 throw RDF::Trine::Error::ParserError -text => "Missing expected '.' at line $lineno";
161             }
162            
163 750         3153 $self->_emit_statement( $handler, \@nodes, $lineno );
164 750 50       2458 if (@extra) {
165 750         1940 $line = shift(@extra);
166 750         3341 goto LINE;
167             }
168             }
169             }
170              
171             sub _emit_statement {
172 742     742   1530 my $self = shift;
173 742         1271 my $handler = shift;
174 742         1441 my $nodes = shift;
175 742         1268 my $lineno = shift;
176 742         1197 my $st;
177 742 50       2419 if (scalar(@$nodes) == 3) {
178 742 100       2250 if ($self->{canonicalize}) {
179 6 100 66     39 if ($nodes->[2]->isa('RDF::Trine::Node::Literal') and $nodes->[2]->has_datatype) {
180 5         13 my $value = $nodes->[2]->literal_value;
181 5         13 my $dt = $nodes->[2]->literal_datatype;
182 5         18 my $canon = RDF::Trine::Node::Literal->canonicalize_literal_value( $value, $dt, 1 );
183 5         23 $nodes->[2] = literal( $canon, undef, $dt );
184             }
185             }
186 742         4862 $st = RDF::Trine::Statement->new( @$nodes );
187             # } elsif (scalar(@$nodes) == 4) {
188             # $st = RDF::Trine::Statement::Quad->new( @$nodes );
189             } else {
190             # warn Dumper($nodes);
191 0         0 throw RDF::Trine::Error::ParserError -text => qq[Not valid N-Triples data at line $lineno];
192             }
193 742         2627 $handler->( $st );
194             }
195              
196             sub _eat_node {
197 3007     3007   5547 my $self = shift;
198 3007         4965 my $base = shift;
199 3007         4873 my $lineno = shift;
200 3007         8700 $_[0] =~ s/^\s*//;
201 3007 50       8391 return unless length($_[0]);
202 3007         6665 my $char = substr($_[0], 0, 1);
203 3007 100       10249 return if ($char eq '.');
204            
205 2257 100       6225 if ($char eq '<') {
    100          
    50          
206 1649         7303 my ($uri) = $_[0] =~ m/^<([^>]*)>/;
207 1649         4933 substr($_[0], 0, length($uri)+2) = '';
208 1649         4324 return RDF::Trine::Node::Resource->new( _unescape($uri, $lineno) );
209             } elsif ($char eq '_') {
210 282         1253 my ($name) = $_[0] =~ m/^_:([A-Za-z][A-Za-z0-9]*)/;
211 282         950 substr($_[0], 0, length($name)+2) = '';
212 282         1526 return RDF::Trine::Node::Blank->new( $name );
213             } elsif ($char eq '"') {
214 326         916 substr($_[0], 0, 1) = '';
215 326         1603 my $value = decode('utf8', '');
216 326   66     17956 while (length($_[0]) and substr($_[0], 0, 1) ne '"') {
217 379         1783 while ($_[0] =~ m/^([^"\\]+)/) {
218 350         1084 $value .= $1;
219 350         1790 substr($_[0],0,length($1)) = '';
220             }
221 379 100       2079 if (substr($_[0],0,1) eq '\\') {
222 98         348 while ($_[0] =~ m/^\\(.)/) {
223 211 100       1023 if ($1 eq 't') {
    100          
    100          
    100          
    100          
    100          
    50          
224 15         34 $value .= "\t";
225 15         99 substr($_[0],0,2) = '';
226             } elsif ($1 eq 'r') {
227 4         12 $value .= "\r";
228 4         26 substr($_[0],0,2) = '';
229             } elsif ($1 eq 'n') {
230 44         85 $value .= "\n";
231 44         204 substr($_[0],0,2) = '';
232             } elsif ($1 eq '"') {
233 19         84 $value .= '"';
234 19         141 substr($_[0],0,2) = '';
235             } elsif ($1 eq '\\') {
236 3         9 $value .= "\\";
237 3         23 substr($_[0],0,2) = '';
238             } elsif ($1 eq 'u') {
239 99 50       330 $_[0] =~ m/^\\u([0-9A-Fa-f]{4})/ or throw RDF::Trine::Error::ParserError -text => qq[Bad N-Triples \\u escape at line $lineno, near "$_[0]"];
240 99         324 $value .= chr(oct('0x' . $1));
241 99         430 substr($_[0],0,6) = '';
242             } elsif ($1 eq 'U') {
243 27 50       88 $_[0] =~ m/^\\U([0-9A-Fa-f]{8})/ or throw RDF::Trine::Error::ParserError -text => qq[Bad N-Triples \\U escape at line $lineno, near "$_[0]"];
244 27         84 $value .= chr(oct('0x' . $1));
245 27         116 substr($_[0],0,10) = '';
246             } else {
247 0         0 throw RDF::Trine::Error::ParserError -text => qq[Not valid N-Triples escape character '\\$1' at line $lineno, near "$_[0]"];
248             }
249             }
250             }
251             }
252 326 50       1161 if (substr($_[0],0,1) eq '"') {
253 326         938 substr($_[0],0,1) = '';
254             } else {
255 0         0 throw RDF::Trine::Error::ParserError -text => qq[Ending double quote not found at line $lineno];
256             }
257            
258 326 100       1838 if ($_[0] =~ m/^@([a-z]+(-[a-zA-Z0-9]+)*)/) {
    100          
259 20         58 my $lang = $1;
260 20         64 substr($_[0],0,1+length($lang)) = '';
261 20         123 return RDF::Trine::Node::Literal->new($value, $lang);
262             } elsif (substr($_[0],0,3) eq '^^<') {
263 89         260 substr($_[0],0,3) = '';
264 89         505 my ($uri) = $_[0] =~ m/^([^>]*)>/;
265 89         422 substr($_[0], 0, length($uri)+1) = '';
266 89         663 return RDF::Trine::Node::Literal->new($value, undef, $uri);
267             } else {
268 217         1430 return RDF::Trine::Node::Literal->new($value);
269             }
270             } else {
271 0         0 throw RDF::Trine::Error::ParserError -text => qq[Not valid N-Triples node start character '$char' at line $lineno, near "$_[0]"];
272             }
273             }
274              
275             sub _unescape {
276 1649     1649   3295 my $string = shift;
277 1649         2815 my $lineno = shift;
278 1649         3075 my $value = '';
279 1649         4870 while (length($string)) {
280 1653         6640 while ($string =~ m/^([^\\]+)/) {
281 1653         5178 $value .= $1;
282 1653         6300 substr($string,0,length($1)) = '';
283             }
284 1653 100       5928 if (length($string)) {
285 11 50       45 if ($string eq '\\') {
286 0         0 throw RDF::Trine::Error::ParserError -text => qq[Backslash in N-Triples node without escaped character at line $lineno];
287             }
288 11 50       111 if ($string =~ m/^\\([tbnrf"'uU])/) {
289 11         60 while ($string =~ m/^\\([tbnrf"'uU])/) {
290 78 50       512 if ($1 eq 't') {
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    50          
291 0         0 $value .= "\t";
292 0         0 substr($string,0,2) = '';
293             } elsif ($1 eq 'b') {
294 0         0 $value .= "\b";
295 0         0 substr($string,0,2) = '';
296             } elsif ($1 eq 'n') {
297 0         0 $value .= "\n";
298 0         0 substr($string,0,2) = '';
299             } elsif ($1 eq 'r') {
300 0         0 $value .= "\r";
301 0         0 substr($string,0,2) = '';
302             } elsif ($1 eq 'f') {
303 0         0 $value .= "\f";
304 0         0 substr($string,0,2) = '';
305             } elsif ($1 eq '"') {
306 0         0 $value .= '"';
307 0         0 substr($string,0,2) = '';
308             } elsif ($1 eq '\\') {
309 0         0 $value .= "\\";
310 0         0 substr($string,0,2) = '';
311             } elsif ($1 eq 'u') {
312 74 50       213 $string =~ m/^\\u([0-9A-F]{4})/ or throw RDF::Trine::Error::ParserError -text => qq[Bad N-Triples \\u escape at line $lineno, near "$string"];
313 74         228 $value .= chr(oct('0x' . $1));
314 74         284 substr($string,0,6) = '';
315             } elsif ($1 eq 'U') {
316 4 50       18 $string =~ m/^\\U([0-9A-F]{8})/ or throw RDF::Trine::Error::ParserError -text => qq[Bad N-Triples \\U escape at line $lineno, near "$string"];
317 4         16 $value .= chr(oct('0x' . $1));
318 4         18 substr($string,0,10) = '';
319             }
320             }
321             } else {
322 0         0 my $esc = substr($string, 0, 2);
323 0         0 throw RDF::Trine::Error::ParserError -text => qq[Not a valid N-Triples escape sequence '$esc' at line $lineno, near "$string"];
324             }
325             }
326             }
327 1649         8597 return $value;
328             }
329              
330             1;
331              
332             __END__
333              
334             =back
335              
336             =head1 BUGS
337              
338             Please report any bugs or feature requests to through the GitHub web interface
339             at L<https://github.com/kasei/perlrdf/issues>.
340              
341             =head1 AUTHOR
342              
343             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
344              
345             =head1 COPYRIGHT
346              
347             Copyright (c) 2006-2012 Gregory Todd Williams. This
348             program is free software; you can redistribute it and/or modify it under
349             the same terms as Perl itself.
350              
351             =cut