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   115500 use YAML::Mo;
  34         65  
  34         200  
4             extends 'YAML::Dumper::Base';
5              
6 34     34   13000 use YAML::Dumper::Base;
  34         87  
  34         925  
7 34     34   235 use YAML::Node;
  34         53  
  34         1245  
8 34     34   10881 use YAML::Types;
  34         92  
  34         893  
9 34     34   183 use Scalar::Util qw();
  34         59  
  34         507  
10 34     34   144 use B ();
  34         61  
  34         414  
11 34     34   124 use Carp ();
  34         62  
  34         570  
12              
13             # Context constants
14 34     34   146 use constant KEY => 3;
  34         54  
  34         1697  
15 34     34   179 use constant BLESSED => 4;
  34         57  
  34         1411  
16 34     34   174 use constant FROMARRAY => 5;
  34         96  
  34         1578  
17 34     34   218 use constant VALUE => "\x07YAML\x07VALUE\x07";
  34         63  
  34         115513  
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 230 my $self = shift;
27 160         418 $self->stream('');
28 160         426 $self->document(0);
29 160         300 for my $document (@_) {
30 166         220 $self->{document}++;
31 166         436 $self->transferred({});
32 166         427 $self->id_refcnt({});
33 166         391 $self->id_anchor({});
34 166         389 $self->anchor(1);
35 166         388 $self->level(0);
36 166         398 $self->offset->[0] = 0 - $self->indent_width;
37 166         406 $self->_prewalk($document);
38 166         440 $self->_emit_header($document);
39 163         316 $self->_emit_node($document);
40             }
41 157         357 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   218 my $self = shift;
48 166         271 my ($node) = @_;
49 166 100 100     409 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     19 $self->die('YAML_DUMP_ERR_NO_HEADER')
55             if ref($node) eq 'HASH' and keys(%$node) == 0;
56 2 100 66     28 $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         6 $self->headless(1);
60 1         2 return;
61             }
62 162         339 $self->{stream} .= '---';
63             # XXX Consider switching to 1.1 style
64 162 50       371 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   1821 my $self = shift;
74 1499         2304 my $stringify = $self->stringify;
75 1499         2629 my ($class, $type, $node_id) = $self->node_info(\$_[0], $stringify);
76              
77             # Handle typeglobs
78 1499 100       2588 if ($type eq 'GLOB') {
79 4         14 $self->transferred->{$node_id} =
80             YAML::Type::glob->yaml_dump($_[0]);
81 4         11 $self->_prewalk($self->transferred->{$node_id});
82 4         6 return;
83             }
84              
85             # Handle regexps
86 1495 100       2395 if (ref($_[0]) eq 'Regexp') {
87 11         26 return;
88             }
89              
90             # Handle Purity for scalars.
91             # XXX can't find a use case yet. Might be YAGNI.
92 1484 100       2048 if (not ref $_[0]) {
93 950 50       1786 $self->{id_refcnt}{$node_id}++ if $self->purity;
94 950         2048 return;
95             }
96              
97             # Make a copy of original
98 534         570 my $value = $_[0];
99 534         911 ($class, $type, $node_id) = $self->node_info($value, $stringify);
100              
101             # Must be a stringified object.
102 534 100 66     1625 return if (ref($value) and not $type);
103              
104             # Look for things already transferred.
105 532 100       979 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       21 : $self->node_info(\ $self->transferred->{$node_id}, $stringify);
109 11         38 $self->{id_refcnt}{$node_id}++;
110 11         23 return;
111             }
112              
113             # Handle code refs
114 521 100       935 if ($type eq 'CODE') {
115 9         17 $self->transferred->{$node_id} = 'placeholder';
116             YAML::Type::code->yaml_dump(
117             $self->dump_code,
118             $_[0],
119 9         23 $self->transferred->{$node_id}
120             );
121             ($class, $type, $node_id) =
122 9         23 $self->node_info(\ $self->transferred->{$node_id}, $stringify);
123 9         24 $self->{id_refcnt}{$node_id}++;
124 9         23 return;
125             }
126              
127             # Handle blessed things
128 512 100       786 if (defined $class) {
129 31 100       216 if ($value->can('yaml_dump')) {
    100          
130 5         17 $value = $value->yaml_dump;
131             }
132             elsif ($type eq 'SCALAR') {
133 7         22 $self->transferred->{$node_id} = 'placeholder';
134             YAML::Type::blessed->yaml_dump
135 7         24 ($_[0], $self->transferred->{$node_id});
136             ($class, $type, $node_id) =
137 7         25 $self->node_info(\ $self->transferred->{$node_id}, $stringify);
138 7         23 $self->{id_refcnt}{$node_id}++;
139 7         15 return;
140             }
141             else {
142 19         69 $value = YAML::Type::blessed->yaml_dump($value);
143             }
144 24         63 $self->transferred->{$node_id} = $value;
145 24         59 (undef, $type, $node_id) = $self->node_info($value, $stringify);
146             }
147              
148             # Handle YAML Blessed things
149 505         1655 require YAML;
150 505 100       1044 if (defined YAML->global_object()->{blessed_map}{$node_id}) {
151 4         6 $value = YAML->global_object()->{blessed_map}{$node_id};
152 4         7 $self->transferred->{$node_id} = $value;
153 4         9 ($class, $type, $node_id) = $self->node_info($value, $stringify);
154 4         9 $self->_prewalk($value);
155 4         6 return;
156             }
157              
158             # Handle hard refs
159 501 100 100     1576 if ($type eq 'REF' or $type eq 'SCALAR') {
    100          
160 31         81 $value = YAML::Type::ref->yaml_dump($value);
161 31         70 $self->transferred->{$node_id} = $value;
162 31         61 (undef, $type, $node_id) = $self->node_info($value, $stringify);
163             }
164              
165             # Handle ref-to-glob's
166             elsif ($type eq 'GLOB') {
167 2         10 my $ref_ynode = $self->transferred->{$node_id} =
168             YAML::Type::ref->yaml_dump($value);
169              
170 2         7 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         7 $self->transferred->{$node_id} = $glob_ynode;
175 2         9 $self->_prewalk($glob_ynode);
176 2         3 return;
177             }
178              
179             # Increment ref count for node
180 499 100       1462 return if ++($self->{id_refcnt}{$node_id}) > 1;
181              
182             # Keep on walking
183 496 100       798 if ($type eq 'HASH') {
    50          
184             $self->_prewalk($value->{$_})
185 436         452 for keys %{$value};
  436         1849  
186 436         1010 return;
187             }
188             elsif ($type eq 'ARRAY') {
189             $self->_prewalk($_)
190 60         81 for @{$value};
  60         255  
191 60         114 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   1681 my $self = shift;
213 1492         1717 my ($type, $node_id);
214 1492         2534 my $ref = ref($_[0]);
215 1492 100       2083 if ($ref) {
216 536 100       887 if ($ref eq 'Regexp') {
217 11         23 $self->_emit(' !!perl/regexp');
218 11         29 $self->_emit_str("$_[0]");
219 11         29 return;
220             }
221 525         1017 (undef, $type, $node_id) = $self->node_info($_[0], $self->stringify);
222             }
223             else {
224 956   50     2264 $type = $ref || 'SCALAR';
225 956         1753 (undef, undef, $node_id) = $self->node_info(\$_[0], $self->stringify);
226             }
227              
228 1481         2833 my ($ynode, $tag) = ('') x 2;
229 1481         2311 my ($value, $context) = (@_, 0);
230              
231 1481 100       2602 if (defined $self->transferred->{$node_id}) {
    100          
232 92         157 $value = $self->transferred->{$node_id};
233 92         188 $ynode = ynode($value);
234 92 100       160 if (ref $value) {
235 74 50       220 $tag = defined $ynode ? $ynode->tag->short : '';
236 74         154 (undef, $type, $node_id) =
237             $self->node_info($value, $self->stringify);
238             }
239             else {
240 18         38 $ynode = ynode($self->transferred->{$node_id});
241 18 50       77 $tag = defined $ynode ? $ynode->tag->short : '';
242 18         29 $type = 'SCALAR';
243             (undef, undef, $node_id) =
244             $self->node_info(
245 18         40 \ $self->transferred->{$node_id},
246             $self->stringify
247             );
248             }
249             }
250             elsif ($ynode = ynode($value)) {
251 5         15 $tag = $ynode->tag->short;
252             }
253              
254 1481 100       2742 if ($self->use_aliases) {
255 1476   100     5038 $self->{id_refcnt}{$node_id} ||= 0;
256 1476 100       2450 if ($self->{id_refcnt}{$node_id} > 1) {
257 20 100       38 if (defined $self->{id_anchor}{$node_id}) {
258 11         23 $self->{stream} .= ' *' . $self->{id_anchor}{$node_id} . "\n";
259 11         31 return;
260             }
261 9         40 my $anchor = $self->anchor_prefix . $self->{anchor}++;
262 9         18 $self->{stream} .= ' &' . $anchor;
263 9         49 $self->{id_anchor}{$node_id} = $anchor;
264             }
265             }
266              
267 1470 100 100     3146 return $self->_emit_str("$value") # Stringified object
268             if ref($value) and not $type;
269 1468 100 100     3314 return $self->_emit_scalar($value, $tag)
270             if $type eq 'SCALAR' and $tag;
271 1452 100       2545 return $self->_emit_str($value)
272             if $type eq 'SCALAR';
273 500 100       1243 return $self->_emit_mapping($value, $tag, $node_id, $context)
274             if $type eq 'HASH';
275 60 50       189 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   589 my $self = shift;
284 440         738 my ($value, $tag, $node_id, $context) = @_;
285 440 100       656 $self->{stream} .= " !$tag" if $tag;
286              
287             # Sometimes 'keys' fails. Like on a bad tie implementation.
288 440         578 my $empty_hash = not(eval {keys %$value});
  440         868  
289 440 50       644 $self->warn('YAML_EMIT_WARN_KEYS', $@) if $@;
290 440 100       736 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     967 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         34 $self->{stream} .= ' ';
299 20         38 $self->offset->[$self->level+1] = $self->offset->[$self->level] + 2;
300             }
301             else {
302 412         470 $context = 0;
303 412 50 33     811 $self->{stream} .= "\n"
304             unless $self->headless && not($self->headless(0));
305 412         722 $self->offset->[$self->level+1] =
306             $self->offset->[$self->level] + $self->indent_width;
307             }
308              
309 432         634 $self->{level}++;
310 432         496 my @keys;
311 432 100       764 if ($self->sort_keys == 1) {
    50          
    50          
312 431 100       692 if (ynode($value)) {
313 62         145 @keys = keys %$value;
314             }
315             else {
316 369         1517 @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         3 my $i = 1;
325 1         2 my %order = map { ($_, $i++) } @{$self->sort_keys};
  3         9  
  1         3  
326             @keys = sort {
327 1         8 (defined $order{$a} and defined $order{$b})
328 2 50 33     14 ? ($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       1274 if (exists $value->{&VALUE}) {
337 36         99 for (my $i = 0; $i < @keys; $i++) {
338 36 50       110 if ($keys[$i] eq &VALUE) {
339 36         61 splice(@keys, $i, 1);
340 36         75 push @keys, &VALUE;
341 36         52 last;
342             }
343             }
344             }
345              
346 432         694 for my $key (@keys) {
347 1196         2289 $self->_emit_key($key, $context);
348 1196         1228 $context = 0;
349 1196         1584 $self->{stream} .= ':';
350 1196         2498 $self->_emit_node($value->{$key});
351             }
352 432         1028 $self->{level}--;
353             }
354              
355             # A YAML series is akin to a Perl array.
356             sub _emit_sequence {
357 60     60   78 my $self = shift;
358 60         102 my ($value, $tag) = @_;
359 60 100       127 $self->{stream} .= " !$tag" if $tag;
360              
361 60 100       131 return ($self->{stream} .= " []\n") if @$value == 0;
362              
363 56 100 66     123 $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     158 if ($self->inline_series and
      100        
368             @$value <= $self->inline_series and
369 8 100       33 not (scalar grep {ref or /\n/} @$value)
370             ) {
371 2         10 $self->{stream} =~ s/\n\Z/ /;
372 2         4 $self->{stream} .= '[';
373 2         7 for (my $i = 0; $i < @$value; $i++) {
374 5         14 $self->_emit_str($value->[$i], KEY);
375 5 100       9 last if $i == $#{$value};
  5         12  
376 3         7 $self->{stream} .= ', ';
377             }
378 2         4 $self->{stream} .= "]\n";
379 2         6 return;
380             }
381              
382 54         109 $self->offset->[$self->level + 1] =
383             $self->offset->[$self->level] + $self->indent_width;
384 54         79 $self->{level}++;
385 54         103 for my $val (@$value) {
386 133         248 $self->{stream} .= ' ' x $self->offset->[$self->level];
387 133         194 $self->{stream} .= '-';
388 133         320 $self->_emit_node($val, FROMARRAY);
389             }
390 54         132 $self->{level}--;
391             }
392              
393             # Emit a mapping key
394             sub _emit_key {
395 1196     1196   1273 my $self = shift;
396 1196         1704 my ($value, $context) = @_;
397 1196 100       2719 $self->{stream} .= ' ' x $self->offset->[$self->level]
398             unless $context == FROMARRAY;
399 1196         2217 $self->_emit_str($value, KEY);
400             }
401              
402             # Emit a blessed SCALAR
403             sub _emit_scalar {
404 16     16   24 my $self = shift;
405 16         30 my ($value, $tag) = @_;
406 16         39 $self->{stream} .= " !$tag";
407 16         33 $self->_emit_str($value, BLESSED);
408             }
409              
410             sub _emit {
411 4338     4338   4541 my $self = shift;
412 4338         8143 $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   2509 my $self = shift;
419 2182   100     3796 my $type = $_[1] || 0;
420              
421             # Use heuristics to find the best scalar emission style.
422 2182         3290 $self->offset->[$self->level + 1] =
423             $self->offset->[$self->level] + $self->indent_width;
424 2182         2791 $self->{level}++;
425              
426 2182 100       3189 my $sf = $type == KEY ? '' : ' ';
427 2182 100       2844 my $sb = $type == KEY ? '? ' : ' ';
428 2182 100       2793 my $ef = $type == KEY ? '' : "\n";
429 2182         2276 my $eb = "\n";
430              
431 2182         2280 while (1) {
432 2182 100       3755 $self->_emit($sf),
433             $self->_emit_plain($_[0]),
434             $self->_emit($ef), last
435             if not defined $_[0];
436 2178 100       3199 $self->_emit($sf, '=', $ef), last
437             if $_[0] eq VALUE;
438 2141 100       6916 $self->_emit($sf),
439             $self->_emit_double($_[0]),
440             $self->_emit($ef), last
441             if $_[0] =~ /$ESCAPE_CHAR/;
442 2140 100       3801 if ($_[0] =~ /\n/) {
443 28 100       117 $self->_emit($sb),
444             $self->_emit_block($LIT_CHAR, $_[0]),
445             $self->_emit($eb), last
446             if $self->use_block;
447 27 50       89 Carp::cluck "[YAML] \$UseFold is no longer supported"
448             if $self->use_fold;
449 27 100       75 $self->_emit($sf),
450             $self->_emit_double($_[0]),
451             $self->_emit($ef), last
452             if length $_[0] <= 30;
453 20 100       85 $self->_emit($sf),
454             $self->_emit_double($_[0]),
455             $self->_emit($ef), last
456             if $_[0] !~ /\n\s*\S/;
457 18         37 $self->_emit($sb),
458             $self->_emit_block($LIT_CHAR, $_[0]),
459             $self->_emit($eb), last;
460             }
461 2112 100       3203 $self->_emit($sf),
462             $self->_emit_number($_[0]),
463             $self->_emit($ef), last
464             if $self->is_literal_number($_[0]);
465 2048 100       3196 $self->_emit($sf),
466             $self->_emit_plain($_[0]),
467             $self->_emit($ef), last
468             if $self->is_valid_plain($_[0]);
469 49 100       129 $self->_emit($sf),
470             $self->_emit_double($_[0]),
471             $self->_emit($ef), last
472             if $_[0] =~ /'/;
473 48         110 $self->_emit($sf),
474             $self->_emit_single($_[0]),
475             $self->_emit($ef);
476 48         63 last;
477             }
478              
479 2182         2723 $self->{level}--;
480              
481 2182         4069 return;
482             }
483              
484             sub is_literal_number {
485 2115     2115 0 2860 my $self = shift;
486             # Stolen from JSON::Tiny
487 2115   66     9083 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   85 my $self = shift;
493 64         115 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 2186 my $self = shift;
499 2048 100       3455 return 0 unless length $_[0];
500 2042 100 100     3272 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       4275 return 0 if $_[0] =~ /^[\s\{\[\~\`\'\"\!\@\#\>\|\%\&\?\*\^]/;
503 2019 100       3335 return 0 if $_[0] =~ /[\{\[\]\},]/;
504 2010 100       3177 return 0 if $_[0] =~ /[:\-\?]\s/;
505 2006 100       2823 return 0 if $_[0] =~ /\s#/;
506 2005 50       2883 return 0 if $_[0] =~ /\:(\s|$)/;
507 2005 100       3506 return 0 if $_[0] =~ /[\s\|\>]$/;
508 2001 100       2836 return 0 if $_[0] eq '-';
509 2000 100       2621 return 0 if $_[0] eq '=';
510 1999         3871 return 1;
511             }
512              
513             sub _emit_block {
514 19     19   28 my $self = shift;
515 19         37 my ($indicator, $value) = @_;
516 19         31 $self->{stream} .= $indicator;
517 19         387 $value =~ /(\n*)\Z/;
518 19 100       75 my $chomp = length $1 ? (length $1 > 1) ? '+' : '' : '-';
    100          
519 19 50       51 $value = '~' if not defined $value;
520 19         31 $self->{stream} .= $chomp;
521 19 50       47 $self->{stream} .= $self->indent_width if $value =~ /^\s/;
522 19         45 $self->{stream} .= $self->indent($value);
523             }
524              
525             # Plain means that the scalar is unquoted.
526             sub _emit_plain {
527 2067     2067   2185 my $self = shift;
528 2067 100       4099 $self->{stream} .= defined $_[0] ? $_[0] : '~';
529             }
530              
531             # Double quoting is for single lined escaped strings.
532             sub _emit_double {
533 11     11   16 my $self = shift;
534 11         27 (my $escaped = $self->escape($_[0])) =~ s/"/\\"/g;
535 11         1277 $self->{stream} .= qq{"$escaped"};
536             }
537              
538             # Single quoting is for single lined unescaped strings.
539             sub _emit_single {
540 48     48   67 my $self = shift;
541 48         111 my $item = shift;
542 48         126 $item =~ s{'}{''}g;
543 48         155 $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 69 my $self = shift;
553 19         36 my ($text) = @_;
554 19 50       40 return $text unless length $text;
555 19         83 $text =~ s/\n\Z//;
556 19         56 my $indent = ' ' x $self->offset->[$self->level];
557 19         121 $text =~ s/^/$indent/gm;
558 19         42 $text = "\n$text";
559 19         65 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 14 my $self = shift;
572 11         26 my ($text) = @_;
573 11         55 $text =~ s/\\/\\\\/g;
574 11         1509 $text =~ s/([\x00-\x1f])/$escapes[ord($1)]/ge;
  17         60  
575 11         8826 return $text;
576             }
577              
578             1;