File Coverage

blib/lib/RDF/Trine/Parser/RDFPatch.pm
Criterion Covered Total %
statement 113 154 73.3
branch 21 36 58.3
condition n/a
subroutine 22 26 84.6
pod 6 6 100.0
total 162 222 72.9


line stmt bran cond sub pod time code
1             # RDF::Trine::Parser::RDFPatch
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Trine::Parser::RDFPatch - RDF-Patch Parser
7              
8             =head1 VERSION
9              
10             This document describes RDF::Trine::Parser::RDFPatch version 1.018
11              
12             =head1 SYNOPSIS
13              
14             use RDF::Trine::Parser::RDFPatch;
15             my $serializer = RDF::Trine::Parser::RDFPatch->new();
16              
17             =head1 DESCRIPTION
18              
19             The RDF::Trine::Parser::RDFPatch class provides an API for serializing RDF
20             graphs to the RDF-Patch syntax.
21              
22             =head1 METHODS
23              
24             =over 4
25              
26             =cut
27              
28             package RDF::Trine::Parser::RDFPatch;
29              
30 1     1   505 use strict;
  1         2  
  1         24  
31 1     1   5 use warnings;
  1         2  
  1         23  
32              
33 1     1   5 use URI;
  1         2  
  1         17  
34 1     1   4 use Carp;
  1         2  
  1         65  
35 1     1   5 use Data::Dumper;
  1         3  
  1         42  
36 1     1   6 use Scalar::Util qw(blessed);
  1         2  
  1         40  
37 1     1   6 use List::Util qw(min);
  1         3  
  1         51  
38              
39 1     1   7 use RDF::Trine::Node;
  1         2  
  1         25  
40 1     1   6 use RDF::Trine::Statement;
  1         2  
  1         21  
41 1     1   5 use RDF::Trine::Error qw(:try);
  1         2  
  1         9  
42 1     1   151 use RDF::Trine::Parser::Turtle;
  1         4  
  1         17  
43 1     1   4 use RDF::Trine::Parser::Turtle::Constants;
  1         2  
  1         125  
