/[cvs]/nfo/perl/libs/Data/Mungle/Transform/Deep.pm
ViewVC logotype

Annotation of /nfo/perl/libs/Data/Mungle/Transform/Deep.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.24 - (hide 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 joko 1.7 ## ---------------------------------------------------------------------------
2 joko 1.24 ## $Id: Deep.pm,v 1.23 2003/05/13 07:39:22 joko Exp $
3 joko 1.7 ## ---------------------------------------------------------------------------
4 joko 1.8 ## $Log: Deep.pm,v $
5 joko 1.24 ## 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 joko 1.23 ## Revision 1.22 2003/05/10 17:09:18 jonen
9     ## + added keep of empty arrays/hashes if 'expand' for php
10     ##
11 jonen 1.22 ## Revision 1.21 2003/04/09 07:21:56 joko
12     ## childObj2string now inside Encode.pm, renamed to 'twingle_reference'
13     ##
14 joko 1.21 ## Revision 1.20 2003/04/04 17:31:23 joko
15     ## minor update to 'childObj2string'
16     ##
17 joko 1.20 ## Revision 1.19 2003/03/28 03:11:25 jonen
18     ## + bugfix
19     ##
20 jonen 1.19 ## Revision 1.18 2003/03/28 03:07:26 jonen
21     ## + minor changes
22     ##
23 jonen 1.18 ## Revision 1.17 2003/03/27 15:17:07 joko
24     ## namespace fixes for Data::Mungle::*
25     ##
26 joko 1.17 ## Revision 1.16 2003/03/27 15:04:52 joko
27     ## minor update: comment
28     ##
29 joko 1.16 ## Revision 1.15 2003/02/27 14:39:48 jonen
30     ## + fixed bug at _hash2object()
31     ##
32 jonen 1.15 ## 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 jonen 1.14 ## Revision 1.13 2003/02/21 01:48:50 joko
37     ## renamed core function
38     ##
39 joko 1.13 ## Revision 1.12 2003/02/20 22:45:19 joko
40     ## fix regarding new deep_copy
41     ##
42 joko 1.12 ## 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 joko 1.11 ## 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 joko 1.10 ## Revision 1.9 2003/02/18 19:35:56 joko
50     ## +- modified 'sub refexpr2perlref': now can get the $delimiter passed, too
51     ##
52 joko 1.9 ## Revision 1.8 2003/02/09 05:10:56 joko
53     ## + minor update
54     ##
55 joko 1.8 ## 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 joko 1.7 ## 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 joko 1.1
82    
83 joko 1.17 package Data::Mungle::Transform::Deep;
84 joko 1.1
85     use strict;
86     use warnings;
87    
88     require Exporter;
89     our @ISA = qw( Exporter );
90 joko 1.2 our @EXPORT_OK = qw(
91 joko 1.4 &expand
92 joko 1.7 &deep_copy
93 joko 1.10 &merge_to
94 joko 1.1 );
95 joko 1.10
96 joko 1.1
97     use attributes;
98 joko 1.2 use Data::Dumper;
99 joko 1.10 use Iterate;
100 joko 1.7
101 joko 1.12 use Pitonyak::DeepCopy;
102 joko 1.21 use Data::Mungle::Transform::Encode qw( latin_to_utf8 latin_to_utf8_scalar utf8_to_latin utf8_to_latin_scalar twingle_reference );
103 joko 1.17 use Data::Mungle::Code::Ref qw( ref_slot );
104 joko 1.1
105 joko 1.10 sub numhash2list {
106 joko 1.1 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 joko 1.10 numhash2list($vref->{$_});
122 joko 1.1 }
123     # } else {
124     # #$vref->{$_} = scalar2iso($vref->{$_});
125     # }
126     }
127     }
128     }
129    
130    
131 joko 1.9 # 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 joko 1.10 sub _var_deref_test {
135 joko 1.1 my $obj = shift;
136 joko 1.4 my $options = shift;
137 joko 1.1 my $result;
138     if ((ref $obj) eq 'ARRAY') {
139     foreach (@{$obj}) {
140     my $ref = ref $_;
141     if ($ref) {
142 joko 1.4 push(@{$result}, var_deref($_, $options));
143     #undef $_;
144 joko 1.1 } else {
145 joko 1.4 #push(@{$result}, $_);
146     push(@{$result}, deep_copy($_));
147 joko 1.1 }
148 joko 1.4 #undef $_ if $options->{destroy};
149     #$options->{destroy}->($_) if $options->{destroy};
150 joko 1.1 }
151 joko 1.2 # TODO: "} elsif (ref $obj eq 'HASH') { [...] } else { croak 'could not deref blah'; }" ???
152 joko 1.1 } else {
153     foreach (keys %{$obj}) {
154     my $key = $_;
155     my $ref = ref $obj->{$_};
156     if ($ref) {
157 joko 1.4 $result->{$_} = var_deref($obj->{$_}, $options);
158     #undef $obj->{$_};
159 joko 1.1 } else {
160 joko 1.4 #$result->{$_} = $obj->{$_};
161     $result->{$_} = deep_copy($obj->{$_});
162 joko 1.1 }
163 joko 1.4 #undef $obj->{$_} if $options->{destroy};
164     #$options->{destroy}->($obj->{$_}) if $options->{destroy};
165 joko 1.1 }
166     }
167 joko 1.4 #undef $obj if $options->{destroy};
168     $options->{cb_destroy}->($obj) if $options->{cb_destroy};
169 joko 1.1 return $result;
170     }
171    
172    
173 joko 1.13 # 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 joko 1.23 # TODO: Review: Could this be revamped using Clone.pm?
176 joko 1.4 sub expand {
177    
178     my $obj = shift;
179     my $options = shift;
180     my $result;
181    
182 joko 1.10 #print "ref: ", ref $obj, "\n";
183    
184     if (ref $obj eq 'ARRAY') {
185 jonen 1.22
186     # if we expand for php, keep empty ARRAY
187     if($#$obj == -1 && $options->{childObj2string}) {
188     $result = $obj;
189     } else {
190 joko 1.4 IterArray @$obj, sub {
191     my $item;
192     # if current item is a reference ...
193     if (ref $_[0]) {
194 jonen 1.14 $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 joko 1.21 $item = twingle_reference($item);
199 jonen 1.14 } else {
200     # ... expand structure recursively
201     $item = expand($_[0], $options);
202     }
203 joko 1.4 # 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 joko 1.10 $item = latin_to_utf8_scalar($item) if ($item && $options->{utf8});
212     $item = utf8_to_latin_scalar($item) if ($item && $options->{to_latin});
213 joko 1.4 }
214 joko 1.23
215     $item = '' if $options->{define} and not defined $item;
216 joko 1.4 #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 jonen 1.22 }
221 joko 1.4
222 joko 1.10 } elsif (ref $obj eq 'CODE') {
223     #print Dumper($obj);
224     #exit;
225    
226 joko 1.4 # TODO: "} elsif (ref $obj eq 'HASH') { [...] } else { croak 'could not deref blah'; }" ???
227 joko 1.10 } elsif (ref $obj) {
228 jonen 1.22
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 joko 1.4 IterHash %$obj, sub {
235 joko 1.24 my $key = $_[0];
236 joko 1.4 my $item;
237 joko 1.24
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 joko 1.4 # if current item is a reference ...
243     if (ref $_[1]) {
244 jonen 1.14 $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 jonen 1.22 if ($item && $options->{childObj2string} && !(ref $_[1] eq "ARRAY") && !(ref $_[1] eq "HASH") && !(ref $_[1] eq "Set::Object")) {
248 joko 1.21 $item = twingle_reference($item);
249 jonen 1.14 } else {
250     # ... expand structure recursively
251     $item = expand($_[1], $options);
252     }
253 joko 1.4 # 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 joko 1.10 $item = latin_to_utf8_scalar($item) if ($item && $options->{utf8});
262     $item = utf8_to_latin_scalar($item) if ($item && $options->{to_latin});
263 joko 1.4 }
264 joko 1.23
265     $item = '' if $options->{define} and not defined $item;
266 joko 1.4 #$result->{$_[0]} = $item if $item; # use item only if not undef (TODO: make configurable via $options)
267 joko 1.24 $result->{$key} = $item; # use item in any case
268 joko 1.4 }
269 jonen 1.22 }
270 joko 1.4
271 joko 1.10 } else {
272     #die ("not a reference!");
273     $result = $obj;
274    
275 joko 1.4 }
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 joko 1.13 # latin_to_utf8($result) if ($options->{utf8});
280 joko 1.4
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 joko 1.1
290    
291     my @indexstack;
292 joko 1.10 sub _var_traverse_mixin_update_old {
293 joko 1.1 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 joko 1.10 sub merge_to {
337     _hash2object(@_);
338     # TODO:
339 joko 1.16 # re-implement using CPAN's "Iterate" and/or a modified Hash::Merge.
340 joko 1.2 }
341    
342 joko 1.10
343 joko 1.2 # todo: maybe do diff(s) here sometimes!?
344 joko 1.10 sub _hash2object {
345 joko 1.2 my $object = shift;
346     my $data = shift;
347     my $options = shift;
348    
349 joko 1.3 #print "hash2object", "\n";
350    
351 joko 1.2 # "patch" needed 'cause php passes numerical-indexed-arrays as hash-tables,
352     # this method corrects this
353 joko 1.10 numhash2list($data) if ($options->{php});
354 joko 1.2
355     # utf8-conversion/-encoding (essential for I18N)
356 jonen 1.15 utf8_to_latin($data) if ($options->{utf8});
357 joko 1.2
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 joko 1.10 _hash2object_traverse_mixin($object, $data, 0, $options);
366 joko 1.2
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 joko 1.10 sub _hash2object_traverse_mixin {
389 joko 1.2 my $object = shift;
390     my $data = shift;
391     my $bool_recursion = shift;
392 joko 1.5 my $options = shift;
393 joko 1.2
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 joko 1.10
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 joko 1.2
416     # print STDERR "===", "reftype: ", attributes::reftype($obj), "\n";
417    
418 joko 1.5 my @fields;
419 joko 1.2 # loop through fields of object (Tangram-object)
420 joko 1.5 @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 jonen 1.6 # @fields = keys %{$data} if $options->{mixin};
425 joko 1.5
426     foreach (@fields) {
427 joko 1.7 my $field = $_;
428     push @indexstack, $field;
429 joko 1.2
430     # determine type of object
431 joko 1.7 my $ref = ref $object->{$field};
432 joko 1.2 # print STDERR "attrname: $_ ATTRref: $ref", "\n";
433     if ($ref) {
434 joko 1.10 _hash2object_traverse_mixin($object->{$field}, $data, 1, $options);
435 joko 1.2 } else {
436 joko 1.10 my $val = ref_slot($data, \@indexstack);
437 joko 1.7
438 joko 1.8 #print Dumper($options);
439 joko 1.7 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 joko 1.2 }
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 jonen 1.6 # if ($ref && $_) {
478     if ($ref) {
479 joko 1.10 _hash2object_traverse_mixin($_, $data, 1, $options);
480 joko 1.2 } else {
481 jonen 1.6 $object->[$i] = $_ if defined $_;
482 joko 1.2 }
483     pop @indexstack;
484     $i++;
485     }
486 joko 1.10
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 joko 1.2 }
496    
497     }
498    
499    
500 joko 1.10 # 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 joko 1.2 }
518    
519 joko 1.10 # 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 joko 1.7 sub deep_copy {
523 joko 1.12 Pitonyak::DeepCopy::deep_copy(@_);
524 joko 1.1 }
525 joko 1.12
526 joko 1.1
527     1;
528 joko 1.10 __END__

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