/[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.6 - (hide annotations)
Mon Dec 23 11:27:53 2002 UTC (21 years, 6 months ago) by jonen
Branch: MAIN
Changes since 1.5: +9 -4 lines
+ changed behavior WATCH!

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

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