/[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.22 - (show annotations)
Sat May 10 17:09:18 2003 UTC (21 years, 2 months ago) by jonen
Branch: MAIN
Changes since 1.21: +18 -4 lines
+ added keep of empty arrays/hashes if 'expand' for php

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

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