/[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.5 - (hide annotations)
Mon Dec 16 19:57:54 2002 UTC (21 years, 6 months ago) by joko
Branch: MAIN
Changes since 1.4: +17 -5 lines
+ option 'init'

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

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