/[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.13 - (show annotations)
Fri Feb 21 01:48:50 2003 UTC (21 years, 4 months ago) by joko
Branch: MAIN
Changes since 1.12: +7 -2 lines
renamed core function

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

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