/[cvs]/nfo/perl/libs/Data/Mungle/Transform/Deep.pm
ViewVC logotype

Annotation of /nfo/perl/libs/Data/Mungle/Transform/Deep.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.8 - (hide annotations)
Sun Feb 9 05:10:56 2003 UTC (21 years, 5 months ago) by joko
Branch: MAIN
Changes since 1.7: +7 -4 lines
+ minor update

1 joko 1.7 ## ---------------------------------------------------------------------------
2 joko 1.8 ## $Id: Deep.pm,v 1.7 2003/01/19 03:26:59 joko Exp $
3 joko 1.7 ## ---------------------------------------------------------------------------
4 joko 1.8 ## $Log: Deep.pm,v $
5     ## Revision 1.7 2003/01/19 03:26:59 joko
6     ## + added 'sub deep_copy' - refactored from libp
7     ## + add 'sub merge' - exported from CPAN's 'Hash::Merge'
8     ##
9 joko 1.7 ## Revision 1.6 2002/12/23 11:27:53 jonen
10     ## + changed behavior WATCH!
11     ##
12     ## Revision 1.5 2002/12/16 19:57:54 joko
13     ## + option 'init'
14     ##
15     ## Revision 1.4 2002/12/05 13:56:49 joko
16     ## - var_deref
17     ## + expand - more sophisticated dereferencing with callbacks using Iterate
18     ##
19     ## Revision 1.3 2002/12/03 05:34:55 joko
20     ## + bugfix: now utilizing var_utf2iso from Data::Transform::Encode
21     ##
22     ## Revision 1.2 2002/12/01 04:44:07 joko
23     ## + code from Data::Transform::OO
24     ##
25     ## Revision 1.1 2002/11/29 04:49:20 joko
26     ## + initial check-in
27     ##
28     ## Revision 1.1 2002/10/10 03:26:00 cvsjoko
29     ## + new
30     ## ---------------------------------------------------------------------------
31 joko 1.1
32    
33     package Data::Transform::Deep;
34    
35     use strict;
36     use warnings;
37    
38     require Exporter;
39     our @ISA = qw( Exporter );
40 joko 1.2 our @EXPORT_OK = qw(
41     &var_NumericalHashToArray
42     &var_mixin
43     &object2hash
44     &hash2object
45     &refexpr2perlref
46 joko 1.4 &expand
47 joko 1.7 &deep_copy
48     &merge
49 joko 1.1 );
50 joko 1.4 # &var_deref
51 joko 1.2 # &getStructSlotByStringyAddress
52 joko 1.1
53     use attributes;
54 joko 1.2 use Data::Dumper;
55 joko 1.4 use Data::Transform::Encode qw( var_utf2iso var2utf8 scalar2utf8 );
56 joko 1.7
57     use Hash::Merge qw( merge );
58 joko 1.4 use Iterate;
59 joko 1.1
60     sub var_NumericalHashToArray {
61     my $vref = shift;
62     #my $context = shift;
63     if (ref $vref eq 'HASH') {
64     foreach (keys %{$vref}) {
65     # if ((ref $vref->{$_}) =~ m/HASH/) {
66     if ((ref $vref->{$_}) =~ m/HASH/) {
67     if ($vref->{$_}{0}) { # might be a numerical-indexed array
68     #print "000\n";
69     #print "context: $context\n";
70     my @arr;
71     foreach my $key_numeric (sort keys %{$vref->{$_}}) {
72     push @arr, $vref->{$_}{$key_numeric};
73     }
74     $vref->{$_} = \@arr;
75     }
76     var_NumericalHashToArray($vref->{$_});
77     }
78     # } else {
79     # #$vref->{$_} = scalar2iso($vref->{$_});
80     # }
81     }
82     }
83     }
84    
85    
86     sub var_deref {
87     my $obj = shift;
88 joko 1.4 my $options = shift;
89 joko 1.1 my $result;
90     if ((ref $obj) eq 'ARRAY') {
91     foreach (@{$obj}) {
92     my $ref = ref $_;
93     if ($ref) {
94 joko 1.4 push(@{$result}, var_deref($_, $options));
95     #undef $_;
96 joko 1.1 } else {
97 joko 1.4 #push(@{$result}, $_);
98     push(@{$result}, deep_copy($_));
99 joko 1.1 }
100 joko 1.4 #undef $_ if $options->{destroy};
101     #$options->{destroy}->($_) if $options->{destroy};
102 joko 1.1 }
103 joko 1.2 # TODO: "} elsif (ref $obj eq 'HASH') { [...] } else { croak 'could not deref blah'; }" ???
104 joko 1.1 } else {
105     foreach (keys %{$obj}) {
106     my $key = $_;
107     my $ref = ref $obj->{$_};
108     if ($ref) {
109 joko 1.4 $result->{$_} = var_deref($obj->{$_}, $options);
110     #undef $obj->{$_};
111 joko 1.1 } else {
112 joko 1.4 #$result->{$_} = $obj->{$_};
113     $result->{$_} = deep_copy($obj->{$_});
114 joko 1.1 }
115 joko 1.4 #undef $obj->{$_} if $options->{destroy};
116     #$options->{destroy}->($obj->{$_}) if $options->{destroy};
117 joko 1.1 }
118     }
119 joko 1.4 #undef $obj if $options->{destroy};
120     $options->{cb_destroy}->($obj) if $options->{cb_destroy};
121 joko 1.1 return $result;
122     }
123    
124    
125 joko 1.4 sub expand {
126    
127     my $obj = shift;
128     my $options = shift;
129     my $result;
130    
131     if ((ref $obj) eq 'ARRAY') {
132    
133     IterArray @$obj, sub {
134     my $item;
135     # if current item is a reference ...
136     if (ref $_[0]) {
137     # ... expand structure recursively
138     $item = expand($_[0], $options);
139     # destroy item via seperate callback method (a POST) if requested
140     #$options->{cb}->{destroy}->($_[0]) if $options->{destroy};
141    
142     # ... assume plain scalar
143     } else {
144     #$item = deep_copy($_[0]);
145     $item = $_[0];
146     # conversions/encodings
147     $item = scalar2utf8($item) if ($item && $options->{utf8});
148     }
149     #push(@{$result}, $item) if $item; # use item only if not undef (TODO: make configurable via $options)
150     push(@{$result}, $item); # use item in any case
151    
152     }
153    
154     # TODO: "} elsif (ref $obj eq 'HASH') { [...] } else { croak 'could not deref blah'; }" ???
155     } else {
156    
157     IterHash %$obj, sub {
158     my $item;
159    
160     # if current item is a reference ...
161     if (ref $_[1]) {
162     # ... expand structure recursively
163     $item = expand($_[1], $options);
164     # destroy item via seperate callback method (a POST) if requested
165     #$options->{cb}->{destroy}->($_[1]) if $options->{destroy};
166    
167     # ... assume plain scalar
168     } else {
169     #$item = deep_copy($_[1]);
170     $item = $_[1];
171     # conversions/encodings
172     $item = scalar2utf8($item) if ($item && $options->{utf8});
173     }
174     #$result->{$_[0]} = $item if $item; # use item only if not undef (TODO: make configurable via $options)
175     $result->{$_[0]} = $item; # use item in any case
176     }
177    
178     }
179    
180     # convert all values to utf8 (inside complex struct)
181     # now done in core-item-callbacks via Greg London's "Iterate" from CPAN
182     # var2utf8($result) if ($options->{utf8});
183    
184     # destroy persistent object from memory to be sure to get a fresh one next time
185     #undef $obj if $options->{destroy};
186     #$options->{cb_destroy}->($obj) if $options->{cb_destroy};
187     #$options->{cb}->{destroy}->($obj) if $options->{destroy};
188    
189     return $result;
190     }
191    
192 joko 1.1
193    
194     my @indexstack;
195     sub var_traverse_mixin_update {
196     my $obj = shift;
197     my $data = shift;
198     my $bool_recursion = shift;
199     if (!$bool_recursion) {
200     @indexstack = ();
201     }
202     my $result;
203    
204     # 'ARRAY' does not work!
205     if ((ref $obj) eq 'ARRAY') {
206     #print "ARRAY\n";
207     my $i = 0;
208     foreach (@{$obj}) {
209     push @indexstack, $i;
210     my $ref = ref $_;
211     if ($ref) {
212     #$obj->[$i] = var_mixin($_, $data, 1);
213     var_traverse_mixin_update($_, $data, 1);
214     } else {
215     $obj->[$i] = $_;
216     }
217     pop @indexstack;
218     $i++;
219     }
220     } else {
221     foreach (keys %{$obj}) {
222     push @indexstack, $_;
223     my $ref = ref $obj->{$_};
224     if ($ref) {
225     #$obj->{$_} = var_mixin($obj->{$_}, $data, 1);
226     var_traverse_mixin_update($obj->{$_}, $data, 1);
227     } else {
228     #$obj->{$_} = $obj->{$_};
229     my $evstring = getSlot($data);
230     $obj->{$_} = $evstring;
231    
232     }
233     pop @indexstack;
234     }
235     }
236     #return $result;
237     }
238    
239 joko 1.2 sub object2hash {
240     my $object = shift;
241     my $options = shift;
242 joko 1.4 #my $deref = var_deref($object);
243     my $deref = expand($object, $options);
244     #var2utf8($deref) if ($options->{utf8});
245 joko 1.2 return $deref;
246     }
247    
248     # todo: maybe do diff(s) here sometimes!?
249     sub hash2object {
250     my $object = shift;
251     my $data = shift;
252     my $options = shift;
253    
254 joko 1.3 #print "hash2object", "\n";
255    
256 joko 1.2 # "patch" needed 'cause php passes numerical-indexed-arrays as hash-tables,
257     # this method corrects this
258     var_NumericalHashToArray($data) if ($options->{php});
259    
260     # utf8-conversion/-encoding (essential for I18N)
261     var_utf2iso($data) if ($options->{utf8});
262    
263     # get fresh object from database
264     # todo:
265     # - handle "locked" objects !!!
266     # - check if already another update occoured (object-revisioning needed!)
267     #my $obj = $self->getObject($oid);
268    
269     # mix changes into fresh object and save it back
270 joko 1.5 hash2object_traverse_mixin($object, $data, 0, $options);
271 joko 1.2
272     # done in core mixin function?
273     #$self->{storage}->update($obj);
274    
275     # we should "undef" some objects here to destroy them in memory and enforce reloading when used next time
276     # simulate:
277     #$self->{storage}->disconnect();
278    
279     }
280    
281     # ------------------------------------
282     # core mixin function
283     # TODO-FEATURE: make this possible in a reverse way: object is empty (no fields) and gets filled up by mixin-data
284    
285     # remember keys of structures we are traversing
286     # this is a HACK:
287     # - don't (!!!) "packageglobal" @indexstack
288     # - it will lead to problems with parallelism!
289    
290     #my @indexstack;
291    
292     # traverse a deeply nested structure, mix in values from given hash, update underlying tangram-object
293     sub hash2object_traverse_mixin {
294     my $object = shift;
295     my $data = shift;
296     my $bool_recursion = shift;
297 joko 1.5 my $options = shift;
298 joko 1.2
299     # clear our key - stack if we are called from user-code (non-recursively)
300     @indexstack = () if (!$bool_recursion);
301    
302     my $classname = ref $object;
303     # if ($classname) {
304     # print STDERR "*****************", Dumper(Class::Tangram::attribute_types($classname)), "\n";
305     # }
306    
307     # parser: detected OBJECT (a Tangram one?) (reftype == HASH) (assume)
308     # what's exactly done here? hmmm.... it works ;)
309     # maybe a HACK: please try not to use "IntrHash"es, maybe this cannot handle them!!!
310     # extend! check!
311     if ((attributes::reftype($object) eq 'HASH') && (ref($object) ne 'HASH') && (ref($object) ne 'ARRAY')) {
312    
313     # print STDERR "===", "reftype: ", attributes::reftype($obj), "\n";
314    
315 joko 1.5 my @fields;
316 joko 1.2 # loop through fields of object (Tangram-object)
317 joko 1.5 @fields = keys %{$object};
318    
319     # loop through fields of to.be.injected-data (arbitrary Perl-data-structure)
320     @fields = keys %{$data} if $options->{init};
321 jonen 1.6 # @fields = keys %{$data} if $options->{mixin};
322 joko 1.5
323     foreach (@fields) {
324 joko 1.7 my $field = $_;
325     push @indexstack, $field;
326 joko 1.2
327     # determine type of object
328 joko 1.7 my $ref = ref $object->{$field};
329 joko 1.2 # print STDERR "attrname: $_ ATTRref: $ref", "\n";
330     if ($ref) {
331 joko 1.7 hash2object_traverse_mixin($object->{$field}, $data, 1, $options);
332 joko 1.2 } else {
333     my $val = getStructSlotByStringyAddress($data, \@indexstack);
334 joko 1.7
335 joko 1.8 #print Dumper($options);
336 joko 1.7 my $field_target = $field;
337     if (my $pattern = $options->{pattern_strip_key}) {
338     print "pattern: $pattern", "\n";
339     $field_target =~ s/$pattern//;
340     print "field: $field_target", "\n";
341     }
342    
343     $object->{$field_target} = $val if defined $val;
344 joko 1.2 }
345     pop @indexstack;
346     }
347    
348     # save object to database ...
349     # ... do an update if it already exists, do an insert if it doesn't
350     # my $objectId = $self->{storage}->id($obj);
351     # $logger->debug( __PACKAGE__ . "->saveObjectFromHash_traverse_mixin_update( object $obj objectId $objectId )" );
352     # if ($objectId) {
353     # $self->{storage}->update($obj);
354    
355     #print __PACKAGE__ . ":", "\n";
356     #print Dumper($object);
357    
358     # } else {
359     # $self->{storage}->insert($obj);
360     # }
361    
362     }
363    
364     #&& ( ref($obj) ne 'HASH' )
365    
366     # loop through entries of array (IntrArray, isn't it?)
367     if ((ref $object) eq 'ARRAY') {
368     # print STDERR "===", "refttype ", attributes::reftype($obj), "\n";
369     my $i = 0;
370     foreach (@{$object}) {
371     push @indexstack, $i;
372     my $ref = ref $_;
373     # print STDERR "attrname: $_ ATTRref: $ref", "\n";
374 jonen 1.6 # if ($ref && $_) {
375     if ($ref) {
376 joko 1.5 hash2object_traverse_mixin($_, $data, 1, $options);
377 joko 1.2 } else {
378 jonen 1.6 $object->[$i] = $_ if defined $_;
379 joko 1.2 }
380     pop @indexstack;
381     $i++;
382     }
383     }
384    
385     }
386    
387    
388     # this function seems to do similar stuff like these below (refexpr2perlref & co.)
389 joko 1.5 # TODO: maybe this mechanism can be replaced completely through some nice module from CPAN .... ? ;-)
390 joko 1.1 sub getStructSlotByStringyAddress {
391     my $var = shift;
392     my $indexstack_ref = shift;
393     my @indexstack = @{$indexstack_ref};
394     my $joiner = '->';
395     my @evlist;
396     foreach (@indexstack) {
397     my $elem;
398     if ($_ =~ m/\d+/) { $elem = "[$_]"; }
399     if ($_ =~ m/\D+/) { $elem = "{$_}"; }
400     push @evlist, $elem;
401     }
402     my $evstring = join($joiner, @evlist);
403     $evstring = 'return $var->' . $evstring . ';';
404     #print "ev: $evstring\n";
405     return eval($evstring);
406 joko 1.2 }
407    
408    
409     sub refexpr2perlref {
410    
411     my $obj = shift;
412     my $expr = shift;
413     my $callbackMap = shift;
414    
415     my $value;
416     # detect for callback (code-reference)
417     if (ref($expr) eq 'CODE') {
418     $value = &$expr($obj);
419    
420     } elsif ($expr =~ m/->/) {
421     # use expr as complex object reference declaration (obj->subObj->subSubObj->0->attribute)
422     (my $objPerlRefString, my $parts) = refexpr2perlref_parts($expr);
423     #print "\n", "expr: $expr";
424     #print "\n", "objPerlRefString: $objPerlRefString";
425     $value = eval('$obj' . '->' . $objPerlRefString);
426    
427     # if value isn't set, try to "fallback" to callbackMap
428     # callbacks are applied this way:
429     # take the last element of the expression parts and check if this string exists as a key in the callback-map
430     if (!$value && $callbackMap) {
431     #print Dumper($callbackMap);
432    
433     # prepare needle
434     my @parts = @$parts;
435     my $count = $#parts;
436     my $needle = $parts[$count];
437    
438     # prepare haystack
439     my @haystack = keys %$callbackMap;
440     if (grep($needle, @haystack)) {
441     $value = $callbackMap->{$needle}->();
442     #print "value: ", $value, "\n";
443     }
444     }
445    
446     } else {
447     # use expr as simple scalar key (attributename)
448     $value = $obj->{$expr};
449     }
450    
451     return $value;
452    
453     }
454    
455    
456     sub refexpr2perlref_parts {
457     my $expr = shift;
458    
459     # split expression by dereference operators first
460     my @parts_pure = split(/->/, $expr);
461    
462     # wrap []'s around each part, if it consists of numeric characters only (=> numeric = array-index),
463     # use {}'s, if there are word-characters in it (=> alphanumeric = hash-key)
464     my @parts_capsule = @parts_pure;
465     map {
466     m/^\d+$/ && ($_ = "[$_]") || ($_ = "{$_}");
467     } @parts_capsule;
468    
469     # join parts with dereference operators together again and return built string
470     return (join('->', @parts_capsule), \@parts_pure);
471 joko 1.7 }
472    
473     # ACK's go to ...
474     sub deep_copy {
475     my $this = shift;
476     if (not ref $this) {
477     $this;
478     } elsif (ref $this eq "ARRAY") {
479     [map deep_copy($_), @$this];
480     } elsif (ref $this eq "HASH") {
481     +{map { $_ => deep_copy($this->{$_}) } keys %$this};
482     } elsif (ref $this eq "CODE") {
483     $this;
484     #} else { die "deep_copy asks: what type is $this?" }
485     } else { print "deep_copy asks: what type is $this?", "\n"; }
486 joko 1.1 }
487    
488     1;

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed