File Coverage

lib/Data/Deep.pm
Criterion Covered Total %
statement 533 591 90.1
branch 285 358 79.6
condition 105 166 63.2
subroutine 28 32 87.5
pod 15 23 65.2
total 966 1170 82.5


line stmt bran cond sub pod time code
1             ######################################################################
2             #############################################################################
3             package Data::Deep;
4             ##############################################################################
5             # Ultimate tool for Perl data manipulation
6             ############################################################################
7             ### Deep.pm
8             ############################################################################
9             # Copyright (c) 2005 Matthieu Damerose. All rights reserved.
10             # This program is free software; you can redistribute it and/or
11             # modify it under the same terms as Perl itself.
12             ############################################################################
13             ###
14             ##
15             #
16             #
17              
18              
19             =head1 NAME
20              
21             Data::Deep - Complexe Data Structure analysis and manipulation
22              
23             =head1 SYNOPSIS
24              
25             use Data::Deep;
26              
27             $dom1=[ \{'toto' => 12}, 33, {o=>5,d=>12}, 'titi' ];
28              
29             $dom2=[ \{'toto' => 12, E=>3},{d=>12,o=>5}, 'titi' ];
30              
31             my @patch = compare($dom1, $dom2);
32              
33             use Data::Deep qw(:DEFAULT :convert :config);
34              
35             o_complex(1); # deeper analysis results
36              
37             print join("\n", domPatch2TEXT( @patch ) );
38              
39             @patch = (
40             'add(@0$,@0$%E)=3','remove(@1,)=33','move(@2,@1)=','move(@3,@2)='
41             );
42              
43             $dom2 = applyPatch($dom1,@patch);
44              
45             @list_found = search($dom1, ['@',1])
46              
47             @list_found = search($dom1, patternText2Dom('@1'))
48              
49              
50              
51             =head1 DESCRIPTION
52              
53             Data::Deep provides search, path, compare and applyPatch functions which may operate on complex Perl Data Structure
54             for introspection, usage and manipulation
55             (ref, hash or array, array of hash, blessed object and siple scalar).
56             Package, Filehandles and functions are partially supported (type and location is considered).
57             Loop circular references are also considered as a $t1 variable and partially supported.
58              
59              
60             =head2 path definition
61              
62             path expression identify the current element node location in a complex Perl data structure.
63             pattern used in function search is used to match a part of this path.
64              
65             Path is composed internally of an array of following elements :
66              
67             ('%', '') to match a hash table at value
68             ('@', ) to match an array at specified index value
69             ('*', '') to match a global reference
70             ('|', '') to match a blessed module reference
71              
72             ('$') to match a reference
73             ('&') to match a code reference
74             ('$loop') to match a loop reference (circular reference)
75              
76             ('=' ) to match the leaf node
77              
78             In text mode a keyname may be defined by entering an hash-ref of keys in o_key()
79             then '/keyname' will appears in the path text results or could be provided
80             to convert function textPatch2dom() and patternText2dom()
81              
82              
83             Modifier can be placed in the path with types to checks :
84              
85             EX:
86              
87             ?% : match with hash-table content (any key match)
88             ?@ : match with an array content (any index match)
89             ?= : any value
90             ?* : any glob type
91             ?$ : any reference
92             ?=%@ : any value, hash-table or array
93             ?%@*|$&= : everything
94              
95             Evaluation function :
96             sub{... test with $_ ... } will be executed to match the node
97             EX: sub { /\d{2,}/ } match numbers of minimal size of two
98              
99             Patch is a directional operation to apply difference between two nodes resulting from compare($a, $b)
100             Patch allow the $a complex perl data structure to be changed to $b using applyPatch($a,@patch)
101              
102             Each Patch operation is composed of :
103             - an action :
104             'add' for addition of an element from source to destination
105             'remove' is the suppression from source to destination
106             'move' if possible the move of a value or Perl Dom
107             'change' describe the modification of a value
108             'erase' is managed internally for array cleanup when using 'move'
109             - a source path on which the value is taken from
110             - a destination path on which is applied the change (most of the time same as source)
111              
112             Three patch formats can be use :
113             - dom : interaction with search, path, compare, ApplyPatch
114             - text : programmer facilities to use a single scalar for a patch operation
115             - ihm : a small readble IHM text aim for output only
116              
117             Convert function may operation the change between this formats.
118              
119              
120             DOM : dom patch hash-ref sample
121              
122             EX: my $patch1 =
123             { action=>'change',
124             path_orig=>['@0','$','%a'],
125             path_dest=>['@0','$','%a'],
126             val_orig=>"toto",
127             val_dest=>"tata"
128             };
129              
130             TEXT : text output mode patch could be :
131              
132             add(,)=
133             remove(,)=
134             change(,)=/=>
135             move(,)
136              
137              
138             =head2 Important note :
139              
140             * search() and path() functions use paths in "dom" format :
141              
142             DOM (simple array of elements described above)
143             EX: ['@',1,'%','r','=',432]
144              
145             * applyPath() can use TEXT or DOM patch format in input.
146              
147             * compare() produce "dom" patch format in output.
148              
149              
150             All function prefer the use of dom (internal format) then no convertion is done.
151             Output (user point of view) is text or ihm.
152              
153             format patches dom can be converted to TEXT : domPatch2TEXT
154             format patches text can be converted to DOM : textPatch2DOM
155             format patches dom can be converted to IHM : domPatch2IHM
156              
157             See conversion function
158              
159             =cut
160              
161              
162             ##############################################################################
163             # General version and rules
164             ##############################################################################
165 1     1   6586 use 5.004;
  1         3  
166             $VERSION = '0.13';
167             #$| = 1;
168              
169             ##############################################################################
170             # Module dep
171             ##############################################################################
172              
173 1     1   7 use Carp;
  1         2  
  1         73  
174 1     1   5 use strict;
  1         2  
  1         17  
175 1     1   3 no warnings;
  1         1  
  1         28  
176 1     1   278 no integer;
  1         11  
  1         4  
177 1     1   27 no strict 'refs';
  1         1  
  1         26  
178              
179              
180 1     1   758 use overload; require Exporter; our @ISA = qw(Exporter);
  1         723  
  1         6  
