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;