/[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.2 - (hide annotations)
Sat Jan 18 18:25:39 2003 UTC (21 years, 7 months ago) by joko
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +4 -1 lines
FILE REMOVED
- removed - now taken from nfo/perl/libs

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

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