File Coverage

blib/lib/YAML/Old/Dumper.pm
Criterion Covered Total %
statement 299 305 98.0
branch 153 170 90.0
condition 46 57 80.7
subroutine 30 30 100.0
pod 0 5 0.0
total 528 567 93.1


line stmt bran cond sub pod time code
1             package YAML::Old::Dumper;
2              
3 32     32   53743 use YAML::Old::Mo;
  32         67  
  32         179  
4             extends 'YAML::Old::Dumper::Base';
5              
6 32     32   10466 use YAML::Old::Dumper::Base;
  32         92  
  32         840  
7 32     32   192 use YAML::Old::Node;
  32         57  
  32         1085  
8 32     32   8973 use YAML::Old::Types;
  32         76  
  32         756  
9 32     32   168 use Scalar::Util qw();
  32         56  
  32         367  
10 32     32   141 use B ();
  32         79  
  32         327  
11 32     32   118 use Carp ();
  32         56  
  32         463  
12              
13             # Context constants
14 32     32   133 use constant KEY => 3;
  32         59  
  32         1565  
15 32     32   188 use constant BLESSED => 4;
  32         68  
  32         1217  
16 32     32   165 use constant FROMARRAY => 5;
  32         57  
  32         1315  
17 32     32   156 use constant VALUE => "\x07YAML\x07VALUE\x07";
  32         62  
  32         87028  
