/[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.4 - (show annotations)
Thu Dec 5 13:56:49 2002 UTC (21 years, 7 months ago) by joko
Branch: MAIN
Changes since 1.3: +94 -9 lines
- var_deref
+ expand - more sophisticated dereferencing with callbacks using Iterate

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

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