/[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.14 - (show annotations)
Sat Feb 22 17:13:55 2003 UTC (21 years, 4 months ago) by jonen
Branch: MAIN
Changes since 1.13: +34 -5 lines
+ added function 'childObject2string()' to encode 'child'-references to option related string
+ use new option at 'expand()' for replacing 'childObject2string'

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

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