| File: | local/lib/perl5/Clone/PP.pm |
| Coverage: | 76.7% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | package Clone::PP; | |||||
| 2 | ||||||
| 3 | 2 2 | 16 2 | use 5.006; | |||
| 4 | 2 2 2 | 4 2 22 | use strict; | |||
| 5 | 2 2 2 | 4 1 24 | use warnings; | |||
| 6 | 2 2 2 | 4 2 45 | use vars qw($VERSION @EXPORT_OK); | |||
| 7 | 2 2 2 | 3 1 66 | use Exporter; | |||
| 8 | ||||||
| 9 | $VERSION = 1.05; | |||||
| 10 | ||||||
| 11 | @EXPORT_OK = qw( clone ); | |||||
| 12 | 4 | 508 | sub import { goto &Exporter::import } # lazy Exporter | |||
| 13 | ||||||
| 14 | # These methods can be temporarily overridden to work with a given class. | |||||
| 15 | 2 2 2 | 4 2 40 | use vars qw( $CloneSelfMethod $CloneInitMethod ); | |||
| 16 | $CloneSelfMethod ||= 'clone_self'; | |||||
| 17 | $CloneInitMethod ||= 'clone_init'; | |||||
| 18 | ||||||
| 19 | # Used to detect looped networks and avoid infinite recursion. | |||||
| 20 | 2 2 2 | 3 1 347 | use vars qw( %CloneCache ); | |||
| 21 | ||||||
| 22 | # Generic cloning function | |||||
| 23 | sub clone { | |||||
| 24 | 1072 | 471 | my $source = shift; | |||
| 25 | ||||||
| 26 | 1072 | 813 | return undef if not defined($source); | |||
| 27 | ||||||
| 28 | # Optional depth limit: after a given number of levels, do shallow copy. | |||||
| 29 | 1072 | 460 | my $depth = shift; | |||
| 30 | 1072 | 969 | return $source if ( defined $depth and $depth -- < 1 ); | |||
| 31 | ||||||
| 32 | # Maintain a shared cache during recursive calls, then clear it at the end. | |||||
| 33 | 1072 | 918 | local %CloneCache = ( undef => undef ) unless ( exists $CloneCache{undef} ); | |||
| 34 | ||||||
| 35 | 1072 | 977 | return $CloneCache{ $source } if ( defined $CloneCache{ $source } ); | |||
| 36 | ||||||
| 37 | # Non-reference values are copied shallowly | |||||
| 38 | 1072 | 920 | my $ref_type = ref $source or return $source; | |||
| 39 | ||||||
| 40 | # Extract both the structure type and the class name of referent | |||||
| 41 | 1052 | 412 | my $class_name; | |||
| 42 | 1052 | 5451 | if ( "$source" =~ /^\Q$ref_type\E\=([A-Z]+)\(0x[0-9a-f]+\)$/ ) { | |||
| 43 | 0 | 0 | $class_name = $ref_type; | |||
| 44 | 0 | 0 | $ref_type = $1; | |||
| 45 | # Some objects would prefer to clone themselves; check for clone_self(). | |||||
| 46 | 0 | 0 | return $CloneCache{ $source } = $source->$CloneSelfMethod() | |||
| 47 | if $source->can($CloneSelfMethod); | |||||
| 48 | } | |||||
| 49 | ||||||
| 50 | # To make a copy: | |||||
| 51 | # - Prepare a reference to the same type of structure; | |||||
| 52 | # - Store it in the cache, to avoid looping if it refers to itself; | |||||
| 53 | # - Tie in to the same class as the original, if it was tied; | |||||
| 54 | # - Assign a value to the reference by cloning each item in the original; | |||||
| 55 | ||||||
| 56 | 1052 | 508 | my $copy; | |||
| 57 | 1052 | 1591 | if ($ref_type eq 'HASH') { | |||
| 58 | 120 | 126 | $CloneCache{ $source } = $copy = {}; | |||
| 59 | 120 0 | 114 0 | if ( my $tied = tied( %$source ) ) { tie %$copy, ref $tied } | |||
| 60 | 120 3472 | 247 2888 | %$copy = map { ! ref($_) ? $_ : clone($_, $depth) } %$source; | |||
| 61 | } elsif ($ref_type eq 'ARRAY') { | |||||
| 62 | 438 | 417 | $CloneCache{ $source } = $copy = []; | |||
| 63 | 438 0 | 362 0 | if ( my $tied = tied( @$source ) ) { tie @$copy, ref $tied } | |||
| 64 | 438 474 | 222 446 | @$copy = map { ! ref($_) ? $_ : clone($_, $depth) } @$source; | |||
| 65 | } elsif ($ref_type eq 'REF' or $ref_type eq 'SCALAR') { | |||||
| 66 | 20 | 29 | $CloneCache{ $source } = $copy = \( my $var = "" ); | |||
| 67 | 20 0 | 31 0 | if ( my $tied = tied( $$source ) ) { tie $$copy, ref $tied } | |||
| 68 | 20 | 20 | $$copy = clone($$source, $depth); | |||
| 69 | } else { | |||||
| 70 | # Shallow copy anything else; this handles a reference to code, glob, regex | |||||
| 71 | 474 | 394 | $CloneCache{ $source } = $copy = $source; | |||
| 72 | } | |||||
| 73 | ||||||
| 74 | # - Bless it into the same class as the original, if it was blessed; | |||||
| 75 | # - If it has a post-cloning initialization method, call it. | |||||
| 76 | 1052 | 887 | if ( $class_name ) { | |||
| 77 | 0 | 0 | bless $copy, $class_name; | |||
| 78 | 0 | 0 | $copy->$CloneInitMethod() if $copy->can($CloneInitMethod); | |||
| 79 | } | |||||
| 80 | ||||||
| 81 | 1052 | 1054 | return $copy; | |||
| 82 | } | |||||
| 83 | ||||||
| 84 | 1; | |||||
| 85 | ||||||