/[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.19 - (show annotations)
Fri Mar 28 03:11:25 2003 UTC (21 years, 3 months ago) by jonen
Branch: MAIN
Changes since 1.18: +6 -2 lines
+ bugfix

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

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