/[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.10 - (show annotations)
Thu Feb 20 20:48:00 2003 UTC (21 years, 4 months ago) by joko
Branch: MAIN
Changes since 1.9: +124 -154 lines
- refactored lots of code to Data::Code::Ref
+ alternative 'deep_copy' implementation

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

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