File Coverage

blib/lib/YAML/Old/Dumper.pm
Criterion Covered Total %
statement 284 290 97.9
branch 149 166 89.7
condition 41 51 80.3
subroutine 25 25 100.0
pod 0 4 0.0
total 499 536 93.1


line stmt bran cond sub pod time code
1             package YAML::Old::Dumper;
2              
3 27     27   94918 use YAML::Old::Mo;
  27         55  
  27         204  
4             extends 'YAML::Old::Dumper::Base';
5              
6 27     27   19245 use YAML::Old::Dumper::Base;
  27         822  
  27         1026  
7 27     27   177 use YAML::Old::Node;
  27         48  
  27         1489  
8 27     27   18113 use YAML::Old::Types;
  27         90  
  27         1018  
9              
10             # Context constants
11 27     27   292 use constant KEY => 3;
  27         61  
  27         2074  
12 27     27   136 use constant BLESSED => 4;
  27         54  
  27         1186  
13 27     27   132 use constant FROMARRAY => 5;
  27         48  
  27         1450  
14 27     27   138 use constant VALUE => "\x07YAML\x07VALUE\x07";
  27         47  
  27         165720  
15              
16             # Common YAML character sets
17             my $ESCAPE_CHAR = '[\\x00-\\x08\\x0b-\\x0d\\x0e-\\x1f]';
18             my $LIT_CHAR = '|';
19              
20             #==============================================================================
21             # OO version of Dump. YAML::Old->new->dump($foo);
22             sub dump {
23 140     140 0 237 my $self = shift;
24 140         616 $self->stream('');
25 140         644 $self->document(0);
26 140         346 for my $document (@_) {
27 146         354 $self->{document}++;
28 146         613 $self->transferred({});
29 146         920 $self->id_refcnt({});
30 146         710 $self->id_anchor({});
31 146         762 $self->anchor(1);
32 146         551 $self->level(0);
33 146         601 $self->offset->[0] = 0 - $self->indent_width;
34 146         582 $self->_prewalk($document);
35 146         457 $self->_emit_header($document);
36 143         592 $self->_emit_node($document);
37             }
38 137         490 return $self->stream;
39             }
40              
41             # Every YAML document in the stream must begin with a YAML header, unless
42             # there is only a single document and the user requests "no header".
43             sub _emit_header {
44 146     146   251 my $self = shift;
45 146         228 my ($node) = @_;
46 146 100 100     819 if (not $self->use_header and
47             $self->document == 1
48             ) {
49 4 100       29 $self->die('YAML_DUMP_ERR_NO_HEADER')
50             unless ref($node) =~ /^(HASH|ARRAY)$/;
51 3 100 66     19 $self->die('YAML_DUMP_ERR_NO_HEADER')
52             if ref($node) eq 'HASH' and keys(%$node) == 0;
53 2 100 66     21 $self->die('YAML_DUMP_ERR_NO_HEADER')
54             if ref($node) eq 'ARRAY' and @$node == 0;
55             # XXX Also croak if aliased, blessed, or ynode
56 1         8 $self->headless(1);
57 1         3 return;
58             }
59 142         637 $self->{stream} .= '---';
60             # XXX Consider switching to 1.1 style
61 142 50       564 if ($self->use_version) {
62             # $self->{stream} .= " #YAML:1.0";
63             }
64             }
65              
66             # Walk the tree to be dumped and keep track of its reference counts.
67             # This function is where the Dumper does all its work. All type
68             # transfers happen here.
69             sub _prewalk {
70 1399     1399   1754 my $self = shift;
71 1399         3873 my $stringify = $self->stringify;
72 1399         4596 my ($class, $type, $node_id) = $self->node_info(\$_[0], $stringify);
73              
74             # Handle typeglobs
75 1399 100       3207 if ($type eq 'GLOB') {
76 4         29 $self->transferred->{$node_id} =
77             YAML::Old::Type::glob->yaml_dump($_[0]);
78 4         13 $self->_prewalk($self->transferred->{$node_id});
79 4         8 return;
80             }
81              
82             # Handle regexps
83 1395 100       3195 if (ref($_[0]) eq 'Regexp') {
84 5         13 return;
85             }
86              
87             # Handle Purity for scalars.
88             # XXX can't find a use case yet. Might be YAGNI.
89 1390 100       3345 if (not ref $_[0]) {
90 885 50       2535 $self->{id_refcnt}{$node_id}++ if $self->purity;
91 885         2908 return;
92             }
93              
94             # Make a copy of original
95 505         615 my $value = $_[0];
96 505         1416 ($class, $type, $node_id) = $self->node_info($value, $stringify);
97              
98             # Must be a stringified object.
99 505 100 66     4381 return if (ref($value) and not $type);
100              
101             # Look for things already transferred.
102 503 100       1281 if ($self->transferred->{$node_id}) {
103 10 100       32 (undef, undef, $node_id) = (ref $self->transferred->{$node_id})
104             ? $self->node_info($self->transferred->{$node_id}, $stringify)
105             : $self->node_info(\ $self->transferred->{$node_id}, $stringify);
106 10         31 $self->{id_refcnt}{$node_id}++;
107 10         36 return;
108             }
109              
110             # Handle code refs
111 493 100       4518 if ($type eq 'CODE') {
112 6         18 $self->transferred->{$node_id} = 'placeholder';
113 6         21 YAML::Old::Type::code->yaml_dump(
114             $self->dump_code,
115             $_[0],
116             $self->transferred->{$node_id}
117             );
118 6         27 ($class, $type, $node_id) =
119             $self->node_info(\ $self->transferred->{$node_id}, $stringify);
120 6         30 $self->{id_refcnt}{$node_id}++;
121 6         15 return;
122             }
123              
124             # Handle blessed things
125 487 100       966 if (defined $class) {
126 32 100       343 if ($value->can('yaml_dump')) {
    100          
127 5         26 $value = $value->yaml_dump;
128             }
129             elsif ($type eq 'SCALAR') {
130 7         27 $self->transferred->{$node_id} = 'placeholder';
131 7         29 YAML::Old::Type::blessed->yaml_dump
132             ($_[0], $self->transferred->{$node_id});
133 7         616 ($class, $type, $node_id) =
134             $self->node_info(\ $self->transferred->{$node_id}, $stringify);
135 7         31 $self->{id_refcnt}{$node_id}++;
136 7         16 return;
137             }
138             else {
139 20         145 $value = YAML::Old::Type::blessed->yaml_dump($value);
140             }
141 25         99 $self->transferred->{$node_id} = $value;
142 25         94 (undef, $type, $node_id) = $self->node_info($value, $stringify);
143             }
144              
145             # Handle YAML Blessed things
146 480         2774 require YAML::Old;
147 480 100       4120 if (defined YAML::Old->global_object()->{blessed_map}{$node_id}) {
148 4         11 $value = YAML::Old->global_object()->{blessed_map}{$node_id};
149 4         12 $self->transferred->{$node_id} = $value;
150 4         13 ($class, $type, $node_id) = $self->node_info($value, $stringify);
151 4         11 $self->_prewalk($value);
152 4         62 return;
153             }
154              
155             # Handle hard refs
156 476 100 100     2757 if ($type eq 'REF' or $type eq 'SCALAR') {
    100          
157 31         115 $value = YAML::Old::Type::ref->yaml_dump($value);
158 31         90 $self->transferred->{$node_id} = $value;
159 31         92 (undef, $type, $node_id) = $self->node_info($value, $stringify);
160             }
161              
162             # Handle ref-to-glob's
163             elsif ($type eq 'GLOB') {
164 1         9 my $ref_ynode = $self->transferred->{$node_id} =
165             YAML::Old::Type::ref->yaml_dump($value);
166              
167 1         5 my $glob_ynode = $ref_ynode->{&VALUE} =
168             YAML::Old::Type::glob->yaml_dump($$value);
169              
170 1         4 (undef, undef, $node_id) = $self->node_info($glob_ynode, $stringify);
171 1         4 $self->transferred->{$node_id} = $glob_ynode;
172 1         3 $self->_prewalk($glob_ynode);
173 1         1 return;
174             }
175              
176             # Increment ref count for node
177 475 100       2119 return if ++($self->{id_refcnt}{$node_id}) > 1;
178              
179             # Keep on walking
180 472 100       1127 if ($type eq 'HASH') {
    50          
181 415         2795 $self->_prewalk($value->{$_})
182 415         449 for keys %{$value};
183 415         1565 return;
184             }
185             elsif ($type eq 'ARRAY') {
186 57         526 $self->_prewalk($_)
187 57         102 for @{$value};
188 57         154 return;
189             }
190              
191             # Unknown type. Need to know about it.
192 0         0 $self->warn(<<"...");
193             YAML::Old::Dumper can't handle dumping this type of data.
194             Please report this to the author.
195              
196             id: $node_id
197             type: $type
198             class: $class
199             value: $value
200              
201             ...
202              
203 0         0 return;
204             }
205              
206             # Every data element and sub data element is a node.
207             # Everything emitted goes through this function.
208             sub _emit_node {
209 1390     1390   1588 my $self = shift;
210 1390         1359 my ($type, $node_id);
211 1390         3003 my $ref = ref($_[0]);
212 1390 100       2336 if ($ref) {
213 501 100       1066 if ($ref eq 'Regexp') {
214 5         14 $self->_emit(' !!perl/regexp');
215 5         20 $self->_emit_str("$_[0]");
216 5         16 return;
217             }
218 496         1378 (undef, $type, $node_id) = $self->node_info($_[0], $self->stringify);
219             }
220             else {
221 889   50     4304 $type = $ref || 'SCALAR';
222 889         2679 (undef, undef, $node_id) = $self->node_info(\$_[0], $self->stringify);
223             }
224              
225 1385         3657 my ($ynode, $tag) = ('') x 2;
226 1385         2361 my ($value, $context) = (@_, 0);
227              
228 1385 100       4173 if (defined $self->transferred->{$node_id}) {
    100          
229 88         240 $value = $self->transferred->{$node_id};
230 88         292 $ynode = ynode($value);
231 88 100       212 if (ref $value) {
232 73 50       366 $tag = defined $ynode ? $ynode->tag->short : '';
233 73         2044 (undef, $type, $node_id) =
234             $self->node_info($value, $self->stringify);
235             }
236             else {
237 15         49 $ynode = ynode($self->transferred->{$node_id});
238 15 50       94 $tag = defined $ynode ? $ynode->tag->short : '';
239 15         43 $type = 'SCALAR';
240 15         51 (undef, undef, $node_id) =
241             $self->node_info(
242             \ $self->transferred->{$node_id},
243             $self->stringify
244             );
245             }
246             }
247             elsif ($ynode = ynode($value)) {
248 3         11 $tag = $ynode->tag->short;
249             }
250              
251 1385 100       4141 if ($self->use_aliases) {
252 1380   100     6036 $self->{id_refcnt}{$node_id} ||= 0;
253 1380 100       4168 if ($self->{id_refcnt}{$node_id} > 1) {
254 20 100       58 if (defined $self->{id_anchor}{$node_id}) {
255 11         28 $self->{stream} .= ' *' . $self->{id_anchor}{$node_id} . "\n";
256 11         35 return;
257             }
258 9         41 my $anchor = $self->anchor_prefix . $self->{anchor}++;
259 9         29 $self->{stream} .= ' &' . $anchor;
260 9         25 $self->{id_anchor}{$node_id} = $anchor;
261             }
262             }
263              
264 1374 100 100     4381 return $self->_emit_str("$value") # Stringified object
265             if ref($value) and not $type;
266 1372 100 100     5079 return $self->_emit_scalar($value, $tag)
267             if $type eq 'SCALAR' and $tag;
268 1359 100       3629 return $self->_emit_str($value)
269             if $type eq 'SCALAR';
270 474 100       1738 return $self->_emit_mapping($value, $tag, $node_id, $context)
271             if $type eq 'HASH';
272 57 50       294 return $self->_emit_sequence($value, $tag)
273             if $type eq 'ARRAY';
274 0         0 $self->warn('YAML_DUMP_WARN_BAD_NODE_TYPE', $type);
275 0         0 return $self->_emit_str("$value");
276             }
277              
278             # A YAML mapping is akin to a Perl hash.
279             sub _emit_mapping {
280 417     417   515 my $self = shift;
281 417         743 my ($value, $tag, $node_id, $context) = @_;
282 417 100       1066 $self->{stream} .= " !$tag" if $tag;
283              
284             # Sometimes 'keys' fails. Like on a bad tie implementation.
285 417         515 my $empty_hash = not(eval {keys %$value});
  417         1024  
286 417 50       1307 $self->warn('YAML_EMIT_WARN_KEYS', $@) if $@;
287 417 100       764 return ($self->{stream} .= " {}\n") if $empty_hash;
288              
289             # If CompressSeries is on (default) and legal is this context, then
290             # use it and make the indent level be 2 for this node.
291 409 100 100     1347 if ($context == FROMARRAY and
      66        
      100        
292             $self->compress_series and
293             not (defined $self->{id_anchor}{$node_id} or $tag or $empty_hash)
294             ) {
295 18         118 $self->{stream} .= ' ';
296 18         52 $self->offset->[$self->level+1] = $self->offset->[$self->level] + 2;
297             }
298             else {
299 391         430 $context = 0;
300 391 50 33     1274 $self->{stream} .= "\n"
301             unless $self->headless && not($self->headless(0));
302 391         1248 $self->offset->[$self->level+1] =
303             $self->offset->[$self->level] + $self->indent_width;
304             }
305              
306 409         782 $self->{level}++;
307 409         445 my @keys;
308 409 100       1474 if ($self->sort_keys == 1) {
    50          
    50          
309 408 100       970 if (ynode($value)) {
310 59         181 @keys = keys %$value;
311             }
312             else {
313 349         1958 @keys = sort keys %$value;
314             }
315             }
316             elsif ($self->sort_keys == 2) {
317 0         0 @keys = sort keys %$value;
318             }
319             # XXX This is hackish but sometimes handy. Not sure whether to leave it in.
320             elsif (ref($self->sort_keys) eq 'ARRAY') {
321 1         2 my $i = 1;
322 1         2 my %order = map { ($_, $i++) } @{$self->sort_keys};
  3         9  
  1         4  
323 3 50 33     19 @keys = sort {
324 1         7 (defined $order{$a} and defined $order{$b})
325             ? ($order{$a} <=> $order{$b})
326             : ($a cmp $b);
327             } keys %$value;
328             }
329             else {
330 0         0 @keys = keys %$value;
331             }
332             # Force the YAML::VALUE ('=') key to sort last.
333 409 100       1750 if (exists $value->{&VALUE}) {
334 35         102 for (my $i = 0; $i < @keys; $i++) {
335 35 50       107 if ($keys[$i] eq &VALUE) {
336 35         60 splice(@keys, $i, 1);
337 35         85 push @keys, &VALUE;
338 35         52 last;
339             }
340             }
341             }
342              
343 409         683 for my $key (@keys) {
344 1120         2219 $self->_emit_key($key, $context);
345 1120         1247 $context = 0;
346 1120         1571 $self->{stream} .= ':';
347 1120         3363 $self->_emit_node($value->{$key});
348             }
349 409         2657 $self->{level}--;
350             }
351              
352             # A YAML series is akin to a Perl array.
353             sub _emit_sequence {
354 57     57   104 my $self = shift;
355 57         99 my ($value, $tag) = @_;
356 57 100       140 $self->{stream} .= " !$tag" if $tag;
357              
358 57 100       178 return ($self->{stream} .= " []\n") if @$value == 0;
359              
360 53 100 66     207 $self->{stream} .= "\n"
361             unless $self->headless && not($self->headless(0));
362              
363             # XXX Really crufty feature. Better implemented by ynodes.
364 53 100 100     648 if ($self->inline_series and
  8 100 100     36  
365             @$value <= $self->inline_series and
366             not (scalar grep {ref or /\n/} @$value)
367             ) {
368 2         9 $self->{stream} =~ s/\n\Z/ /;
369 2         6 $self->{stream} .= '[';
370 2         7 for (my $i = 0; $i < @$value; $i++) {
371 5         12 $self->_emit_str($value->[$i], KEY);
372 5 100       5 last if $i == $#{$value};
  5         15  
373 3         10 $self->{stream} .= ', ';
374             }
375 2         3 $self->{stream} .= "]\n";
376 2         7 return;
377             }
378              
379 51         161 $self->offset->[$self->level + 1] =
380             $self->offset->[$self->level] + $self->indent_width;
381 51         129 $self->{level}++;
382 51         129 for my $val (@$value) {
383 127         385 $self->{stream} .= ' ' x $self->offset->[$self->level];
384 127         294 $self->{stream} .= '-';
385 127         520 $self->_emit_node($val, FROMARRAY);
386             }
387 51         276 $self->{level}--;
388             }
389              
390             # Emit a mapping key
391             sub _emit_key {
392 1120     1120   1306 my $self = shift;
393 1120         1628 my ($value, $context) = @_;
394 1120 100       4089 $self->{stream} .= ' ' x $self->offset->[$self->level]
395             unless $context == FROMARRAY;
396 1120         2711 $self->_emit_str($value, KEY);
397             }
398              
399             # Emit a blessed SCALAR
400             sub _emit_scalar {
401 13     13   233 my $self = shift;
402 13         30 my ($value, $tag) = @_;
403 13         42 $self->{stream} .= " !$tag";
404 13         46 $self->_emit_str($value, BLESSED);
405             }
406              
407             sub _emit {
408 4029     4029   4547 my $self = shift;
409 4029         11695 $self->{stream} .= join '', @_;
410             }
411              
412             # Emit a string value. YAML has many scalar styles. This routine attempts to
413             # guess the best style for the text.
414             sub _emit_str {
415 2030     2030   2279 my $self = shift;
416 2030   100     5141 my $type = $_[1] || 0;
417              
418             # Use heuristics to find the best scalar emission style.
419 2030         5394 $self->offset->[$self->level + 1] =
420             $self->offset->[$self->level] + $self->indent_width;
421 2030         3890 $self->{level}++;
422              
423 2030 100       3630 my $sf = $type == KEY ? '' : ' ';
424 2030 100       4202 my $sb = $type == KEY ? '? ' : ' ';
425 2030 100       8651 my $ef = $type == KEY ? '' : "\n";
426 2030         2200 my $eb = "\n";
427              
428 2030         2035 while (1) {
429 2030 100       4205 $self->_emit($sf),
430             $self->_emit_plain($_[0]),
431             $self->_emit($ef), last
432             if not defined $_[0];
433 2027 100       4949 $self->_emit($sf, '=', $ef), last
434             if $_[0] eq VALUE;
435 1991 100       10185 $self->_emit($sf),
436             $self->_emit_double($_[0]),
437             $self->_emit($ef), last
438             if $_[0] =~ /$ESCAPE_CHAR/;
439 1990 100       7587 if ($_[0] =~ /\n/) {
440 26 100       231 $self->_emit($sb),
441             $self->_emit_block($LIT_CHAR, $_[0]),
442             $self->_emit($eb), last
443             if $self->use_block;
444 25 50       152 Carp::cluck "[YAML] \$UseFold is no longer supported"
445             if $self->use_fold;
446 25 100       270 $self->_emit($sf),
447             $self->_emit_double($_[0]),
448             $self->_emit($ef), last
449             if length $_[0] <= 30;
450 19 100       104 $self->_emit($sf),
451             $self->_emit_double($_[0]),
452             $self->_emit($ef), last
453             if $_[0] !~ /\n\s*\S/;
454 17         276 $self->_emit($sb),
455             $self->_emit_block($LIT_CHAR, $_[0]),
456             $self->_emit($eb), last;
457             }
458 1964 100       4220 $self->_emit($sf),
459             $self->_emit_plain($_[0]),
460             $self->_emit($ef), last
461             if $self->is_valid_plain($_[0]);
462 39 100       156 $self->_emit($sf),
463             $self->_emit_double($_[0]),
464             $self->_emit($ef), last
465             if $_[0] =~ /'/;
466 38         125 $self->_emit($sf),
467             $self->_emit_single($_[0]),
468             $self->_emit($ef);
469 38         91 last;
470             }
471              
472 2030         3212 $self->{level}--;
473              
474 2030         5261 return;
475             }
476              
477             # Check whether or not a scalar should be emitted as an plain scalar.
478             sub is_valid_plain {
479 1964     1964 0 2083 my $self = shift;
480 1964 100       4408 return 0 unless length $_[0];
481             # refer to YAML::Old::Loader::parse_inline_simple()
482 1958 100       5581 return 0 if $_[0] =~ /^[\s\{\[\~\`\'\"\!\@\#\>\|\%\&\?\*\^]/;
483 1942 100       4141 return 0 if $_[0] =~ /[\{\[\]\},]/;
484 1933 100       4014 return 0 if $_[0] =~ /[:\-\?]\s/;
485 1931 100       3742 return 0 if $_[0] =~ /\s#/;
486 1930 50       3757 return 0 if $_[0] =~ /\:(\s|$)/;
487 1930 100       4649 return 0 if $_[0] =~ /[\s\|\>]$/;
488 1926 100       3622 return 0 if $_[0] eq '-';
489 1925         6425 return 1;
490             }
491              
492             sub _emit_block {
493 18     18   27 my $self = shift;
494 18         38 my ($indicator, $value) = @_;
495 18         40 $self->{stream} .= $indicator;
496 18         501 $value =~ /(\n*)\Z/;
497 18 100       92 my $chomp = length $1 ? (length $1 > 1) ? '+' : '' : '-';
    100          
498 18 50       71 $value = '~' if not defined $value;
499 18         46 $self->{stream} .= $chomp;
500 18 50       107 $self->{stream} .= $self->indent_width if $value =~ /^\s/;
501 18         80 $self->{stream} .= $self->indent($value);
502             }
503              
504             # Plain means that the scalar is unquoted.
505             sub _emit_plain {
506 1928     1928   2338 my $self = shift;
507 1928 100       6382 $self->{stream} .= defined $_[0] ? $_[0] : '~';
508             }
509              
510             # Double quoting is for single lined escaped strings.
511             sub _emit_double {
512 10     10   19 my $self = shift;
513 10         53 (my $escaped = $self->escape($_[0])) =~ s/"/\\"/g;
514 10         2718 $self->{stream} .= qq{"$escaped"};
515             }
516              
517             # Single quoting is for single lined unescaped strings.
518             sub _emit_single {
519 38     38   67 my $self = shift;
520 38         66 my $item = shift;
521 38         84 $item =~ s{'}{''}g;
522 38         154 $self->{stream} .= "'$item'";
523             }
524              
525             #==============================================================================
526             # Utility subroutines.
527             #==============================================================================
528              
529             # Indent a scalar to the current indentation level.
530             sub indent {
531 18     18 0 24 my $self = shift;
532 18         42 my ($text) = @_;
533 18 50       57 return $text unless length $text;
534 18         65 $text =~ s/\n\Z//;
535 18         62 my $indent = ' ' x $self->offset->[$self->level];
536 18         162 $text =~ s/^/$indent/gm;
537 18         47 $text = "\n$text";
538 18         88 return $text;
539             }
540              
541             # Escapes for unprintable characters
542             my @escapes = qw(\0 \x01 \x02 \x03 \x04 \x05 \x06 \a
543             \x08 \t \n \v \f \r \x0e \x0f
544             \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17
545             \x18 \x19 \x1a \e \x1c \x1d \x1e \x1f
546             );
547              
548             # Escape the unprintable characters
549             sub escape {
550 10     10 0 19 my $self = shift;
551 10         22 my ($text) = @_;
552 10         1283 $text =~ s/\\/\\\\/g;
553 10         3570 $text =~ s/([\x00-\x1f])/$escapes[ord($1)]/ge;
  13         65  
554 10         23876 return $text;
555             }
556              
557             1;