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 30 33 90.9
pod 8 8 100.0
total 521 575 90.6


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.017
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   438 use strict;
  68         144  
  68         1623  
36 68     68   311 use warnings;
  68         144  
  68         1617  
37 68     68   333 use base qw(RDF::Trine::Serializer);
  68         141  
  68         22825  
38              
39 68     68   448 use URI;
  68         156  
  68         1069  
40 68     68   353 use Carp;
  68         149  
  68         2761  
41 68     68   344 use Encode;
  68         138  
  68         3637  
42 68     68   370 use Data::Dumper;
  68         153  
  68         2497  
43 68     68   388 use Scalar::Util qw(blessed refaddr reftype);
  68         172  
  68         2978  
44              
45 68     68   1465 use RDF::Trine qw(variable iri);
  68         142  
  68         2641  
46 68     68   388 use RDF::Trine::Node;
  68         154  
  68         1760  
47 68     68   353 use RDF::Trine::Statement;
  68         155  
  68         1240  
48 68     68   321 use RDF::Trine::Error qw(:try);
  68         159  
  68         377  
49 68     68   29495 use RDF::Trine::Namespace qw(rdf);
  68         176  
  68         423  
50              
51             ######################################################################
52              
53             our ($VERSION, $debug);
54             BEGIN {
55 68     68   290 $debug = 0;
56 68         149 $VERSION = '1.017';
57 68         249 $RDF::Trine::Serializer::serializer_names{ 'turtle' } = __PACKAGE__;
58 68         226 $RDF::Trine::Serializer::format_uris{ 'http://www.w3.org/ns/formats/Turtle' } = __PACKAGE__;
59 68         172 foreach my $type (qw(application/x-turtle application/turtle text/turtle text/rdf+n3)) {
60 272         167388 $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 21544 my $class = shift;
74 51         133 my $ns = {};
75 51         101 my $base_uri;
76              
77 51 100       182 if (@_) {
78 15 100 66     120 if (scalar(@_) == 1 and reftype($_[0]) eq 'HASH') {
79 9         24 $ns = shift;
80             } else {
81 6         23 my %args = @_;
82 6 100       24 if (exists $args{ base }) {
83 1         3 $base_uri = $args{ base };
84             }
85 6 100       23 if (exists $args{ base_uri }) {
86 1         3 $base_uri = $args{ base_uri };
87             }
88 6 100       22 if (exists $args{ namespaces }) {
89 4         16 $ns = $args{ namespaces };
90             }
91             }
92             }
93              
94 51         108 my %rev;
95 51 100 66     270 if (blessed($ns) and $ns->isa('RDF::Trine::NamespaceMap')) {
96 1         6 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         125 while (my ($ns, $uri) = each(%{ $ns })) {
  84         340  
104 34 100       104 if (blessed($uri)) {
105 1         6 $uri = $uri->uri_value;
106 1 50       9 if (blessed($uri)) {
107 1         6 $uri = $uri->uri_value;
108             }
109             }
110 34         109 $rev{ $uri } = $ns;
111             }
112             }
113              
114 51         238 my $self = bless( {
115             ns => \%rev,
116             base_uri => $base_uri,
117             }, $class );
118 51         203 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 1128 my $self = shift;
130 1         2 my $fh = shift;
131 1         2 my $model = shift;
132 1         12 my $sink = RDF::Trine::Serializer::FileSink->new($fh);
133            
134 1         3 my $st = RDF::Trine::Statement->new( map { variable($_) } qw(s p o) );
  3         10  
135 1         8 my $pat = RDF::Trine::Pattern->new( $st );
136 1         7 my $stream = $model->get_pattern( $pat, undef, orderby => [ qw(s ASC p ASC o ASC) ] );
137 1         10 my $iter = $stream->as_statements( qw(s p o) );
138            
139 1         10 $self->serialize_iterator( $sink, $iter, seen => {}, level => 0, tab => "\t", @_, model => $model );
140 1         20 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 205 my $self = shift;
151 36         73 my $model = shift;
152 36         197 my $sink = RDF::Trine::Serializer::StringSink->new();
153              
154 36         101 my $st = RDF::Trine::Statement->new( map { variable($_) } qw(s p o) );
  108         317  
155 36         212 my $pat = RDF::Trine::Pattern->new( $st );
156 36         213 my $stream = $model->get_pattern( $pat, undef, orderby => [ qw(s ASC p ASC o ASC) ] );
157 36         206 my $iter = $stream->as_statements( qw(s p o) );
158            
159 36         190 $self->serialize_iterator( $sink, $iter, seen => {}, level => 0, tab => "\t", @_, model => $model, string => 1 );
160 36         163 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 114 my $self = shift;
190 40         72 my $sink = shift;
191 40         117 my $iter = shift;
192 40         188 my %args = @_;
193            
194 40   50     153 my $seen = $args{ seen } || {};
195 40   50     210 my $level = $args{ level } || 0;
196 40   50     118 my $tab = $args{ tab } || "\t";
197 40         128 my $indent = $tab x $level;
198            
199 40         80 my %ns = reverse(%{ $self->{ns} });
  40         195  
200 40         131 my @nskeys = sort keys %ns;
201            
202 40 100       215 unless ($sink->can('prepend')) {
203 1 50       4 if (@nskeys) {
204 1         5 foreach my $ns (sort @nskeys) {
205 3         7 my $uri = $ns{ $ns };
206 3         16 $sink->emit("\@prefix $ns: <$uri> .\n");
207             }
208 1         5 $sink->emit("\n");
209             }
210             }
211 40 100       140 if ($self->{base_uri}) {
212 2         13 $sink->emit("\@base <$self->{base_uri}> .\n\n");
213             }
214            
215 40         95 my $last_subj;
216             my $last_pred;
217            
218 40         88 my $open_triple = 0;
219 40         137 while (my $st = $iter->next) {
220             # warn "------------------\n";
221             # warn $st->as_string . "\n";
222 141         416 my $subj = $st->subject;
223 141         457 my $pred = $st->predicate;
224 141         393 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         521 $seen->{ ' heads' }{ $subj->as_string }++;
235            
236 141 100       441 if (my $model = $args{model}) {
237 132 100       369 if (my $head = $self->_statement_describes_list($model, $st)) {
238 17 50       62 warn "found a rdf:List head " . $head->as_string . " for the subject in statement " . $st->as_string if ($debug);
239 17 100       76 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       22 warn "next" if ($debug);
244 8         17 next;
245             }
246             }
247             }
248            
249 133 100       466 if ($seen->{ $subj->as_string }) {
250 48 50       132 warn "next on seen subject " . $st->as_string if ($debug);
251 48         96 next;
252             }
253            
254 85 100       292 if ($subj->equal( $last_subj )) {
255             # continue an existing subject
256 32 100       112 if ($pred->equal( $last_pred )) {
257             # continue an existing predicate
258 7         31 $sink->emit(qq[, ]);
259 7         32 $self->_serialize_object_to_file( $sink, $obj, $seen, $level, $tab, %args );
260             } else {
261             # start a new predicate
262 25         156 $sink->emit(qq[ ;\n${indent}$tab]);
263 25         150 $self->_turtle( $sink, $pred, 1, $seen, $level, $tab, %args );
264 25         94 $sink->emit(' ');
265 25         106 $self->_serialize_object_to_file( $sink, $obj, $seen, $level, $tab, %args );
266             }
267             } else {
268             # start a new subject
269 53 100       188 if ($open_triple) {
270 13         78 $sink->emit(qq[ .\n${indent}]);
271             }
272 53         130 $open_triple = 1;
273 53         292 $self->_turtle( $sink, $subj, 0, $seen, $level, $tab, %args );
274            
275 53 50       165 warn '-> ' . $pred->as_string if ($debug);
276 53         176 $sink->emit(' ');
277 53         242 $self->_turtle( $sink, $pred, 1, $seen, $level, $tab, %args );
278 53         207 $sink->emit(' ');
279 53         241 $self->_serialize_object_to_file( $sink, $obj, $seen, $level, $tab, %args );
280             }
281             } continue {
282 141 100 100     769 if (blessed($last_subj) and not($last_subj->equal($st->subject))) {
283             # warn "marking " . $st->subject->as_string . " as seen";
284 41         130 $seen->{ $last_subj->as_string }++;
285             }
286             # warn "setting last subject to " . $st->subject->as_string;
287 141         436 $last_subj = $st->subject;
288 141         391 $last_pred = $st->predicate;
289             }
290            
291 40 50       117 if ($open_triple) {
292 40         125 $sink->emit(qq[ .\n]);
293             }
294            
295 40 100       213 if ($sink->can('prepend')) {
296 39         78 my @used_nskeys = keys %{ $self->{used_ns} };
  39         183  
297 39 100       178 if (@used_nskeys) {
298 10         22 my $string = '';
299 10         39 foreach my $ns (sort @used_nskeys) {
300 18         54 my $uri = $ns{ $ns };
301 18         62 $string .= "\@prefix $ns: <$uri> .\n";
302             }
303 10         26 $string .= "\n";
304 10         37 $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 19 my $self = shift;
317 3         6 my $iter = shift;
318 3         15 my $sink = RDF::Trine::Serializer::StringSink->new();
319 3         17 $self->serialize_iterator( $sink, $iter, seen => {}, level => 0, tab => "\t", @_, string => 1 );
320 3         14 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   247 my $self = shift;
337 137         221 my $sink = shift;
338 137         268 my $subj = shift;
339 137         240 my $seen = shift;
340 137         209 my $level = shift;
341 137         233 my $tab = shift;
342 137         448 my %args = @_;
343 137         383 my $indent = $tab x $level;
344            
345 137 100       465 if (my $model = $args{model}) {
346 128 100       593 if ($subj->isa('RDF::Trine::Node::Blank')) {
347 34 100       111 if ($self->_check_valid_rdf_list( $subj, $model )) {
348             # warn "node is a valid rdf:List: " . $subj->as_string . "\n";
349 2         15 return $self->_turtle_rdf_list( $sink, $subj, $model, $seen, $level, $tab, %args );
350             } else {
351 32         127 my $count = $model->count_statements( undef, undef, $subj );
352 32         129 my $rec = $model->count_statements( $subj, undef, $subj );
353 32 50       132 warn "count=$count, rec=$rec for node " . $subj->as_string if ($debug);
354 32 100 66     210 if ($count == 1 and $rec == 0) {
355 30 100 100     101 unless ($seen->{ $subj->as_string }++ or $seen->{ ' heads' }{ $subj->as_string }) {
356 26         149 my $pat = RDF::Trine::Pattern->new( RDF::Trine::Statement->new($subj, variable('p'), variable('o')) );
357 26         150 my $stream = $model->get_pattern( $pat, undef, orderby => [ qw(p ASC o ASC) ] );
358 26         143 my $iter = $stream->as_statements( qw(s p o) );
359 26         47 my $last_pred;
360 26         50 my $triple_count = 0;
361 26         122 $sink->emit("[");
362 26         86 while (my $st = $iter->next) {
363 48         138 my $pred = $st->predicate;
364 48         117 my $obj = $st->object;
365            
366             # continue an existing subject
367 48 100       161 if ($pred->equal( $last_pred )) {
368             # continue an existing predicate
369 3         15 $sink->emit(qq[, ]);
370 3         14 $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       129 if ($triple_count == 0) {
375 24         111 $sink->emit(qq[\n${indent}${tab}${tab}]);
376             } else {
377 21         91 $sink->emit(qq[ ;\n${indent}$tab${tab}]);
378             }
379 45         245 $self->_turtle( $sink, $pred, 1, $seen, $level, $tab, %args );
380 45         154 $sink->emit(' ');
381 45         215 $self->_serialize_object_to_file( $sink, $obj, $seen, $level+1, $tab, %args );
382             }
383            
384 48         120 $last_pred = $pred;
385 48         161 $triple_count++;
386             }
387 26 100       75 if ($triple_count) {
388 24         102 $sink->emit("\n${indent}${tab}");
389             }
390 26         85 $sink->emit("]");
391 26         349 return;
392             }
393             }
394             }
395             }
396             }
397            
398 109         393 $self->_turtle( $sink, $subj, 2, $seen, $level, $tab, %args );
399             }
400              
401             sub _statement_describes_list {
402 132     132   241 my $self = shift;
403 132         243 my $model = shift;
404 132         201 my $st = shift;
405 132         348 my $subj = $st->subject;
406 132         346 my $pred = $st->predicate;
407 132         339 my $obj = $st->object;
408 132 100 66     970 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       103 if (my $head = $self->_node_belongs_to_valid_list( $model, $subj )) {
411 17         91 return $head;
412             }
413             }
414            
415 115         407 return;
416             }
417              
418             sub _node_belongs_to_valid_list {
419 26     26   62 my $self = shift;
420 26         53 my $model = shift;
421 26         44 my $node = shift;
422 26         190 while ($model->count_statements( undef, $rdf->rest, $node )) {
423 12         58 my $iter = $model->get_statements( undef, $rdf->rest, $node );
424 12         53 my $s = $iter->next;
425 12         36 my $ancestor = $s->subject;
426 12 50       56 unless (blessed($ancestor)) {
427             # warn "failed to get an expected rdf:List element ancestor";
428 0         0 return 0;
429             }
430 12         153 ($node) = $ancestor;
431             # warn "stepping back to rdf:List element ancestor " . $node->as_string;
432             }
433 26 100       163 if ($self->_check_valid_rdf_list( $node, $model )) {
434 17         93 return $node;
435             } else {
436 9         40 return;
437             }
438             }
439              
440             sub _check_valid_rdf_list {
441 60     60   136 my $self = shift;
442 60         110 my $head = shift;
443 60         133 my $model = shift;
444             # warn '--------------------------';
445             # warn "checking if node " . $head->as_string . " is a valid rdf:List\n";
446            
447 60         427 my $headrest = $model->count_statements( undef, $rdf->rest, $head );
448 60 100       238 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         118 my %list_elements;
454 57         101 my $node = $head;
455 57         409 until ($node->equal( $rdf->nil )) {
456 81         243 $list_elements{ $node->as_string }++;
457            
458 81 100       329 unless ($node->isa('RDF::Trine::Node::Blank')) {
459             # warn "\tnode " . $node->as_string . " isn't a blank node\n";
460 5         32 return 0;
461             }
462            
463 76         423 my $first = $model->count_statements( $node, $rdf->first );
464 76 100       291 unless ($first == 1) {
465             # warn "\tnode " . $node->as_string . " has $first rdf:first links when 1 was expected\n";
466 33         121 return 0;
467             }
468            
469 43         300 my $rest = $model->count_statements( $node, $rdf->rest );
470 43 50       164 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         169 my $in = $model->count_statements( undef, undef, $node );
476 43 50       154 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       151 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         67 my $out = $model->count_statements( $node );
486 19 50 33     97 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       61 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         348 my @links = $model->objects_for_predicate_list( $node, $rdf->first, $rdf->rest );
503 43         156 foreach my $l (@links) {
504 86 50       305 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         301 ($node) = $model->objects_for_predicate_list( $node, $rdf->rest );
511 43 50       145 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         74 return 1;
520             }
521              
522             sub _turtle_rdf_list {
523 2     2   7 my $self = shift;
524 2         5 my $sink = shift;
525 2         7 my $head = shift;
526 2         6 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         5 my $node = $head;
532 2         5 my $count = 0;
533 2         11 $sink->emit('(');
534 2         14 until ($node->equal( $rdf->nil )) {
535 4 100       15 if ($count) {
536 2         10 $sink->emit(' ');
537             }
538 4         21 my ($value) = $model->objects_for_predicate_list( $node, $rdf->first );
539 4         13 $self->_serialize_object_to_file( $sink, $value, $seen, $level, $tab, %args );
540 4         15 $seen->{ $node->as_string }++;
541 4         26 ($node) = $model->objects_for_predicate_list( $node, $rdf->rest );
542 4         13 $count++;
543             }
544 2         7 $sink->emit(')');
545             }
546              
547             sub _node_concise_string {
548 258     258   444 my $self = shift;
549 258         423 my $obj = shift;
550 258 100 100     894 if ($obj->is_literal and $obj->has_datatype) {
    100          
551 28         82 my $dt = $obj->literal_datatype;
552 28 100 100     265 if ($dt =~ m<^http://www.w3.org/2001/XMLSchema#(integer|double|decimal)$> and $obj->is_canonical_lexical_form) {
553 21         68 my $value = $obj->literal_value;
554 21         82 return $value;
555             } else {
556 7         34 my $dtr = iri($dt);
557 7         24 my $literal = $obj->literal_value;
558 7         16 my $qname;
559             try {
560 7     7   210 my ($ns,$local) = $dtr->qname;
561 7 100 66     65 if (blessed($self) and exists $self->{ns}{$ns}) {
562 3         12 $qname = join(':', $self->{ns}{$ns}, $local);
563 3         14 $self->{used_ns}{ $self->{ns}{$ns} }++;
564             }
565 7     0   56 } catch RDF::Trine::Error with {};
566 7 100       132 if ($qname) {
567 3         14 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         347 my $value;
573             try {
574 184     184   7930 my ($ns,$local) = $obj->qname;
575 180 100 66     1477 if (blessed($self) and exists $self->{ns}{$ns}) {
576 61         236 $value = join(':', $self->{ns}{$ns}, $local);
577 61         260 $self->{used_ns}{ $self->{ns}{$ns} }++;
578             }
579 184     4   1493 } catch RDF::Trine::Error with {} otherwise {};
580 184 100       3628 if ($value) {
581 61         254 return $value;
582             }
583             }
584 173         570 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 37 my $self = shift;
595 23         36 my $obj = shift;
596 23         51 my $str = $self->_node_concise_string( $obj );
597 23 100       49 if (defined($str)) {
598 4         15 return $str;
599             } else {
600 19         58 return $obj->as_ntriples;
601             }
602             }
603              
604             sub _turtle {
605 285     285   548 my $self = shift;
606 285         437 my $sink = shift;
607 285         526 my $obj = shift;
608 285         472 my $pos = shift;
609 285         447 my $seen = shift;
610 285         453 my $level = shift;
611 285         486 my $tab = shift;
612 285         978 my %args = @_;
613            
614 285 100 100     2543 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         52 $sink->emit('a');
616 14         36 return;
617             } elsif ($obj->isa('RDF::Trine::Node::Blank') and $pos == 0) {
618 36 100       131 if (my $model = $args{ model }) {
619 35         142 my $count = $model->count_statements( undef, undef, $obj );
620 35         134 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     218 if ($count < 1 and $rec == 0) {
623 31         145 $sink->emit('[]');
624 31         126 return;
625             }
626             }
627             } elsif (defined(my $str = $self->_node_concise_string( $obj ))) {
628 81         325 $sink->emit($str);
629 81         313 return;
630             }
631            
632 159         555 $sink->emit($obj->as_ntriples);
633 159         626 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