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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (show annotations)
Sat Jan 18 18:25:39 2003 UTC (21 years, 5 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 ##############################################
2 #
3 # $Id: Deep.pm,v 1.1 2003/01/18 15:38:05 joko Exp $
4 #
5 # $Log: Deep.pm,v $
6 # Revision 1.1 2003/01/18 15:38:05 joko
7 # + initial check-in
8 #
9 # 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