/[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.1 - (show 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 ##############################################
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