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

Annotation of /nfo/perl/scripts/outlook2ldap/libs/Data/Transform/Deep.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (hide annotations)
Sat Jan 18 15:38:05 2003 UTC (21 years, 7 months ago) by joko
Branch: MAIN
CVS Tags: v008-2, v008-1
+ initial check-in

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

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