/[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.24 - (show annotations)
Mon Jun 7 16:44:54 2004 UTC (20 years, 1 month ago) by joko
Branch: MAIN
CVS Tags: HEAD
Changes since 1.23: +11 -3 lines
sub expand: Now also converts hash-keys to/from utf-8

1 ## ---------------------------------------------------------------------------
2 ## $Id: Deep.pm,v 1.23 2003/05/13 07:39:22 joko Exp $
3 ## ---------------------------------------------------------------------------
4 ## $Log: Deep.pm,v $
5 ## Revision 1.23 2003/05/13 07:39:22 joko
6 ## new option 'define' for "sub expand": set value to empty string if desired
7 ##
8 ## Revision 1.22 2003/05/10 17:09:18 jonen
9 ## + added keep of empty arrays/hashes if 'expand' for php
10 ##
11 ## Revision 1.21 2003/04/09 07:21:56 joko
12 ## childObj2string now inside Encode.pm, renamed to 'twingle_reference'
13 ##
14 ## Revision 1.20 2003/04/04 17:31:23 joko
15 ## minor update to 'childObj2string'
16 ##
17 ## Revision 1.19 2003/03/28 03:11:25 jonen
18 ## + bugfix
19 ##
20 ## Revision 1.18 2003/03/28 03:07:26 jonen
21 ## + minor changes
22 ##
23 ## Revision 1.17 2003/03/27 15:17:07 joko
24 ## namespace fixes for Data::Mungle::*
25 ##
26 ## Revision 1.16 2003/03/27 15:04:52 joko
27 ## minor update: comment
28 ##
29 ## Revision 1.15 2003/02/27 14:39:48 jonen
30 ## + fixed bug at _hash2object()
31 ##
32 ## Revision 1.14 2003/02/22 17:13:55 jonen
33 ## + added function 'childObject2string()' to encode 'child'-references to option related string
34 ## + use new option at 'expand()' for replacing 'childObject2string'
35 ##
36 ## Revision 1.13 2003/02/21 01:48:50 joko
37 ## renamed core function
38 ##
39 ## Revision 1.12 2003/02/20 22:45:19 joko
40 ## fix regarding new deep_copy
41 ##
42 ## Revision 1.11 2003/02/20 21:13:54 joko
43 ## - removed implementation of deep_copy2 - get this from the Pitonyak namespace (now cloned to repository)
44 ##
45 ## Revision 1.10 2003/02/20 20:48:00 joko
46 ## - refactored lots of code to Data::Code::Ref
47 ## + alternative 'deep_copy' implementation
48 ##
49 ## Revision 1.9 2003/02/18 19:35:56 joko
50 ## +- modified 'sub refexpr2perlref': now can get the $delimiter passed, too
51 ##
52 ## Revision 1.8 2003/02/09 05:10:56 joko
53 ## + minor update
54 ##
55 ## Revision 1.7 2003/01/19 03:26:59 joko
56 ## + added 'sub deep_copy' - refactored from libp
57 ## + add 'sub merge' - exported from CPAN's 'Hash::Merge'
58 ##
59 ## Revision 1.6 2002/12/23 11:27:53 jonen
60 ## + changed behavior WATCH!
61 ##
62 ## Revision 1.5 2002/12/16 19:57:54 joko
63 ## + option 'init'
64 ##
65 ## Revision 1.4 2002/12/05 13:56:49 joko
66 ## - var_deref
67 ## + expand - more sophisticated dereferencing with callbacks using Iterate
68 ##
69 ## Revision 1.3 2002/12/03 05:34:55 joko
70 ## + bugfix: now utilizing var_utf2iso from Data::Transform::Encode
71 ##
72 ## Revision 1.2 2002/12/01 04:44:07 joko
73 ## + code from Data::Transform::OO
74 ##
75 ## Revision 1.1 2002/11/29 04:49:20 joko
76 ## + initial check-in
77 ##
78 ## Revision 1.1 2002/10/10 03:26:00 cvsjoko
79 ## + new
80 ## ---------------------------------------------------------------------------
81
82
83 package Data::Mungle::Transform::Deep;
84
85 use strict;
86 use warnings;
87
88 require Exporter;
89 our @ISA = qw( Exporter );
90 our @EXPORT_OK = qw(
91 &expand
92 &deep_copy
93 &merge_to
94 );
95
96
97 use attributes;
98 use Data::Dumper;
99 use Iterate;
100
101 use Pitonyak::DeepCopy;
102 use Data::Mungle::Transform::Encode qw( latin_to_utf8 latin_to_utf8_scalar utf8_to_latin utf8_to_latin_scalar twingle_reference );
103 use Data::Mungle::Code::Ref qw( ref_slot );
104
105 sub numhash2list {
106 my $vref = shift;
107 #my $context = shift;
108 if (ref $vref eq 'HASH') {
109 foreach (keys %{$vref}) {
110 # if ((ref $vref->{$_}) =~ m/HASH/) {
111 if ((ref $vref->{$_}) =~ m/HASH/) {
112 if ($vref->{$_}{0}) { # might be a numerical-indexed array
113 #print "000\n";
114 #print "context: $context\n";
115 my @arr;
116 foreach my $key_numeric (sort keys %{$vref->{$_}}) {
117 push @arr, $vref->{$_}{$key_numeric};
118 }
119 $vref->{$_} = \@arr;
120 }
121 numhash2list($vref->{$_});
122 }
123 # } else {
124 # #$vref->{$_} = scalar2iso($vref->{$_});
125 # }
126 }
127 }
128 }
129
130
131 # FIXME: could this be refactored using expand?
132 # btw: "expand": look at scripts@CPAN (System Administration):
133 # there is a perl make with perl
134 sub _var_deref_test {
135 my $obj = shift;
136 my $options = shift;
137 my $result;
138 if ((ref $obj) eq 'ARRAY') {
139 foreach (@{$obj}) {
140 my $ref = ref $_;
141 if ($ref) {
142 push(@{$result}, var_deref($_, $options));
143 #undef $_;
144 } else {
145 #push(@{$result}, $_);
146 push(@{$result}, deep_copy($_));
147 }
148 #undef $_ if $options->{destroy};
149 #$options->{destroy}->($_) if $options->{destroy};
150 }
151 # TODO: "} elsif (ref $obj eq 'HASH') { [...] } else { croak 'could not deref blah'; }" ???
152 } else {
153 foreach (keys %{$obj}) {
154 my $key = $_;
155 my $ref = ref $obj->{$_};
156 if ($ref) {
157 $result->{$_} = var_deref($obj->{$_}, $options);
158 #undef $obj->{$_};
159 } else {
160 #$result->{$_} = $obj->{$_};
161 $result->{$_} = deep_copy($obj->{$_});
162 }
163 #undef $obj->{$_} if $options->{destroy};
164 #$options->{destroy}->($obj->{$_}) if $options->{destroy};
165 }
166 }
167 #undef $obj if $options->{destroy};
168 $options->{cb_destroy}->($obj) if $options->{cb_destroy};
169 return $result;
170 }
171
172
173 # convert values in hash to utf8 (and back) to be ready for (e.g.) encapsulation in XML
174 # but still using the known latin locale stuff
175 # TODO: Review: Could this be revamped using Clone.pm?
176 sub expand {
177
178 my $obj = shift;
179 my $options = shift;
180 my $result;
181
182 #print "ref: ", ref $obj, "\n";
183
184 if (ref $obj eq 'ARRAY') {
185
186 # if we expand for php, keep empty ARRAY
187 if($#$obj == -1 && $options->{childObj2string}) {
188 $result = $obj;
189 } else {
190 IterArray @$obj, sub {
191 my $item;
192 # if current item is a reference ...
193 if (ref $_[0]) {
194 $item = $_[0];
195 # if $options->{childObj2string} is TRUE or STRING don't expand referenced object,
196 # instead replace it by per option choosed string (default: o_<classname>_<ref type>_<guid> )
197 if ($item && $options->{childObj2string}) {
198 $item = twingle_reference($item);
199 } else {
200 # ... expand structure recursively
201 $item = expand($_[0], $options);
202 }
203 # destroy item via seperate callback method (a POST) if requested
204 #$options->{cb}->{destroy}->($_[0]) if $options->{destroy};
205
206 # ... assume plain scalar
207 } else {
208 #$item = deep_copy($_[0]);
209 $item = $_[0];
210 # conversions/encodings
211 $item = latin_to_utf8_scalar($item) if ($item && $options->{utf8});
212 $item = utf8_to_latin_scalar($item) if ($item && $options->{to_latin});
213 }
214
215 $item = '' if $options->{define} and not defined $item;
216 #push(@{$result}, $item) if $item; # use item only if not undef (TODO: make configurable via $options)
217 push(@{$result}, $item); # use item in any case
218
219 }
220 }
221
222 } elsif (ref $obj eq 'CODE') {
223 #print Dumper($obj);
224 #exit;
225
226 # TODO: "} elsif (ref $obj eq 'HASH') { [...] } else { croak 'could not deref blah'; }" ???
227 } elsif (ref $obj) {
228
229 # if we expand for php, keep empty HASH
230 my @tmp = keys %$obj;
231 if($#tmp == -1 && $options->{childObj2string}) {
232 $result = $obj;
233 } else {
234 IterHash %$obj, sub {
235 my $key = $_[0];
236 my $item;
237
238 # conversions/encodings
239 $key = latin_to_utf8_scalar($key) if ($key && $options->{utf8});
240 $key = utf8_to_latin_scalar($key) if ($key && $options->{to_latin});
241
242 # if current item is a reference ...
243 if (ref $_[1]) {
244 $item = $_[1];
245 # if $options->{childObj2string} is TRUE or STRING don't expand referenced object,
246 # instead replace it by per option choosed string (default: o_<classname>_<ref type>_<guid> )
247 if ($item && $options->{childObj2string} && !(ref $_[1] eq "ARRAY") && !(ref $_[1] eq "HASH") && !(ref $_[1] eq "Set::Object")) {
248 $item = twingle_reference($item);
249 } else {
250 # ... expand structure recursively
251 $item = expand($_[1], $options);
252 }
253 # destroy item via seperate callback method (a POST) if requested
254 #$options->{cb}->{destroy}->($_[1]) if $options->{destroy};
255
256 # ... assume plain scalar
257 } else {
258 #$item = deep_copy($_[1]);
259 $item = $_[1];
260 # conversions/encodings
261 $item = latin_to_utf8_scalar($item) if ($item && $options->{utf8});
262 $item = utf8_to_latin_scalar($item) if ($item && $options->{to_latin});
263 }
264
265 $item = '' if $options->{define} and not defined $item;
266 #$result->{$_[0]} = $item if $item; # use item only if not undef (TODO: make configurable via $options)
267 $result->{$key} = $item; # use item in any case
268 }
269 }
270
271 } else {
272 #die ("not a reference!");
273 $result = $obj;
274
275 }
276
277 # convert all values to utf8 (inside complex struct)
278 # now done in core-item-callbacks via Greg London's "Iterate" from CPAN
279 # latin_to_utf8($result) if ($options->{utf8});
280
281 # destroy persistent object from memory to be sure to get a fresh one next time
282 #undef $obj if $options->{destroy};
283 #$options->{cb_destroy}->($obj) if $options->{cb_destroy};
284 #$options->{cb}->{destroy}->($obj) if $options->{destroy};
285
286 return $result;
287 }
288
289
290
291 my @indexstack;
292 sub _var_traverse_mixin_update_old {
293 my $obj = shift;
294 my $data = shift;
295 my $bool_recursion = shift;
296 if (!$bool_recursion) {
297 @indexstack = ();
298 }
299 my $result;
300
301 # 'ARRAY' does not work!
302 if ((ref $obj) eq 'ARRAY') {
303 #print "ARRAY\n";
304 my $i = 0;
305 foreach (@{$obj}) {
306 push @indexstack, $i;
307 my $ref = ref $_;
308 if ($ref) {
309 #$obj->[$i] = var_mixin($_, $data, 1);
310 var_traverse_mixin_update($_, $data, 1);
311 } else {
312 $obj->[$i] = $_;
313 }
314 pop @indexstack;
315 $i++;
316 }
317 } else {
318 foreach (keys %{$obj}) {
319 push @indexstack, $_;
320 my $ref = ref $obj->{$_};
321 if ($ref) {
322 #$obj->{$_} = var_mixin($obj->{$_}, $data, 1);
323 var_traverse_mixin_update($obj->{$_}, $data, 1);
324 } else {
325 #$obj->{$_} = $obj->{$_};
326 my $evstring = getSlot($data);
327 $obj->{$_} = $evstring;
328
329 }
330 pop @indexstack;
331 }
332 }
333 #return $result;
334 }
335
336 sub merge_to {
337 _hash2object(@_);
338 # TODO:
339 # re-implement using CPAN's "Iterate" and/or a modified Hash::Merge.
340 }
341
342
343 # todo: maybe do diff(s) here sometimes!?
344 sub _hash2object {
345 my $object = shift;
346 my $data = shift;
347 my $options = shift;
348
349 #print "hash2object", "\n";
350
351 # "patch" needed 'cause php passes numerical-indexed-arrays as hash-tables,
352 # this method corrects this
353 numhash2list($data) if ($options->{php});
354
355 # utf8-conversion/-encoding (essential for I18N)
356 utf8_to_latin($data) if ($options->{utf8});
357
358 # get fresh object from database
359 # todo:
360 # - handle "locked" objects !!!
361 # - check if already another update occoured (object-revisioning needed!)
362 #my $obj = $self->getObject($oid);
363
364 # mix changes into fresh object and save it back
365 _hash2object_traverse_mixin($object, $data, 0, $options);
366
367 # done in core mixin function?
368 #$self->{storage}->update($obj);
369
370 # we should "undef" some objects here to destroy them in memory and enforce reloading when used next time
371 # simulate:
372 #$self->{storage}->disconnect();
373
374 }
375
376 # ------------------------------------
377 # core mixin function
378 # TODO-FEATURE: make this possible in a reverse way: object is empty (no fields) and gets filled up by mixin-data
379
380 # remember keys of structures we are traversing
381 # this is a HACK:
382 # - don't (!!!) "packageglobal" @indexstack
383 # - it will lead to problems with parallelism!
384
385 #my @indexstack;
386
387 # traverse a deeply nested structure, mix in values from given hash, update underlying tangram-object
388 sub _hash2object_traverse_mixin {
389 my $object = shift;
390 my $data = shift;
391 my $bool_recursion = shift;
392 my $options = shift;
393
394 # clear our key - stack if we are called from user-code (non-recursively)
395 @indexstack = () if (!$bool_recursion);
396
397 my $classname = ref $object;
398 # if ($classname) {
399 # print STDERR "*****************", Dumper(Class::Tangram::attribute_types($classname)), "\n";
400 # }
401
402 # parser: detected OBJECT (a Tangram one?) (reftype == HASH) (assume)
403 # what's exactly done here? hmmm.... it works ;)
404 # maybe a HACK: please try not to use "IntrHash"es, maybe this cannot handle them!!!
405 # extend! check!
406
407 #print attributes::reftype($object), "\n";
408
409 # V1
410 #if ((attributes::reftype($object) eq 'HASH') && (ref($object) ne 'HASH') && (ref($object) ne 'ARRAY')) {
411 # V2
412 my $reftype = attributes::reftype($object);
413 #print "reftype: '$reftype'", "\n";
414 if ($reftype eq 'HASH') {
415
416 # print STDERR "===", "reftype: ", attributes::reftype($obj), "\n";
417
418 my @fields;
419 # loop through fields of object (Tangram-object)
420 @fields = keys %{$object};
421
422 # loop through fields of to.be.injected-data (arbitrary Perl-data-structure)
423 @fields = keys %{$data} if $options->{init};
424 # @fields = keys %{$data} if $options->{mixin};
425
426 foreach (@fields) {
427 my $field = $_;
428 push @indexstack, $field;
429
430 # determine type of object
431 my $ref = ref $object->{$field};
432 # print STDERR "attrname: $_ ATTRref: $ref", "\n";
433 if ($ref) {
434 _hash2object_traverse_mixin($object->{$field}, $data, 1, $options);
435 } else {
436 my $val = ref_slot($data, \@indexstack);
437
438 #print Dumper($options);
439 my $field_target = $field;
440 if (my $pattern = $options->{pattern_strip_key}) {
441 print "pattern: $pattern", "\n";
442 $field_target =~ s/$pattern//;
443 print "field: $field_target", "\n";
444 }
445
446 $object->{$field_target} = $val if defined $val;
447 }
448 pop @indexstack;
449 }
450
451 # save object to database ...
452 # ... do an update if it already exists, do an insert if it doesn't
453 # my $objectId = $self->{storage}->id($obj);
454 # $logger->debug( __PACKAGE__ . "->saveObjectFromHash_traverse_mixin_update( object $obj objectId $objectId )" );
455 # if ($objectId) {
456 # $self->{storage}->update($obj);
457
458 #print __PACKAGE__ . ":", "\n";
459 #print Dumper($object);
460
461 # } else {
462 # $self->{storage}->insert($obj);
463 # }
464
465 }
466
467 #&& ( ref($obj) ne 'HASH' )
468
469 # loop through entries of array (IntrArray, isn't it?)
470 if ((ref $object) eq 'ARRAY') {
471 # print STDERR "===", "refttype ", attributes::reftype($obj), "\n";
472 my $i = 0;
473 foreach (@{$object}) {
474 push @indexstack, $i;
475 my $ref = ref $_;
476 # print STDERR "attrname: $_ ATTRref: $ref", "\n";
477 # if ($ref && $_) {
478 if ($ref) {
479 _hash2object_traverse_mixin($_, $data, 1, $options);
480 } else {
481 $object->[$i] = $_ if defined $_;
482 }
483 pop @indexstack;
484 $i++;
485 }
486
487 =pod
488 # object?
489 } else {
490 print "reference: ", ref $object, "\n";
491 print "reftype: ", $reftype, "\n";
492 die(__PACKAGE__ . "->_hash2object_traverse_mixin: can not handle this!");
493 =cut
494
495 }
496
497 }
498
499
500 # ACK's go to Randal L. Schwartz <merlyn@stonehenge.com>
501 # on the website:
502 # This text is copyright by Miller-Freeman, Inc., and is used with their permission.
503 # Further distribution or use is not permitted.
504 # please visit http://www.stonehenge.com/merlyn/UnixReview/col30.html
505 sub deep_copy1 {
506 my $this = shift;
507 if (not ref $this) {
508 $this;
509 } elsif (ref $this eq "ARRAY") {
510 [map deep_copy1($_), @$this];
511 } elsif (ref $this eq "HASH") {
512 +{map { $_ => deep_copy1($this->{$_}) } keys %$this};
513 } elsif (ref $this eq "CODE") {
514 $this;
515 } else { die "deep_copy1 asks: what type is $this?" }
516 #} else { print "deep_copy asks: what type is $this?", "\n"; }
517 }
518
519 # ACK's go to Andrew Pitonyak
520 # Copyright 2002, Andrew Pitonyak (perlboy@pitonyak.org)
521 # please visit: http://www.pitonyak.org/code/perl/Pitonyak/DeepCopy.pm.html
522 sub deep_copy {
523 Pitonyak::DeepCopy::deep_copy(@_);
524 }
525
526
527 1;
528 __END__

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