181              
182              
183             our @DEFAULT =
184             qw(
185             travel
186             visitor_patch
187             visitor_dump
188             visitor_perl_dump
189             search
190             compare
191             path
192             applyPatch
193             __d
194             );
195              
196             our @EXPORT = @DEFAULT;
197              
198              
199             our @CONFIG =
200             qw(
201             o_debug
202             o_follow_ref
203             o_complex
204             o_key
205             );
206              
207             our @CONVERT =
208             qw(
209             patternText2Dom
210             patternDom2Text
211             textPatch2DOM
212             domPatch2TEXT
213             domPatch2IHM
214             );
215              
216             our @EXPORT_OK = (@DEFAULT,
217             @CONFIG,
218             @CONVERT
219             );
220              
221              
222             our %EXPORT_TAGS=(
223             convert=>[@CONVERT],
224             config=>[@CONFIG]
225             );
226             ##############################################################################
227             #/````````````````````````````````````````````````````````````````````````````\
228              
229              
230             my $CONSOLE_LINE=78;
231              
232             ##############################################################################
233              
234              
235             =head2 Options Methods
236              
237             =over 4
238              
239             =item I()
240              
241             configure nodes to skip (in search or compare)
242             without parameter will return those nodes
243              
244             =cut
245              
246              
247             sub zap {
248             @_ and $Data::Deep::CFG->{zap}=shift()
249 0 0 0 0 1 0 or return $Data::Deep::CFG->{zap};
250             }
251              
252              
253             #############################################################################
254             ### OPTIONS DECLARATION
255             ##############################################################################
256             # Declare option : _opt_dcl 'o_flg'
257             # Read the option : o_flg()
258             # Set the option : o_flg(1)
259             ############################################################################
260              
261             our $CFG = {};
262              
263             my $__opt_dcl = sub { my $name = shift();
264             my $proto = shift() || '$';
265              
266 146 100 100 146 1 16785 eval 'sub '.$name."(;$proto) {"
  12794 50 66 12794 1 68276  
  1 50 33 1 1 8  
267             .' @_ and $Data::Deep::CFG->{'.$name.'}=shift()
268             or return $Data::Deep::CFG->{'.$name.'} }';
269             $@ and die '__bool_opt_dcl('.$name.') : '.$@;
270             };
271             ############################################################################
272              
273             =item I([])
274              
275             debug mode :
276             1: set debug mode on
277             0: set debug mode off
278             undef : return debug mode
279              
280             =cut
281              
282             $__opt_dcl->('o_debug');
283              
284             ############################################################################
285              
286             =item I([])
287              
288             follow mode :
289             1: follow every reference (default)
290             0: do not enter into any reference
291             undef: return if reference are followed
292              
293             =cut
294              
295             $__opt_dcl->('o_follow_ref');
296              
297             o_follow_ref(1);
298              
299              
300             ############################################################################
301              
302             =item I([])
303              
304             complex mode is used for intelligency complex (EX: elements move in an array)
305             1: complex mode used in search() & compare()
306             0: simple analysis (no complex search)
307             undef: return if reference are followed
308              
309             =cut
310              
311             $__opt_dcl->('o_complex');
312              
313              
314             ##############################################################################
315             sub debug {
316             ##############################################################################
317 11422 50   11422 0 124735 o_debug() or return;
318              
319             # B.S./WIN : no output using STDERR
320 0 0   0 0 0 sub out__ { (($^O=~/win/i)?print @_:print SDTERR @_) }
321              
322 0         0 my $l;
323 0         0 foreach $l(@_) {
324             (ref $l)
325             and out__ "\n".__d($l)
326 0 0 0     0 or do {
327 0         0 out__$l;
328 0 0       0 if (length($l)>$CONSOLE_LINE) { out__ "\n" }
  0         0  
329 0         0 else { out__ ' ' }
330             }
331             }
332 0         0 out__ "\n"
333             }
334              
335              
336             ##############################################################################
337             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
338             sub __d {
339             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
340 1831     1831   5894 my $res = join('', travel(shift(), \&visitor_perl_dump));
341             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
342              
343 1831         10025 $res =~ s/
344             ([\000-\037]|[\177-\377])
345 31         664 /sprintf("\\%o", ord ($1))/egx;
346              
347 1831         4208 return $res;
348             }
349              
350             ##############################################################################
351             ###############################################################################
352             ###############################################################################
353             # PRIVATE FX
354             ###############################################################################
355             ###############################################################################
356              
357              
358             ##############################################################################
359             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
360             my $matchPath = sub {
361             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
362             my @pattern=@{shift()}; # to match
363             my @where=@_; # current path
364             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
365              
366             # warn 'matchPath('.join(' ',@where).' , '.join(' ',@pattern).')';
367              
368              
369             my $ok;
370             # warn 'matchPath:LongAlgo( '.join(' ',@pattern).', '.join(' ',@where).' )';
371             my $i = 0;
372             PATH:while ($i<=$#where) {
373              
374             my $j = 0;
375             my $sav_i = $i;
376              
377             PATTERN: while ($i<=$#where) {
378              
379             ### CURRENT PATH
380             my $t_where = $where[$i++]; # TYPE
381              
382             ## PATTERN
383             my $t_patt = $pattern[$j++]; # TYPE
384              
385             if ($t_patt eq '/') {
386             die 'internal matchPath('.join('',@pattern).') : key usage is only in textual format (use Text and convertion patternText2Dom)';
387             }
388              
389             #print "$t_where =~ $t_patt : ";
390              
391             (index($t_patt,$t_where)==-1) and last PATTERN; # type where should be found in the pattern
392              
393             if ($t_where eq '&') { }
394             elsif ($t_where eq '$') { }
395             elsif ($t_where eq '=' or
396             $t_where eq '%' or
397             $t_where eq '@' or
398             $t_where eq '*' or
399             $t_where eq '|'
400             ) {
401              
402             my $v_where = $where[$i++];
403              
404             unless (substr($t_patt,0,1) eq '?') {
405             #print 'v';
406              
407             my $v_patt = $pattern[$j++];
408              
409             if (ref($v_patt) eq 'CODE') { # regexp or complexe val
410             local ($_) = ($v_where);
411             $v_patt->($_) or last PATTERN
412             }
413             elsif (ref($v_patt) and (__d($v_patt) ne __d($v_where))) {
414             last PATTERN;
415             }
416             elsif (!defined($v_where) and defined($v_patt)) {
417             # print '!';
418             last PATTERN;
419             }
420             elsif (defined($v_where) and !defined($v_patt)) {
421             # print '!';
422             last PATTERN;
423             }
424             elsif (defined($v_where) and defined($v_patt) and $v_patt ne $v_where) {
425             # print '!';
426             last PATTERN;
427             }
428             }
429             }
430             else {
431             #print '#';
432             ($i-1==$#where)
433             or
434             die 'Error in matched expression "'.join('',@where).'" not supported char type "'.$t_where.'".';
435             }
436             #print '.';
437             if ($j-1==$#pattern and $i-1==$#where) {
438             # warn "#found($i,$j)";
439             return $sav_i;
440             }
441              
442             }# PATTERN:
443              
444             # next time
445             ($j>1) and $i = $sav_i+1;
446              
447             }# WHERE:
448              
449             #print "\n";
450             return undef;
451             };
452              
453             ##############################################################################
454             # KEY DCL :
455              
456             sub o_key {
457             @_ and $CFG->{o_key}=shift()
458 4 100 66 4 1 119 or return $CFG->{o_key};
459             }
460              
461             =item I()
462              
463             key is a search pattern for simplifying search or compare.
464             or a group of pattern for best identification of nodes.
465              
466             hash of key path:
467              
468              
469             EX:
470             key(
471             CRC => {regexp=>['%','crc32'],
472             eval=>'{crc32}',
473             priority=>1
474             },
475             SZ => {regexp=>['%','sz'),
476             eval=>'{sz}',
477             priority=>2
478             }
479             )
480              
481              
482             regexp : path to search in the dom
483             eval : is the perl way to match the node
484             priority : on the same node two ambigues keys are prioritized
485             depth : how many upper node to return from the current match node
486              
487             =back
488              
489             =cut
490              
491              
492              
493              
494             ##############################################################################
495             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
496             my $patchDOM = sub($$$;$$) {
497             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
498             my $action = shift;
499             my $p1= shift();
500             my $p2= shift();
501             my $v1 = shift();
502             my $v2 = shift();
503             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
504              
505             my $dom = {};
506             $dom->{action} = $action;
507             $dom->{path_orig} = $p1;
508             $dom->{path_dest} = $p2;
509             $dom->{val_orig} = $v1;
510             $dom->{val_dest} = $v2;
511              
512             return $dom;
513             };
514              
515              
516             ##############################################################################
517             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
518             my $path2eval__ = sub {
519             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
520             my $first_eval = shift();
521             my $deepness = shift(); # [ 0.. N ] return N from root
522             # [-N..-1] return N stage from leaves
523             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
524              
525             my $evaled = $first_eval;
526              
527             my $dbg_head = __PACKAGE__."::path2eval__(".join(',',@_).") : ";
528             debug $dbg_head;
529             my $max=$#_;
530              
531             @_ or return $evaled;
532              
533             if (defined $deepness and $deepness<=0) { # start from the end
534             while ($deepness++<0 and $max>=0) {
535             $_[$max-1] =~ /^[\@%\*\|\/=]$/ and $max-=2
536             or
537             $_[$max] =~ /^[\$\&]$/ and $max--;
538             }
539             ($max==0) and return $evaled; # upper as root
540              
541             debug "\n negative depth $deepness: -> remaining path(".join(',',@_[0..$max]).")\n";
542             $deepness=undef;
543             }
544             my $deref='->';
545              
546             my $i=0;
547             while($i<=$max) {
548             $_ = $_[$i++];
549              
550             if ($_ eq '$') {
551             $evaled = '${'.$evaled.'}';
552             $deref = '->';
553             }
554             elsif ($_ eq '%') {
555             $evaled .= $deref."{'".$_[$i++]."'}";
556             $deref='';
557             }
558             elsif ($_ eq '@') {
559             $evaled .= $deref.'['.$_[$i++].']';
560             $deref='';
561             }
562             elsif ($_ eq '|') {
563             $i++;
564             }
565             elsif ($_ eq '*') {
566             $i++;
567             my $suiv = $_[$i] or next;
568             if ($suiv eq '%') {
569             $evaled = '*{'.$evaled.'}{HASH}';
570             $deref = '->';
571             }
572             elsif ($suiv eq '@'){
573             $evaled = '*{'.$evaled.'}{ARRAY}';
574             $deref = '->';
575             }
576             elsif ($suiv eq '$' or $suiv eq '='){
577             $evaled = '*{'.$evaled.'}{SCALAR}';
578             $deref = '->';
579             }
580             }
581             elsif ($_ eq '/') { # KEY->{eval}
582             my $keyname = $_[$i++];
583             my $THEKEY = $CFG->{o_key}{$keyname};
584             my $ev = $THEKEY->{eval} or die $dbg_head.'bad eval code for '.$keyname;
585             $evaled .= $deref.$ev;
586             $deref='';
587             }
588             elsif ($_ eq '&') {
589             $evaled = $evaled.'->()';
590             }
591             elsif ($_ eq '=') {
592             ($i==$#_) or die $dbg_head.'bad path format : value waited in path after "="';
593              
594             if ($_[$i]=~/^\d+$/) {
595             $evaled = 'int('.$evaled.'=='.$_[$i++].')'
596             }
597             else {
598             $evaled = 'int('.$evaled.' eq \''.$_[$i++].'\')'
599             }
600              
601             $deref='';
602             }
603             else {
604             die $dbg_head.'bad path format : Type '.$_.' not supported.'
605             }
606              
607             if (defined($deepness)) { # >0 start from root
608             #print "\n positive depth $deepness:";
609             last if (--$deepness==0);
610             }
611             }
612             debug "-> $evaled #\n";
613             return $evaled;
614             };
615              
616             my %loop_ref=();
617             ##############################################################################
618             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
619             sub loop_det($;@) {
620             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
621              
622 13136     13136 0 15176 my $r = shift();
623 13136 100       22412 ref($r) or return 0;
624              
625 4687         8936 $r = $r.' ';
626              
627 4687 100       6742 if (exists($loop_ref{$r})) {
628 66         163 debug "loop_det => LOOP".join('',@_) ;
629              
630 66         121 return 1;
631             }
632              
633 4621         6569 $loop_ref{$r}=1;
634 4621         7619 return 0;
635             }
636              
637             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
638             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
639             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
640             # PUBLIC FX
641             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
642             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
643              
644              
645              
646              
647             =head2 Operation Methods
648              
649             =over 4
650              
651             =cut
652              
653              
654             #############################################################
655             sub visitor_patch {
656             # Visitor which create patch for dom creation
657             #############################################################
658 111     111 0 113 my $node = shift();
659 111         96 my $depth = shift;
660 111         89 my $open = shift;
661 111         169 my @cur_path = @_;
662              
663 111         148 my $path = join('',@cur_path);
664              
665             # warn $depth.($open==1?' > ':(defined($open)?' < ':' ')).join('',@cur_path).' : '.ref($node);
666              
667 111         107 my $ref = ref($node);
668 111 100       135 if ($ref) {
669 91 100       133 if (!defined $open ) {
    100          
    50          
670 49 100       87 ($_[-1] eq '$loop') and return 'loop('.$path.','.$path.')=';
671 45 100       66 ($ref eq 'CODE') and return 'add('.$path.','.$path.')=sub{}';
672             #($ref eq 'REF') and return 'add('.$path.','.$path.')={}';
673 43 50       58 ($ref eq 'GLOB') and return 'new '.$_[-1].'()';
674             }
675             elsif ($open ==1 ) {
676 21 100       62 ($ref eq 'ARRAY') and return 'add('.$path.','.$path.')=[]';
677 11 100       45 ($ref eq 'HASH') and return 'add('.$path.','.$path.')={}';
678 2         4 return ;
679             }
680             elsif ($open ==0 ) {
681             #($ref eq 'ARRAY') and return ']';
682             #($ref eq 'HASH') and return '}';
683 21         35 return;
684             }
685              
686             }
687              
688 63 100 66     185 defined($node) and $node = "'$node'" or $node = 'undef';
689              
690              
691 63         61 pop(@cur_path);
692 63         63 pop(@cur_path);
693 63         74 $path = join('',@cur_path);
694 63         55 pop(@cur_path);
695 63         61 pop(@cur_path);
696              
697 63 100       150 ($_[-2] eq '=') and return 'add('.join('',@cur_path).','.$path.')='.$node;
698              
699              
700 43         73 return;
701              
702             # get the source code => How ?
703             # (ref($node) eq 'CODE') and return $dump.'CODE';#(&$node());
704             # return $dump.ref($node);
705              
706             }
707              
708              
709             #############################################################
710             sub visitor_perl_dump {
711             # Visitor to dump Perl structure
712             #############################################################
713 23830     23830 0 24709 my $node = shift();
714 23830         21325 my $depth = shift;
715 23830         19962 my $open = shift;
716 23830         36547 my @cur_path = @_;
717              
718 23830         21518 my $path = @cur_path;
719              
720 23830         25982 my $ref = ref($node);
721              
722 23830         35828 my ($realpack, $realtype, $id) =
723             (overload::StrVal($node) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
724              
725             # warn $depth.($open==1?' > ':(defined($open)?' < ':' ')).join('',@cur_path).' : '.ref($node)." ($realpack/$realtype/$id)";
726              
727              
728 23830 100       124885 if ($ref) {
729 15830 100       23648 if (!defined $open ) {
    100          
    50          
730 9936 50 66     15319 ($realpack and $realtype and $id) and $ref = $realtype;
      66        
731              
732 9936 100 100     25347 ($ref eq 'REF' or $ref eq 'SCALAR') and return '\\';
733              
734 9370 100       11241 ($ref eq 'CODE') and return 'sub { "DUMMY" }';
735              
736 9306 100       12849 if ($_[-1] eq '$loop') {
737 56         129 return '$t1';
738             }
739              
740 9250 100 66     16172 if ($ref eq 'HASH' and $_[-2] eq '%') {
741 4584         14126 my @keys = sort {$a cmp $b} keys(%$node);
  185918         152996  
742 4584         7396 my $is_first = ($_[-1] eq $keys[0]);
743              
744 4584 100       8322 $is_first
745             and
746             return '\''.$_[-1].'\'=>';
747              
748 3289         10888 return ',\''.$_[-1].'\'=>';
749             }
750 4666 100 66     17915 ($ref eq 'ARRAY' and $_[-2] eq '@' and $_[-1] != 0) and return ',';
      100        
751 1380         3045 return;
752             }
753             elsif ($open ==1 ) {
754 2947 100       5480 ($ref eq 'ARRAY') and return '[';
755 1403 100       3787 ($ref eq 'HASH') and return '{';
756              
757 37 50       89 ($realtype eq 'ARRAY') and return 'bless([';
758 37 50       132 ($realtype eq 'HASH') and return 'bless({';
759             }
760             elsif ($open ==0 ) {
761 2947 100       6145 ($ref eq 'ARRAY') and return ']';
762 1403 100       3642 ($ref eq 'HASH') and return '}';
763              
764 37 50       55 ($realtype eq 'ARRAY') and return "] , '$ref')";
765 37 50       160 ($realtype eq 'HASH') and return "} , '$ref')";
766              
767             }
768             }
769              
770 8000 100       10713 (defined($node)) or return 'undef';
771              
772 7825 50       10852 if ($_[-2] eq '=') {
773 7825         9757 $node=~s/\'/\\\'/g;
774 7825 100       23104 ($node=~/^\d+$/) and return $node;
775 3025         8585 return '\''.$node.'\'';
776             }
777              
778 0         0 return;
779              
780             }
781              
782              
783             #############################################################
784             sub visitor_dump {
785             # Visitor to dump Perl structure
786             #############################################################
787 40     40 0 43 my $node = shift();
788 40         80 my $depth = shift;
789 40         36 my $open = shift;
790 40         62 my @cur_path = @_;
791              
792 40         65 my $path = join('',@cur_path);
793              
794 40         68 my ($realpack, $realtype, $id) =
795             (overload::StrVal($node) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
796              
797 40 100       329 return $depth.($open==1?' > ':(defined($open)?' < ':' ')).join('',@cur_path).' : '.ref($node); #." ( $realpack/$realtype/$id)";
    100          
798             }
799              
800              
801             #############################################################
802             # IDEA : sub visitor_search {
803             # IDEA : searching visitor to replace search
804             #############################################################
805             # my $node = shift();
806             # my $depth = shift;
807             # my $open = shift;
808             # my @cur_path = @_;
809              
810             # if (defined $matchPath->($pattern, @cur_path)) {
811             # defined($nb_occ) and (--$nb_occ<1) and die 'STOP';
812              
813             # return $node;
814             # }
815             #}
816              
817              
818             ##############################################################################
819             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
820             sub travel($;@) {
821             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
822              
823 11782     11782 1 12858 my $where=shift();
824 11782   100     16526 my $visitor = shift() || \&visitor_patch;
825 11782   100     15757 my $depth = shift()||0;
826 11782         17506 my @path = @_;
827              
828              
829             =over 4
830              
831             =item I( [,])
832              
833             travel make the visitor function to travel through each node of the
834              
835             complexe perl data structure to travel into
836             ()
837              
838             Return a list path where the argument match with the
839             corresponding node in the tree data type
840              
841             I
842              
843             travel( {ky=>['l','r','t',124],r=>2}
844              
845             returns ( [ '%', 'ky', '@' , 3 , '=' , 124 ] )
846              
847             =cut
848              
849             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
850 11782 100       14939 if (@path) {
851 9942         15147 debug "travel( dom=",@path, ' is ',ref($where),")";
852             #debug "return ".($arr && ' ARRAY ' || 'SCALAR');
853             }
854             else {
855 1840         3013 %loop_ref=();
856             }
857              
858             #
859              
860             sub __appendVisitorResult {
861 33923     33923   36738 my $is_array = shift();
862 33923         27913 my @list;
863              
864 33923         37217 foreach (@_) {
865 321940 50       337626 if (defined $_) {
866 321940 50       330958 $is_array or return $_;
867 321940         330182 push(@list, $_);
868             }
869             }
870 33923         102228 return @list;
871             }
872              
873 11782         18434 my ($k,$res);
874 11782         0 my @res;
875              
876             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
877              
878 11782         13573 my $ref_type = ref $where;
879              
880              
881             ######################################## !!!!! Modules type resolution
882             # if (index($ref_type,'::')!=-1) {
883 11782         19435 my ($realpack, $realtype, $id) =
884             (overload::StrVal(scalar($where)) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
885              
886 11782 100 66     57170 if ($realpack and $realtype and $id) {
      100        
887 40         58 push @path,'|',$ref_type;
888              
889 40         48 my $y = undef;
890 40 50       101 if ($realtype eq 'SCALAR') {
    50          
    0          
891 0         0 $y=$$where;
892             }
893             elsif ($realtype eq 'HASH') {
894 40         64 $y=\%$where
895             }
896             elsif ($realtype eq 'ARRAY') {
897 0         0 $y=\@$where
898             }
899             else {
900             #die $realtype.' : '.$where;
901             }
902              
903             #debug ref($y)." = $realpack -> real $realtype, $id";
904              
905 40         68 $where=$y;
906 40         55 $ref_type = $realtype;
907             }
908              
909              
910             ######################################## !!!!! Loop detection
911 11782         10698 my @p;
912              
913 11782 100       14335 if (loop_det($where)) {
914              
915 60         106 return __appendVisitorResult(wantarray(), @res,
916             &$visitor($where, $depth, undef , (@path, '$loop')));
917              
918             }
919             else {
920             ######################################## !!!!! SCALAR TRAVEL
921 11722 100 100     17851 if (!$ref_type) {
    100          
    100          
    100          
922              
923 8029         12812 return __appendVisitorResult(wantarray(),
924             @res,
925             &$visitor($where, $depth , undef, (@path, '=', $where)));
926              
927             }
928             ######################################## !!!!! HASH TRAVEL
929             elsif ($ref_type eq 'HASH')
930             {
931              
932 1418         2329 @res = __appendVisitorResult(wantarray(),
933             @res,
934             &$visitor($where, $depth, 1, @path));
935              
936 1418         1473 my $k;
937 1418         1430 foreach $k (sort {$a cmp $b} keys(%{ $where })) {
  8953         8770  
  1418         4326  
938 4605         7575 @p = (@path, '%', $k);
939              
940 4605         6876 @res = __appendVisitorResult(wantarray(),
941             @res,
942             &$visitor($where, $depth, undef, @p)
943             );
944              
945             @res = __appendVisitorResult(
946             wantarray(),
947             @res,
948 4605         10663 travel($where->{$k},$visitor,$depth+1, @p)
949             );
950             }
951              
952 1418         2650 return __appendVisitorResult( wantarray(),
953             @res,
954             &$visitor($where, $depth, 0, @path)
955             );
956              
957             }
958             ######################################## !!!!! ARRAY TRAVEL
959             elsif ($ref_type eq 'ARRAY')
960             {
961 1557         2174 $res = &$visitor($where, $depth, 1, @path);
962              
963 1557         2451 @res = __appendVisitorResult( wantarray(), @res, $res );
964              
965 1557         1647 for my $i (0..$#{ $where }) {
  1557         3165  
966             #print "\narray $i (".$where->[$i].','.join('.',@p).")\n" if (join('_',@p)=~ /\@_1_\%_g_/);
967 4692         7274 @p = (@path, '@', $i);
968              
969 4692         7140 @res = __appendVisitorResult(wantarray(),
970             @res,
971             &$visitor($where, $depth, undef, @p)
972             );
973              
974 4692         9739 @res = __appendVisitorResult(
975             wantarray(),
976             @res,
977             travel($where->[$i],$visitor,$depth+1, @p)
978             );
979              
980             }
981              
982 1557         2815 return __appendVisitorResult( wantarray(),
983             @res,
984             &$visitor($where, $depth, 0, @path)
985             );
986              
987             }
988             ######################################## !!!!! REFERENCE TRAVEL
989             elsif ($ref_type eq 'REF' or $ref_type eq 'SCALAR')
990             {
991 572         1048 @p = (@path, "\$");
992              
993 572         1012 @res = __appendVisitorResult( wantarray(),
994             @res,
995             &$visitor($where, $depth, undef, @p )
996             );
997              
998             return __appendVisitorResult( wantarray(),
999             @res,
1000 572         688 travel( ${ $where }, $visitor, $depth+1, @p )
  572         1053  
1001             );
1002             }
1003             else { # others types
1004             ######################################## !!!!! CODE TRAVEL
1005 146 100       281 if ($ref_type eq 'CODE') {
    50          
1006 66         172 @p = (@path, '&');
1007             }
1008             ######################################## !!!!! GLOB TRAVEL
1009             elsif ($ref_type eq 'GLOB') {
1010 80         148 my $name=$$where;
1011 80         163 $name=~s/b^\*//;
1012 80         223 @p = (@path, '*', $name);
1013             }
1014             ######################################## !!!!! MODULE TRAVEL
1015             else {
1016             #die $ref_type;
1017             }
1018              
1019             ######################################## !!!!! GLOB TRAVEL
1020             # cf IO::Handle or Symbol::gensym()
1021              
1022 146 100       263 if ($p[-2] eq '*') { # GLOB
1023 80         104 for $k (qw(SCALAR ARRAY HASH)) {
1024 171         206 my $gval = *$where{$k};
1025 171 100       219 defined($gval) or next;
1026 130 100 100     297 next if ($k eq "SCALAR" && ! defined $$gval); # always there
1027              
1028 73         133 return __appendVisitorResult( wantarray(),
1029             @res,
1030             travel($gval, $visitor, $depth+1, undef, @p)
1031             );
1032             }
1033             }
1034              
1035 73         140 return __appendVisitorResult(
1036             wantarray(),
1037             @res,
1038             &$visitor($where, $depth, undef, @p )
1039             );
1040             }
1041             }
1042              
1043 0         0 return ();
1044             }
1045              
1046              
1047              
1048             my %circular_ref;
1049             ##############################################################################
1050             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1051             sub search($$;$@) {
1052             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1053 2759     2759 1 3405 my $where = shift();
1054 2759         2629 my $pattern = shift();
1055 2759         2266 my $nb_occ = shift();
1056 2759         3842 my @path=@_;
1057              
1058             # warn "search for #$nb_occ (",join('',@{$pattern}),")";
1059              
1060              
1061             =item I(, [,])
1062              
1063             search the into
1064              
1065             is a complexe perl data structure to search into
1066             is an array of type description to match
1067             optional argument to limit the number of results
1068             if undef all results are returned
1069             if 1 first one is returned
1070              
1071             Return a list path where the argument match with the
1072             corresponding node in the tree data type
1073              
1074             EX:
1075             search( {ky=>['l','r','t',124],r=>2}
1076             ['?@','=',124])
1077              
1078             Returns ( [ '%', 'ky', '@' , 3 , '=' , 124 ] )
1079              
1080              
1081             search( [5,2,3,{r=>3,h=>5},4,\{r=>4},{r=>5}],
1082             ['%','r'], 2 )
1083              
1084             Returns (['@',3,'%','r'],['@',5,'$','%','r'])
1085              
1086              
1087             search( [5,2,3,{r=>3},4,\3],
1088             ['?$@%','=',sub {$_ == 3 }],
1089             2;
1090              
1091             Returns (['@',2,'=',3], ['@',3,'%','r','=',3])
1092              
1093             =cut
1094              
1095             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1096             # warn "search($where / ref=".ref($where).','.$nb_occ.' ,'.join('',@path).")";
1097              
1098 2759 100       3743 @path or %loop_ref=();
1099              
1100 2759 50 66     6406 (defined($nb_occ) and ($nb_occ<1)) and return ();
1101              
1102 2759         2975 my $ref_type = ref $where;
1103              
1104 2759         2256 my @found;
1105 2759         2473 my $next = undef;
1106 2759         2185 my @p;
1107              
1108             ######################################## !!!!! Modules type resolution
1109 2759 100       2999 if ($ref_type) {
1110              
1111             #if (index($where,'::')!=-1) { ## !!!!! MODULE SEARCH
1112              
1113 676         1223 my ($realpack, $realtype, $id) =
1114             (overload::StrVal($where) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
1115              
1116 676 50 66     5418 if ($realpack and $realtype and $id) {
      66        
1117 11         20 push @path, ('|', $ref_type);
1118              
1119 11         19 $ref_type = $realtype;
1120              
1121             #warn "$ref_type -> ($realpack, $realtype, $id )";
1122             }
1123              
1124              
1125             ######################################## !!!!! Loop detection
1126              
1127 676 100 100     898 if (loop_det($where)) {
    100          
    100          
    100          
    100          
    50          
1128 2         6 @p = (@path, '$loop');
1129             }
1130             ######################################## HASH Search
1131             elsif ($ref_type eq 'HASH') {
1132 272         269 my $k;
1133 272         289 foreach $k (sort {$a cmp $b} keys(%{ $where })) {
  3637         3178  
  272         898  
1134 1422         2147 @p = (@path, '%', $k);
1135              
1136 1422 100       1832 if (defined $matchPath->($pattern, @p)) {
1137 64         136 push @found,[@p];
1138 64 100 66     276 defined($nb_occ) and (--$nb_occ<1) and last;
1139             }
1140             else {
1141 1358         2500 my @res = search($where->{$k}, $pattern, $nb_occ, @p);
1142 1358 100       2161 @res and push @found,@res;
1143             }
1144             }
1145 272         706 return @found;
1146             }
1147             ######################################## HASH Search
1148             elsif ($ref_type eq 'ARRAY')
1149             {
1150 263         277 for my $i (0..$#{ $where }) {
  263         606  
1151 1170         1711 @p = (@path, '@', $i);
1152              
1153 1170 100       1481 if (defined $matchPath->($pattern, @p)) {
1154 25         53 push @found,[@p];
1155 25 100 66     84 defined($nb_occ) and (--$nb_occ<1) and last;
1156             }
1157             else {
1158 1145         1898 my @res = search($where->[$i], $pattern, $nb_occ, @p);
1159 1145 100       1890 @res and push @found,@res;
1160             }
1161             }
1162 263         634 return @found;
1163             }
1164             ######################################## REF Search
1165             elsif ($ref_type eq 'REF' or $ref_type eq 'SCALAR') {
1166 110         184 @p = (@path, '$');
1167 110         103 $next = ${ $where };
  110         142  
1168             }
1169             ######################################## CODE Search
1170             elsif ($ref_type eq 'CODE') {
1171 15         36 @p = (@path, '&');
1172             }
1173             ######################################## GLOB Search
1174             elsif ($ref_type eq 'GLOB') {
1175 14         28 my $name = $$where;
1176 14         73 $name=~s/^\*//;
1177 14         31 @p = (@path, '*',$name);
1178 14 100 66     32 if (defined *$where{SCALAR} and defined(${*$where{SCALAR}})) {
  14 100       45  
    100          
1179 4         7 $next = *$where{SCALAR};
1180             }
1181             elsif (defined *$where{ARRAY}) {
1182 4         8 $next = *$where{ARRAY};
1183             }
1184             elsif (defined *$where{HASH}) {
1185 4         8 $next = *$where{HASH};
1186             }
1187             }
1188             }
1189             ######################################
1190             else { ## !!!!! SCALAR Search
1191 2083         2939 @p = (@path, '=', $where);
1192             }
1193             ######################################
1194              
1195 2224 100       2659 if (defined $matchPath->($pattern, @p)) {
1196 136         310 push @found,[@p];
1197 136 100       221 defined($nb_occ) and --$nb_occ;
1198             }
1199              
1200 2224 100       2849 if ((defined($next))) {
1201 114         237 my @res = search($next, $pattern, $nb_occ, @p);
1202              
1203 114 100       178 @res and push @found,@res;
1204             }
1205              
1206 2224         3427 return @found;
1207             }
1208              
1209              
1210             ##############################################################################
1211             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1212             sub path($$;$) {
1213             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1214 108     108 1 208 my $dom = shift();
1215 108         148 my @paths = @{shift()};
  108         255  
1216 108 100       334 my $father_nb = shift() or 0;
1217              
1218              
1219             =item I(, [,])
1220              
1221             gives a list of nodes pointed by
1222             is the complex perl data structure
1223             is the array reference of paths
1224             is the depth level to return from tree
1225             start counting from the top
1226             - start counting from the leaf
1227             0 return the leaf or check the leaf with '=' or '&' types):
1228             * if code give the return of execution
1229             * scalar will check the value
1230              
1231             Return a list of nodes reference to the
1232              
1233             EX:
1234              
1235             $eq_3 = path([5,{a=>3,b=>sub {return 'test'}}],
1236             ['@1%a'])
1237              
1238             $eq_3 = path([5,{a=>3,b=>sub {return 'test'}}],
1239             '@1%a','@1%b')
1240              
1241              
1242             @nodes = path([5,{a=>3,b=>sub {return 'test'}}],
1243             ['@1%b&'], # or [['@',1,'%','b','&']]
1244              
1245             0 # return ('test')
1246             # -1 or 2 return ( sub { "DUMMY" } )
1247             # -2 or 1 get the hash table
1248             # -3 get the root tree
1249             )]);
1250              
1251             @nodes = path([5,{a=>3,b=>sub {return 'test'}}],
1252             ['@1%a'], # or [['@',1,'%','b','&']]
1253              
1254             0 # return 3
1255             # -1 or 2 get the hash table
1256             # -2 or 1 get the root tree
1257             )]);
1258              
1259              
1260             =cut
1261              
1262             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1263              
1264              
1265 108         496 debug "path( \$dom, $#paths patch, $father_nb)";
1266              
1267 108         164 my @nodes;
1268              
1269 108         193 foreach my $node (@paths) {
1270 153 50       323 (ref($node) eq 'ARRAY') or die 'path() : pattern "'.$node.'" should be a Dom pattern ("Dom" internal array, perhaps use patternText2dom)';
1271              
1272 153         185 my @path = @{$node};
  153         362  
1273              
1274             # perl evaluation of the dom path
1275 153         383 my $e = $path2eval__->('$dom', $father_nb, @path);
1276              
1277 153         8377 my $r = eval $e;
1278 153         660 debug $dom;
1279 153         391 debug $e.' evaluated to '.__d($r);
1280 153 50       335 die __FILE__.' : path() '.$e.' : '.$@ if ($@);
1281 153         383 push @nodes,$r
1282             }
1283 108 50       281 return shift @nodes unless (wantarray());
1284 108         285 return @nodes;
1285             }
1286              
1287             ##############################################################################
1288             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1289             sub compare {
1290             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1291              
1292             # ############ ret : 0 if equal / 1 else
1293 936     936 1 1364 my $d1 = shift();
1294 936         900 my $d2 = shift();
1295              
1296 936         928 my (@p1,@p2,$do_resolv_patch);
1297 936 100       1054 if (@_) {
1298 757         589 @p1 = @{$_[0]};
  757         1115  
1299 757         666 @p2 = @{$_[1]};
  757         943  
1300             }
1301             else {
1302 179         324 %loop_ref=();
1303             # equiv TEST on each function call: if ($CFG->{o_complex} and ($#a1==-1 and $#a2==-1)) {
1304 179 100       325 $CFG->{o_complex} and $do_resolv_patch=1;
1305             }
1306              
1307             =item I(, )
1308              
1309             compare nodes from origine to destination
1310             nodes are complex perl data structure
1311              
1312             Return a list of (empty if node structures are equals)
1313              
1314             EX:
1315              
1316             compare(
1317             [{r=>new Data::Dumper([5],ui=>54},4],
1318             [{r=>new Data::Dumper([5,2],ui=>52},4]
1319             )
1320              
1321             return ({ action=>'add',
1322             ...
1323             },
1324             { action=>'change',
1325             ...
1326             },
1327             ...
1328             )
1329              
1330             =cut
1331              
1332              
1333             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1334              
1335              
1336              
1337             ###############################################################################
1338             sub searchSuffix__{
1339 0     0 0 0 my @a1=@{shift()};
  0         0  
1340 0         0 my @a2=@{shift()};
  0         0  
1341 0         0 my @patch=@{shift()};
  0         0  
1342              
1343 0         0 my @common;
1344 0   0     0 while (@a1 and @a2) {
1345 0         0 $_= pop(@a1);
1346 0 0 0     0 ($_ eq pop(@a2)) and unshift @common,$_ or return @common
1347             }
1348             return @common
1349 0         0 }
1350             ###############################################################################
1351              
1352             sub resolve_patch {
1353 53     53 0 87 my @patch = @_;
1354 53         57 my ($p1,$p2);
1355              
1356 53         93 foreach $p1 (@patch) {
1357 108         116 foreach $p2 (@patch) {
1358              
1359 470 100 100     789 if ($p1->{action} eq 'remove' and
      100        
1360             $p2->{action} eq 'add' and
1361             (__d($p1->{val_orig}) eq __d($p2->{val_dest}))) {
1362              
1363             #my @com = searchSuffix__($p1->{path_orig}, $p2->{path_dest}, \@patch);
1364             #@com or next;
1365             #grep({$_ eq '&'} @com) or next;
1366             push @patch,
1367             compare($p1->{val_orig},
1368             $p2->{val_dest},
1369 8         23 [@{$p1->{path_orig}}],
1370 8         16 [@{$p2->{path_dest}}]
  8         20  
1371             );
1372              
1373 8         21 $p1->{action}='move';
1374 8         14 $p1->{val_orig}= $p1->{val_dest}= undef;
1375 8         13 $p1->{path_dest}= $p2->{path_dest};
1376 8         17 $p2->{action}='erase';
1377             }
1378             }
1379             }
1380              
1381 53         68 my $o = 0;
1382 53         92 while ($o<=$#patch) {
1383 108 100 66     185 ($patch[$o]->{action} eq 'erase') and splice(@patch,$o,1) and next;
1384 100         117 $o++
1385             }
1386              
1387             return @patch
1388 53         207 }
1389              
1390             ###############################################################################
1391             #warn "\nComparing ORIG(".join(@p1,'=',ref($d1)||$d1).") <> DEST(".join('.',@p2,'=',ref($d2)||$d2).")\n";
1392              
1393             # ############ ret : 0 if equal / 1 else
1394 936         939 my @msg=();
1395              
1396             ######################################## !!!!! Type resolution
1397 936         958 my $ref_type = ref $d1;
1398              
1399 936 100       1166 if ($ref_type) {
1400              
1401 446 100       714 ($ref_type ne ref($d2))
1402             and
1403             return ( $patchDOM->('change', \@p1,\@p2, $d1,$d2) );
1404              
1405             #if (index($ref_type,'::')!=-1) {
1406              
1407 379         603 my ($realpack, $realtype, $id) =
1408             (overload::StrVal($d1) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
1409              
1410 379 50 66     2662 if ($realpack and $realtype and $id) {
      66        
1411 2         6 my ($realpack2, $realtype2, $id2) =
1412             (overload::StrVal($d2) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
1413              
1414 2 50       27 ($realtype ne $realtype2)
1415             and
1416             push @msg, $patchDOM->('change', \@p1 ,\@p2 , $realtype ,$realtype2);
1417              
1418 2         5 push @p1, '|',$ref_type;
1419 2         4 push @p2, '|',$ref_type;
1420            
1421 2         10 debug "$ref_type -> ($realpack, $realtype, $id : $ref_type)";
1422              
1423 2         4 $ref_type = $realtype;
1424             }
1425             }
1426              
1427             ######################################## !!!!! SCALAR COMPARE
1428 869 100 100     1375 if (!$ref_type)
    100          
    100          
    100          
    100          
    50          
1429             {
1430 490 100 100     1292 (defined($d1) and $d1 ne $d2) and return ($patchDOM->('change', \@p1,\@p2, $d1,$d2) );
1431 407 100 100     642 (!defined($d1) and defined($d2)) and return ($patchDOM->('change', \@p1,\@p2, $d1,$d2) );
1432 405         864 return ();
1433             }
1434             ######################################## !!!!! HASH COMPARE
1435             elsif ($ref_type eq 'HASH')
1436             {
1437 167         281 my (%seen,$k);
1438              
1439 167         167 foreach $k (sort {$a cmp $b}
  384         444  
1440 167         431 keys(%{ $d1 }))
1441             {
1442 360         473 $seen{$k}=1;
1443              
1444 360 100       445 if (exists $d2->{$k}) {
1445              
1446 338 50       466 loop_det($d1->{$k},@p1) and next;
1447              
1448             push @msg,
1449             compare( $d1->{$k},
1450 338         959 $d2->{$k},
1451             [ @p1, '%',$k ],
1452             [ @p2, '%',$k ],
1453             );
1454             } else {
1455 22         67 push @msg,$patchDOM->('remove', [ @p1, '%', $k ] ,\@p2 , $d1->{$k} ,undef)
1456             }
1457              
1458             }#foreach($d1)
1459              
1460 167         207 foreach $k (sort {$a cmp $b} keys(%{ $d2 })) {
  387         415  
  167         348  
1461 361 100       502 next if exists $seen{$k};
1462              
1463 23         30 my $v = $d2->{$k};
1464 23         62 push @msg,$patchDOM->('add', \@p1, [ @p2, '%', $k ], undef, $v)
1465             }
1466              
1467 167 100       475 $do_resolv_patch or return @msg;
1468 35         79 return resolve_patch(@msg);
1469             }
1470             elsif ($ref_type eq 'ARRAY')
1471             {
1472             ######################################## !!!!! ARRAY COMPARE (not complex mode)
1473              
1474 132 100       238 unless ($CFG->{o_complex}) {
1475              
1476 63         65 my $min = $#{$d1};
  63         89  
1477 63 100       67 $min = $#{$d2} if ($#{$d2}<$min); # min ($#{$d1},$#{$d2})
  4         8  
  63         121  
1478              
1479 63         80 my $i;
1480 63         104 foreach $i (0..$min) {
1481              
1482 120 100       181 loop_det($d1->[$i], @p1)
1483             and
1484             next;
1485              
1486 118         335 push @msg,
1487             compare( $d1->[$i], $d2->[$i], [@p1, '@',$i], [@p2, '@',$i]);
1488             }
1489              
1490 63         85 foreach $i ($min+1..$#{$d1}) { # $d1 is bigger
  63         113  
1491             # silent just for complexe search mode
1492 5         13 push @msg,$patchDOM->('remove', [ @p1, '@', $i ], \@p2 ,$d1->[$i], undef)
1493             }
1494 63         68 foreach $i ($#{$d1}+1..$#{$d2}) { # d2 is bigger
  63         80  
  63         91  
1495 9         23 push @msg,$patchDOM->('add', \@p1, [ @p2, '@', $i ], undef, $d2->[$i])
1496             }
1497 63         152 return @msg;
1498             }
1499              
1500             ######################################## !!!!! ARRAY COMPARE (in complex mode)
1501 69         143 my @seen_src;
1502             my @seen_dst;
1503 69         0 my @res_Eq;
1504             # perhaps not on the same index (search in the dest @)
1505 69         0 my $i;
1506             ARRAY_CPLX:
1507 69         70 foreach $i (0..$#{$d1}) {
  69         142  
1508 161         171 my $val1 = $d1->[$i];
1509            
1510             #print "\n SAR($i) {";
1511             #if ($i<$#{$d2}) {
1512 161 100       220 if (exists $d2->[$i]) {
1513 155         127 my @res;
1514              
1515 155 100       202 loop_det($val1, @p1)
1516             or
1517             @res = compare($val1,
1518             $d2->[$i],
1519             [ @p1, '@',$i ],
1520             [ @p2, '@',$i ]);
1521              
1522 155 100       294 if (@res) { $res_Eq[$i] = [@res] } # (*)
  29         51  
1523             else
1524             {
1525 126         150 $seen_src[$i]=$i;
1526 126         114 $seen_dst[$i]=$i;
1527 126         182 next ARRAY_CPLX;
1528             }
1529             }
1530 35         47 my $j;
1531 35         34 foreach $j (0..$#{$d2}) { #print " -> $j ";
  35         78  
1532 102 100       141 next if ($i==$j);
1533 78 100       105 next if (defined($seen_dst[$j]));
1534              
1535 61 100       125 unless (compare( $val1,
1536             $d2->[$j],
1537             [ @p1, '@',$i ],
1538             [ @p2, '@',$j ]))
1539             { #print " (found) ";
1540              
1541 15         18 $seen_dst[$j] = 1;
1542 15         39 $seen_src[$i] = $patchDOM->('move',
1543             [ @p1, '@', $i ],
1544             [ @p2, '@', $j ]);
1545 15         35 next ARRAY_CPLX;
1546             }
1547             }
1548 20 50       77 (defined $seen_src[$i])
1549             or
1550             $seen_src[$i] = $patchDOM->('remove',
1551             [ @p1, '@', $i ],
1552             \@p2,
1553             $val1,
1554             undef
1555             );
1556              
1557             #print " }SAR($i)";
1558             } # for $d1 (0..$min)
1559              
1560             ### destination table $d2 is bigger
1561             ##
1562 69         78 foreach $i (0..$#{$d2}) {
  69         107  
1563 165 100       222 defined($seen_dst[$i]) and next;
1564              
1565 24         57 $seen_dst[$i] = $patchDOM->('add',
1566             \@p1,
1567             [ @p2, '@', $i ],
1568             undef,
1569             $d2->[$i]
1570             )
1571             }
1572              
1573 69         82 my $max = $#seen_dst;
1574              
1575 69 100       111 ($#seen_src>$max) and $max = $#seen_src;
1576              
1577 69         100 foreach (0..$max) {
1578 171         154 my $src = $seen_src[$_];
1579 171         143 my $dst = $seen_dst[$_];
1580              
1581 171 100 66     301 if (ref($res_Eq[$_]) and # differences on the same index (*)
      100        
1582             ref($src) and ref($dst)) {
1583              
1584             #print "\n src/dst : ".domPatch2TEXT($src)."/ ".domPatch2TEXT($dst)."\n";
1585              
1586             # remove(@2,)= add(,@2)=
1587             ($src->{action} eq 'remove') and
1588             ($dst->{action} eq 'add') and
1589 17 50 66     61 (push @msg, @{ $res_Eq[$_] })
  12   66     41  
1590             and next;
1591             }
1592 159 100       191 (ref $src) and push @msg,$src;
1593 159 100       215 (ref $dst) and push @msg,$dst;
1594             }
1595              
1596 69 100       214 $do_resolv_patch or return @msg;
1597 14         27 return resolve_patch(@msg);
1598             }
1599             ######################################## !!!!! REF COMPARE
1600             elsif ($ref_type eq 'REF' or $ref_type eq 'SCALAR')
1601             {
1602 65 50       103 if (loop_det($$d1, @p1)) {
1603             }
1604             else {
1605 65         172 @msg = ( compare($$d1, $$d2,
1606             [ @p1, '$' ],
1607             [ @p2, '$' ])
1608             );
1609             }
1610 65 100       203 $do_resolv_patch or return @msg;
1611 4         7 return resolve_patch(@msg);
1612             }
1613             ######################################## !!!!! GLOBAL REF COMPARE
1614             elsif ($ref_type eq 'GLOB')
1615             {
1616 14         28 my $name1=$$d1;
1617 14         65 $name1=~s/^\*//;
1618 14         21 my $name2=$$d2;
1619 14         44 $name2=~s/^\*//;
1620              
1621 14         26 push @p1,'*', $name1;
1622 14         17 push @p2,'*', $name2;
1623              
1624 14         23 push @msg, $patchDOM->('change', \@p1 ,\@p2);
1625              
1626 14         22 my ($k,$g_d1,$g_d2)=(undef,undef,undef);
1627              
1628 14 100 66     32 if (defined *$d1{SCALAR} and defined(${*$d1{SCALAR}})) {
  14 100       51  
    50          
    0          
1629 5         6 $g_d1 = *$d1{SCALAR};
1630             }
1631             elsif (defined *$d1{ARRAY}) {
1632 4         5 $g_d1 = *$d1{ARRAY};
1633             }
1634             elsif (defined*$d1{HASH}) {
1635 5         7 $g_d1 = *$d1{HASH};
1636             }
1637             elsif (defined*$d1{GLOB}) {
1638 0         0 $g_d1 = *$d1{GLOB};
1639 0 0       0 loop_det($g_d1, @p1) and return ();
1640             }
1641             else {
1642 0         0 die $name1;
1643             }
1644              
1645 14 100 66     28 if (defined *$d2{SCALAR} and defined(${*$d2{SCALAR}})) {
  14 100       35  
    50          
    0          
1646 4         3 $g_d2 = *$d2{SCALAR};
1647             }
1648             elsif (defined *$d2{ARRAY}) {
1649 5         5 $g_d2 = *$d2{ARRAY};
1650             }
1651             elsif (defined*$d2{HASH}) {
1652 5         6 $g_d2 = *$d2{HASH};
1653             }
1654             elsif (defined*$d2{GLOB}) {
1655 0         0 $g_d2 = *$d2{GLOB};
1656             }
1657             else {
1658 0         0 die $name2;
1659             }
1660              
1661 14         30 my @msg = ( compare($g_d1, $g_d2, \@p1, \@p2));
1662              
1663 14 50       54 $do_resolv_patch or return @msg;
1664 0         0 return resolve_patch(@msg);
1665              
1666             }
1667             ######################################## !!!!! CODE REF COMPARE
1668             elsif ($ref_type eq 'CODE') { # cannot compare this type
1669              
1670             #push @msg,$patchDOM->('change', \@p1, [@p2, '@', $i ], undef, $d2->[$i])
1671 1         3 return ();
1672             }
1673             ######################################## !!!!! What's that ?
1674             else {
1675 0         0 die 'unknown type /'.$ref_type.'/ '.join('',@p1);
1676             }
1677 0         0 return ();
1678             }
1679              
1680              
1681              
1682             ##############################################################################
1683             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1684             sub applyPatch($@) { # modify a dom source with a patch
1685             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1686 41     41 1 356 my $dom = shift();
1687              
1688              
1689             =item I(, [, ] )
1690              
1691             applies the patches to the (perl data structure)
1692             [, ] is the list of your patches to apply
1693             supported patch format should be text or dom types,
1694             the patch should a clear description of a modification
1695             no '?' modifier or ambiguities)
1696              
1697             Return the modified dom, die if patch are badly formated
1698              
1699             EX:
1700             applyPatch([1,2,3],'add(,@4)=4')
1701             return [1,2,3,4]
1702              
1703             =back
1704              
1705             =cut
1706              
1707             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1708 41         80 debug 'applyPatch('.__d($dom).') :';
1709 41         95 my (@remove,@add,@change,@move);
1710              
1711 41         0 my $p;
1712 41         65 foreach $p (@_) { # ordering the patch operations
1713 145 50       239 defined($p) or next;
1714 145         150 my $dom_patch = $p;
1715              
1716 145 100       273 (ref($p) eq 'HASH')
1717             or ($dom_patch) = textPatch2DOM($p);
1718              
1719 145         264 debug(domPatch2TEXT($dom_patch));
1720              
1721 145         5337 eval 'push @'.$dom_patch->{action}.', $dom_patch;';
1722 145 50       501 $@ and die 'applyPatch() : '.$@;
1723             }
1724              
1725 41         109 my ($d,$t);
1726              
1727 41         0 my ($d1,$d2,$d3,$d4,$d5);
1728 41         0 my ($t1,$t2,$t3,$t4,$t5);
1729              
1730 41         68 my $patch_eval='$d='.__d($dom).";\n";
1731              
1732 41         75 $patch_eval .= '$t='.__d($dom).";\n";
1733              
1734 41         61 my $post_eval;
1735              
1736             my $r;
1737 41         73 foreach $r (@remove) {
1738 23         35 my @porig = @{$r->{path_orig}};
  23         59  
1739              
1740 23         38 my $key = pop @porig;
1741 23         35 my $type = pop @porig;
1742              
1743 23 100       57 if ($type eq '@') {
1744 12         28 $patch_eval .= 'splice @{'.$path2eval__->('$d',undef,@porig) ."},$key,1;\n";
1745             }
1746             else {
1747 11         30 $patch_eval .= 'delete '.$path2eval__->('$d',undef,@porig,$type,$key) .";\n";
1748             }
1749             }
1750              
1751 41         47 my $m;
1752             my @remove_patch = sort
1753             {
1754             # the array indexes order from smallest to biggest
1755 41 50       83 if (${$a->{path_orig}}[-2] eq '@') {
  4         6  
  4         14  
1756 4         6 return (${$a->{path_orig}}[-1] >
1757 4         6 ${$b->{path_orig}}[-1])
  4         14  
1758             }
1759             # smallest path after bigger ones
1760 0         0 return $#{$a->{path_orig}} < $#{$b->{path_orig}};
  0         0  
  0         0  
1761             } @move;
1762              
1763 41         55 foreach $m (@remove_patch) {
1764 16         19 my @porig = @{$m->{path_orig}};
  16         34  
1765              
1766 16         22 my $key = pop @porig;
1767 16         23 my $type = pop @porig;
1768              
1769 16 100       30 if ($type eq '@') {
1770 8         16 $patch_eval .= 'splice @{'.$path2eval__->('$d',undef,@porig)."},$key,1;\n";
1771             }
1772             else {
1773 8         23 $patch_eval .= 'delete '.$path2eval__->('$d',undef,@porig,$type,$key) .";\n";
1774             }
1775             }
1776              
1777 41         53 foreach $m (@remove_patch) {
1778 16         17 my @porig = @{$m->{path_orig}};
  16         28  
1779 16         26 $patch_eval .= $path2eval__->('$d',undef,@{$m->{path_dest}}).
  16         31  
1780             ' = '.$path2eval__->('$t',undef,@porig).";\n";
1781             }
1782              
1783              
1784 41         43 my $a;
1785 41         55 foreach $a (@add) {
1786             $patch_eval .=
1787 38         78 $path2eval__->('$d',undef,@{$a->{path_dest}}).
1788 38         50 ' = '.__d($a->{val_dest}) .";\n";
1789             }
1790 41         43 my $c;
1791 41         56 foreach $c (@change) {
1792             $patch_eval .=
1793 68         116 $path2eval__->('$d',undef,@{$c->{path_dest}}).
1794 68         73 ' = '.__d($c->{val_dest}).";\n";
1795             }
1796              
1797 41         52 $patch_eval = $patch_eval.'$d;';
1798              
1799 41         3887 my $res = eval($patch_eval);
1800              
1801 41         184 debug "\nEval=>> $patch_eval >>=".__d($res).".\n";
1802              
1803 41 50       85 $@
1804             and
1805             die 'applyPatch() : '.$patch_eval.$@;
1806              
1807 41         240 return $res
1808             }
1809              
1810             =back
1811              
1812             =head2 Conversion Methods
1813              
1814             =over 4
1815              
1816             =cut
1817              
1818              
1819             ##############################################################################
1820             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1821             sub patternDom2Text($) {
1822             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1823 605     605 1 776 my @path=@{shift()};
  605         996  
1824             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1825              
1826             =item I()
1827              
1828             convert the pattern DOM (array of element used by search(), path()) to text scalar string.
1829              
1830              
1831             is an array list of splited element of the pattern
1832              
1833             Return equivalent text
1834              
1835             EX:
1836             patternDom2Text( ['?@'] );
1837              
1838             Return '?@'
1839              
1840             patternDom2Text( ['%', 'r'] );
1841              
1842             Return '%r'
1843              
1844             patternDom2Text( ['@',3,'%','r'] );
1845              
1846             Return '@3%r'
1847              
1848             patternDom2Text( ['@',2,'=','3'] );
1849              
1850             Return '@2=3'
1851              
1852             =cut
1853              
1854             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1855              
1856             # patternDom2Text is a single join without key defined
1857              
1858 605 100       2139 (defined $CFG->{o_key}) or return join('',@path);
1859              
1860 74 50       65 (%{$CFG->{o_key}}) or join('',@path);
  74         117  
1861              
1862              
1863             # matching Keys
1864              
1865 74         85 my $sz_path = scalar(@path);
1866              
1867             # debug "\n###".join('.',@{$path}).' '.join('|',keys %{$CFG->{o_key}}); <>;
1868              
1869 74         74 my %keys=%{$CFG->{o_key}};
  74         289  
1870              
1871             # TODO : key priority sould be managed by a small getPrioritizedKey() function (warning)
1872              
1873 74         154 my @sorted_keys =
1874             # sort { ( $keys{$a}->{priority} > $keys{$b}->{priority} ) }
1875             keys %keys;
1876              
1877 74         87 my $k;
1878              
1879 74         90 my $i = 0;
1880 74         112 while ($i
1881              
1882 393         440 foreach $k (@sorted_keys)
1883             {
1884 1831         1983 my $match = $keys{$k}{regexp};
1885              
1886             #warn "\n=$k on ".join('',@path[0..$i]);
1887              
1888 1831         2784 my $min_index = $matchPath->($match, @path[0..$i]);
1889              
1890 1831 100       2856 if (defined $min_index) {
1891             # debug
1892             #warn " -> key($k -> ".join(' ',@{$match}).") = $min_index\n";
1893              
1894             # replace the (matched key expression) by ('/' , )
1895              
1896 79         196 splice @path, $min_index, scalar(@$match), '/',$k;
1897              
1898 79         123 $i = $i + 2 - scalar(@$match);
1899              
1900             #warn "-> path -> ".join('.',@path)." \$i=$i\n";
1901             }
1902             }
1903 393         518 $i++;
1904             }
1905 74         363 return join('',@path);
1906              
1907             };
1908              
1909              
1910              
1911             ##############################################################################
1912             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1913             sub domPatch2TEXT(@) {
1914             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1915              
1916             =item I(, [,])
1917              
1918             convert a list of perl usable patches into a readable text format.
1919             Also convert to key patterns which are matching the regexp key definnition
1920             Mainly used to convert the compare result (format dom)
1921              
1922             ARGS:
1923             a list of
1924              
1925             Return a list of patches in TEXT mode
1926              
1927             EX:
1928              
1929              
1930             domPatch2TEXT($patch1)
1931              
1932             returns 'change(@0$%magic_key,@0$%magic_key)="toto"/=>"tata"'
1933              
1934              
1935             # one key defined
1936             o_key({ key_1 => {regexp=>['%','magic_key'], eval=>'{magic_key}' } } );
1937              
1938             # same but with the related matched key in path
1939              
1940             domPatch2TEXT($patch1)
1941              
1942             returns 'change(@0$/key_1,@0$/key_1)="toto"/=>"tata"'
1943              
1944              
1945             =cut
1946              
1947 262     262 1 548 my @res;
1948             my $patch;
1949 262         327 foreach $patch (@_) {
1950             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1951              
1952             (ref($patch) eq 'HASH') and do {
1953              
1954             (exists $patch->{action})
1955 262 50       458 or die 'domPatch2TEXT(): bad internal dom structure '.__d($patch);
1956              
1957              
1958 262         287 my $action = $patch->{action};
1959 262         291 my $v1 = $patch->{val_orig};
1960 262         274 my $v2 = $patch->{val_dest};
1961              
1962             my $txt = $action
1963             .'('
1964             .patternDom2Text($patch->{path_orig})
1965             .','
1966             .patternDom2Text($patch->{path_dest})
1967 262         461 .')=';
1968              
1969 262 100 100     784 if (($action eq 'remove') or ($action eq 'change')) {
1970 168         240 $v1 = __d($v1);
1971 168         222 $v1 =~ s|/=>|\/\\054\>|g;
1972 168         191 $v1 =~ s/\s=>\s/=>/sg;
1973 168         230 $txt .= $v1;
1974             }
1975              
1976 262 100       395 ($action eq 'change') and $txt .= '/=>';
1977              
1978 262 100 100     597 if (($action eq 'add') or ($action eq 'change')) {
1979 193         239 $v2 = __d($v2);
1980 193         241 $v2 =~ s|/=>|\/\\054\>|g;
1981 193         197 $v2 =~ s/\s=>\s/=>/sg;
1982 193         227 $txt .= $v2;
1983             }
1984              
1985 262         298 push @res, $txt;
1986             next
1987 262         388 } or
1988 262 0 0     514 (ref($_) eq 'ARRAY') and do {
      33        
1989 0         0 push @res,join '', @{$_};
  0         0  
1990             next
1991 0         0 };
1992             }
1993              
1994             #
1995 262 100       694 (wantarray()) and return @res;
1996 2         9 return join("\n",@res);
1997             }
1998              
1999             ##############################################################################
2000             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
2001             sub domPatch2IHM(@) {
2002             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2003              
2004             =item I(, [,])
2005              
2006             convert a list of patches in DOM format (internal Data;;Deep format)
2007             into a IHM format.
2008             Mainly used to convert the compare result (format dom)
2009              
2010             ARGS:
2011             a list of
2012              
2013             Return a list of patches in IHM mode
2014             IHM format is not convertible
2015              
2016             EX:
2017             C($patch1)
2018             returns
2019             '"toto" changed in "tata" from @0$%a
2020             into @0$%a
2021             =cut
2022              
2023              
2024 0     0 1 0 my ($msg,$patch);
2025              
2026 0         0 foreach $patch (@_) {
2027             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2028 0         0 $_ = $patch->{action};
2029              
2030             /^add$/ and ($msg .= __d($patch->{val_orig}).' added')
2031             or
2032             /^remove$/ and ($msg .= __d($patch->{val_orig}).' removed')
2033             or
2034             /^move$/ and ($msg .= 'Moved ')
2035             or
2036             /^change$/ and ($msg .= __d($patch->{val_orig})
2037             .' changed in '
2038 0 0 0     0 .__d($patch->{val_dest}));
      0        
      0        
      0        
      0        
      0        
2039 0         0 my $l = length($msg);
2040 0         0 my $MAX_COLS=40;
2041 0 0       0 if ($l>$MAX_COLS) {
2042 0         0 $msg .= "\n from ".join('',@{$patch->{path_orig}});
  0         0  
2043 0         0 $msg .= "\n into ".join('',@{$patch->{path_dest}});
  0         0  
2044             }
2045             else {
2046 0         0 $l-=($msg=~ s/\n//g);
2047 0         0 $msg .= ' from '.join('',@{$patch->{path_orig}});
  0         0  
2048 0         0 $msg .= "\n".(' 'x $l).' into '.join('',@{$patch->{path_dest}});
  0         0  
2049             }
2050 0         0 $msg .= "\n";
2051             }
2052 0         0 return $msg;
2053             }
2054              
2055              
2056             ##############################################################################
2057             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
2058             sub patternText2Dom($) {
2059             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2060              
2061 82     82 1 253 my $pathTxt = shift();
2062              
2063 82 50       128 (ref($pathTxt)) and die 'patternText2Dom() : bad call with a reference instead of scalar containing pattern text ';
2064              
2065             =item I()
2066              
2067             convert pattern scalar string to the array of element to be used by search(), path()
2068              
2069              
2070             is an array of type description to match
2071             optional argument to limit the number of results
2072             if undef all results are returned
2073             if 1 first one is returned
2074              
2075             Return an array list of splited element of the for usage
2076              
2077             EX:
2078             patternText2Dom( '?@' );
2079              
2080             Return ['?@']
2081              
2082             patternText2Dom( '%r' );
2083              
2084             Return '%', r']
2085              
2086             patternText2Dom( '@3%r' );
2087              
2088             Return ['@',3,'%','r']
2089              
2090             patternText2Dom( '@2=3' );
2091              
2092             Return ['@',2,'=','3']
2093              
2094             =cut
2095              
2096             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2097              
2098 82         65 my @path;
2099              
2100             #debug "patternText2Dom($pathTxt)";;
2101              
2102 82         94 my %keys=();
2103              
2104 82 100       144 (ref($CFG->{o_key})) and %keys = %{$CFG->{o_key}};
  12         68  
2105              
2106 82         202 my @pathTxt = split('',$pathTxt);
2107              
2108 82         135 while (@pathTxt) {
2109              
2110 427         464 $_ = shift @pathTxt;
2111              
2112 427 100 100     1690 if (defined($path[-1]) and $path[-1] =~ /^\?/ and m/^[\=\%\$\@\%\*]/) {
    100 66        
    100          
    100          
    100          
2113 3         8 $path[-1].= $_;
2114             }
2115             elsif ($_ eq '$') {
2116 22         35 push(@path,'$');
2117             }
2118             elsif ($_ eq '?') {
2119 3         9 push(@path,'?');
2120             }
2121             elsif ($_ eq '&') {
2122 3         7 push(@path,'&');
2123             }
2124             elsif (/([%\@\=\|\*\/])/) {
2125 144         330 push(@path,$1,'');
2126             }
2127             else {
2128 252 100 100     435 if ($path[-2] eq '/' and exists($keys{$path[-1]})) {
2129             # cf test "Search Complex key 3..5"
2130 3         6 push(@path,'');
2131             }
2132 252         372 $path[-1].= $_;
2133             }
2134             }
2135              
2136             # post - convertion of array & key convertion
2137              
2138 82         86 my $i;
2139 82         141 for $i (0..$#path) {
2140              
2141 319 100       503 if ($path[$i] eq '@') {
    100          
2142 56         93 $path[$i+1] = int($path[$i+1]);
2143             }
2144             elsif ($path[$i] eq '/') {
2145 16         22 my $keyname = $path[$i+1];
2146 16 50       31 (exists($keys{$keyname})) or die 'patternText2Dom() ! no key '.$keyname;
2147              
2148 16         22 splice @path, $i, 2, @{ $keys{$keyname}{regexp} };
  16         65  
2149              
2150             }
2151             }
2152              
2153             #warn "patternText2Dom(".join('',@pathTxt).')=> '.join(' ',@path)." .";
2154              
2155             #debug '=>'.join('.',@path);
2156 82         284 return [@path];
2157             };
2158              
2159              
2160             ##############################################################################
2161             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
2162             sub textPatch2DOM(@) {
2163             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2164              
2165             =item I(, [,])
2166              
2167             convert a list of patches formatted in text (readable text format format)
2168             to a perl DOM format (man perldsc).
2169             Mainly used to convert the compare result (format dom)
2170              
2171             ARGS:
2172             a list of
2173              
2174             Return a list of patches in dom mode
2175              
2176             EX:
2177             C( 'change(@0$%a,@0$%a)="toto"/=>"tata"',
2178             'move(... '
2179             )
2180              
2181             returns (
2182             { action=>'change',
2183             path_orig=>['@0','$','%a'],
2184             path_dest=>['@0','$','%a'],
2185             val_orig=>"toto",
2186             val_dest=>"tata"
2187             },
2188             { action=>'move',
2189             ...
2190             });
2191              
2192             =cut
2193              
2194 27     27 1 33 my @res;
2195 27         50 while (@_) {
2196             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2197 27         33 my $patch=pop;
2198              
2199 27 50       38 defined($patch) or next;
2200              
2201 27         71 debug "textPatch2DOM in ".$patch;
2202              
2203 27         35 my ($p1,$p2,$v1,$v2);
2204 27 50       111 $patch =~ s/^(\w+)\(// or die 'Data::Deep::textPatch2DOM / bad patch format :'.$patch.' !!!';
2205              
2206 27         51 my $action = $1; # or die 'action ???';
2207              
2208 27 50       102 ( $patch =~ s/^([^,]*?),//
2209             ) and $p1 = patternText2Dom($1);
2210              
2211 27 50       115 ( $patch =~ s/^([^\(]*?)\)=//
2212             ) and $p2 = patternText2Dom($1);
2213              
2214 27 50       47 if ($action ne 'move') {
2215 27         47 my $i = index($patch, '/=>');
2216 27 100       64 if ($i ==-1 ) {
2217 19 100 66     59 ($action eq 'add') && ($v2 = $patch) or ($v1 = $patch);
2218             }
2219             else {
2220 8         13 $v1 = substr($patch, 0, $i);
2221 8         17 $v2 = substr($patch, $i+3);
2222             }
2223             }
2224 27         662 my $a = eval($v1);
2225 27 50       84 ($@) and die "textPatch2DOM() error in eval($v1) : ".$@;
2226              
2227 27         648 my $b = eval($v2);
2228 27 50       81 ($@) and die "textPatch2DOM() error in eval($v2) : ".$@;
2229              
2230 27         53 push @res,$patchDOM->($action, $p1, $p2, $a, $b);
2231             }
2232              
2233             #
2234 27 50       88 (wantarray()) and return @res;
2235 0           return [@res];
2236             }
2237              
2238             =back
2239              
2240             =begin end
2241              
2242             =head1 AUTHOR
2243              
2244              
2245             Data::Deep was written by Matthieu Damerose Idamo@cpan.orgE> in 2005.
2246              
2247             =cut
2248              
2249              
2250             ###########################################################################
2251             1;#############################################################################
2252             __END__ Deep::Manip.pm