/[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.16 - (show annotations)
Thu Mar 27 15:04:52 2003 UTC (21 years, 3 months ago) by joko
Branch: MAIN
Changes since 1.15: +5 -2 lines
minor update: comment

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

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