/[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.17 - (show annotations)
Thu Mar 27 15:17:07 2003 UTC (21 years, 3 months ago) by joko
Branch: MAIN
Changes since 1.16: +7 -4 lines
namespace fixes for Data::Mungle::*

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

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