4 extends 'YAML::Dumper::Base';
6 use YAML::Dumper::Base;
12 use constant KEY => 3;
13 use constant BLESSED => 4;
14 use constant FROMARRAY => 5;
15 use constant VALUE => "\x07YAML\x07VALUE\x07";
17 # Common YAML character sets
18 my $ESCAPE_CHAR = '[\\x00-\\x08\\x0b-\\x0d\\x0e-\\x1f]';
21 #==============================================================================
22 # OO version of Dump. YAML->new->dump($foo);
27 for my $document (@_) {
29 $self->transferred({});
34 $self->offset->[0] = 0 - $self->indent_width;
35 $self->_prewalk($document);
36 $self->_emit_header($document);
37 $self->_emit_node($document);
42 # Every YAML document in the stream must begin with a YAML header, unless
43 # there is only a single document and the user requests "no header".
47 if (not $self->use_header and
50 $self->die('YAML_DUMP_ERR_NO_HEADER')
51 unless ref($node) =~ /^(HASH|ARRAY)$/;
52 $self->die('YAML_DUMP_ERR_NO_HEADER')
53 if ref($node) eq 'HASH' and keys(%$node) == 0;
54 $self->die('YAML_DUMP_ERR_NO_HEADER')
55 if ref($node) eq 'ARRAY' and @$node == 0;
56 # XXX Also croak if aliased, blessed, or ynode
60 $self->{stream} .= '---';
61 # XXX Consider switching to 1.1 style
62 if ($self->use_version) {
63 # $self->{stream} .= " #YAML:1.0";
67 # Walk the tree to be dumped and keep track of its reference counts.
68 # This function is where the Dumper does all its work. All type
69 # transfers happen here.
72 my $stringify = $self->stringify;
73 my ($class, $type, $node_id) = $self->node_info(\$_[0], $stringify);
76 if ($type eq 'GLOB') {
77 $self->transferred->{$node_id} =
78 YAML::Type::glob->yaml_dump($_[0]);
79 $self->_prewalk($self->transferred->{$node_id});
84 if (ref($_[0]) eq 'Regexp') {
88 # Handle Purity for scalars.
89 # XXX can't find a use case yet. Might be YAGNI.
91 $self->{id_refcnt}{$node_id}++ if $self->purity;
95 # Make a copy of original
97 ($class, $type, $node_id) = $self->node_info($value, $stringify);
99 # Must be a stringified object.
100 return if (ref($value) and not $type);
102 # Look for things already transferred.
103 if ($self->transferred->{$node_id}) {
104 (undef, undef, $node_id) = (ref $self->transferred->{$node_id})
105 ? $self->node_info($self->transferred->{$node_id}, $stringify)
106 : $self->node_info(\ $self->transferred->{$node_id}, $stringify);
107 $self->{id_refcnt}{$node_id}++;
112 if ($type eq 'CODE') {
113 $self->transferred->{$node_id} = 'placeholder';
114 YAML::Type::code->yaml_dump(
117 $self->transferred->{$node_id}
119 ($class, $type, $node_id) =
120 $self->node_info(\ $self->transferred->{$node_id}, $stringify);
121 $self->{id_refcnt}{$node_id}++;
125 # Handle blessed things
126 if (defined $class) {
127 if ($value->can('yaml_dump')) {
128 $value = $value->yaml_dump;
130 elsif ($type eq 'SCALAR') {
131 $self->transferred->{$node_id} = 'placeholder';
132 YAML::Type::blessed->yaml_dump
133 ($_[0], $self->transferred->{$node_id});
134 ($class, $type, $node_id) =
135 $self->node_info(\ $self->transferred->{$node_id}, $stringify);
136 $self->{id_refcnt}{$node_id}++;
140 $value = YAML::Type::blessed->yaml_dump($value);
142 $self->transferred->{$node_id} = $value;
143 (undef, $type, $node_id) = $self->node_info($value, $stringify);
146 # Handle YAML Blessed things
148 if (defined YAML->global_object()->{blessed_map}{$node_id}) {
149 $value = YAML->global_object()->{blessed_map}{$node_id};
150 $self->transferred->{$node_id} = $value;
151 ($class, $type, $node_id) = $self->node_info($value, $stringify);
152 $self->_prewalk($value);
157 if ($type eq 'REF' or $type eq 'SCALAR') {
158 $value = YAML::Type::ref->yaml_dump($value);
159 $self->transferred->{$node_id} = $value;
160 (undef, $type, $node_id) = $self->node_info($value, $stringify);
163 # Handle ref-to-glob's
164 elsif ($type eq 'GLOB') {
165 my $ref_ynode = $self->transferred->{$node_id} =
166 YAML::Type::ref->yaml_dump($value);
168 my $glob_ynode = $ref_ynode->{&VALUE} =
169 YAML::Type::glob->yaml_dump($$value);
171 (undef, undef, $node_id) = $self->node_info($glob_ynode, $stringify);
172 $self->transferred->{$node_id} = $glob_ynode;
173 $self->_prewalk($glob_ynode);
177 # Increment ref count for node
178 return if ++($self->{id_refcnt}{$node_id}) > 1;
181 if ($type eq 'HASH') {
182 $self->_prewalk($value->{$_})
186 elsif ($type eq 'ARRAY') {
192 # Unknown type. Need to know about it.
193 $self->warn(<<"...");
194 YAML::Dumper can't handle dumping this type of data.
195 Please report this to the author.
207 # Every data element and sub data element is a node.
208 # Everything emitted goes through this function.
211 my ($type, $node_id);
212 my $ref = ref($_[0]);
214 if ($ref eq 'Regexp') {
215 $self->_emit(' !!perl/regexp');
216 $self->_emit_str("$_[0]");
219 (undef, $type, $node_id) = $self->node_info($_[0], $self->stringify);
222 $type = $ref || 'SCALAR';
223 (undef, undef, $node_id) = $self->node_info(\$_[0], $self->stringify);
226 my ($ynode, $tag) = ('') x 2;
227 my ($value, $context) = (@_, 0);
229 if (defined $self->transferred->{$node_id}) {
230 $value = $self->transferred->{$node_id};
231 $ynode = ynode($value);
233 $tag = defined $ynode ? $ynode->tag->short : '';
234 (undef, $type, $node_id) =
235 $self->node_info($value, $self->stringify);
238 $ynode = ynode($self->transferred->{$node_id});
239 $tag = defined $ynode ? $ynode->tag->short : '';
241 (undef, undef, $node_id) =
243 \ $self->transferred->{$node_id},
248 elsif ($ynode = ynode($value)) {
249 $tag = $ynode->tag->short;
252 if ($self->use_aliases) {
253 $self->{id_refcnt}{$node_id} ||= 0;
254 if ($self->{id_refcnt}{$node_id} > 1) {
255 if (defined $self->{id_anchor}{$node_id}) {
256 $self->{stream} .= ' *' . $self->{id_anchor}{$node_id} . "\n";
259 my $anchor = $self->anchor_prefix . $self->{anchor}++;
260 $self->{stream} .= ' &' . $anchor;
261 $self->{id_anchor}{$node_id} = $anchor;
265 return $self->_emit_str("$value") # Stringified object
266 if ref($value) and not $type;
267 return $self->_emit_scalar($value, $tag)
268 if $type eq 'SCALAR' and $tag;
269 return $self->_emit_str($value)
270 if $type eq 'SCALAR';
271 return $self->_emit_mapping($value, $tag, $node_id, $context)
273 return $self->_emit_sequence($value, $tag)
275 $self->warn('YAML_DUMP_WARN_BAD_NODE_TYPE', $type);
276 return $self->_emit_str("$value");
279 # A YAML mapping is akin to a Perl hash.
282 my ($value, $tag, $node_id, $context) = @_;
283 $self->{stream} .= " !$tag" if $tag;
285 # Sometimes 'keys' fails. Like on a bad tie implementation.
286 my $empty_hash = not(eval {keys %$value});
287 $self->warn('YAML_EMIT_WARN_KEYS', $@) if $@;
288 return ($self->{stream} .= " {}\n") if $empty_hash;
290 # If CompressSeries is on (default) and legal is this context, then
291 # use it and make the indent level be 2 for this node.
292 if ($context == FROMARRAY and
293 $self->compress_series and
294 not (defined $self->{id_anchor}{$node_id} or $tag or $empty_hash)
296 $self->{stream} .= ' ';
297 $self->offset->[$self->level+1] = $self->offset->[$self->level] + 2;
301 $self->{stream} .= "\n"
302 unless $self->headless && not($self->headless(0));
303 $self->offset->[$self->level+1] =
304 $self->offset->[$self->level] + $self->indent_width;
309 if ($self->sort_keys == 1) {
311 @keys = keys %$value;
314 @keys = sort keys %$value;
317 elsif ($self->sort_keys == 2) {
318 @keys = sort keys %$value;
320 # XXX This is hackish but sometimes handy. Not sure whether to leave it in.
321 elsif (ref($self->sort_keys) eq 'ARRAY') {
323 my %order = map { ($_, $i++) } @{$self->sort_keys};
325 (defined $order{$a} and defined $order{$b})
326 ? ($order{$a} <=> $order{$b})
331 @keys = keys %$value;
333 # Force the YAML::VALUE ('=') key to sort last.
334 if (exists $value->{&VALUE}) {
335 for (my $i = 0; $i < @keys; $i++) {
336 if ($keys[$i] eq &VALUE) {
337 splice(@keys, $i, 1);
344 for my $key (@keys) {
345 $self->_emit_key($key, $context);
347 $self->{stream} .= ':';
348 $self->_emit_node($value->{$key});
353 # A YAML series is akin to a Perl array.
356 my ($value, $tag) = @_;
357 $self->{stream} .= " !$tag" if $tag;
359 return ($self->{stream} .= " []\n") if @$value == 0;
361 $self->{stream} .= "\n"
362 unless $self->headless && not($self->headless(0));
364 # XXX Really crufty feature. Better implemented by ynodes.
365 if ($self->inline_series and
366 @$value <= $self->inline_series and
367 not (scalar grep {ref or /\n/} @$value)
369 $self->{stream} =~ s/\n\Z/ /;
370 $self->{stream} .= '[';
371 for (my $i = 0; $i < @$value; $i++) {
372 $self->_emit_str($value->[$i], KEY);
373 last if $i == $#{$value};
374 $self->{stream} .= ', ';
376 $self->{stream} .= "]\n";
380 $self->offset->[$self->level + 1] =
381 $self->offset->[$self->level] + $self->indent_width;
383 for my $val (@$value) {
384 $self->{stream} .= ' ' x $self->offset->[$self->level];
385 $self->{stream} .= '-';
386 $self->_emit_node($val, FROMARRAY);
394 my ($value, $context) = @_;
395 $self->{stream} .= ' ' x $self->offset->[$self->level]
396 unless $context == FROMARRAY;
397 $self->_emit_str($value, KEY);
400 # Emit a blessed SCALAR
403 my ($value, $tag) = @_;
404 $self->{stream} .= " !$tag";
405 $self->_emit_str($value, BLESSED);
410 $self->{stream} .= join '', @_;
413 # Emit a string value. YAML has many scalar styles. This routine attempts to
414 # guess the best style for the text.
417 my $type = $_[1] || 0;
419 # Use heuristics to find the best scalar emission style.
420 $self->offset->[$self->level + 1] =
421 $self->offset->[$self->level] + $self->indent_width;
424 my $sf = $type == KEY ? '' : ' ';
425 my $sb = $type == KEY ? '? ' : ' ';
426 my $ef = $type == KEY ? '' : "\n";
431 $self->_emit_plain($_[0]),
432 $self->_emit($ef), last
433 if not defined $_[0];
434 $self->_emit($sf, '=', $ef), last
437 $self->_emit_double($_[0]),
438 $self->_emit($ef), last
439 if $_[0] =~ /$ESCAPE_CHAR/;
442 $self->_emit_block($LIT_CHAR, $_[0]),
443 $self->_emit($eb), last
445 Carp::cluck "[YAML] \$UseFold is no longer supported"
448 $self->_emit_double($_[0]),
449 $self->_emit($ef), last
450 if length $_[0] <= 30;
452 $self->_emit_double($_[0]),
453 $self->_emit($ef), last
454 if $_[0] !~ /\n\s*\S/;
456 $self->_emit_block($LIT_CHAR, $_[0]),
457 $self->_emit($eb), last;
460 $self->_emit_number($_[0]),
461 $self->_emit($ef), last
462 if $self->is_literal_number($_[0]);
464 $self->_emit_plain($_[0]),
465 $self->_emit($ef), last
466 if $self->is_valid_plain($_[0]);
468 $self->_emit_double($_[0]),
469 $self->_emit($ef), last
472 $self->_emit_single($_[0]),
482 sub is_literal_number {
484 # Stolen from JSON::Tiny
485 return B::svref_2object(\$_[0])->FLAGS & (B::SVp_IOK | B::SVp_NOK)
486 && 0 + $_[0] eq $_[0];
491 return $self->_emit_plain($_[0]);
494 # Check whether or not a scalar should be emitted as an plain scalar.
497 return 0 unless length $_[0];
498 return 0 if $self->quote_numeric_strings and Scalar::Util::looks_like_number($_[0]);
499 # refer to YAML::Loader::parse_inline_simple()
500 return 0 if $_[0] =~ /^[\s\{\[\~\`\'\"\!\@\#\>\|\%\&\?\*\^]/;
501 return 0 if $_[0] =~ /[\{\[\]\},]/;
502 return 0 if $_[0] =~ /[:\-\?]\s/;
503 return 0 if $_[0] =~ /\s#/;
504 return 0 if $_[0] =~ /\:(\s|$)/;
505 return 0 if $_[0] =~ /[\s\|\>]$/;
506 return 0 if $_[0] eq '-';
512 my ($indicator, $value) = @_;
513 $self->{stream} .= $indicator;
515 my $chomp = length $1 ? (length $1 > 1) ? '+' : '' : '-';
516 $value = '~' if not defined $value;
517 $self->{stream} .= $chomp;
518 $self->{stream} .= $self->indent_width if $value =~ /^\s/;
519 $self->{stream} .= $self->indent($value);
522 # Plain means that the scalar is unquoted.
525 $self->{stream} .= defined $_[0] ? $_[0] : '~';
528 # Double quoting is for single lined escaped strings.
531 (my $escaped = $self->escape($_[0])) =~ s/"/\\"/g;
532 $self->{stream} .= qq{"$escaped"};
535 # Single quoting is for single lined unescaped strings.
540 $self->{stream} .= "'$item'";
543 #==============================================================================
544 # Utility subroutines.
545 #==============================================================================
547 # Indent a scalar to the current indentation level.
551 return $text unless length $text;
553 my $indent = ' ' x $self->offset->[$self->level];
554 $text =~ s/^/$indent/gm;
559 # Escapes for unprintable characters
560 my @escapes = qw(\0 \x01 \x02 \x03 \x04 \x05 \x06 \a
561 \x08 \t \n \v \f \r \x0e \x0f
562 \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17
563 \x18 \x19 \x1a \e \x1c \x1d \x1e \x1f
566 # Escape the unprintable characters
570 $text =~ s/\\/\\\\/g;
571 $text =~ s/([\x00-\x1f])/$escapes[ord($1)]/ge;