sub __merge { my ($old, $new) = @_; my @stack; push @stack, { 'ptr' => '', 'old' => $old, 'new' => $new }; while (@stack) { my $el = shift @stack; my ($oref, $nref) = (ref($el->{'old'}), ref($el->{'new'})); if ($oref eq $nref) { unless ($nref) { eval "\$old$el->{'ptr'} = \$el->{'new'}"; $logs->enqueue($@) if $@; } # we simply assume that there will be no scalar references, and we # also tacitly assume all blessed references being hashrefs! # for arrays things run rather unspectacularly: # iterate over the old list, push the new value on the stack in case it's # a reference, overwrite the old value with that from the new list. # if the old list is shorter than the new one perform the same with all overstanding elements. elsif ($oref =~ /ARRAY/) { my $i; for ($i = 0; $i < @{$el->{'old'}}; $i++) { if (ref $el->{'new'}->[$i]) { push @stack, { 'ptr' => $el->{'ptr'}."->[$i]", 'old' => $el->{'old'}->[$i], 'new' => $el->{'new'}->[$i] }; } if (exists $el->{'new'}->[$i]) { eval "\$old$el->{'ptr'}"."->[$i] = \$el->{'new'}->[$i]"; $logs->enqueue($@) if $@; } else { splice @{$el->{'old'}}, $i, 1; $i--; } } while (exists $el->{'new'}->[$i]) { if (ref $el->{'new'}->[$i]) { push @stack, { 'ptr' => $el->{'ptr'}."->[$i]", 'old' => $el->{'old'}->[$i], 'new' => $el->{'new'}->[$i] }; } eval "\$old$el->{'ptr'}"."->[$i] = \$el->{'new'}->[$i]"; $logs->enqueue($@) if $@; $i++; } } # with hashes things are a bit more complicated, since we know nothing # about the keys in each table. thus we have to delete key-value pairs from # the old tree in case they don't exist in the new tree. on the other hand, if # a key in the new tree exists that doesn't exist in the old tree it has to be # 'installed' there too. else { my @okeys = sort keys(%{$el->{'old'}}); my @nkeys = sort keys(%{$el->{'new'}}); while (@okeys) { my $key = shift @okeys; # we don't now anything about @nkeys, hence we can't rely on the # order in which its elements occur: if (exists $el->{'new'}->{$key}) { for (my $i = 0; $i < @nkeys; $i++) { if ($nkeys[$i] eq $key) { splice @nkeys, $i, 1; last; } } } if (ref $el->{'new'}->{$key}) { push @stack, { 'ptr' => $el->{'ptr'}."->{'$key'}", 'old' => $el->{'old'}->{$key}, 'new' => $el->{'new'}->{$key} }; } unless (exists $el->{'new'}->{$key}) { delete $el->{'old'}->{$key}; } else { eval "\$old$el->{'ptr'}"."->{'$key'} = \$el->{'new'}->{'$key'}"; $logs->enqueue($@) if $@; } } while (@nkeys) { my $key = shift @nkeys; if (ref $el->{'new'}->{$key}) { push @stack, { 'ptr' => $el->{'ptr'}."->{'$key'}", 'old' => undef, 'new' => $el->{'new'}->{$key} }; } eval "\$old$el->{'ptr'}"."->{'$key'} = \$el->{'new'}->{'$key'}"; $logs->enqueue($@) if $@; } } } else { eval "\$old$el->{'ptr'} = \$el->{'new'}"; $logs->enqueue($@) if $@; } } $old; }