/[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.6 - (show annotations)
Mon Dec 23 11:27:53 2002 UTC (21 years, 6 months ago) by jonen
Branch: MAIN
Changes since 1.5: +9 -4 lines
+ changed behavior WATCH!

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

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