/[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.20 - (show annotations)
Fri Apr 4 17:31:23 2003 UTC (21 years, 3 months ago) by joko
Branch: MAIN
Changes since 1.19: +15 -6 lines
minor update to 'childObj2string'

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

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