/[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.21 - (show annotations)
Wed Apr 9 07:21:56 2003 UTC (21 years, 3 months ago) by joko
Branch: MAIN
Changes since 1.20: +7 -23 lines
childObj2string now inside Encode.pm, renamed to 'twingle_reference'

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

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