File Coverage

blib/lib/YAML/Dumper.pm
Criterion Covered Total %
statement 300 306 98.0
branch 155 172 90.1
condition 46 57 80.7
subroutine 30 30 100.0
pod 0 5 0.0
total 531 570 93.1


line stmt bran cond sub pod time code
1             package YAML::Dumper;
2              
3 34     34   647952 use YAML::Mo;
  34         74  
  34         291  
4             extends 'YAML::Dumper::Base';
5              
6 34     34   19918 use YAML::Dumper::Base;
  34         122  
  34         1440  
7 34     34   243 use YAML::Node;
  34         80  
  34         1941  
8 34     34   16456 use YAML::Types;
  34         102  
  34         1489  
9 34     34   241 use Scalar::Util qw();
  34         58  
  34         700  
10 34     34   167 use B ();
  34         105  
  34         505  
11 34     34   132 use Carp ();
  34         61  
  34         835  
12              
13             # Context constants
14 34     34   159 use constant KEY => 3;
  34         54  
  34         2277  
15 34     34   223 use constant BLESSED => 4;
  34         65  
  34         1836  
16 34     34   162 use constant FROMARRAY => 5;
  34         78  
  34         2293  
17 34     34   198 use constant VALUE => "\x07YAML\x07VALUE\x07";
  34         78  
  34         200289  
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 160     160 0 358 my $self = shift;
27 160         680 $self->stream('');
28 160         606 $self->document(0);
29 160         483 for my $document (@_) {
30 166         335 $self->{document}++;
31 166         678 $self->transferred({});
32 166         670 $self->id_refcnt({});
33 166         697 $self->id_anchor({});
34 166         640 $self->anchor(1);
35 166         585 $self->level(0);
36 166         634 $self->offset->[0] = 0 - $self->indent_width;
37 166         716 $self->_prewalk($document);
38 166         526 $self->_emit_header($document);
39 163         515 $self->_emit_node($document);
40             }
41 157         494 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 166     166   282 my $self = shift;
48 166         386 my ($node) = @_;
49 166 100 100     663 if (not $self->use_header and
50             $self->document == 1
51             ) {
52 4 100       31 $self->die('YAML_DUMP_ERR_NO_HEADER')
53             unless ref($node) =~ /^(HASH|ARRAY)$/;
54 3 100 66     70 $self->die('YAML_DUMP_ERR_NO_HEADER')
55             if ref($node) eq 'HASH' and keys(%$node) == 0;
56 2 100 66     21 $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         9 $self->headless(1);
60 1         2 return;
61             }
62 162         451 $self->{stream} .= '---';
63             # XXX Consider switching to 1.1 style
64 162 50       590 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 1499     1499   2386 my $self = shift;
74 1499         3332 my $stringify = $self->stringify;
75 1499         3692 my ($class, $type, $node_id) = $self->node_info(\$_[0], $stringify);
76              
77             # Handle typeglobs
78 1499 100       3270 if ($type eq 'GLOB') {
79 4         21 $self->transferred->{$node_id} =
80             YAML::Type::glob->yaml_dump($_[0]);
81 4         9 $self->_prewalk($self->transferred->{$node_id});
82 4         9 return;
83             }
84              
85             # Handle regexps
86 1495 100       6797 if (ref($_[0]) eq 'Regexp') {
87 11         32 return;
88             }
89              
90             # Handle Purity for scalars.
91             # XXX can't find a use case yet. Might be YAGNI.
92 1484 100       2824 if (not ref $_[0]) {
93 950 50       2197 $self->{id_refcnt}{$node_id}++ if $self->purity;
94 950         2600 return;
95             }
96              
97             # Make a copy of original
98 534         795 my $value = $_[0];
99 534         1361 ($class, $type, $node_id) = $self->node_info($value, $stringify);
100              
101             # Must be a stringified object.
102 534 100 66     2521 return if (ref($value) and not $type);
103              
104             # Look for things already transferred.
105 532 100       1324 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       29 : $self->node_info(\ $self->transferred->{$node_id}, $stringify);
109 11         34 $self->{id_refcnt}{$node_id}++;
110 11         36 return;
111             }
112              
113             # Handle code refs
114 521 100       1170 if ($type eq 'CODE') {
115 9         25 $self->transferred->{$node_id} = 'placeholder';
116             YAML::Type::code->yaml_dump(
117             $self->dump_code,
118             $_[0],
119 9         41 $self->transferred->{$node_id}
120             );
121             ($class, $type, $node_id) =
122 9         33 $self->node_info(\ $self->transferred->{$node_id}, $stringify);
123 9         35 $self->{id_refcnt}{$node_id}++;
124 9         25 return;
125             }
126              
127             # Handle blessed things
128 512 100       1078 if (defined $class) {
129 31 100       409 if ($value->can('yaml_dump')) {
    100          
130 5         22 $value = $value->yaml_dump;
131             }
132             elsif ($type eq 'SCALAR') {
133 7         28 $self->transferred->{$node_id} = 'placeholder';
134             YAML::Type::blessed->yaml_dump
135 7         46 ($_[0], $self->transferred->{$node_id});
136             ($class, $type, $node_id) =
137 7         31 $self->node_info(\ $self->transferred->{$node_id}, $stringify);
138 7         31 $self->{id_refcnt}{$node_id}++;
139 7         19 return;
140             }
141             else {
142 19         138 $value = YAML::Type::blessed->yaml_dump($value);
143             }
144 24         98 $self->transferred->{$node_id} = $value;
145 24         76 (undef, $type, $node_id) = $self->node_info($value, $stringify);
146             }
147              
148             # Handle YAML Blessed things
149 505         2206 require YAML;
150 505 100       1625 if (defined YAML->global_object()->{blessed_map}{$node_id}) {
151 4         9 $value = YAML->global_object()->{blessed_map}{$node_id};
152 4         10 $self->transferred->{$node_id} = $value;
153 4         10 ($class, $type, $node_id) = $self->node_info($value, $stringify);
154 4         12 $self->_prewalk($value);
155 4         8 return;
156             }
157              
158             # Handle hard refs
159 501 100 100     2022 if ($type eq 'REF' or $type eq 'SCALAR') {
    100          
160 31         140 $value = YAML::Type::ref->yaml_dump($value);
161 31         110 $self->transferred->{$node_id} = $value;
162 31         106 (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::Type::ref->yaml_dump($value);
169              
170 2         9 my $glob_ynode = $ref_ynode->{&VALUE} =
171             YAML::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         21 $self->_prewalk($glob_ynode);
176 2         6 return;
177             }
178              
179             # Increment ref count for node
180 499 100       2089 return if ++($self->{id_refcnt}{$node_id}) > 1;
181              
182             # Keep on walking
183 496 100       1032 if ($type eq 'HASH') {
    50          
184             $self->_prewalk($value->{$_})
185 436         807 for keys %{$value};
  436         2440  
186 436         1402 return;
187             }
188             elsif ($type eq 'ARRAY') {
189             $self->_prewalk($_)
190 60         166 for @{$value};
  60         395  
191 60         159 return;
192             }
193              
194             # Unknown type. Need to know about it.
195 0         0 $self->warn(<<"...");
196             YAML::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 1492     1492   2525 my $self = shift;
213 1492         3332 my ($type, $node_id);
214 1492         3078 my $ref = ref($_[0]);
215 1492 100       3141 if ($ref) {
216 536 100       1253 if ($ref eq 'Regexp') {
217 11         60 $self->_emit(' !!perl/regexp');
218 11         44 $self->_emit_str("$_[0]");
219 11         57 return;
220             }
221 525         1581 (undef, $type, $node_id) = $self->node_info($_[0], $self->stringify);
222             }
223             else {
224 956   50     3318 $type = $ref || 'SCALAR';
225 956         2590 (undef, undef, $node_id) = $self->node_info(\$_[0], $self->stringify);
226             }
227              
228 1481         4032 my ($ynode, $tag) = ('') x 2;
229 1481         3223 my ($value, $context) = (@_, 0);
230              
231 1481 100       4836 if (defined $self->transferred->{$node_id}) {
    100          
232 92         231 $value = $self->transferred->{$node_id};
233 92         3323 $ynode = ynode($value);
234 92 100       230 if (ref $value) {
235 74 50       359 $tag = defined $ynode ? $ynode->tag->short : '';
236 74         222 (undef, $type, $node_id) =
237             $self->node_info($value, $self->stringify);
238             }
239             else {
240 18         52 $ynode = ynode($self->transferred->{$node_id});
241 18 50       162 $tag = defined $ynode ? $ynode->tag->short : '';
242 18         49 $type = 'SCALAR';
243             (undef, undef, $node_id) =
244             $self->node_info(
245 18         58 \ $self->transferred->{$node_id},
246             $self->stringify
247             );
248             }
249             }
250             elsif ($ynode = ynode($value)) {
251 5         24 $tag = $ynode->tag->short;
252             }
253              
254 1481 100       3973 if ($self->use_aliases) {
255 1476   100     7195 $self->{id_refcnt}{$node_id} ||= 0;
256 1476 100       3753 if ($self->{id_refcnt}{$node_id} > 1) {
257 20 100       62 if (defined $self->{id_anchor}{$node_id}) {
258 11         29 $self->{stream} .= ' *' . $self->{id_anchor}{$node_id} . "\n";
259 11         37 return;
260             }
261 9         40 my $anchor = $self->anchor_prefix . $self->{anchor}++;
262 9         23 $self->{stream} .= ' &' . $anchor;
263 9         25 $self->{id_anchor}{$node_id} = $anchor;
264             }
265             }
266              
267 1470 100 100     5822 return $self->_emit_str("$value") # Stringified object
268             if ref($value) and not $type;
269 1468 100 100     4940 return $self->_emit_scalar($value, $tag)
270             if $type eq 'SCALAR' and $tag;
271 1452 100       3885 return $self->_emit_str($value)
272             if $type eq 'SCALAR';
273 500 100       1990 return $self->_emit_mapping($value, $tag, $node_id, $context)
274             if $type eq 'HASH';
275 60 50       327 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 440     440   743 my $self = shift;
284 440         1601 my ($value, $tag, $node_id, $context) = @_;
285 440 100       1070 $self->{stream} .= " !$tag" if $tag;
286              
287             # Sometimes 'keys' fails. Like on a bad tie implementation.
288 440         745 my $empty_hash = not(eval {keys %$value});
  440         1272  
289 440 50       1061 $self->warn('YAML_EMIT_WARN_KEYS', $@) if $@;
290 440 100       910 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 432 100 100     1421 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         44 $self->{stream} .= ' ';
299 20         54 $self->offset->[$self->level+1] = $self->offset->[$self->level] + 2;
300             }
301             else {
302 412         619 $context = 0;
303 412 50 33     1048 $self->{stream} .= "\n"
304             unless $self->headless && not($self->headless(0));
305 412         1313 $self->offset->[$self->level+1] =
306             $self->offset->[$self->level] + $self->indent_width;
307             }
308              
309 432         859 $self->{level}++;
310 432         699 my @keys;
311 432 100       1167 if ($self->sort_keys == 1) {
    50          
    50          
312 431 100       1066 if (ynode($value)) {
313 62         250 @keys = keys %$value;
314             }
315             else {
316 369         2390 @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         4 my %order = map { ($_, $i++) } @{$self->sort_keys};
  3         11  
  1         50  
326             @keys = sort {
327 1         8 (defined $order{$a} and defined $order{$b})
328 3 50 33     22 ? ($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 432 100       2144 if (exists $value->{&VALUE}) {
337 36         125 for (my $i = 0; $i < @keys; $i++) {
338 36 50       124 if ($keys[$i] eq &VALUE) {
339 36         82 splice(@keys, $i, 1);
340 36         96 push @keys, &VALUE;
341 36         133 last;
342             }
343             }
344             }
345              
346 432         1022 for my $key (@keys) {
347 1196         3362 $self->_emit_key($key, $context);
348 1196         1778 $context = 0;
349 1196         2271 $self->{stream} .= ':';
350 1196         3982 $self->_emit_node($value->{$key});
351             }
352 432         1740 $self->{level}--;
353             }
354              
355             # A YAML series is akin to a Perl array.
356             sub _emit_sequence {
357 60     60   108 my $self = shift;
358 60         155 my ($value, $tag) = @_;
359 60 100       178 $self->{stream} .= " !$tag" if $tag;
360              
361 60 100       179 return ($self->{stream} .= " []\n") if @$value == 0;
362              
363 56 100 66     198 $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     187 if ($self->inline_series and
      100        
368             @$value <= $self->inline_series and
369 8 100       39 not (scalar grep {ref or /\n/} @$value)
370             ) {
371 2         12 $self->{stream} =~ s/\n\Z/ /;
372 2         5 $self->{stream} .= '[';
373 2         9 for (my $i = 0; $i < @$value; $i++) {
374 5         15 $self->_emit_str($value->[$i], KEY);
375 5 100       35 last if $i == $#{$value};
  5         19  
376 3         13 $self->{stream} .= ', ';
377             }
378 2         6 $self->{stream} .= "]\n";
379 2         8 return;
380             }
381              
382 54         146 $self->offset->[$self->level + 1] =
383             $self->offset->[$self->level] + $self->indent_width;
384 54         135 $self->{level}++;
385 54         163 for my $val (@$value) {
386 133         451 $self->{stream} .= ' ' x $self->offset->[$self->level];
387 133         296 $self->{stream} .= '-';
388 133         443 $self->_emit_node($val, FROMARRAY);
389             }
390 54         194 $self->{level}--;
391             }
392              
393             # Emit a mapping key
394             sub _emit_key {
395 1196     1196   1819 my $self = shift;
396 1196         2510 my ($value, $context) = @_;
397 1196 100       3949 $self->{stream} .= ' ' x $self->offset->[$self->level]
398             unless $context == FROMARRAY;
399 1196         3100 $self->_emit_str($value, KEY);
400             }
401              
402             # Emit a blessed SCALAR
403             sub _emit_scalar {
404 16     16   54 my $self = shift;
405 16         43 my ($value, $tag) = @_;
406 16         72 $self->{stream} .= " !$tag";
407 16         56 $self->_emit_str($value, BLESSED);
408             }
409              
410             sub _emit {
411 4338     4338   6224 my $self = shift;
412 4338         13417 $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 2182     2182   7859 my $self = shift;
419 2182   100     5387 my $type = $_[1] || 0;
420              
421             # Use heuristics to find the best scalar emission style.
422 2182         4723 $self->offset->[$self->level + 1] =
423             $self->offset->[$self->level] + $self->indent_width;
424 2182         4206 $self->{level}++;
425              
426 2182 100       4555 my $sf = $type == KEY ? '' : ' ';
427 2182 100       4631 my $sb = $type == KEY ? '? ' : ' ';
428 2182 100       4065 my $ef = $type == KEY ? '' : "\n";
429 2182         3222 my $eb = "\n";
430              
431 2182         3095 while (1) {
432 2182 100       4763 $self->_emit($sf),
433             $self->_emit_plain($_[0]),
434             $self->_emit($ef), last
435             if not defined $_[0];
436 2178 100       5849 $self->_emit($sf, '=', $ef), last
437             if $_[0] eq VALUE;
438 2141 100       12368 $self->_emit($sf),
439             $self->_emit_double($_[0]),
440             $self->_emit($ef), last
441             if $_[0] =~ /$ESCAPE_CHAR/;
442 2140 100       5712 if ($_[0] =~ /\n/) {
443 28 100       130 $self->_emit($sb),
444             $self->_emit_block($LIT_CHAR, $_[0]),
445             $self->_emit($eb), last
446             if $self->use_block;
447 27 50       115 Carp::cluck "[YAML] \$UseFold is no longer supported"
448             if $self->use_fold;
449 27 100       104 $self->_emit($sf),
450             $self->_emit_double($_[0]),
451             $self->_emit($ef), last
452             if length $_[0] <= 30;
453 20 100       142 $self->_emit($sf),
454             $self->_emit_double($_[0]),
455             $self->_emit($ef), last
456             if $_[0] !~ /\n\s*\S/;
457 18         50 $self->_emit($sb),
458             $self->_emit_block($LIT_CHAR, $_[0]),
459             $self->_emit($eb), last;
460             }
461 2112 100       4900 $self->_emit($sf),
462             $self->_emit_number($_[0]),
463             $self->_emit($ef), last
464             if $self->is_literal_number($_[0]);
465 2048 100       7885 $self->_emit($sf),
466             $self->_emit_plain($_[0]),
467             $self->_emit($ef), last
468             if $self->is_valid_plain($_[0]);
469 49 100       266 $self->_emit($sf),
470             $self->_emit_double($_[0]),
471             $self->_emit($ef), last
472             if $_[0] =~ /'/;
473 48         191 $self->_emit($sf),
474             $self->_emit_single($_[0]),
475             $self->_emit($ef);
476 48         82 last;
477             }
478              
479 2182         4187 $self->{level}--;
480              
481 2182         5907 return;
482             }
483              
484             sub is_literal_number {
485 2115     2115 0 225105 my $self = shift;
486             # Stolen from JSON::Tiny
487 2115   66     14263 return B::svref_2object(\$_[0])->FLAGS & (B::SVp_IOK | B::SVp_NOK)
488             && 0 + $_[0] eq $_[0];
489             }
490              
491             sub _emit_number {
492 64     64   101 my $self = shift;
493 64         144 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 2048     2048 0 3007 my $self = shift;
499 2048 100       4456 return 0 unless length $_[0];
500 2042 100 100     4868 return 0 if $self->quote_numeric_strings and Scalar::Util::looks_like_number($_[0]);
501             # refer to YAML::Loader::parse_inline_simple()
502 2036 100       6389 return 0 if $_[0] =~ /^[\s\{\[\~\`\'\"\!\@\#\>\|\%\&\?\*\^]/;
503 2019 100       4845 return 0 if $_[0] =~ /[\{\[\]\},]/;
504 2010 100       4816 return 0 if $_[0] =~ /[:\-\?]\s/;
505 2006 100       4528 return 0 if $_[0] =~ /\s#/;
506 2005 50       4288 return 0 if $_[0] =~ /\:(\s|$)/;
507 2005 100       5497 return 0 if $_[0] =~ /[\s\|\>]$/;
508 2001 100       4142 return 0 if $_[0] eq '-';
509 2000 100       3913 return 0 if $_[0] eq '=';
510 1999         9800 return 1;
511             }
512              
513             sub _emit_block {
514 19     19   39 my $self = shift;
515 19         54 my ($indicator, $value) = @_;
516 19         40 $self->{stream} .= $indicator;
517 19         758 $value =~ /(\n*)\Z/;
518 19 100       89 my $chomp = length $1 ? (length $1 > 1) ? '+' : '' : '-';
    100          
519 19 50       48 $value = '~' if not defined $value;
520 19         66 $self->{stream} .= $chomp;
521 19 50       73 $self->{stream} .= $self->indent_width if $value =~ /^\s/;
522 19         70 $self->{stream} .= $self->indent($value);
523             }
524              
525             # Plain means that the scalar is unquoted.
526             sub _emit_plain {
527 2067     2067   3144 my $self = shift;
528 2067 100       5850 $self->{stream} .= defined $_[0] ? $_[0] : '~';
529             }
530              
531             # Double quoting is for single lined escaped strings.
532             sub _emit_double {
533 11     11   49 my $self = shift;
534 11         35 (my $escaped = $self->escape($_[0])) =~ s/"/\\"/g;
535 11         722 $self->{stream} .= qq{"$escaped"};
536             }
537              
538             # Single quoting is for single lined unescaped strings.
539             sub _emit_single {
540 48     48   101 my $self = shift;
541 48         101 my $item = shift;
542 48         111 $item =~ s{'}{''}g;
543 48         218 $self->{stream} .= "'$item'";
544             }
545              
546             #==============================================================================
547             # Utility subroutines.
548             #==============================================================================
549              
550             # Indent a scalar to the current indentation level.
551             sub indent {
552 19     19 0 28 my $self = shift;
553 19         43 my ($text) = @_;
554 19 50       104 return $text unless length $text;
555 19         105 $text =~ s/\n\Z//;
556 19         76 my $indent = ' ' x $self->offset->[$self->level];
557 19         175 $text =~ s/^/$indent/gm;
558 19         43 $text = "\n$text";
559 19         79 return $text;
560             }
561              
562             # Escapes for unprintable characters
563             my @escapes = qw(\0 \x01 \x02 \x03 \x04 \x05 \x06 \a
564             \x08 \t \n \v \f \r \x0e \x0f
565             \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17
566             \x18 \x19 \x1a \e \x1c \x1d \x1e \x1f
567             );
568              
569             # Escape the unprintable characters
570             sub escape {
571 11     11 0 19 my $self = shift;
572 11         116 my ($text) = @_;
573 11         58 $text =~ s/\\/\\\\/g;
574 11         236 $text =~ s/([\x00-\x1f])/$escapes[ord($1)]/ge;
  17         96  
575 11         8590 return $text;
576             }
577              
578             1;