/[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.5 - (show annotations)
Mon Dec 16 19:57:54 2002 UTC (21 years, 6 months ago) by joko
Branch: MAIN
Changes since 1.4: +17 -5 lines
+ option 'init'

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

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