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

Contents of /nfo/perl/libs/Data/Mungle/Transform/Deep.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.8 - (show annotations)
Sun Feb 9 05:10:56 2003 UTC (21 years, 5 months ago) by joko
Branch: MAIN
Changes since 1.7: +7 -4 lines
+ minor update

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

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