44              
45             ######################################################################
46              
47             our ($VERSION);
48             BEGIN {
49 1     1   857 $VERSION = '1.018';
50             }
51              
52             ######################################################################
53              
54             =item C<< new ( ) >>
55              
56             Returns a new RDF-Patch Parser object.
57              
58             =cut
59              
60             sub new {
61 2     2 1 449 my $class = shift;
62 2         21 my $self = bless( {
63             last => [],
64             namespaces => RDF::Trine::NamespaceMap->new(),
65             }, $class );
66 2         6 return $self;
67             }
68              
69             =item C<< namespace_map >>
70              
71             Returns the RDF::Trine::NamespaceMap object used in parsing.
72              
73             =cut
74              
75             sub namespace_map {
76 0     0 1 0 my $self = shift;
77 0         0 return $self->{namespaces};
78             }
79              
80             =item C<< parse ( $base_uri, $rdf, \&handler ) >>
81              
82             =cut
83              
84             sub parse {
85 0     0 1 0 my $self = shift;
86 0         0 my $base = shift;
87 0         0 my $string = shift;
88 0         0 my $handler = shift;
89 0         0 open( my $fh, '<:encoding(UTF-8)', \$string );
90 0         0 return $self->parse_file( $base, $fh, $handler );
91             }
92              
93             =item C<< parse_file ( $base, $fh, \&handler ) >>
94              
95             =cut
96              
97             sub parse_file {
98 0     0 1 0 my $self = shift;
99 0         0 my $base = shift;
100 0         0 my $fh = shift;
101 0         0 my $handler = shift;
102            
103 0 0       0 unless (ref($fh)) {
104 0         0 my $filename = $fh;
105 0         0 undef $fh;
106 0 0       0 open( $fh, '<:encoding(UTF-8)', $filename ) or throw RDF::Trine::Error::ParserError -text => $!;
107             }
108            
109 0         0 my $lineno = 0;
110 0         0 while (defined(my $line = <$fh>)) {
111 0         0 $lineno++;
112 0         0 my $op = $self->parse_line( $line, $base );
113 0 0       0 last unless blessed($op);
114 0         0 $self->handle_op( $op, $handler, $lineno );
115             }
116             }
117              
118             =item C<< handle_op ( $op, $handler, $lineno ) >>
119              
120             Handles the RDF::Trine::Parser::RDFPatch::Op operation object.
121             For 'A'dd operations, the C<< $handler >> callback is called with the RDF statement.
122             Otherwise an exception is thrown.
123              
124             =cut
125              
126             sub handle_op {
127 0     0 1 0 my $self = shift;
128 0         0 my $op = shift;
129 0         0 my $handler = shift;
130 0         0 my $lineno = shift;
131 0         0 my $opid = $op->op;
132 0 0       0 if ($opid eq 'A') {
133 0         0 my ($st) = $op->args;
134 0         0 $handler->( $st );
135             } else {
136 0         0 my $col = 0;
137 0         0 throw RDF::Trine::Error::ParserError::Positioned (
138             -text => "Cannot handle RDF Patch operation type '$opid' during RDF parsing at $lineno:$col",
139             -value => [$lineno, $col],
140             );
141             }
142             }
143              
144             =item C<< parse_line ( $line, $base ) >>
145              
146             Returns an operation object.
147              
148             =cut
149              
150             sub _get_token_type {
151 4     4   10 my $self = shift;
152 4         6 my $l = shift;
153 4         8 my $type = shift;
154 4         14 my $t = $l->get_token;
155 4 50       15 unless ($t) {
156 0         0 $l->_throw_error(sprintf("Expecting %s but got EOF", decrypt_constant($type)));
157 0         0 return;
158             }
159 4 50       99 unless ($t->type eq $type) {
160 0         0 $self->_throw_error(sprintf("Expecting %s but got %s", decrypt_constant($type), decrypt_constant($t->type)), $t, $l);
161             }
162 4         43 return $t;
163             }
164              
165             sub parse_line {
166 11     11 1 3400 my $self = shift;
167 11         25 my $line = shift;
168 11         19 my $base = shift;
169 11 50       43 return if ($line =~ /^#/);
170 11 100       45 if (substr($line, 0, 7) eq '@prefix') {
171 1         31 open( my $fh, '<:encoding(UTF-8)', \$line );
172 1         119 my $l = RDF::Trine::Parser::Turtle::Lexer->new($fh);
173 1         8 $self->_get_token_type($l, PREFIX);
174 1         4 my $t = $self->_get_token_type($l, PREFIXNAME);
175 1         6 my $name = $t->value;
176 1         6 $name =~ s/:$//;
177 1         5 $t = $self->_get_token_type($l, IRI);
178 1         26 my $r = RDF::Trine::Node::Resource->new($t->value, $base);
179 1         5 my $iri = $r->uri_value;
180 1         4 $t = $self->_get_token_type($l, DOT);
181 1         26 $self->{namespaces}->add_mapping( $name => $iri );
182 1         16 return;
183             }
184            
185 10         45 my ($op, $tail) = split(/ /, $line, 2);
186 10 100       49 unless ($op =~ /^[ADQ]$/) {
187 1         21 throw RDF::Trine::Error::ParserError -text => "Unknown RDF Patch operation ID '$op'";
188             }
189            
190 9         81 my $p = RDF::Trine::Parser::Turtle->new( 'map' => $self->{namespaces} );
191 9         21 my @nodes;
192 9         23 foreach my $pos (1,2,3,4) {
193 34 100       182 if ($tail =~ /^\s*U\b/) {
    50          
    100          
194 2         9 substr($tail, 0, $+[0], '');
195 2         18 my $v = RDF::Trine::Node::Variable->new("v$pos");
196 2         6 $self->{last}[$pos] = $v;
197 2         6 push(@nodes, $v);
198             } elsif ($tail =~ /^\s*R\b/) {
199 0         0 substr($tail, 0, $+[0], '');
200 0         0 my $node = $self->{last}[$pos];
201 0 0       0 unless (blessed($node)) {
202 0         0 throw RDF::Trine::Error -text => "Use of non-existent `R`epeated term";
203             }
204 0         0 push(@nodes, $node);
205             } elsif ($tail =~ /^\s*[.]/) {
206 8         18 last;
207             } else {
208 24         43 my $token;
209 24         91 my $n = $p->parse_node($tail, $base, token => \$token);
210 24         66 $self->{last}[$pos] = $n;
211 24         64 push(@nodes, $n);
212 24         625 my $len = $token->column;
213 24         612 substr($tail, 0, $len, '');
214             }
215             }
216            
217 9         15 my $st;
218 9 100       31 if (scalar(@nodes) == 3) {
    100          
219 7         60 $st = RDF::Trine::Statement->new(@nodes);
220             } elsif (scalar(@nodes) == 4) {
221 1         9 $st = RDF::Trine::Statement::Quad->new(@nodes);
222             } else {
223 1         3 my $arity = scalar(@nodes);
224 1         11 throw RDF::Trine::Error::ParserError -text => "RDFPatch operation '$op' has unexpected arity ($arity)";
225             }
226            
227 8         59 return RDF::Trine::Parser::RDFPatch::Op->new( $op, $st );
228             }
229              
230              
231             package RDF::Trine::Parser::RDFPatch::Op;
232              
233 1     1   8 use strict;
  1         2  
  1         17  
234 1     1   4 use warnings;
  1         2  
  1         190  
235              
236             =item C<< new ( $op, @args ) >>
237              
238             Returns a new RDF-Patch Parser operation object.
239              
240             =cut
241              
242             sub new {
243 8     8   19 my $class = shift;
244 8         19 my $op = shift;
245 8         26 my @args = @_;
246 8         32 my $self = bless( { op => $op, args => \@args }, $class );
247 8         44 return $self;
248             }
249              
250             sub op {
251 12     12   2321 my $self = shift;
252 12         48 return $self->{op};
253             }
254              
255             sub args {
256 11     11   19 my $self = shift;
257 11         23 return @{ $self->{args} };
  11         50  
258             }
259              
260             sub execute {
261 7     7   710 my $self = shift;
262 7         15 my $model = shift;
263 7         17 my $op = $self->op;
264 7 100       31 if ($op eq 'A') {
    100          
    50          
265 4         15 return $model->add_statement( $self->args );
266             } elsif ($op eq 'D') {
267 2         7 return $model->remove_statement( $self->args );
268             } elsif ($op eq 'Q') {
269 1         4 my ($st) = $self->args;
270 1         5 return $model->get_statements( $st->nodes );
271             } else {
272 0           throw RDF::Trine::Error -text => "Unexpected operation '$op' in RDF::Trine::Parser::RDFPatch::Op->execute";
273             }
274             }
275              
276             1;
277              
278             __END__
279              
280             =back
281              
282             =head1 BUGS
283              
284             Please report any bugs or feature requests to through the GitHub web interface
285             at L<https://github.com/kasei/perlrdf/issues>.
286              
287             =head1 SEE ALSO
288              
289             L<http://afs.github.io/rdf-patch/>
290              
291             =head1 AUTHOR
292              
293             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
294              
295             =head1 COPYRIGHT
296              
297             Copyright (c) 2006-2012 Gregory Todd Williams. This
298             program is free software; you can redistribute it and/or modify it under
299             the same terms as Perl itself.
300              
301             =cut