/[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.4 - (hide annotations)
Thu Dec 5 13:56:49 2002 UTC (21 years, 7 months ago) by joko
Branch: MAIN
Changes since 1.3: +94 -9 lines
- var_deref
+ expand - more sophisticated dereferencing with callbacks using Iterate

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

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