File Coverage

blib/lib/RDF/Trine/Serializer/Turtle.pm
Criterion Covered Total %
statement 333 353 94.3
branch 111 130 85.3
condition 39 51 76.4
subroutine 29 33 87.8
pod 8 8 100.0
total 520 575 90.4


line stmt bran cond sub pod time code
1             # RDF::Trine::Serializer::Turtle
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Trine::Serializer::Turtle - Turtle Serializer
7              
8             =head1 VERSION
9              
10             This document describes RDF::Trine::Serializer::Turtle version 1.018
11              
12             =head1 SYNOPSIS
13              
14             use RDF::Trine::Serializer::Turtle;
15             my $serializer = RDF::Trine::Serializer::Turtle->new( namespaces => { ex => 'http://example/' } );
16             print $serializer->serialize_model_to_string($model);
17              
18             =head1 DESCRIPTION
19              
20             The RDF::Trine::Serializer::Turtle class provides an API for serializing RDF
21             graphs to the Turtle syntax. XSD numeric types are serialized as bare literals,
22             and where possible the more concise syntax is used for rdf:Lists.
23              
24             =head1 METHODS
25              
26             Beyond the methods documented below, this class inherits methods from the
27             L<RDF::Trine::Serializer> class.
28              
29             =over 4
30              
31             =cut
32              
33             package RDF::Trine::Serializer::Turtle;
34              
35 68     68   414 use strict;
  68         148  
  68         1644  
36 68     68   298 use warnings;
  68         136  
  68         1539  
37 68     68   314 use base qw(RDF::Trine::Serializer);
  68         137  
  68         22117  
38              
39 68     68   424 use URI;
  68         147  
  68         1014  
40 68     68   295 use Carp;
  68         138  
  68         2793  
41 68     68   338 use Encode;
  68         131  
  68         3534  
42 68     68   366 use Data::Dumper;
  68         141  
  68         2390  
43 68     68   360 use Scalar::Util qw(blessed refaddr reftype);
  68         144  
  68         2854  
44              
45 68     68   1254 use RDF::Trine qw(variable iri);
  68         141  
  68         2440  
46 68     68   393 use RDF::Trine::Node;
  68         132  
  68         1778  
47 68     68   384 use RDF::Trine::Statement;
  68         148  
  68         1195  
48 68     68   312 use RDF::Trine::Error qw(:try);
  68         141  
  68         342  
49 68     68   29087 use RDF::Trine::Namespace qw(rdf);
  68         185  
  68         397  
50              
51             ######################################################################
52              
53             our ($VERSION, $debug);
54             BEGIN {
55 68     68   274 $debug = 0;
56 68         135 $VERSION = '1.018';
57 68         221 $RDF::Trine::Serializer::serializer_names{ 'turtle' } = __PACKAGE__;
58 68         199 $RDF::Trine::Serializer::format_uris{ 'http://www.w3.org/ns/formats/Turtle' } = __PACKAGE__;
59 68         165 foreach my $type (qw(application/x-turtle application/turtle text/turtle text/rdf+n3)) {
60 272         162125 $RDF::Trine::Serializer::media_types{ $type } = __PACKAGE__;
61             }
62             }
63              
64             ######################################################################
65              
66             =item C<< new ( namespaces => \%namespaces, base_uri => $base_uri ) >>
67              
68             Returns a new Turtle serializer object.
69              
70             =cut
71              
72             sub new {
73 51     51 1 16338 my $class = shift;
74 51         110 my $ns = {};
75 51         91 my $base_uri;
76              
77 51 100       157 if (@_) {
78 15 100 66     98 if (scalar(@_) == 1 and reftype($_[0]) eq 'HASH') {
79 9         21 $ns = shift;
80             } else {
81 6         22 my %args = @_;
82 6 100       24 if (exists $args{ base }) {
83 1         2 $base_uri = $args{ base };
84             }
85 6 100       21 if (exists $args{ base_uri }) {
86 1         2 $base_uri = $args{ base_uri };
87             }
88 6 100       18 if (exists $args{ namespaces }) {
89 4         11 $ns = $args{ namespaces };
90             }
91             }
92             }
93              
94 51         99 my %rev;
95 51 100 66     226 if (blessed($ns) and $ns->isa('RDF::Trine::NamespaceMap')) {
96 1         5 for my $prefix ($ns->list_prefixes) {
97             # way convoluted
98 2         8 my $nsuri = $ns->namespace_uri($prefix)->uri->value;
99 2         6 $rev{$nsuri} = $prefix;
100             }
101             }
102             else {
103 50         99 while (my ($ns, $uri) = each(%{ $ns })) {
  84         308  
104 34 100       85 if (blessed($uri)) {
105 1         6 $uri = $uri->uri_value;
106 1 50       5 if (blessed($uri)) {
107 1         4 $uri = $uri->uri_value;
108             }
109             }
110 34         109 $rev{ $uri } = $ns;
111             }
112             }
113              
114 51         190 my $self = bless( {
115             ns => \%rev,
116             base_uri => $base_uri,
117             }, $class );
118 51         168 return $self;
119             }
120              
121             =item C<< serialize_model_to_file ( $fh, $model ) >>
122              
123             Serializes the C<$model> to Turtle, printing the results to the supplied
124             filehandle C<<$fh>>.
125              
126             =cut
127              
128             sub serialize_model_to_file {
129 1     1 1 920 my $self = shift;
130 1         2 my $fh = shift;
131 1         2 my $model = shift;
132 1         9 my $sink = RDF::Trine::Serializer::FileSink->new($fh);
133            
134 1         2 my $st = RDF::Trine::Statement->new( map { variable($_) } qw(s p o) );
  3         9  
135 1         6 my $pat = RDF::Trine::Pattern->new( $st );
136 1         5 my $stream = $model->get_pattern( $pat, undef, orderby => [ qw(s ASC p ASC o ASC) ] );
137 1         6 my $iter = $stream->as_statements( qw(s p o) );
138            
139 1         6 $self->serialize_iterator( $sink, $iter, seen => {}, level => 0, tab => "\t", @_, model => $model );
140 1         13 return 1;
141             }
142              
143             =item C<< serialize_model_to_string ( $model ) >>
144              
145             Serializes the C<$model> to Turtle, returning the result as a string.
146              
147             =cut
148              
149             sub serialize_model_to_string {
150 36     36 1 176 my $self = shift;
151 36         66 my $model = shift;
152 36         146 my $sink = RDF::Trine::Serializer::StringSink->new();
153              
154 36         85 my $st = RDF::Trine::Statement->new( map { variable($_) } qw(s p o) );
  108         302  
155 36         186 my $pat = RDF::Trine::Pattern->new( $st );
156 36         178 my $stream = $model->get_pattern( $pat, undef, orderby => [ qw(s ASC p ASC o ASC) ] );
157 36         167 my $iter = $stream->as_statements( qw(s p o) );
158            
159 36         151 $self->serialize_iterator( $sink, $iter, seen => {}, level => 0, tab => "\t", @_, model => $model, string => 1 );
160 36         160 return $sink->string;
161             }
162              
163             =item C<< serialize_iterator_to_file ( $file, $iter ) >>
164              
165             Serializes the iterator to Turtle, printing the results to the supplied
166             filehandle C<<$fh>>.
167              
168             =cut
169              
170             sub serialize_iterator_to_file {
171 0     0 1 0 my $self = shift;
172 0         0 my $fh = shift;
173 0         0 my $iter = shift;
174 0         0 my %args = @_;
175              
176 0         0 my $sink = RDF::Trine::Serializer::FileSink->new($fh);
177 0         0 $self->serialize_iterator( $sink, $iter, %args );
178 0         0 return 1;
179             }
180              
181             =item C<< serialize_iterator ( $sink, $iter ) >>
182              
183             Serializes the iterator to Turtle, printing the results to the supplied
184             sink object.
185              
186             =cut
187              
188             sub serialize_iterator {
189 40     40 1 64 my $self = shift;
190 40         66 my $sink = shift;
191 40         66 my $iter = shift;
192 40         151 my %args = @_;
193            
194 40   50     121 my $seen = $args{ seen } || {};
195 40   50     148 my $level = $args{ level } || 0;
196 40   50     103 my $tab = $args{ tab } || "\t";
197 40         101 my $indent = $tab x $level;
198            
199 40         63 my %ns = reverse(%{ $self->{ns} });
  40         146  
200 40         127 my @nskeys = sort keys %ns;
201            
202 40 100       209 unless ($sink->can('prepend')) {
203 1 50       6 if (@nskeys) {
204 1         3 foreach my $ns (sort @nskeys) {
205 3         6 my $uri = $ns{ $ns };
206 3         11 $sink->emit("\@prefix $ns: <$uri> .\n");
207             }
208 1         4 $sink->emit("\n");
209             }
210             }
211 40 100       121 if ($self->{base_uri}) {
212 2         10 $sink->emit("\@base <$self->{base_uri}> .\n\n");
213             }
214            
215 40         80 my $last_subj;
216             my $last_pred;
217            
218 40         71 my $open_triple = 0;
219 40         114 while (my $st = $iter->next) {
220             # warn "------------------\n";
221             # warn $st->as_string . "\n";
222 141         402 my $subj = $st->subject;
223 141         380 my $pred = $st->predicate;
224 141         344 my $obj = $st->object;
225            
226             # we're abusing the seen hash here as the key isn't really a node value,
227             # but since it isn't a valid node string being used it shouldn't collide
228             # with real data. we set this here so that later on when we check for
229             # single-owner bnodes (when attempting to use the [...] concise syntax),
230             # bnodes that have already been serialized as the 'head' of a statement
231             # aren't considered as single-owner. This is because the output string
232             # is acting as a second ownder of the node -- it's already been emitted
233             # as something like '_:foobar', so it can't also be output as '[...]'.
234 141         512 $seen->{ ' heads' }{ $subj->as_string }++;
235            
236 141 100       440 if (my $model = $args{model}) {
237 132 100       391 if (my $head = $self->_statement_describes_list($model, $st)) {
238 17 50       55 warn "found a rdf:List head " . $head->as_string . " for the subject in statement " . $st->as_string if ($debug);
239 17 100       70 if ($model->count_statements(undef, undef, $head)) {
240             # the rdf:List appears as the object of a statement, and so
241             # will be serialized whenever we get to serializing that
242             # statement
243 8 50       23 warn "next" if ($debug);
244 8         24 next;
245             }
246             }
247             }
248            
249 133 100       409 if ($seen->{ $subj->as_string }) {
250 48 50       154 warn "next on seen subject " . $st->as_string if ($debug);
251 48         98 next;
252             }
253            
254 85 100       250 if ($subj->equal( $last_subj )) {
255             # continue an existing subject
256 32 100       94 if ($pred->equal( $last_pred )) {
257             # continue an existing predicate
258 7         28 $sink->emit(qq[, ]);
259 7         30 $self->_serialize_object_to_file( $sink, $obj, $seen, $level, $tab, %args );
260             } else {
261             # start a new predicate
262 25         122 $sink->emit(qq[ ;\n${indent}$tab]);
263 25         115 $self->_turtle( $sink, $pred, 1, $seen, $level, $tab, %args );
264 25         85 $sink->emit(' ');
265 25         97 $self->_serialize_object_to_file( $sink, $obj, $seen, $level, $tab, %args );
266             }
267             } else {
268             # start a new subject
269 53 100       142 if ($open_triple) {
270 13         58 $sink->emit(qq[ .\n${indent}]);
271             }
272 53         92 $open_triple = 1;
273 53         239 $self->_turtle( $sink, $subj, 0, $seen, $level, $tab, %args );
274            
275 53 50       157 warn '-> ' . $pred->as_string if ($debug);
276 53         185 $sink->emit(' ');
277 53         221 $self->_turtle( $sink, $pred, 1, $seen, $level, $tab, %args );
278 53         182 $sink->emit(' ');
279 53         219 $self->_serialize_object_to_file( $sink, $obj, $seen, $level, $tab, %args );
280             }
281             } continue {
282 141 100 100     712 if (blessed($last_subj) and not($last_subj->equal($st->subject))) {
283             # warn "marking " . $st->subject->as_string . " as seen";
284 41         125 $seen->{ $last_subj->as_string }++;
285             }
286             # warn "setting last subject to " . $st->subject->as_string;
287 141         419 $last_subj = $st->subject;
288 141         362 $last_pred = $st->predicate;
289             }
290            
291 40 50       110 if ($open_triple) {
292 40         129 $sink->emit(qq[ .\n]);
293             }
294            
295 40 100       177 if ($sink->can('prepend')) {
296 39         62 my @used_nskeys = keys %{ $self->{used_ns} };
  39         155  
297 39 100       166 if (@used_nskeys) {
298 10         21 my $string = '';
299 10         35 foreach my $ns (sort @used_nskeys) {
300 18         29 my $uri = $ns{ $ns };
301 18         49 $string .= "\@prefix $ns: <$uri> .\n";
302             }
303 10         16 $string .= "\n";
304 10         34 $sink->prepend($string);
305             }
306             }
307             }
308              
309             =item C<< serialize_iterator_to_string ( $iter ) >>
310              
311             Serializes the iterator to Turtle, returning the result as a string.
312              
313             =cut
314              
315             sub serialize_iterator_to_string {
316 3     3 1 16 my $self = shift;
317 3         7 my $iter = shift;
318 3         12 my $sink = RDF::Trine::Serializer::StringSink->new();
319 3         13 $self->serialize_iterator( $sink, $iter, seen => {}, level => 0, tab => "\t", @_, string => 1 );
320 3         11 return $sink->string;
321             }
322              
323             =item C<< serialize_node ( $node ) >>
324              
325             Returns a string containing the Turtle serialization of C<< $node >>.
326              
327             =cut
328              
329             sub serialize_node {
330 0     0 1 0 my $self = shift;
331 0         0 my $node = shift;
332 0         0 return $self->node_as_concise_string( $node );
333             }
334              
335             sub _serialize_object_to_file {
336 137     137   237 my $self = shift;
337 137         222 my $sink = shift;
338 137         210 my $subj = shift;
339 137         215 my $seen = shift;
340 137         217 my $level = shift;
341 137         220 my $tab = shift;
342 137         429 my %args = @_;
343 137         297 my $indent = $tab x $level;
344            
345 137 100       379 if (my $model = $args{model}) {
346 128 100       622 if ($subj->isa('RDF::Trine::Node::Blank')) {
347 34 100       107 if ($self->_check_valid_rdf_list( $subj, $model )) {
348             # warn "node is a valid rdf:List: " . $subj->as_string . "\n";
349 2         12 return $self->_turtle_rdf_list( $sink, $subj, $model, $seen, $level, $tab, %args );
350             } else {
351 32         102 my $count = $model->count_statements( undef, undef, $subj );
352 32         110 my $rec = $model->count_statements( $subj, undef, $subj );
353 32 50       96 warn "count=$count, rec=$rec for node " . $subj->as_string if ($debug);
354 32 100 66     174 if ($count == 1 and $rec == 0) {
355 30 100 100     101 unless ($seen->{ $subj->as_string }++ or $seen->{ ' heads' }{ $subj->as_string }) {
356 26         110 my $pat = RDF::Trine::Pattern->new( RDF::Trine::Statement->new($subj, variable('p'), variable('o')) );
357 26         119 my $stream = $model->get_pattern( $pat, undef, orderby => [ qw(p ASC o ASC) ] );
358 26         104 my $iter = $stream->as_statements( qw(s p o) );
359 26         56 my $last_pred;
360 26         44 my $triple_count = 0;
361 26         103 $sink->emit("[");
362 26         83 while (my $st = $iter->next) {
363 48         137 my $pred = $st->predicate;
364 48         134 my $obj = $st->object;
365            
366             # continue an existing subject
367 48 100       151 if ($pred->equal( $last_pred )) {
368             # continue an existing predicate
369 3         15 $sink->emit(qq[, ]);
370 3         13 $self->_serialize_object_to_file( $sink, $obj, $seen, $level, $tab, %args );
371             # $self->_turtle( $fh, $obj, 2, $seen, $level, $tab, %args );
372             } else {
373             # start a new predicate
374 45 100       108 if ($triple_count == 0) {
375 24         99 $sink->emit(qq[\n${indent}${tab}${tab}]);
376             } else {
377 21         98 $sink->emit(qq[ ;\n${indent}$tab${tab}]);
378             }
379 45         212 $self->_turtle( $sink, $pred, 1, $seen, $level, $tab, %args );
380 45         146 $sink->emit(' ');
381 45         197 $self->_serialize_object_to_file( $sink, $obj, $seen, $level+1, $tab, %args );
382             }
383            
384 48         116 $last_pred = $pred;
385 48         152 $triple_count++;
386             }
387 26 100       65 if ($triple_count) {
388 24         83 $sink->emit("\n${indent}${tab}");
389             }
390 26         76 $sink->emit("]");
391 26         272 return;
392             }
393             }
394             }
395             }
396             }
397            
398 109         373 $self->_turtle( $sink, $subj, 2, $seen, $level, $tab, %args );
399             }
400              
401             sub _statement_describes_list {
402 132     132   218 my $self = shift;
403 132         208 my $model = shift;
404 132         200 my $st = shift;
405 132         297 my $subj = $st->subject;
406 132         318 my $pred = $st->predicate;
407 132         312 my $obj = $st->object;
408 132 100 66     816 if ($model->count_statements($subj, $rdf->first) and $model->count_statements($subj, $rdf->rest)) {
409             # warn $subj->as_string . " looks like a rdf:List element";
410 26 100       95 if (my $head = $self->_node_belongs_to_valid_list( $model, $subj )) {
411 17         88 return $head;
412             }
413             }
414            
415 115         366 return;
416             }
417              
418             sub _node_belongs_to_valid_list {
419 26     26   52 my $self = shift;
420 26         47 my $model = shift;
421 26         46 my $node = shift;
422 26         177 while ($model->count_statements( undef, $rdf->rest, $node )) {
423 12         48 my $iter = $model->get_statements( undef, $rdf->rest, $node );
424 12         45 my $s = $iter->next;
425 12         35 my $ancestor = $s->subject;
426 12 50       60 unless (blessed($ancestor)) {
427             # warn "failed to get an expected rdf:List element ancestor";
428 0         0 return 0;
429             }
430 12         137 ($node) = $ancestor;
431             # warn "stepping back to rdf:List element ancestor " . $node->as_string;
432             }
433 26 100       92 if ($self->_check_valid_rdf_list( $node, $model )) {
434 17         85 return $node;
435             } else {
436 9         33 return;
437             }
438             }
439              
440             sub _check_valid_rdf_list {
441 60     60   119 my $self = shift;
442 60         100 my $head = shift;
443 60         100 my $model = shift;
444             # warn '--------------------------';
445             # warn "checking if node " . $head->as_string . " is a valid rdf:List\n";
446            
447 60         368 my $headrest = $model->count_statements( undef, $rdf->rest, $head );
448 60 100       228 if ($headrest) {
449             # warn "\tnode " . $head->as_string . " seems to be the middle of an rdf:List\n";
450 3         11 return 0;
451             }
452            
453 57         103 my %list_elements;
454 57         91 my $node = $head;
455 57         366 until ($node->equal( $rdf->nil )) {
456 81         246 $list_elements{ $node->as_string }++;
457            
458 81 100       300 unless ($node->isa('RDF::Trine::Node::Blank')) {
459             # warn "\tnode " . $node->as_string . " isn't a blank node\n";
460 5         23 return 0;
461             }
462            
463 76         344 my $first = $model->count_statements( $node, $rdf->first );
464 76 100       265 unless ($first == 1) {
465             # warn "\tnode " . $node->as_string . " has $first rdf:first links when 1 was expected\n";
466 33         117 return 0;
467             }
468            
469 43         275 my $rest = $model->count_statements( $node, $rdf->rest );
470 43 50       149 unless ($rest == 1) {
471             # warn "\tnode " . $node->as_string . " has $rest rdf:rest links when 1 was expected\n";
472 0         0 return 0;
473             }
474            
475 43         145 my $in = $model->count_statements( undef, undef, $node );
476 43 50       166 unless ($in < 2) {
477             # warn "\tnode " . $node->as_string . " has $in incoming links when 2 were expected\n";
478 0         0 return 0;
479             }
480            
481 43 100       130 if (not($head->equal( $node ))) {
482             # It's OK for the head of a list to have any outgoing links (e.g. (1 2) ex:p "o"
483             # but internal list elements should have only the expected links of rdf:first,
484             # rdf:rest, and optionally an rdf:type rdf:List
485 19         63 my $out = $model->count_statements( $node );
486 19 50 33     78 unless ($out == 2 or $out == 3) {
487             # warn "\tnode " . $node->as_string . " has $out outgoing links when 2 or 3 were expected\n";
488 0         0 return 0;
489             }
490            
491 19 50       58 if ($out == 3) {
492 0         0 my $type = $model->count_statements( $node, $rdf->type, $rdf->List );
493 0 0       0 unless ($type == 1) {
494             # warn "\tnode " . $node->as_string . " has more outgoing links than expected\n";
495 0         0 return 0;
496             }
497             }
498             }
499            
500            
501            
502 43         310 my @links = $model->objects_for_predicate_list( $node, $rdf->first, $rdf->rest );
503 43         177 foreach my $l (@links) {
504 86 50       295 if ($list_elements{ $l->as_string }) {
505 0 0       0 warn $node->as_string . " is repeated in the list" if ($debug);
506 0         0 return 0;
507             }
508             }
509            
510 43         265 ($node) = $model->objects_for_predicate_list( $node, $rdf->rest );
511 43 50       154 unless (blessed($node)) {
512             # warn "\tno valid rdf:rest object found";
513 0         0 return 0;
514             }
515             # warn "\tmoving on to rdf:rest object " . $node->as_string . "\n";
516             }
517            
518             # warn "\tlooks like a valid rdf:List\n";
519 19         70 return 1;
520             }
521              
522             sub _turtle_rdf_list {
523 2     2   4 my $self = shift;
524 2         4 my $sink = shift;
525 2         4 my $head = shift;
526 2         4 my $model = shift;
527 2         5 my $seen = shift;
528 2         3 my $level = shift;
529 2         4 my $tab = shift;
530 2         9 my %args = @_;
531 2         3 my $node = $head;
532 2         3 my $count = 0;
533 2         9 $sink->emit('(');
534 2         12 until ($node->equal( $rdf->nil )) {
535 4 100       13 if ($count) {
536 2         10 $sink->emit(' ');
537             }
538 4         20 my ($value) = $model->objects_for_predicate_list( $node, $rdf->first );
539 4         18 $self->_serialize_object_to_file( $sink, $value, $seen, $level, $tab, %args );
540 4         18 $seen->{ $node->as_string }++;
541 4         30 ($node) = $model->objects_for_predicate_list( $node, $rdf->rest );
542 4         14 $count++;
543             }
544 2         9 $sink->emit(')');
545             }
546              
547             sub _node_concise_string {
548 258     258   431 my $self = shift;
549 258         385 my $obj = shift;
550 258 100 100     806 if ($obj->is_literal and $obj->has_datatype) {
    100          
551 28         83 my $dt = $obj->literal_datatype;
552 28 100 100     238 if ($dt =~ m<^http://www.w3.org/2001/XMLSchema#(integer|double|decimal)$> and $obj->is_canonical_lexical_form) {
553 21         67 my $value = $obj->literal_value;
554 21         93 return $value;
555             } else {
556 7         31 my $dtr = iri($dt);
557 7         23 my $literal = $obj->literal_value;
558 7         12 my $qname;
559             try {
560 7     7   217 my ($ns,$local) = $dtr->qname;
561 7 100 66     56 if (blessed($self) and exists $self->{ns}{$ns}) {
562 3         10 $qname = join(':', $self->{ns}{$ns}, $local);
563 3         13 $self->{used_ns}{ $self->{ns}{$ns} }++;
564             }
565 7     0   50 } catch RDF::Trine::Error with {};
566 7 100       128 if ($qname) {
567 3         12 my $escaped = $obj->_unicode_escape( $literal );
568 3         16 return qq["$escaped"^^$qname];
569             }
570             }
571             } elsif ($obj->isa('RDF::Trine::Node::Resource')) {
572 184         356 my $value;
573             try {
574 184     184   7007 my ($ns,$local) = $obj->qname;
575 180 100 66     1371 if (blessed($self) and exists $self->{ns}{$ns}) {
576 61         208 $value = join(':', $self->{ns}{$ns}, $local);
577 61         216 $self->{used_ns}{ $self->{ns}{$ns} }++;
578             }
579 184     0   1327 } catch RDF::Trine::Error with {} otherwise {};
580 184 100       3365 if ($value) {
581 61         221 return $value;
582             }
583             }
584 173         527 return;
585             }
586              
587             =item C<< node_as_concise_string >>
588              
589             Returns a string representation using common Turtle syntax shortcuts (e.g. for numeric literals).
590              
591             =cut
592              
593             sub node_as_concise_string {
594 23     23 1 34 my $self = shift;
595 23         45 my $obj = shift;
596 23         54 my $str = $self->_node_concise_string( $obj );
597 23 100       47 if (defined($str)) {
598 4         14 return $str;
599             } else {
600 19         54 return $obj->as_ntriples;
601             }
602             }
603              
604             sub _turtle {
605 285     285   473 my $self = shift;
606 285         414 my $sink = shift;
607 285         429 my $obj = shift;
608 285         413 my $pos = shift;
609 285         484 my $seen = shift;
610 285         393 my $level = shift;
611 285         455 my $tab = shift;
612 285         928 my %args = @_;
613            
614 285 100 100     2336 if ($obj->isa('RDF::Trine::Node::Resource') and $pos == 1 and $obj->uri_value eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') {
    100 100        
    100 100        
615 14         46 $sink->emit('a');
616 14         47 return;
617             } elsif ($obj->isa('RDF::Trine::Node::Blank') and $pos == 0) {
618 36 100       110 if (my $model = $args{ model }) {
619 35         104 my $count = $model->count_statements( undef, undef, $obj );
620 35         123 my $rec = $model->count_statements( $obj, undef, $obj );
621             # XXX if $count == 1, then it would be better to ignore this triple for now, since it's a 'single-owner' bnode, and better serialized as a '[ ... ]' bnode in the object position as part of the 'owning' triple
622 35 100 66     183 if ($count < 1 and $rec == 0) {
623 31         127 $sink->emit('[]');
624 31         98 return;
625             }
626             }
627             } elsif (defined(my $str = $self->_node_concise_string( $obj ))) {
628 81         296 $sink->emit($str);
629 81         283 return;
630             }
631            
632 159         514 $sink->emit($obj->as_ntriples);
633 159         586 return;
634             }
635              
636             1;
637              
638             __END__
639              
640             =back
641              
642             =head1 BUGS
643              
644             Please report any bugs or feature requests to through the GitHub web interface
645             at L<https://github.com/kasei/perlrdf/issues>.
646              
647             =head1 SEE ALSO
648              
649             L<http://www.w3.org/TeamSubmission/turtle/>
650              
651             =head1 AUTHOR
652              
653             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
654              
655             =head1 COPYRIGHT
656              
657             Copyright (c) 2006-2012 Gregory Todd Williams. This
658             program is free software; you can redistribute it and/or modify it under
659             the same terms as Perl itself.
660              
661             =cut