| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | ##-*- Mode: CPerl -*- | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | ##====================================================================== | 
| 4 |  |  |  |  |  |  | package DDC::PP::Object; | 
| 5 | 20 |  |  | 20 |  | 13100 | use JSON; | 
|  | 20 |  |  |  |  | 250644 |  | 
|  | 20 |  |  |  |  | 120 |  | 
| 6 | 20 |  |  | 20 |  | 3130 | use Carp qw(carp confess); | 
|  | 20 |  |  |  |  | 44 |  | 
|  | 20 |  |  |  |  | 1024 |  | 
| 7 | 20 |  |  | 20 |  | 120 | use strict; | 
|  | 20 |  |  |  |  | 41 |  | 
|  | 20 |  |  |  |  | 1053 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | ##====================================================================== | 
| 10 |  |  |  |  |  |  | ## debugging & wrapping utilities | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | ## undef = $CLASS->nomethod($method_name) | 
| 13 |  |  |  |  |  |  | ##  + defines a method $CLASS::$method_name which just throws an error | 
| 14 |  |  |  |  |  |  | sub nomethod { | 
| 15 | 100 |  |  | 100 | 0 | 231 | my ($class,$method_name) = @_; | 
| 16 | 100 |  |  |  |  | 280 | my $method = "${class}::${method_name}"; | 
| 17 | 20 |  |  | 20 |  | 149 | no strict "refs"; | 
|  | 20 |  |  |  |  | 43 |  | 
|  | 20 |  |  |  |  | 2095 |  | 
| 18 |  |  |  |  |  |  | *$method = sub { | 
| 19 | 0 |  |  | 0 |  | 0 | confess("${method}(): method not implemented"); | 
| 20 | 100 |  |  |  |  | 744 | }; | 
| 21 |  |  |  |  |  |  | } | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | ## undef = $CLASS->defprop($property) | 
| 24 |  |  |  |  |  |  | ##  + defines $CLASS::get$Property and $CLASS::set$Property methods | 
| 25 |  |  |  |  |  |  | sub defprop { | 
| 26 | 1200 |  |  | 1200 | 0 | 2499 | my ($class,$prop)=@_; | 
| 27 | 1200 |  |  |  |  | 2846 | my $getmethod = "${class}::get".ucfirst($prop); | 
| 28 | 1200 |  |  |  |  | 2129 | my $setmethod = "${class}::set".ucfirst($prop); | 
| 29 | 20 |  |  | 20 |  | 124 | no strict 'refs'; | 
|  | 20 |  |  |  |  | 46 |  | 
|  | 20 |  |  |  |  | 2538 |  | 
| 30 | 1200 |  |  | 2362 |  | 7591 | *$getmethod = sub { return $_[0]{$prop}; }; | 
|  | 2362 |  |  |  |  | 14719 |  | 
| 31 | 1200 |  |  | 638 |  | 7545 | *$setmethod = sub { return $_[0]{$prop}=$_[1]; }; | 
|  | 638 |  |  |  |  | 7202 |  | 
| 32 |  |  |  |  |  |  | } | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | ## undef = $CLASS->defalias($propertyFrom,$propertyTo, $doGet=1, $doSet=1) | 
| 35 |  |  |  |  |  |  | ##  + aliases $CLASS::get$PropertyFrom and $CLASS::set$PropertyFrom methods to $CLASS::get$PropertyTo etc. | 
| 36 |  |  |  |  |  |  | sub defalias { | 
| 37 | 160 |  |  | 160 | 0 | 412 | my ($class,$pfrom,$pto, $doGet,$doSet)=@_; | 
| 38 | 160 |  |  |  |  | 418 | my $getmethod = "${class}::get".ucfirst($pfrom); | 
| 39 | 160 |  |  |  |  | 309 | my $setmethod = "${class}::set".ucfirst($pfrom); | 
| 40 | 20 |  |  | 20 |  | 125 | no strict 'refs'; | 
|  | 20 |  |  |  |  | 41 |  | 
|  | 20 |  |  |  |  | 17210 |  | 
| 41 | 160 | 50 | 33 |  |  | 723 | *$getmethod = $class->can('get'.ucfirst($pto)) if (!defined($doGet) || $doGet); | 
| 42 | 160 | 50 | 33 |  |  | 2041 | *$setmethod = $class->can('set'.ucfirst($pto)) if (!defined($doSet) || $doSet); | 
| 43 |  |  |  |  |  |  | } | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | ##====================================================================== | 
| 46 |  |  |  |  |  |  | ## xs replacements | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | sub new { | 
| 49 | 863 |  |  | 863 | 0 | 1626 | my $that = shift; | 
| 50 | 863 |  | 33 |  |  | 7151 | return bless { @_ }, ref($that)||$that; | 
| 51 |  |  |  |  |  |  | } | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | __PACKAGE__->nomethod('DumpTree'); | 
| 54 |  |  |  |  |  |  | __PACKAGE__->nomethod('refcnt'); | 
| 55 |  |  |  |  |  |  | __PACKAGE__->nomethod('self'); | 
| 56 |  |  |  |  |  |  | __PACKAGE__->nomethod('free'); | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | #__PACKAGE__->nomethod('Children'); | 
| 59 |  |  |  |  |  |  | #  + override this if order is important (e.g. for DiaCollo CQWith, CQAnd, etc.) | 
| 60 |  |  |  |  |  |  | sub Children { | 
| 61 | 8 | 50 |  | 8 | 0 | 31 | return UNIVERSAL::isa($_[0],'HASH') ? [grep {UNIVERSAL::isa($_,'DDC::PP::Object')} values %{$_[0]}] : []; | 
|  | 40 |  |  |  |  | 98 |  | 
|  | 8 |  |  |  |  | 26 |  | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | #__PACKAGE__->nomethod('Descendants'); | 
| 65 |  |  |  |  |  |  | sub Descendants { | 
| 66 | 2 |  |  | 2 | 0 | 29 | my @stack = (shift); | 
| 67 | 2 |  |  |  |  | 6 | my %visited = qw(); | 
| 68 | 2 |  |  |  |  | 6 | my @kids    = qw(); | 
| 69 | 2 |  |  |  |  | 3 | my ($obj); | 
| 70 | 2 |  |  |  |  | 9 | while (@stack) { | 
| 71 | 14 |  |  |  |  | 26 | $obj = shift(@stack); | 
| 72 | 14 | 50 |  |  |  | 52 | next if (exists $visited{$obj}); | 
| 73 | 14 |  |  |  |  | 28 | push(@kids,$obj); | 
| 74 | 14 |  |  |  |  | 29 | $visited{$obj} = undef; | 
| 75 | 14 | 50 |  |  |  | 33 | unshift(@stack, @{$obj->Children}) if (ref($obj)); | 
|  | 14 |  |  |  |  | 37 |  | 
| 76 |  |  |  |  |  |  | } | 
| 77 | 2 |  |  |  |  | 13 | return \@kids; | 
| 78 |  |  |  |  |  |  | } | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | #__PACKAGE__->nomethod('DisownChildren'); | 
| 81 |  |  |  |  |  |  | sub DisownChildren { | 
| 82 | 0 |  |  | 0 | 0 | 0 | my $obj = shift; | 
| 83 | 0 | 0 |  |  |  | 0 | return if (!ref($obj)); | 
| 84 | 0 |  |  |  |  | 0 | delete @$obj{$obj->members}; | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | #__PACKAGE__->nomethod('toString'); | 
| 88 |  |  |  |  |  |  | sub toString { | 
| 89 | 0 |  |  | 0 | 0 | 0 | return "$_[0]"; | 
| 90 |  |  |  |  |  |  | } | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | sub toJson { | 
| 93 | 156 |  |  | 156 | 0 | 1832 | return JSON::to_json( $_[0], {utf8=>1,pretty=>0,canonical=>1,allow_blessed=>1,convert_blessed=>1} ); | 
| 94 |  |  |  |  |  |  | } | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | ##-- json utils | 
| 97 |  |  |  |  |  |  | sub jsonClass { | 
| 98 | 196 |  | 33 | 196 | 0 | 940 | (my $class = ref($_[0]) || $_[0]) =~ s/^DDC::PP:://; | 
| 99 | 196 |  |  |  |  | 663 | return $class; | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | ##====================================================================== | 
| 104 |  |  |  |  |  |  | ## Traversal | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | ##-------------------------------------------------------------- | 
| 107 |  |  |  |  |  |  | ## $obj = $obj->mapTraverse(\&CODE) | 
| 108 |  |  |  |  |  |  | ##  + calls \&CODE on $obj and each DDC::PP::Object descendant in turn | 
| 109 |  |  |  |  |  |  | ##  + \&CODE is called as \&CODE->($obj), and should return a new value for the corresponding slot | 
| 110 |  |  |  |  |  |  | ##  + object tree is traversed in depth-first visit-last order | 
| 111 |  |  |  |  |  |  | sub mapTraverse { | 
| 112 | 10 |  |  | 10 | 0 | 104 | my ($obj,$code) = @_; | 
| 113 | 10 |  |  |  |  | 46 | return $obj->mapVisit($obj,$code); | 
| 114 |  |  |  |  |  |  | } | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | ## $oldval = CLASS->mapVisit($curval, \$code) | 
| 117 |  |  |  |  |  |  | sub mapVisit { | 
| 118 | 134 |  |  | 134 | 0 | 228 | my ($that,$nod,$code) = @_; | 
| 119 | 134 | 100 | 100 |  |  | 668 | if (#UNIVERSAL::isa($nod,'DDC::PP::Object') ##-- breaks DDC::Any | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 | 33 |  |  |  |  | 
| 120 |  |  |  |  |  |  | ref($nod) && UNIVERSAL::can($nod,'members') | 
| 121 |  |  |  |  |  |  | ) { | 
| 122 | 10 |  |  |  |  | 20 | my ($oldval,$newval); | 
| 123 | 10 |  |  |  |  | 27 | foreach my $slot (grep {$nod->can("get$_")} $nod->members) { | 
|  | 80 |  |  |  |  | 328 |  | 
| 124 | 80 |  |  |  |  | 251 | $oldval = $nod->can("get${slot}")->($nod); | 
| 125 | 80 |  |  |  |  | 167 | $newval = $that->mapVisit($oldval, $code); | 
| 126 | 80 | 100 | 66 |  |  | 645 | $nod->can("set${slot}")->($nod,$newval) if ((defined($newval) && defined($oldval) && $newval ne $oldval) | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 127 |  |  |  |  |  |  | || defined($newval) | 
| 128 |  |  |  |  |  |  | || defined($oldval)); | 
| 129 |  |  |  |  |  |  | } | 
| 130 | 10 |  |  |  |  | 36 | return $code->($nod); | 
| 131 |  |  |  |  |  |  | } | 
| 132 |  |  |  |  |  |  | elsif (ref($nod) && UNIVERSAL::isa($nod,'ARRAY')) { | 
| 133 | 32 |  |  |  |  | 58 | my $newval = [grep {defined($_)} map {$that->mapVisit($_,$code)} @$nod]; | 
|  | 44 |  |  |  |  | 105 |  | 
|  | 44 |  |  |  |  | 88 |  | 
| 134 | 32 | 50 |  |  |  | 89 | return ref($newval) eq 'ARRAY' ? $newval : bless($newval, ref($nod)); | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  | elsif (ref($nod) && UNIVERSAL::isa($nod,'HASH')) { | 
| 137 | 0 |  |  |  |  | 0 | my $newval = {map {($_=>$that->mapVisit($nod->{$_},$code))} keys %$nod}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 138 | 0 | 0 |  |  |  | 0 | return ref($newval) eq 'HASH' ? $newval : bless($newval, ref($nod)); | 
| 139 |  |  |  |  |  |  | } | 
| 140 | 92 |  |  |  |  | 188 | return $nod; | 
| 141 |  |  |  |  |  |  | } | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | ##====================================================================== | 
| 145 |  |  |  |  |  |  | ## C->Perl | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | ##-------------------------------------------------------------- | 
| 148 |  |  |  |  |  |  | ## \%hash = $obj->toHash(%opts) | 
| 149 |  |  |  |  |  |  | ##  + %opts: | 
| 150 |  |  |  |  |  |  | ##    ( | 
| 151 |  |  |  |  |  |  | ##     trimClassNames => $bool,  ##-- auto-trim class-names? | 
| 152 |  |  |  |  |  |  | ##     json => $bool,            ##-- for JSON-ification? | 
| 153 |  |  |  |  |  |  | ##    ) | 
| 154 |  |  |  |  |  |  | ##  + returns an object as a (nested) perl hash | 
| 155 |  |  |  |  |  |  | ##  + pure-perl variant just returns object | 
| 156 |  |  |  |  |  |  | sub toHash { | 
| 157 | 296 |  |  | 296 | 0 | 1340 | my ($obj,%opts) = @_; | 
| 158 | 296 | 0 | 33 |  |  | 668 | return $obj if (!defined($obj) && !ref($obj)); | 
| 159 | 296 |  |  |  |  | 528 | my $class = ref($obj); | 
| 160 | 296 | 100 | 66 |  |  | 2185 | $class =~ s/^DDC::(?:XS|PP|Any)::// if ($opts{trimClassNames} || $opts{json}); ##-- use toJson()-style class names | 
| 161 |  |  |  |  |  |  | return { | 
| 162 |  |  |  |  |  |  | (map { | 
| 163 | 2046 |  |  |  |  | 6220 | ( $_ => $obj->valToPerl($obj->can("get$_")->($obj),%opts) ) | 
| 164 |  |  |  |  |  |  | } grep { | 
| 165 | 296 |  |  |  |  | 820 | $obj->can("get$_") | 
|  | 2046 |  |  |  |  | 5802 |  | 
| 166 |  |  |  |  |  |  | }  $obj->members), | 
| 167 |  |  |  |  |  |  | class => $class, | 
| 168 |  |  |  |  |  |  | }; | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | ##-------------------------------------------------------------- | 
| 172 |  |  |  |  |  |  | ## $perlval = $CLASS_OR_OBJECT->valToPerl($cval,%opts) | 
| 173 |  |  |  |  |  |  | ##  + %opts: as for toHash() | 
| 174 |  |  |  |  |  |  | ##  + returns a perl-encoded representation of $cval | 
| 175 |  |  |  |  |  |  | sub valToPerl { | 
| 176 | 2246 |  |  | 2246 | 0 | 4444 | my ($that,$cval,%opts) = @_; | 
| 177 | 2246 | 100 |  |  |  | 5261 | if (!ref($cval)) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 178 | 1542 |  |  |  |  | 7666 | return $cval; | 
| 179 |  |  |  |  |  |  | } elsif (UNIVERSAL::can($cval,'toHash')) { | 
| 180 | 318 |  |  |  |  | 963 | return $cval->toHash(%opts); | 
| 181 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($cval,'HASH')) { | 
| 182 | 0 |  |  |  |  | 0 | return {(map {($_=>$that->valToPerl($cval->{$_},%opts))} keys %$cval)}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 183 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($cval,'ARRAY')) { | 
| 184 | 386 |  |  |  |  | 1284 | return [map {$that->valToPerl($_,%opts)} @$cval]; | 
|  | 200 |  |  |  |  | 493 |  | 
| 185 |  |  |  |  |  |  | } | 
| 186 | 0 |  |  |  |  | 0 | return $cval; ##-- CODE- or GLOB-ref? | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | ##-------------------------------------------------------------- | 
| 191 |  |  |  |  |  |  | ## @classes = $CLASS_OR_OBJ->inherits() | 
| 192 |  |  |  |  |  |  | ##  + returns a list of all classes from which $CLASS_OR_OBJ inherits | 
| 193 |  |  |  |  |  |  | ##  + called by toHash() | 
| 194 |  |  |  |  |  |  | sub inherits { | 
| 195 | 20 |  |  | 20 |  | 150 | no strict 'refs'; | 
|  | 20 |  |  |  |  | 44 |  | 
|  | 20 |  |  |  |  | 1640 |  | 
| 196 | 5281 |  |  | 5281 | 0 | 6980 | my $that = shift; | 
| 197 | 5281 |  | 66 |  |  | 11843 | my $class = ref($that) || $that; | 
| 198 | 5281 |  |  |  |  | 6401 | return ($class, map {inherits($_)} @{"${class}::ISA"}); | 
|  | 4975 |  |  |  |  | 7469 |  | 
|  | 5281 |  |  |  |  | 15922 |  | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | ##-------------------------------------------------------------- | 
| 202 |  |  |  |  |  |  | ## @keys = $CLASS_OR_OBJ->members() | 
| 203 |  |  |  |  |  |  | ##  + returns a list of all members with a "set${Key}" method supported by $CLASS_OR_OBJ or any superclasss | 
| 204 |  |  |  |  |  |  | ##  + called by toHash() | 
| 205 |  |  |  |  |  |  | sub members { | 
| 206 | 20 |  |  | 20 |  | 130 | no strict 'refs'; | 
|  | 20 |  |  |  |  | 45 |  | 
|  | 20 |  |  |  |  | 14051 |  | 
| 207 | 306 |  |  | 306 | 0 | 661 | my $that = shift; | 
| 208 | 306 |  |  |  |  | 510 | my ($class,$symtab,%keys); | 
| 209 | 306 |  |  |  |  | 633 | foreach $class ($that->inherits) { | 
| 210 | 5281 |  |  |  |  | 7074 | $symtab = \%{"${class}::"}; | 
|  | 5281 |  |  |  |  | 10934 |  | 
| 211 |  |  |  |  |  |  | @keys{( | 
| 212 | 7198 |  |  |  |  | 19691 | grep {exists $symtab->{"set$_"}} | 
| 213 | 5281 | 100 |  |  |  | 17613 | map { /^get([[:upper:]].*)$/ ? $1 : qw() } | 
|  | 101510 |  |  |  |  | 162896 |  | 
| 214 |  |  |  |  |  |  | keys %$symtab | 
| 215 |  |  |  |  |  |  | )} = qw(); | 
| 216 |  |  |  |  |  |  | } | 
| 217 | 306 |  |  |  |  | 1354 | return keys %keys; | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | ##====================================================================== | 
| 221 |  |  |  |  |  |  | ## Perl->C-like | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | ##-------------------------------------------------------------- | 
| 224 |  |  |  |  |  |  | ## $obj = CLASS->newFromHash(\%hash) | 
| 225 |  |  |  |  |  |  | ##  + creates a C++-like object from a (nested) perl hash | 
| 226 |  |  |  |  |  |  | sub newFromHash { | 
| 227 | 6 |  |  | 6 | 0 | 15 | my ($that,$hash) = @_; | 
| 228 | 6 |  | 33 |  |  | 26 | my $class = ref($that) || $that; | 
| 229 | 6 | 50 | 33 |  |  | 29 | return $hash if (!defined($hash) || UNIVERSAL::isa($hash,$class)); | 
| 230 | 6 | 50 |  |  |  | 30 | confess(__PACKAGE__ , "::newFromHash(): argument '$hash' is neither undef, a HASH-ref, nor an object of class $class") | 
| 231 |  |  |  |  |  |  | if (!UNIVERSAL::isa($hash,'HASH')); | 
| 232 |  |  |  |  |  |  |  | 
| 233 | 6 | 50 |  |  |  | 20 | $class = $hash->{class} if (defined($hash->{class})); | 
| 234 | 6 | 50 |  |  |  | 23 | $class = "DDC::PP::$class" if ($class !~ /:/); ##-- honor toJson()-style class names | 
| 235 | 6 | 50 |  |  |  | 30 | my $obj = $class->new() | 
| 236 |  |  |  |  |  |  | or confess(__PACKAGE__, "::newFromHash(): $class->new() failed"); | 
| 237 |  |  |  |  |  |  |  | 
| 238 | 6 |  |  |  |  | 26 | my ($key,$val,$valobj, $setsub); | 
| 239 | 6 |  |  |  |  | 29 | while (($key,$val) = each %$hash) { | 
| 240 | 44 | 100 |  |  |  | 99 | next if ($key eq 'class'); | 
| 241 |  |  |  |  |  |  |  | 
| 242 | 38 | 50 |  |  |  | 197 | if ( !($setsub = $obj->can("set".ucfirst($key))) ) { | 
| 243 | 0 |  |  |  |  | 0 | warn(__PACKAGE__, "::newFromHash(): ignoring key '$key' for object of class '$class'"); | 
| 244 | 0 |  |  |  |  | 0 | next; | 
| 245 |  |  |  |  |  |  | } | 
| 246 | 38 |  |  |  |  | 98 | $valobj = $that->valFromPerl($val); | 
| 247 | 38 |  |  |  |  | 90 | $setsub->($obj,$valobj); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 | 6 |  |  |  |  | 16 | return $obj; | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | ##-------------------------------------------------------------- | 
| 254 |  |  |  |  |  |  | ## $cval = $CLASS_OR_OBJECT->valFromPerl($perlval) | 
| 255 |  |  |  |  |  |  | ##  + returns a c-like representation of $perlval | 
| 256 |  |  |  |  |  |  | sub valFromPerl { | 
| 257 | 40 |  |  | 40 | 0 | 88 | my ($that,$pval) = @_; | 
| 258 | 40 | 100 | 66 |  |  | 139 | if (!ref($pval)) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 259 | 22 |  |  |  |  | 48 | return $pval; | 
| 260 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($pval,'HASH') && $pval->{class}) { | 
| 261 | 4 |  |  |  |  | 22 | return $that->newFromHash($pval); | 
| 262 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($pval,'HASH')) { | 
| 263 | 0 |  |  |  |  | 0 | return {(map {($_=>$that->valFromPerl($pval->{$_}))} keys %$pval)}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 264 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($pval,'ARRAY')) { | 
| 265 | 14 |  |  |  |  | 31 | return [map {$that->valFromPerl($_)} @$pval]; | 
|  | 2 |  |  |  |  | 16 |  | 
| 266 |  |  |  |  |  |  | } | 
| 267 | 0 |  |  |  |  | 0 | return $pval; ##-- CODE- or GLOB-ref? | 
| 268 |  |  |  |  |  |  | } | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | ##====================================================================== | 
| 272 |  |  |  |  |  |  | ## Clone | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | ## $obj2 = $obj->clone() | 
| 275 |  |  |  |  |  |  | sub clone { | 
| 276 | 0 |  |  | 0 | 0 | 0 | return $_[0]->newFromHash($_[0]->toHash); | 
| 277 |  |  |  |  |  |  | } | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | ##====================================================================== | 
| 280 |  |  |  |  |  |  | ## JSON | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | ##-------------------------------------------------------------- | 
| 283 |  |  |  |  |  |  | ## $obj = CLASS->newFromJson($json_string,%json_opts) | 
| 284 |  |  |  |  |  |  | ##  + creates a C++ object from a json string | 
| 285 |  |  |  |  |  |  | sub newFromJson { | 
| 286 | 0 |  |  | 0 | 0 | 0 | my ($that,$json,%opts) = @_; | 
| 287 | 0 |  |  |  |  | 0 | my $hash = JSON::from_json($json, { utf8=>!utf8::is_utf8($json), relaxed=>1, allow_nonref=>1, %opts }); | 
| 288 | 0 |  |  |  |  | 0 | return $that->newFromHash($hash); | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | ## $json = $obj->TO_JSON | 
| 292 |  |  |  |  |  |  | sub TO_JSON { | 
| 293 | 160 |  |  | 160 | 0 | 6098 | return $_[0]->toHash(json=>1); | 
| 294 |  |  |  |  |  |  | } | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | 1; ##-- be happy | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | =pod | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | =head1 NAME | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | DDC::PP::Object - common perl base class for DDC::PP objects | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | #-- Preliminaries | 
| 308 |  |  |  |  |  |  | use DDC::PP; | 
| 309 |  |  |  |  |  |  | $CLASS = 'DDC::PP::Object'; | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | ##--------------------------------------------------------------------- | 
| 312 |  |  |  |  |  |  | ## C -> Perl | 
| 313 |  |  |  |  |  |  | $q    = DDC::PP->parse("foo && bar"); | 
| 314 |  |  |  |  |  |  | $qs   = $q->toString;                  ##-- $qs is "('foo' && 'bar')" | 
| 315 |  |  |  |  |  |  | $hash = $q->toHash();                  ##-- query encoded as perl hash-ref | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | #... the perl object can be manipulated directly (perl refcounting applies) | 
| 318 |  |  |  |  |  |  | $hash->{Dtr1} = {class=>'CQTokExact',Value=>'baz'};    ##-- NO memory leak! | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | ##--------------------------------------------------------------------- | 
| 321 |  |  |  |  |  |  | ## Perl->C | 
| 322 |  |  |  |  |  |  | $q2   = $CLASS->newFromHash($hash);    ##-- $q2 needs explicit free() | 
| 323 |  |  |  |  |  |  | $qs2  = $q2->toString();               ##-- $qs2 is "(@'baz' && 'bar') | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | ##--------------------------------------------------------------------- | 
| 326 |  |  |  |  |  |  | ## Deep copy & Traversal | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | $q3 = $q->clone();                     ##-- wraps newFromHash($q->toHash) | 
| 329 |  |  |  |  |  |  | $q  = $q->mapTraverse(\&CODE);         ##-- recursively tweak sub-objects | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | ##--------------------------------------------------------------------- | 
| 332 |  |  |  |  |  |  | ## JSON utilities | 
| 333 |  |  |  |  |  |  | $json = $q->toJson();                  ##-- ddc-internal json-ification | 
| 334 |  |  |  |  |  |  | $json = $q->TO_JSON();                 ##-- wraps toHash() for the JSON module | 
| 335 |  |  |  |  |  |  | $obj  = $CLASS->newFromJson($str);     ##-- wraps newFromHash(from_json($str)) | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | ##--------------------------------------------------------------------- | 
| 338 |  |  |  |  |  |  | ## Debugging | 
| 339 |  |  |  |  |  |  | $obj->DumpTree();                      ##-- dumps substructure to STDERR | 
| 340 |  |  |  |  |  |  | $obj->free();                          ##-- expplicit deep destruction, use at your own risk | 
| 341 |  |  |  |  |  |  | \@kids = $obj->Children();             ##-- ARRAY-ref of direct children | 
| 342 |  |  |  |  |  |  | \@desc = $obj->Descendants();          ##-- ARRAY-ref of descendants | 
| 343 |  |  |  |  |  |  | undef  = $obj->DisownChildren();       ##-- prevent deep destruction (dummy method; you should never need this) | 
| 344 |  |  |  |  |  |  | $cnt   = $obj->refcnt();               ##-- get internal reference count (dummy method) | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | The DDC::PP::Object class is a pure-perl fork of the L class, which see. | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | perl(1), | 
| 356 |  |  |  |  |  |  | DDC::PP(3perl), | 
| 357 |  |  |  |  |  |  | DDC::PP::CQuery(3perl), | 
| 358 |  |  |  |  |  |  | DDC::PP::CQCount(3perl), | 
| 359 |  |  |  |  |  |  | DDC::PP::CQFilter(3perl), | 
| 360 |  |  |  |  |  |  | DDC::PP::CQueryOptions(3perl), | 
| 361 |  |  |  |  |  |  | DDC::PP::CQueryCompiler(3perl). | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | =head1 AUTHOR | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | Bryan Jurish Emoocow@cpan.orgE | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | Copyright (C) 2016 by Bryan Jurish | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or modify | 
| 372 |  |  |  |  |  |  | it under the same terms as Perl itself, either Perl version 5.14.2 or, | 
| 373 |  |  |  |  |  |  | at your option, any later version of Perl 5 you may have available. | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | =cut | 
| 376 |  |  |  |  |  |  |  |