File Coverage

inc/YAML/Dumper.pm
Criterion Covered Total %
statement 132 294 44.9
branch 49 164 29.8
condition 14 51 27.4
subroutine 19 27 70.3
pod 0 4 0.0
total 214 540 39.6


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