18              
19             # Common YAML character sets
20             my $ESCAPE_CHAR = '[\\x00-\\x08\\x0b-\\x0d\\x0e-\\x1f]';
21             my $LIT_CHAR = '|';
22              
23             #==============================================================================
24             # OO version of Dump. YAML->new->dump($foo);
25             sub dump {
26 151     151 0 283 my $self = shift;
27 151         587 $self->stream('');
28 151         553 $self->document(0);
29 151         318 for my $document (@_) {
30 157         280 $self->{document}++;
31 157         563 $self->transferred({});
32 157         528 $self->id_refcnt({});
33 157         480 $self->id_anchor({});
34 157         495 $self->anchor(1);
35 157         464 $self->level(0);
36 157         422 $self->offset->[0] = 0 - $self->indent_width;
37 157         464 $self->_prewalk($document);
38 157         429 $self->_emit_header($document);
39 154         416 $self->_emit_node($document);
40             }
41 148         422 return $self->stream;
42             }
43              
44             # Every YAML document in the stream must begin with a YAML header, unless
45             # there is only a single document and the user requests "no header".
46             sub _emit_header {
47 157     157   265 my $self = shift;
48 157         297 my ($node) = @_;
49 157 100 100     466 if (not $self->use_header and
50             $self->document == 1
51             ) {
52 4 100       20 $self->die('YAML_DUMP_ERR_NO_HEADER')
53             unless ref($node) =~ /^(HASH|ARRAY)$/;
54 3 100 66     17 $self->die('YAML_DUMP_ERR_NO_HEADER')
55             if ref($node) eq 'HASH' and keys(%$node) == 0;
56 2 100 66     15 $self->die('YAML_DUMP_ERR_NO_HEADER')
57             if ref($node) eq 'ARRAY' and @$node == 0;
58             # XXX Also croak if aliased, blessed, or ynode
59 1         5 $self->headless(1);
60 1         2 return;
61             }
62 153         349 $self->{stream} .= '---';
63             # XXX Consider switching to 1.1 style
64 153 50       442 if ($self->use_version) {
65             # $self->{stream} .= " #YAML:1.0";
66             }
67             }
68              
69             # Walk the tree to be dumped and keep track of its reference counts.
70             # This function is where the Dumper does all its work. All type
71             # transfers happen here.
72             sub _prewalk {
73 1483     1483   2267 my $self = shift;
74 1483         3201 my $stringify = $self->stringify;
75 1483         3493 my ($class, $type, $node_id) = $self->node_info(\$_[0], $stringify);
76              
77             # Handle typeglobs
78 1483 100       3554 if ($type eq 'GLOB') {
79 4         18 $self->transferred->{$node_id} =
80             YAML::Old::Type::glob->yaml_dump($_[0]);
81 4         10 $self->_prewalk($self->transferred->{$node_id});
82 4         6 return;
83             }
84              
85             # Handle regexps
86 1479 100       3173 if (ref($_[0]) eq 'Regexp') {
87 5         12 return;
88             }
89              
90             # Handle Purity for scalars.
91             # XXX can't find a use case yet. Might be YAGNI.
92 1474 100       3027 if (not ref $_[0]) {
93 949 50       2175 $self->{id_refcnt}{$node_id}++ if $self->purity;
94 949         2501 return;
95             }
96              
97             # Make a copy of original
98 525         837 my $value = $_[0];
99 525         1213 ($class, $type, $node_id) = $self->node_info($value, $stringify);
100              
101             # Must be a stringified object.
102 525 100 66     2210 return if (ref($value) and not $type);
103              
104             # Look for things already transferred.
105 523 100       1233 if ($self->transferred->{$node_id}) {
106             (undef, undef, $node_id) = (ref $self->transferred->{$node_id})
107             ? $self->node_info($self->transferred->{$node_id}, $stringify)
108 11 100       30 : $self->node_info(\ $self->transferred->{$node_id}, $stringify);
109 11         33 $self->{id_refcnt}{$node_id}++;
110 11         30 return;
111             }
112              
113             # Handle code refs
114 512 100       1242 if ($type eq 'CODE') {
115 7         16 $self->transferred->{$node_id} = 'placeholder';
116             YAML::Old::Type::code->yaml_dump(
117             $self->dump_code,
118             $_[0],
119 7         24 $self->transferred->{$node_id}
120             );
121             ($class, $type, $node_id) =
122 7         21 $self->node_info(\ $self->transferred->{$node_id}, $stringify);
123 7         50 $self->{id_refcnt}{$node_id}++;
124 7         21 return;
125             }
126              
127             # Handle blessed things
128 505 100       1148 if (defined $class) {
129 34 100       311 if ($value->can('yaml_dump')) {
    100          
130 5         18 $value = $value->yaml_dump;
131             }
132             elsif ($type eq 'SCALAR') {
133 7         27 $self->transferred->{$node_id} = 'placeholder';
134             YAML::Old::Type::blessed->yaml_dump
135 7         22 ($_[0], $self->transferred->{$node_id});
136             ($class, $type, $node_id) =
137 7         24 $self->node_info(\ $self->transferred->{$node_id}, $stringify);
138 7         26 $self->{id_refcnt}{$node_id}++;
139 7         15 return;
140             }
141             else {
142 22         117 $value = YAML::Old::Type::blessed->yaml_dump($value);
143             }
144 27         107 $self->transferred->{$node_id} = $value;
145 27         80 (undef, $type, $node_id) = $self->node_info($value, $stringify);
146             }
147              
148             # Handle YAML Blessed things
149 498         1761 require YAML::Old;
150 498 100       1293 if (defined YAML::Old->global_object()->{blessed_map}{$node_id}) {
151 4         11 $value = YAML::Old->global_object()->{blessed_map}{$node_id};
152 4         11 $self->transferred->{$node_id} = $value;
153 4         12 ($class, $type, $node_id) = $self->node_info($value, $stringify);
154 4         14 $self->_prewalk($value);
155 4         7 return;
156             }
157              
158             # Handle hard refs
159 494 100 100     2176 if ($type eq 'REF' or $type eq 'SCALAR') {
    100          
160 31         107 $value = YAML::Old::Type::ref->yaml_dump($value);
161 31         82 $self->transferred->{$node_id} = $value;
162 31         84 (undef, $type, $node_id) = $self->node_info($value, $stringify);
163             }
164              
165             # Handle ref-to-glob's
166             elsif ($type eq 'GLOB') {
167 2         13 my $ref_ynode = $self->transferred->{$node_id} =
168             YAML::Old::Type::ref->yaml_dump($value);
169              
170 2         9 my $glob_ynode = $ref_ynode->{&VALUE} =
171             YAML::Old::Type::glob->yaml_dump($$value);
172              
173 2         8 (undef, undef, $node_id) = $self->node_info($glob_ynode, $stringify);
174 2         8 $self->transferred->{$node_id} = $glob_ynode;
175 2         13 $self->_prewalk($glob_ynode);
176 2         4 return;
177             }
178              
179             # Increment ref count for node
180 492 100       1684 return if ++($self->{id_refcnt}{$node_id}) > 1;
181              
182             # Keep on walking
183 489 100       1062 if ($type eq 'HASH') {
    50          
184             $self->_prewalk($value->{$_})
185 429         625 for keys %{$value};
  429         1921  
186 429         1246 return;
187             }
188             elsif ($type eq 'ARRAY') {
189             $self->_prewalk($_)
190 60         100 for @{$value};
  60         238  
191 60         126 return;
192             }
193              
194             # Unknown type. Need to know about it.
195 0         0 $self->warn(<<"...");
196             YAML::Old::Dumper can't handle dumping this type of data.
197             Please report this to the author.
198              
199             id: $node_id
200             type: $type
201             class: $class
202             value: $value
203              
204             ...
205              
206 0         0 return;
207             }
208              
209             # Every data element and sub data element is a node.
210             # Everything emitted goes through this function.
211             sub _emit_node {
212 1476     1476   2146 my $self = shift;
213 1476         2242 my ($type, $node_id);
214 1476         2615 my $ref = ref($_[0]);
215 1476 100       2820 if ($ref) {
216 521 100       1198 if ($ref eq 'Regexp') {
217 5         25 $self->_emit(' !!perl/regexp');
218 5         15 $self->_emit_str("$_[0]");
219 5         13 return;
220             }
221 516         1250 (undef, $type, $node_id) = $self->node_info($_[0], $self->stringify);
222             }
223             else {
224 955   50     3259 $type = $ref || 'SCALAR';
225 955         2348 (undef, undef, $node_id) = $self->node_info(\$_[0], $self->stringify);
226             }
227              
228 1471         3311 my ($ynode, $tag) = ('') x 2;
229 1471         2763 my ($value, $context) = (@_, 0);
230              
231 1471 100       3515 if (defined $self->transferred->{$node_id}) {
    100          
232 93         216 $value = $self->transferred->{$node_id};
233 93         244 $ynode = ynode($value);
234 93 100       224 if (ref $value) {
235 77 50       318 $tag = defined $ynode ? $ynode->tag->short : '';
236 77         242 (undef, $type, $node_id) =
237             $self->node_info($value, $self->stringify);
238             }
239             else {
240 16         51 $ynode = ynode($self->transferred->{$node_id});
241 16 50       74 $tag = defined $ynode ? $ynode->tag->short : '';
242 16         51 $type = 'SCALAR';
243             (undef, undef, $node_id) =
244             $self->node_info(
245 16         43 \ $self->transferred->{$node_id},
246             $self->stringify
247             );
248             }
249             }
250             elsif ($ynode = ynode($value)) {
251 5         22 $tag = $ynode->tag->short;
252             }
253              
254 1471 100       3499 if ($self->use_aliases) {
255 1466   100     6212 $self->{id_refcnt}{$node_id} ||= 0;
256 1466 100       3239 if ($self->{id_refcnt}{$node_id} > 1) {
257 20 100       51 if (defined $self->{id_anchor}{$node_id}) {
258 11         31 $self->{stream} .= ' *' . $self->{id_anchor}{$node_id} . "\n";
259 11         27 return;
260             }
261 9         35 my $anchor = $self->anchor_prefix . $self->{anchor}++;
262 9         23 $self->{stream} .= ' &' . $anchor;
263 9         23 $self->{id_anchor}{$node_id} = $anchor;
264             }
265             }
266              
267 1460 100 100     4347 return $self->_emit_str("$value") # Stringified object
268             if ref($value) and not $type;
269 1458 100 100     4850 return $self->_emit_scalar($value, $tag)
270             if $type eq 'SCALAR' and $tag;
271 1444 100       3465 return $self->_emit_str($value)
272             if $type eq 'SCALAR';
273 493 100       1489 return $self->_emit_mapping($value, $tag, $node_id, $context)
274             if $type eq 'HASH';
275 60 50       229 return $self->_emit_sequence($value, $tag)
276             if $type eq 'ARRAY';
277 0         0 $self->warn('YAML_DUMP_WARN_BAD_NODE_TYPE', $type);
278 0         0 return $self->_emit_str("$value");
279             }
280              
281             # A YAML mapping is akin to a Perl hash.
282             sub _emit_mapping {
283 433     433   666 my $self = shift;
284 433         935 my ($value, $tag, $node_id, $context) = @_;
285 433 100       942 $self->{stream} .= " !$tag" if $tag;
286              
287             # Sometimes 'keys' fails. Like on a bad tie implementation.
288 433         680 my $empty_hash = not(eval {keys %$value});
  433         989  
289 433 50       1199 $self->warn('YAML_EMIT_WARN_KEYS', $@) if $@;
290 433 100       882 return ($self->{stream} .= " {}\n") if $empty_hash;
291              
292             # If CompressSeries is on (default) and legal is this context, then
293             # use it and make the indent level be 2 for this node.
294 425 100 100     1294 if ($context == FROMARRAY and
      66        
      100        
295             $self->compress_series and
296             not (defined $self->{id_anchor}{$node_id} or $tag or $empty_hash)
297             ) {
298 20         41 $self->{stream} .= ' ';
299 20         47 $self->offset->[$self->level+1] = $self->offset->[$self->level] + 2;
300             }
301             else {
302 405         606 $context = 0;
303 405 50 33     959 $self->{stream} .= "\n"
304             unless $self->headless && not($self->headless(0));
305 405         956 $self->offset->[$self->level+1] =
306             $self->offset->[$self->level] + $self->indent_width;
307             }
308              
309 425         782 $self->{level}++;
310 425         599 my @keys;
311 425 100       976 if ($self->sort_keys == 1) {
    50          
    50          
312 424 100       974 if (ynode($value)) {
313 65         219 @keys = keys %$value;
314             }
315             else {
316 359         1741 @keys = sort keys %$value;
317             }
318             }
319             elsif ($self->sort_keys == 2) {
320 0         0 @keys = sort keys %$value;
321             }
322             # XXX This is hackish but sometimes handy. Not sure whether to leave it in.
323             elsif (ref($self->sort_keys) eq 'ARRAY') {
324 1         2 my $i = 1;
325 1         2 my %order = map { ($_, $i++) } @{$self->sort_keys};
  3         9  
  1         3  
326             @keys = sort {
327 1         6 (defined $order{$a} and defined $order{$b})
328 2 50 33     15 ? ($order{$a} <=> $order{$b})
329             : ($a cmp $b);
330             } keys %$value;
331             }
332             else {
333 0         0 @keys = keys %$value;
334             }
335             # Force the YAML::VALUE ('=') key to sort last.
336 425 100       1623 if (exists $value->{&VALUE}) {
337 36         111 for (my $i = 0; $i < @keys; $i++) {
338 36 50       121 if ($keys[$i] eq &VALUE) {
339 36         83 splice(@keys, $i, 1);
340 36         85 push @keys, &VALUE;
341 36         66 last;
342             }
343             }
344             }
345              
346 425         845 for my $key (@keys) {
347 1189         2875 $self->_emit_key($key, $context);
348 1189         1607 $context = 0;
349 1189         2009 $self->{stream} .= ':';
350 1189         3083 $self->_emit_node($value->{$key});
351             }
352 425         1188 $self->{level}--;
353             }
354              
355             # A YAML series is akin to a Perl array.
356             sub _emit_sequence {
357 60     60   105 my $self = shift;
358 60         121 my ($value, $tag) = @_;
359 60 100       152 $self->{stream} .= " !$tag" if $tag;
360              
361 60 100       165 return ($self->{stream} .= " []\n") if @$value == 0;
362              
363 56 100 66     163 $self->{stream} .= "\n"
364             unless $self->headless && not($self->headless(0));
365              
366             # XXX Really crufty feature. Better implemented by ynodes.
367 56 100 100     209 if ($self->inline_series and
      100        
368             @$value <= $self->inline_series and
369 8 100       35 not (scalar grep {ref or /\n/} @$value)
370             ) {
371 2         10 $self->{stream} =~ s/\n\Z/ /;
372 2         4 $self->{stream} .= '[';
373 2         9 for (my $i = 0; $i < @$value; $i++) {
374 5         13 $self->_emit_str($value->[$i], KEY);
375 5 100       6 last if $i == $#{$value};
  5         13  
376 3         10 $self->{stream} .= ', ';
377             }
378 2         4 $self->{stream} .= "]\n";
379 2         6 return;
380             }
381              
382 54         147 $self->offset->[$self->level + 1] =
383             $self->offset->[$self->level] + $self->indent_width;
384 54         110 $self->{level}++;
385 54         114 for my $val (@$value) {
386 133         330 $self->{stream} .= ' ' x $self->offset->[$self->level];
387 133         248 $self->{stream} .= '-';
388 133         326 $self->_emit_node($val, FROMARRAY);
389             }
390 54         144 $self->{level}--;
391             }
392              
393             # Emit a mapping key
394             sub _emit_key {
395 1189     1189   1748 my $self = shift;
396 1189         2265 my ($value, $context) = @_;
397 1189 100       3962 $self->{stream} .= ' ' x $self->offset->[$self->level]
398             unless $context == FROMARRAY;
399 1189         2612 $self->_emit_str($value, KEY);
400             }
401              
402             # Emit a blessed SCALAR
403             sub _emit_scalar {
404 14     14   29 my $self = shift;
405 14         30 my ($value, $tag) = @_;
406 14         75 $self->{stream} .= " !$tag";
407 14         39 $self->_emit_str($value, BLESSED);
408             }
409              
410             sub _emit {
411 4300     4300   6121 my $self = shift;
412 4300         10091 $self->{stream} .= join '', @_;
413             }
414              
415             # Emit a string value. YAML has many scalar styles. This routine attempts to
416             # guess the best style for the text.
417             sub _emit_str {
418 2166     2166   3223 my $self = shift;
419 2166   100     5415 my $type = $_[1] || 0;
420              
421             # Use heuristics to find the best scalar emission style.
422 2166         4545 $self->offset->[$self->level + 1] =
423             $self->offset->[$self->level] + $self->indent_width;
424 2166         3598 $self->{level}++;
425              
426 2166 100       4168 my $sf = $type == KEY ? '' : ' ';
427 2166 100       3797 my $sb = $type == KEY ? '? ' : ' ';
428 2166 100       3670 my $ef = $type == KEY ? '' : "\n";
429 2166         3079 my $eb = "\n";
430              
431 2166         2870 while (1) {
432 2166 100       4666 $self->_emit($sf),
433             $self->_emit_plain($_[0]),
434             $self->_emit($ef), last
435             if not defined $_[0];
436 2162 100       4539 $self->_emit($sf, '=', $ef), last
437             if $_[0] eq VALUE;
438 2125 100       8781 $self->_emit($sf),
439             $self->_emit_double($_[0]),
440             $self->_emit($ef), last
441             if $_[0] =~ /$ESCAPE_CHAR/;
442 2124 100       4994 if ($_[0] =~ /\n/) {
443 26 100       99 $self->_emit($sb),
444             $self->_emit_block($LIT_CHAR, $_[0]),
445             $self->_emit($eb), last
446             if $self->use_block;
447 25 50       96 Carp::cluck "[YAML::Old] \$UseFold is no longer supported"
448             if $self->use_fold;
449 25 100       94 $self->_emit($sf),
450             $self->_emit_double($_[0]),
451             $self->_emit($ef), last
452             if length $_[0] <= 30;
453 19 100       81 $self->_emit($sf),
454             $self->_emit_double($_[0]),
455             $self->_emit($ef), last
456             if $_[0] !~ /\n\s*\S/;
457 17         44 $self->_emit($sb),
458             $self->_emit_block($LIT_CHAR, $_[0]),
459             $self->_emit($eb), last;
460             }
461 2098 100       4260 $self->_emit($sf),
462             $self->_emit_number($_[0]),
463             $self->_emit($ef), last
464             if $self->is_literal_number($_[0]);
465 2037 100       4095 $self->_emit($sf),
466             $self->_emit_plain($_[0]),
467             $self->_emit($ef), last
468             if $self->is_valid_plain($_[0]);
469 48 100       163 $self->_emit($sf),
470             $self->_emit_double($_[0]),
471             $self->_emit($ef), last
472             if $_[0] =~ /'/;
473 47         119 $self->_emit($sf),
474             $self->_emit_single($_[0]),
475             $self->_emit($ef);
476 47         83 last;
477             }
478              
479 2166         3648 $self->{level}--;
480              
481 2166         4673 return;
482             }
483              
484             sub is_literal_number {
485 2101     2101 0 3184 my $self = shift;
486             # Stolen from JSON::Tiny
487 2101   66     11164 return B::svref_2object(\$_[0])->FLAGS & (B::SVp_IOK | B::SVp_NOK)
488             && 0 + $_[0] eq $_[0];
489             }
490              
491             sub _emit_number {
492 61     61   105 my $self = shift;
493 61         138 return $self->_emit_plain($_[0]);
494             }
495              
496             # Check whether or not a scalar should be emitted as an plain scalar.
497             sub is_valid_plain {
498 2037     2037 0 2827 my $self = shift;
499 2037 100       4764 return 0 unless length $_[0];
500 2031 100 100     4409 return 0 if $self->quote_numeric_strings and Scalar::Util::looks_like_number($_[0]);
501             # refer to YAML::Old::Loader::parse_inline_simple()
502 2025 100       5574 return 0 if $_[0] =~ /^[\s\{\[\~\`\'\"\!\@\#\>\|\%\&\?\*\^]/;
503 2008 100       5011 return 0 if $_[0] =~ /[\{\[\]\},]/;
504 1999 100       4469 return 0 if $_[0] =~ /[:\-\?]\s/;
505 1995 100       4343 return 0 if $_[0] =~ /\s#/;
506 1994 50       4066 return 0 if $_[0] =~ /\:(\s|$)/;
507 1994 100       4893 return 0 if $_[0] =~ /[\s\|\>]$/;
508 1990 100       4127 return 0 if $_[0] eq '-';
509 1989         5207 return 1;
510             }
511              
512             sub _emit_block {
513 18     18   36 my $self = shift;
514 18         42 my ($indicator, $value) = @_;
515 18         31 $self->{stream} .= $indicator;
516 18         309 $value =~ /(\n*)\Z/;
517 18 100       74 my $chomp = length $1 ? (length $1 > 1) ? '+' : '' : '-';
    100          
518 18 50       47 $value = '~' if not defined $value;
519 18         34 $self->{stream} .= $chomp;
520 18 50       57 $self->{stream} .= $self->indent_width if $value =~ /^\s/;
521 18         71 $self->{stream} .= $self->indent($value);
522             }
523              
524             # Plain means that the scalar is unquoted.
525             sub _emit_plain {
526 2054     2054   2979 my $self = shift;
527 2054 100       5192 $self->{stream} .= defined $_[0] ? $_[0] : '~';
528             }
529              
530             # Double quoting is for single lined escaped strings.
531             sub _emit_double {
532 10     10   19 my $self = shift;
533 10         54 (my $escaped = $self->escape($_[0])) =~ s/"/\\"/g;
534 10         958 $self->{stream} .= qq{"$escaped"};
535             }
536              
537             # Single quoting is for single lined unescaped strings.
538             sub _emit_single {
539 47     47   80 my $self = shift;
540 47         80 my $item = shift;
541 47         94 $item =~ s{'}{''}g;
542 47         163 $self->{stream} .= "'$item'";
543             }
544              
545             #==============================================================================
546             # Utility subroutines.
547             #==============================================================================
548              
549             # Indent a scalar to the current indentation level.
550             sub indent {
551 18     18 0 28 my $self = shift;
552 18         39 my ($text) = @_;
553 18 50       107 return $text unless length $text;
554 18         83 $text =~ s/\n\Z//;
555 18         58 my $indent = ' ' x $self->offset->[$self->level];
556 18         113 $text =~ s/^/$indent/gm;
557 18         50 $text = "\n$text";
558 18         67 return $text;
559             }
560              
561             # Escapes for unprintable characters
562             my @escapes = qw(\0 \x01 \x02 \x03 \x04 \x05 \x06 \a
563             \x08 \t \n \v \f \r \x0e \x0f
564             \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17
565             \x18 \x19 \x1a \e \x1c \x1d \x1e \x1f
566             );
567              
568             # Escape the unprintable characters
569             sub escape {
570 10     10 0 17 my $self = shift;
571 10         25 my ($text) = @_;
572 10         58 $text =~ s/\\/\\\\/g;
573 10         1832 $text =~ s/([\x00-\x1f])/$escapes[ord($1)]/ge;
  13         52  
574 10         8391 return $text;
575             }
576              
577             1;