File Coverage

lib/Data/Deep.pm
Criterion Covered Total %
statement 528 592 89.1
branch 285 358 79.6
condition 105 166 63.2
subroutine 28 32 87.5
pod 15 23 65.2
total 961 1171 82.0


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   22431 use 5.004;
  1         5  
  1         62  
166             $VERSION = '0.12';
167             #$| = 1;
168              
169             ##############################################################################
170             # Module dep
171             ##############################################################################
172              
173 1     1   7 use Carp;
  1         2  
  1         83  
174 1     1   12 use strict;
  1         8  
  1         34  
175 1     1   6 no warnings;
  1         1  
  1         48  
176 1     1   935 no integer;
  1         11  
  1         5  
177 1     1   34 no strict 'refs';
  1         1  
  1         34  
178              
179              
180 1     1   6 use overload; require Exporter; our @ISA = qw(Exporter);
  1         1  
  1         8  
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 0 0 0 0 1 0 @_ and $Data::Deep::CFG->{zap}=shift()
249             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 50878 eval 'sub '.$name."(;$proto) {"
  12738 50 66 12738 1 154909  
  1 50 33 1 1 11  
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 11366 50   11366 0 206989 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   11948 my $res = join('', travel(shift(), \&visitor_perl_dump));
341             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
342              
343 1831         16804 $res =~ s/
344             ([\000-\037]|[\177-\377])
345 31         1322 /sprintf("\\%o", ord ($1))/egx;
346              
347 1831         7124 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 4 100 66 4 1 839 @_ and $CFG->{o_key}=shift()
458             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 13076     13076 0 18698 my $r = shift();
623 13076 100       43872 ref($r) or return 0;
624              
625 4687         8998 $r = $r.' ';
626              
627 4687 100       10133 if (exists($loop_ref{$r})) {
628 66         350 debug "loop_det => LOOP".join('',@_) ;
629              
630 66         184 return 1;
631             }
632              
633 4621         9932 $loop_ref{$r}=1;
634 4621         13184 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 154 my $node = shift();
659 111         145 my $depth = shift;
660 111         129 my $open = shift;
661 111         275 my @cur_path = @_;
662              
663 111         203 my $path = join('',@cur_path);
664              
665             # warn $depth.($open==1?' > ':(defined($open)?' < ':' ')).join('',@cur_path).' : '.ref($node);
666              
667 111         166 my $ref = ref($node);
668 111 100       219 if ($ref) {
669 91 100       203 if (!defined $open ) {
    100          
    50          
670 49 100       129 ($_[-1] eq '$loop') and return 'loop('.$path.','.$path.')=';
671 45 100       97 ($ref eq 'CODE') and return 'add('.$path.','.$path.')=sub{}';
672             #($ref eq 'REF') and return 'add('.$path.','.$path.')={}';
673 43 50       87 ($ref eq 'GLOB') and return 'new '.$_[-1].'()';
674             }
675             elsif ($open ==1 ) {
676 21 100       79 ($ref eq 'ARRAY') and return 'add('.$path.','.$path.')=[]';
677 11 100       70 ($ref eq 'HASH') and return 'add('.$path.','.$path.')={}';
678 2         8 return ;
679             }
680             elsif ($open ==0 ) {
681             #($ref eq 'ARRAY') and return ']';
682             #($ref eq 'HASH') and return '}';
683 21         65 return;
684             }
685              
686             }
687              
688 63 100 66     322 defined($node) and $node = "'$node'" or $node = 'undef';
689              
690              
691 63         79 pop(@cur_path);
692 63         85 pop(@cur_path);
693 63         128 $path = join('',@cur_path);
694 63         73 pop(@cur_path);
695 63         74 pop(@cur_path);
696              
697 63 100       206 ($_[-2] eq '=') and return 'add('.join('',@cur_path).','.$path.')='.$node;
698              
699              
700 43         109 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 23718     23718 0 32079 my $node = shift();
714 23718         31837 my $depth = shift;
715 23718         30660 my $open = shift;
716 23718         60729 my @cur_path = @_;
717              
718 23718         31718 my $path = @cur_path;
719              
720 23718         44139 my $ref = ref($node);
721              
722 23718         65929 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 23718 100       191877 if ($ref) {
729 15774 100       38328 if (!defined $open ) {
    100          
    50          
730 9880 50 66     28469 ($realpack and $realtype and $id) and $ref = $realtype;
      66        
731              
732 9880 100 100     45352 ($ref eq 'REF' or $ref eq 'SCALAR') and return '\\';
733              
734 9314 100       24485 ($ref eq 'CODE') and return 'sub { "DUMMY" }';
735              
736 9250 100       24772 if ($_[-1] eq '$loop') {
737 56         191 return '$t1';
738             }
739              
740 9194 100 66     30274 if ($ref eq 'HASH' and $_[-2] eq '%') {
741 4528         21503 my @keys = sort {$a cmp $b} keys(%$node);
  173811         204970  
742 4528         11433 my $is_first = ($_[-1] eq $keys[0]);
743              
744 4528 100       14708 $is_first
745             and
746             return '\''.$_[-1].'\'=>';
747              
748 3233         21495 return ',\''.$_[-1].'\'=>';
749             }
750 4666 100 66     36427 ($ref eq 'ARRAY' and $_[-2] eq '@' and $_[-1] != 0) and return ',';
      100        
751 1380         4361 return;
752             }
753             elsif ($open ==1 ) {
754 2947 100       9469 ($ref eq 'ARRAY') and return '[';
755 1403 100       5972 ($ref eq 'HASH') and return '{';
756              
757 37 50       167 ($realtype eq 'ARRAY') and return 'bless([';
758 37 50       177 ($realtype eq 'HASH') and return 'bless({';
759             }
760             elsif ($open ==0 ) {
761 2947 100       12231 ($ref eq 'ARRAY') and return ']';
762 1403 100       6471 ($ref eq 'HASH') and return '}';
763              
764 37 50       120 ($realtype eq 'ARRAY') and return "] , '$ref')";
765 37 50       221 ($realtype eq 'HASH') and return "} , '$ref')";
766              
767             }
768             }
769              
770 7944 100       17465 (defined($node)) or return 'undef';
771              
772 7769 50       21680 if ($_[-2] eq '=') {
773 7769         18115 $node=~s/\'/\\\'/g;
774 7769 100       36673 ($node=~/^\d+$/) and return $node;
775 3025         13061 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 60 my $node = shift();
788 40         53 my $depth = shift;
789 40         43 my $open = shift;
790 40         87 my @cur_path = @_;
791              
792 40         93 my $path = join('',@cur_path);
793              
794 40         95 my ($realpack, $realtype, $id) =
795             (overload::StrVal($node) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
796              
797 40 100       469 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 11726     11726 1 17768 my $where=shift();
824 11726   100     33386 my $visitor = shift() || \&visitor_patch;
825 11726   100     33227 my $depth = shift()||0;
826 11726         29624 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 11726 100       22013 if (@path) {
851 9886         21844 debug "travel( dom=",@path, ' is ',ref($where),")";
852             #debug "return ".($arr && ' ARRAY ' || 'SCALAR');
853             }
854             else {
855 1840         5680 %loop_ref=();
856             }
857              
858             #
859              
860             sub __appendVisitorResult {
861 33755     33755   52672 my $is_array = shift();
862 33755         38255 my @list;
863              
864 33755         55481 foreach (@_) {
865 315076 50       576125 if (defined $_) {
866 315076 50       560320 $is_array or return $_;
867 315076         554173 push(@list, $_);
868             }
869             }
870 33755         280244 return @list;
871             }
872              
873 11726         15837 my ($k,$res);
874 0         0 my @res;
875              
876             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
877              
878 11726         17903 my $ref_type = ref $where;
879              
880              
881             ######################################## !!!!! Modules type resolution
882             # if (index($ref_type,'::')!=-1) {
883 11726         34922 my ($realpack, $realtype, $id) =
884             (overload::StrVal(scalar($where)) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
885              
886 11726 100 66     95292 if ($realpack and $realtype and $id) {
      100        
887 40         102 push @path,'|',$ref_type;
888              
889 40         55 my $y = undef;
890 40 50       144 if ($realtype eq 'SCALAR') {
    50          
    0          
891 0         0 $y=$$where;
892             }
893             elsif ($realtype eq 'HASH') {
894 40         131 $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         74 $where=$y;
906 40         77 $ref_type = $realtype;
907             }
908              
909              
910             ######################################## !!!!! Loop detection
911 11726         13885 my @p;
912              
913 11726 100       21164 if (loop_det($where)) {
914              
915 60         145 return __appendVisitorResult(wantarray(), @res,
916             &$visitor($where, $depth, undef , (@path, '$loop')));
917              
918             }
919             else {
920             ######################################## !!!!! SCALAR TRAVEL
921 11666 100 100     26209 if (!$ref_type) {
    100          
    100          
    100          
922              
923 7973         35680 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         3653 @res = __appendVisitorResult(wantarray(),
933             @res,
934             &$visitor($where, $depth, 1, @path));
935              
936 1418         1822 my $k;
937 1418         1846 foreach $k (sort {$a cmp $b} keys(%{ $where })) {
  8841         10618  
  1418         6204  
938 4549         13609 @p = (@path, '%', $k);
939              
940 4549         10953 @res = __appendVisitorResult(wantarray(),
941             @res,
942             &$visitor($where, $depth, undef, @p)
943             );
944              
945 4549         28671 @res = __appendVisitorResult(
946             wantarray(),
947             @res,
948             travel($where->{$k},$visitor,$depth+1, @p)
949             );
950             }
951              
952 1418         4429 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         4008 $res = &$visitor($where, $depth, 1, @path);
962              
963 1557         3595 @res = __appendVisitorResult( wantarray(), @res, $res );
964              
965 1557         2088 for my $i (0..$#{ $where }) {
  1557         4827  
966             #print "\narray $i (".$where->[$i].','.join('.',@p).")\n" if (join('_',@p)=~ /\@_1_\%_g_/);
967 4692         12049 @p = (@path, '@', $i);
968              
969 4692         11071 @res = __appendVisitorResult(wantarray(),
970             @res,
971             &$visitor($where, $depth, undef, @p)
972             );
973              
974 4692         20532 @res = __appendVisitorResult(
975             wantarray(),
976             @res,
977             travel($where->[$i],$visitor,$depth+1, @p)
978             );
979              
980             }
981              
982 1557         5032 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         1588 @p = (@path, "\$");
992              
993 572         1418 @res = __appendVisitorResult( wantarray(),
994             @res,
995             &$visitor($where, $depth, undef, @p )
996             );
997              
998 572         1925 return __appendVisitorResult( wantarray(),
999             @res,
1000 572         5079 travel( ${ $where }, $visitor, $depth+1, @p )
1001             );
1002             }
1003             else { # others types
1004             ######################################## !!!!! CODE TRAVEL
1005 146 100       405 if ($ref_type eq 'CODE') {
    50          
1006 66         213 @p = (@path, '&');
1007             }
1008             ######################################## !!!!! GLOB TRAVEL
1009             elsif ($ref_type eq 'GLOB') {
1010 80         204 my $name=$$where;
1011 80         214 $name=~s/b^\*//;
1012 80         291 @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       397 if ($p[-2] eq '*') { # GLOB
1023 80         141 for $k (qw(SCALAR ARRAY HASH)) {
1024 171         264 my $gval = *$where{$k};
1025 171 100       419 defined($gval) or next;
1026 130 100 100     567 next if ($k eq "SCALAR" && ! defined $$gval); # always there
1027              
1028 73         213 return __appendVisitorResult( wantarray(),
1029             @res,
1030             travel($gval, $visitor, $depth+1, undef, @p)
1031             );
1032             }
1033             }
1034              
1035 73         188 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 2741     2741 1 5225 my $where = shift();
1054 2741         3847 my $pattern = shift();
1055 2741         3554 my $nb_occ = shift();
1056 2741         6358 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 2741 100       7265 @path or %loop_ref=();
1099              
1100 2741 50 66     11699 (defined($nb_occ) and ($nb_occ<1)) and return ();
1101              
1102 2741         4767 my $ref_type = ref $where;
1103              
1104 2741         3068 my @found;
1105 2741         3451 my $next = undef;
1106 2741         3023 my @p;
1107              
1108             ######################################## !!!!! Modules type resolution
1109 2741 100       4685 if ($ref_type) {
1110              
1111             #if (index($where,'::')!=-1) { ## !!!!! MODULE SEARCH
1112              
1113 676         2075 my ($realpack, $realtype, $id) =
1114             (overload::StrVal($where) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
1115              
1116 676 50 66     7574 if ($realpack and $realtype and $id) {
      66        
1117 11         29 push @path, ('|', $ref_type);
1118              
1119 11         29 $ref_type = $realtype;
1120              
1121             #warn "$ref_type -> ($realpack, $realtype, $id )";
1122             }
1123              
1124              
1125             ######################################## !!!!! Loop detection
1126              
1127 676 100 100     1417 if (loop_det($where)) {
    100          
    100          
    100          
    100          
    50          
1128 2         7 @p = (@path, '$loop');
1129             }
1130             ######################################## HASH Search
1131             elsif ($ref_type eq 'HASH') {
1132 272         361 my $k;
1133 272         450 foreach $k (sort {$a cmp $b} keys(%{ $where })) {
  3629         3804  
  272         1322  
1134 1404         3645 @p = (@path, '%', $k);
1135              
1136 1404 100       2675 if (defined $matchPath->($pattern, @p)) {
1137 64         252 push @found,[@p];
1138 64 100 66     361 defined($nb_occ) and (--$nb_occ<1) and last;
1139             }
1140             else {
1141 1340         4324 my @res = search($where->{$k}, $pattern, $nb_occ, @p);
1142 1340 100       4410 @res and push @found,@res;
1143             }
1144             }
1145 272         1359 return @found;
1146             }
1147             ######################################## HASH Search
1148             elsif ($ref_type eq 'ARRAY')
1149             {
1150 263         362 for my $i (0..$#{ $where }) {
  263         845  
1151 1170         3682 @p = (@path, '@', $i);
1152              
1153 1170 100       2368 if (defined $matchPath->($pattern, @p)) {
1154 25         78 push @found,[@p];
1155 25 100 66     148 defined($nb_occ) and (--$nb_occ<1) and last;
1156             }
1157             else {
1158 1145         3075 my @res = search($where->[$i], $pattern, $nb_occ, @p);
1159 1145 100       3766 @res and push @found,@res;
1160             }
1161             }
1162 263         1200 return @found;
1163             }
1164             ######################################## REF Search
1165             elsif ($ref_type eq 'REF' or $ref_type eq 'SCALAR') {
1166 110         291 @p = (@path, '$');
1167 110         150 $next = ${ $where };
  110         232  
1168             }
1169             ######################################## CODE Search
1170             elsif ($ref_type eq 'CODE') {
1171 15         55 @p = (@path, '&');
1172             }
1173             ######################################## GLOB Search
1174             elsif ($ref_type eq 'GLOB') {
1175 14         37 my $name = $$where;
1176 14         96 $name=~s/^\*//;
1177 14         71 @p = (@path, '*',$name);
1178 14 100 66     52 if (defined *$where{SCALAR} and defined(${*$where{SCALAR}})) {
  14 100       67  
    100          
1179 4         9 $next = *$where{SCALAR};
1180             }
1181             elsif (defined *$where{ARRAY}) {
1182 4         12 $next = *$where{ARRAY};
1183             }
1184             elsif (defined *$where{HASH}) {
1185 4         18 $next = *$where{HASH};
1186             }
1187             }
1188             }
1189             ######################################
1190             else { ## !!!!! SCALAR Search
1191 2065         5777 @p = (@path, '=', $where);
1192             }
1193             ######################################
1194              
1195 2206 100       5133 if (defined $matchPath->($pattern, @p)) {
1196 136         541 push @found,[@p];
1197 136 100       358 defined($nb_occ) and --$nb_occ;
1198             }
1199              
1200 2206 100       4589 if ((defined($next))) {
1201 114         297 my @res = search($next, $pattern, $nb_occ, @p);
1202              
1203 114 100       329 @res and push @found,@res;
1204             }
1205              
1206 2206         6453 return @found;
1207             }
1208              
1209              
1210             ##############################################################################
1211             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1212             sub path($$;$) {
1213             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1214 108     108 1 277 my $dom = shift();
1215 108         219 my @paths = @{shift()};
  108         306  
1216 108 100       448 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         632 debug "path( \$dom, $#paths patch, $father_nb)";
1266              
1267 108         250 my @nodes;
1268              
1269 108         262 foreach my $node (@paths) {
1270 153 50       555 (ref($node) eq 'ARRAY') or die 'path() : pattern "'.$node.'" should be a Dom pattern ("Dom" internal array, perhaps use patternText2dom)';
1271              
1272 153         225 my @path = @{$node};
  153         582  
1273              
1274             # perl evaluation of the dom path
1275 153         548 my $e = $path2eval__->('$dom', $father_nb, @path);
1276              
1277 153         13413 my $r = eval $e;
1278 153         714 debug $dom;
1279 153         529 debug $e.' evaluated to '.__d($r);
1280 153 50       512 die __FILE__.' : path() '.$e.' : '.$@ if ($@);
1281 153         566 push @nodes,$r
1282             }
1283 108 50       315 return shift @nodes unless (wantarray());
1284 108         539 return @nodes;
1285             }
1286              
1287             ##############################################################################
1288             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1289             sub compare {
1290             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1291              
1292             # ############ ret : 0 if equal / 1 else
1293 932     932 1 1917 my $d1 = shift();
1294 932         1227 my $d2 = shift();
1295              
1296 932         1178 my (@p1,@p2,$do_resolv_patch);
1297 932 100       1755 if (@_) {
1298 753         1026 @p1 = @{$_[0]};
  753         2049  
1299 753         1118 @p2 = @{$_[1]};
  753         1899  
1300             }
1301             else {
1302 179         504 %loop_ref=();
1303             # equiv TEST on each function call: if ($CFG->{o_complex} and ($#a1==-1 and $#a2==-1)) {
1304 179 100       647 $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 151 my @patch = @_;
1354 53         82 my ($p1,$p2);
1355              
1356 53         115 foreach $p1 (@patch) {
1357 108         156 foreach $p2 (@patch) {
1358              
1359 470 100 100     1504 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 8         32 push @patch,
1367             compare($p1->{val_orig},
1368             $p2->{val_dest},
1369 8         36 [@{$p1->{path_orig}}],
1370 8         32 [@{$p2->{path_dest}}]
1371             );
1372              
1373 8         31 $p1->{action}='move';
1374 8         22 $p1->{val_orig}= $p1->{val_dest}= undef;
1375 8         21 $p1->{path_dest}= $p2->{path_dest};
1376 8         31 $p2->{action}='erase';
1377             }
1378             }
1379             }
1380              
1381 53         92 my $o = 0;
1382 53         161 while ($o<=$#patch) {
1383 108 100 66     352 ($patch[$o]->{action} eq 'erase') and splice(@patch,$o,1) and next;
1384 100         236 $o++
1385             }
1386              
1387             return @patch
1388 53         369 }
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 932         2011 my @msg=();
1395              
1396             ######################################## !!!!! Type resolution
1397 932         1463 my $ref_type = ref $d1;
1398              
1399 932 100       2132 if ($ref_type) {
1400              
1401 446 100       1297 ($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         1110 my ($realpack, $realtype, $id) =
1408             (overload::StrVal($d1) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
1409              
1410 379 50 66     4016 if ($realpack and $realtype and $id) {
      66        
1411 2         8 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         6 push @p1, '|',$ref_type;
1419 2         6 push @p2, '|',$ref_type;
1420            
1421 2         11 debug "$ref_type -> ($realpack, $realtype, $id : $ref_type)";
1422              
1423 2         15 $ref_type = $realtype;
1424             }
1425             }
1426              
1427             ######################################## !!!!! SCALAR COMPARE
1428 865 100 100     2672 if (!$ref_type)
    100          
    100          
    100          
    100          
    50          
1429             {
1430 486 100 100     2522 (defined($d1) and $d1 ne $d2) and return ($patchDOM->('change', \@p1,\@p2, $d1,$d2) );
1431 403 100 100     999 (!defined($d1) and defined($d2)) and return ($patchDOM->('change', \@p1,\@p2, $d1,$d2) );
1432 401         1657 return ();
1433             }
1434             ######################################## !!!!! HASH COMPARE
1435             elsif ($ref_type eq 'HASH')
1436             {
1437 167         290 my (%seen,$k);
1438              
1439 167         263 foreach $k (sort {$a cmp $b}
  355         655  
  167         689  
1440             keys(%{ $d1 }))
1441             {
1442 356         772 $seen{$k}=1;
1443              
1444 356 100       789 if (exists $d2->{$k}) {
1445              
1446 334 50       793 loop_det($d1->{$k},@p1) and next;
1447              
1448 334         1745 push @msg,
1449             compare( $d1->{$k},
1450             $d2->{$k},
1451             [ @p1, '%',$k ],
1452             [ @p2, '%',$k ],
1453             );
1454             } else {
1455 22         166 push @msg,$patchDOM->('remove', [ @p1, '%', $k ] ,\@p2 , $d1->{$k} ,undef)
1456             }
1457              
1458             }#foreach($d1)
1459              
1460 167         421 foreach $k (sort {$a cmp $b} keys(%{ $d2 })) {
  359         515  
  167         520  
1461 357 100       1179 next if exists $seen{$k};
1462              
1463 23         51 my $v = $d2->{$k};
1464 23         100 push @msg,$patchDOM->('add', \@p1, [ @p2, '%', $k ], undef, $v)
1465             }
1466              
1467 167 100       943 $do_resolv_patch or return @msg;
1468 35         123 return resolve_patch(@msg);
1469             }
1470             elsif ($ref_type eq 'ARRAY')
1471             {
1472             ######################################## !!!!! ARRAY COMPARE (not complex mode)
1473              
1474 132 100       441 unless ($CFG->{o_complex}) {
1475              
1476 63         95 my $min = $#{$d1};
  63         160  
1477 63 100       148 $min = $#{$d2} if ($#{$d2}<$min); # min ($#{$d1},$#{$d2})
  4         10  
  63         173  
1478              
1479 63         92 my $i;
1480 63         133 foreach $i (0..$min) {
1481              
1482 120 100       319 loop_det($d1->[$i], @p1)
1483             and
1484             next;
1485              
1486 118         629 push @msg,
1487             compare( $d1->[$i], $d2->[$i], [@p1, '@',$i], [@p2, '@',$i]);
1488             }
1489              
1490 63         147 foreach $i ($min+1..$#{$d1}) { # $d1 is bigger
  63         170  
1491             # silent just for complexe search mode
1492 5         23 push @msg,$patchDOM->('remove', [ @p1, '@', $i ], \@p2 ,$d1->[$i], undef)
1493             }
1494 63         128 foreach $i ($#{$d1}+1..$#{$d2}) { # d2 is bigger
  63         141  
  63         154  
1495 9         40 push @msg,$patchDOM->('add', \@p1, [ @p2, '@', $i ], undef, $d2->[$i])
1496             }
1497 63         277 return @msg;
1498             }
1499              
1500             ######################################## !!!!! ARRAY COMPARE (in complex mode)
1501 69         111 my @seen_src;
1502             my @seen_dst;
1503 0         0 my @res_Eq;
1504             # perhaps not on the same index (search in the dest @)
1505 0         0 my $i;
1506 69         218 ARRAY_CPLX:
1507 69         117 foreach $i (0..$#{$d1}) {
1508 161         293 my $val1 = $d1->[$i];
1509            
1510             #print "\n SAR($i) {";
1511             #if ($i<$#{$d2}) {
1512 161 100       409 if (exists $d2->[$i]) {
1513 155         189 my @res;
1514              
1515 155 100       323 loop_det($val1, @p1)
1516             or
1517             @res = compare($val1,
1518             $d2->[$i],
1519             [ @p1, '@',$i ],
1520             [ @p2, '@',$i ]);
1521              
1522 155 100       561 if (@res) { $res_Eq[$i] = [@res] } # (*)
  29         75  
1523             else
1524             {
1525 126         209 $seen_src[$i]=$i;
1526 126         180 $seen_dst[$i]=$i;
1527 126         355 next ARRAY_CPLX;
1528             }
1529             }
1530 35         50 my $j;
1531 35         46 foreach $j (0..$#{$d2}) { #print " -> $j ";
  35         96  
1532 102 100       219 next if ($i==$j);
1533 78 100       166 next if (defined($seen_dst[$j]));
1534              
1535 61 100       245 unless (compare( $val1,
1536             $d2->[$j],
1537             [ @p1, '@',$i ],
1538             [ @p2, '@',$j ]))
1539             { #print " (found) ";
1540              
1541 15         25 $seen_dst[$j] = 1;
1542 15         57 $seen_src[$i] = $patchDOM->('move',
1543             [ @p1, '@', $i ],
1544             [ @p2, '@', $j ]);
1545 15         66 next ARRAY_CPLX;
1546             }
1547             }
1548 20 50       146 (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         150 foreach $i (0..$#{$d2}) {
  69         183  
1563 165 100       429 defined($seen_dst[$i]) and next;
1564              
1565 24         107 $seen_dst[$i] = $patchDOM->('add',
1566             \@p1,
1567             [ @p2, '@', $i ],
1568             undef,
1569             $d2->[$i]
1570             )
1571             }
1572              
1573 69         163 my $max = $#seen_dst;
1574              
1575 69 100       176 ($#seen_src>$max) and $max = $#seen_src;
1576              
1577 69         157 foreach (0..$max) {
1578 171         233 my $src = $seen_src[$_];
1579 171         232 my $dst = $seen_dst[$_];
1580              
1581 171 100 66     669 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 12         73 ($src->{action} eq 'remove') and
1588             ($dst->{action} eq 'add') and
1589 17 50 66     138 (push @msg, @{ $res_Eq[$_] })
      66        
1590             and next;
1591             }
1592 159 100       311 (ref $src) and push @msg,$src;
1593 159 100       399 (ref $dst) and push @msg,$dst;
1594             }
1595              
1596 69 100       421 $do_resolv_patch or return @msg;
1597 14         47 return resolve_patch(@msg);
1598             }
1599             ######################################## !!!!! REF COMPARE
1600             elsif ($ref_type eq 'REF' or $ref_type eq 'SCALAR')
1601             {
1602 65 50       146 if (loop_det($$d1, @p1)) {
1603             }
1604             else {
1605 65         344 @msg = ( compare($$d1, $$d2,
1606             [ @p1, '$' ],
1607             [ @p2, '$' ])
1608             );
1609             }
1610 65 100       393 $do_resolv_patch or return @msg;
1611 4         11 return resolve_patch(@msg);
1612             }
1613             ######################################## !!!!! GLOBAL REF COMPARE
1614             elsif ($ref_type eq 'GLOB')
1615             {
1616 14         35 my $name1=$$d1;
1617 14         86 $name1=~s/^\*//;
1618 14         41 my $name2=$$d2;
1619 14         71 $name2=~s/^\*//;
1620              
1621 14         34 push @p1,'*', $name1;
1622 14         23 push @p2,'*', $name2;
1623              
1624 14         41 push @msg, $patchDOM->('change', \@p1 ,\@p2);
1625              
1626 14         29 my ($k,$g_d1,$g_d2)=(undef,undef,undef);
1627              
1628 14 100 66     46 if (defined *$d1{SCALAR} and defined(${*$d1{SCALAR}})) {
  14 100       93  
    50          
    0          
1629 5         10 $g_d1 = *$d1{SCALAR};
1630             }
1631             elsif (defined *$d1{ARRAY}) {
1632 4         7 $g_d1 = *$d1{ARRAY};
1633             }
1634             elsif (defined*$d1{HASH}) {
1635 5         12 $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     46 if (defined *$d2{SCALAR} and defined(${*$d2{SCALAR}})) {
  14 100       64  
    50          
    0          
1646 4         10 $g_d2 = *$d2{SCALAR};
1647             }
1648             elsif (defined *$d2{ARRAY}) {
1649 5         12 $g_d2 = *$d2{ARRAY};
1650             }
1651             elsif (defined*$d2{HASH}) {
1652 5         8 $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         40 my @msg = ( compare($g_d1, $g_d2, \@p1, \@p2));
1662              
1663 14 50       103 $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         5 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 1098 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         125 debug 'applyPatch('.__d($dom).') :';
1709 41         259 my (@remove,@add,@change,@move);
1710              
1711 0         0 my $p;
1712 41         117 foreach $p (@_) { # ordering the patch operations
1713 145 50       339 defined($p) or next;
1714 145         234 my $dom_patch = $p;
1715              
1716 145 100       409 (ref($p) eq 'HASH')
1717             or ($dom_patch) = textPatch2DOM($p);
1718              
1719 145         330 debug(domPatch2TEXT($dom_patch));
1720              
1721 145         8169 eval 'push @'.$dom_patch->{action}.', $dom_patch;';
1722 145 50       727 $@ and die 'applyPatch() : '.$@;
1723             }
1724              
1725 41         111 my ($d,$t);
1726              
1727 0         0 my ($d1,$d2,$d3,$d4,$d5);
1728 0         0 my ($t1,$t2,$t3,$t4,$t5);
1729              
1730 41         206 my $patch_eval='$d='.__d($dom).";\n";
1731              
1732 41         138 $patch_eval .= '$t='.__d($dom).";\n";
1733              
1734 41         99 my $post_eval;
1735              
1736             my $r;
1737 41         239 foreach $r (@remove) {
1738 23         84 my @porig = @{$r->{path_orig}};
  23         119  
1739              
1740 23         51 my $key = pop @porig;
1741 23         47 my $type = pop @porig;
1742              
1743 23 100       65 if ($type eq '@') {
1744 12         48 $patch_eval .= 'splice @{'.$path2eval__->('$d',undef,@porig) ."},$key,1;\n";
1745             }
1746             else {
1747 11         56 $patch_eval .= 'delete '.$path2eval__->('$d',undef,@porig,$type,$key) .";\n";
1748             }
1749             }
1750              
1751 41         72 my $m;
1752 4         24 my @remove_patch = sort
1753             {
1754             # the array indexes order from smallest to biggest
1755 41 50       152 if (${$a->{path_orig}}[-2] eq '@') {
  4         8  
1756 4         10 return (${$a->{path_orig}}[-1] >
  4         21  
1757 4         8 ${$b->{path_orig}}[-1])
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         84 foreach $m (@remove_patch) {
1764 16         32 my @porig = @{$m->{path_orig}};
  16         58  
1765              
1766 16         34 my $key = pop @porig;
1767 16         27 my $type = pop @porig;
1768              
1769 16 100       54 if ($type eq '@') {
1770 8         20 $patch_eval .= 'splice @{'.$path2eval__->('$d',undef,@porig)."},$key,1;\n";
1771             }
1772             else {
1773 8         28 $patch_eval .= 'delete '.$path2eval__->('$d',undef,@porig,$type,$key) .";\n";
1774             }
1775             }
1776              
1777 41         97 foreach $m (@remove_patch) {
1778 16         68 my @porig = @{$m->{path_orig}};
  16         55  
1779 16         30 $patch_eval .= $path2eval__->('$d',undef,@{$m->{path_dest}}).
  16         44  
1780             ' = '.$path2eval__->('$t',undef,@porig).";\n";
1781             }
1782              
1783              
1784 41         70 my $a;
1785 41         81 foreach $a (@add) {
1786 38         145 $patch_eval .=
1787 38         68 $path2eval__->('$d',undef,@{$a->{path_dest}}).
1788             ' = '.__d($a->{val_dest}) .";\n";
1789             }
1790 41         73 my $c;
1791 41         85 foreach $c (@change) {
1792 68         222 $patch_eval .=
1793 68         127 $path2eval__->('$d',undef,@{$c->{path_dest}}).
1794             ' = '.__d($c->{val_dest}).";\n";
1795             }
1796              
1797 41         85 $patch_eval = $patch_eval.'$d;';
1798              
1799 41         6881 my $res = eval($patch_eval);
1800              
1801 41         251 debug "\nEval=>> $patch_eval >>=".__d($res).".\n";
1802              
1803 41 50       142 $@
1804             and
1805             die 'applyPatch() : '.$patch_eval.$@;
1806              
1807 41         439 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 988 my @path=@{shift()};
  605         1917  
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 singlé join without key defined
1857              
1858 605 100       3484 (defined $CFG->{o_key}) or return join('',@path);
1859              
1860 74 50       105 (%{$CFG->{o_key}}) or join('',@path);
  74         191  
1861              
1862              
1863             # matching Keys
1864              
1865 74         115 my $sz_path = scalar(@path);
1866              
1867             # debug "\n###".join('.',@{$path}).' '.join('|',keys %{$CFG->{o_key}}); <>;
1868              
1869 74         126 my %keys=%{$CFG->{o_key}};
  74         467  
1870              
1871             # TODO : key priority sould be managed by a small getPrioritizedKey() function (warning)
1872              
1873 74         246 my @sorted_keys =
1874             # sort { ( $keys{$a}->{priority} > $keys{$b}->{priority} ) }
1875             keys %keys;
1876              
1877 74         119 my $k;
1878              
1879 74         92 my $i = 0;
1880 74         224 while ($i
1881              
1882 393         608 foreach $k (@sorted_keys)
1883             {
1884 1831         3192 my $match = $keys{$k}{regexp};
1885              
1886             #warn "\n=$k on ".join('',@path[0..$i]);
1887              
1888 1831         4406 my $min_index = $matchPath->($match, @path[0..$i]);
1889              
1890 1831 100       4977 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         232 splice @path, $min_index, scalar(@$match), '/',$k;
1897              
1898 79         174 $i = $i + 2 - scalar(@$match);
1899              
1900             #warn "-> path -> ".join('.',@path)." \$i=$i\n";
1901             }
1902             }
1903 393         1002 $i++;
1904             }
1905 74         575 return join('',@path);
1906              
1907             };
1908              
1909              
1910              
1911             ##############################################################################
1912             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
1913             sub domPatch2TEXT(@) {
1914             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1915              
1916             =over 4
1917              
1918             =item I(, [,])
1919              
1920             convert a list of perl usable patches into a readable text format.
1921             Also convert to key patterns which are matching the regexp key definnition
1922             Mainly used to convert the compare result (format dom)
1923              
1924             ARGS:
1925             a list of
1926              
1927             Return a list of patches in TEXT mode
1928              
1929             EX:
1930              
1931              
1932             domPatch2TEXT($patch1)
1933              
1934             returns 'change(@0$%magic_key,@0$%magic_key)="toto"/=>"tata"'
1935              
1936              
1937             # one key defined
1938             o_key({ key_1 => {regexp=>['%','magic_key'], eval=>'{magic_key}' } } );
1939              
1940             # same but with the related matched key in path
1941              
1942             domPatch2TEXT($patch1)
1943              
1944             returns 'change(@0$/key_1,@0$/key_1)="toto"/=>"tata"'
1945              
1946              
1947             =cut
1948              
1949 262     262 1 754 my @res;
1950             my $patch;
1951 262         546 foreach $patch (@_) {
1952             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1953              
1954             (ref($patch) eq 'HASH') and do {
1955              
1956 262 50       635 (exists $patch->{action})
1957             or die 'domPatch2TEXT(): bad internal dom structure '.__d($patch);
1958              
1959              
1960 262         502 my $action = $patch->{action};
1961 262         456 my $v1 = $patch->{val_orig};
1962 262         474 my $v2 = $patch->{val_dest};
1963              
1964 262         793 my $txt = $action
1965             .'('
1966             .patternDom2Text($patch->{path_orig})
1967             .','
1968             .patternDom2Text($patch->{path_dest})
1969             .')=';
1970              
1971 262 100 100     1388 if (($action eq 'remove') or ($action eq 'change')) {
1972 168         382 $v1 = __d($v1);
1973 168         324 $v1 =~ s|/=>|\/\\054\>|g;
1974 168         252 $v1 =~ s/\s=>\s/=>/sg;
1975 168         269 $txt .= $v1;
1976             }
1977              
1978 262 100       640 ($action eq 'change') and $txt .= '/=>';
1979              
1980 262 100 100     1243 if (($action eq 'add') or ($action eq 'change')) {
1981 193         503 $v2 = __d($v2);
1982 193         391 $v2 =~ s|/=>|\/\\054\>|g;
1983 193         252 $v2 =~ s/\s=>\s/=>/sg;
1984 193         472 $txt .= $v2;
1985             }
1986              
1987 262         484 push @res, $txt;
1988             next
1989 262         664 } or
1990 262 0 0     922 (ref($_) eq 'ARRAY') and do {
      33        
1991 0         0 push @res,join '', @{$_};
  0         0  
1992             next
1993 0         0 };
1994             }
1995              
1996             #
1997 262 100       1243 (wantarray()) and return @res;
1998 2         13 return join("\n",@res);
1999             }
2000              
2001             ##############################################################################
2002             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
2003             sub domPatch2IHM(@) {
2004             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2005              
2006             =item I(, [,])
2007              
2008             convert a list of patches in DOM format (internal Data;;Deep format)
2009             into a IHM format.
2010             Mainly used to convert the compare result (format dom)
2011              
2012             ARGS:
2013             a list of
2014              
2015             Return a list of patches in IHM mode
2016             IHM format is not convertible
2017              
2018             EX:
2019             C($patch1)
2020             returns
2021             '"toto" changed in "tata" from @0$%a
2022             into @0$%a
2023             =cut
2024              
2025              
2026 0     0 1 0 my ($msg,$patch);
2027              
2028 0         0 foreach $patch (@_) {
2029             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2030 0         0 $_ = $patch->{action};
2031              
2032 0 0 0     0 /^add$/ and ($msg .= __d($patch->{val_orig}).' added')
      0        
      0        
      0        
      0        
      0        
2033             or
2034             /^remove$/ and ($msg .= __d($patch->{val_orig}).' removed')
2035             or
2036             /^move$/ and ($msg .= 'Moved ')
2037             or
2038             /^change$/ and ($msg .= __d($patch->{val_orig})
2039             .' changed in '
2040             .__d($patch->{val_dest}));
2041 0         0 my $l = length($msg);
2042 0         0 my $MAX_COLS=40;
2043 0 0       0 if ($l>$MAX_COLS) {
2044 0         0 $msg .= "\n from ".join('',@{$patch->{path_orig}});
  0         0  
2045 0         0 $msg .= "\n into ".join('',@{$patch->{path_dest}});
  0         0  
2046             }
2047             else {
2048 0         0 $l-=($msg=~ s/\n//g);
2049 0         0 $msg .= ' from '.join('',@{$patch->{path_orig}});
  0         0  
2050 0         0 $msg .= "\n".(' 'x $l).' into '.join('',@{$patch->{path_dest}});
  0         0  
2051             }
2052 0         0 $msg .= "\n";
2053             }
2054 0         0 return $msg;
2055             }
2056              
2057              
2058             ##############################################################################
2059             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
2060             sub patternText2Dom($) {
2061             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2062              
2063 82     82 1 534 my $pathTxt = shift();
2064              
2065 82 50       183 (ref($pathTxt)) and die 'patternText2Dom() : bad call with a reference instead of scalar containing pattern text ';
2066              
2067             =item I()
2068              
2069             convert pattern scalar string to the array of element to be used by search(), path()
2070              
2071              
2072             is an array of type description to match
2073             optional argument to limit the number of results
2074             if undef all results are returned
2075             if 1 first one is returned
2076              
2077             Return an array list of splited element of the for usage
2078              
2079             EX:
2080             patternText2Dom( '?@' );
2081              
2082             Return ['?@']
2083              
2084             patternText2Dom( '%r' );
2085              
2086             Return ['%', 'r']
2087              
2088             patternText2Dom( '@3%r' );
2089              
2090             Return ['@',3,'%','r']
2091              
2092             patternText2Dom( '@2=3' );
2093              
2094             Return ['@',2,'=','3']
2095              
2096             =cut
2097              
2098             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2099              
2100 82         108 my @path;
2101              
2102             #debug "patternText2Dom($pathTxt)";;
2103              
2104 82         142 my %keys=();
2105              
2106 82 100       370 (ref($CFG->{o_key})) and %keys = %{$CFG->{o_key}};
  12         76  
2107              
2108 82         335 my @pathTxt = split('',$pathTxt);
2109              
2110 82         197 while (@pathTxt) {
2111              
2112 427         635 $_ = shift @pathTxt;
2113              
2114 427 100 100     2971 if (defined($path[-1]) and $path[-1] =~ /^\?/ and m/^[\=\%\$\@\%\*]/) {
    100 66        
    100          
    100          
    100          
2115 3         10 $path[-1].= $_;
2116             }
2117             elsif ($_ eq '$') {
2118 22         54 push(@path,'$');
2119             }
2120             elsif ($_ eq '?') {
2121 3         10 push(@path,'?');
2122             }
2123             elsif ($_ eq '&') {
2124 3         10 push(@path,'&');
2125             }
2126             elsif (/([%\@\=\|\*\/])/) {
2127 144         493 push(@path,$1,'');
2128             }
2129             else {
2130 252 100 100     697 if ($path[-2] eq '/' and exists($keys{$path[-1]})) {
2131             # cf test "Search Complex key 3..5"
2132 3         10 push(@path,'');
2133             }
2134 252         621 $path[-1].= $_;
2135             }
2136             }
2137              
2138             # post - convertion § array & key convertion
2139              
2140 82         121 my $i;
2141 82         195 for $i (0..$#path) {
2142              
2143 319 100       874 if ($path[$i] eq '@') {
    100          
2144 56         148 $path[$i+1] = int($path[$i+1]);
2145             }
2146             elsif ($path[$i] eq '/') {
2147 16         33 my $keyname = $path[$i+1];
2148 16 50       43 (exists($keys{$keyname})) or die 'patternText2Dom() ! no key '.$keyname;
2149              
2150 16         23 splice @path, $i, 2, @{ $keys{$keyname}{regexp} };
  16         78  
2151              
2152             }
2153             }
2154              
2155             #warn "patternText2Dom(".join('',@pathTxt).')=> '.join(' ',@path)." .";
2156              
2157             #debug '=>'.join('.',@path);
2158 82         432 return [@path];
2159             };
2160              
2161              
2162             ##############################################################################
2163             #{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
2164             sub textPatch2DOM(@) {
2165             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2166              
2167             =item I(, [,])
2168              
2169             convert a list of patches formatted in text (readable text format format)
2170             to a perl DOM format (man perldsc).
2171             Mainly used to convert the compare result (format dom)
2172              
2173             ARGS:
2174             a list of
2175              
2176             Return a list of patches in dom mode
2177              
2178             EX:
2179             C( 'change(@0$%a,@0$%a)="toto"/=>"tata"',
2180             'move(... '
2181             )
2182              
2183             returns (
2184             { action=>'change',
2185             path_orig=>['@0','$','%a'],
2186             path_dest=>['@0','$','%a'],
2187             val_orig=>"toto",
2188             val_dest=>"tata"
2189             },
2190             { action=>'move',
2191             ...
2192             });
2193              
2194             =cut
2195              
2196 27     27 1 44 my @res;
2197 27         63 while (@_) {
2198             #}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
2199 27         37 my $patch=pop;
2200              
2201 27 50       54 defined($patch) or next;
2202              
2203 27         74 debug "textPatch2DOM in ".$patch;
2204              
2205 27         48 my ($p1,$p2,$v1,$v2);
2206 27 50       154 $patch =~ s/^(\w+)\(// or die 'Data::Deep::textPatch2DOM / bad patch format :'.$patch.' !!!';
2207              
2208 27         61 my $action = $1; # or die 'action ???';
2209              
2210 27 50       170 ( $patch =~ s/^([^,]*?),//
2211             ) and $p1 = patternText2Dom($1);
2212              
2213 27 50       159 ( $patch =~ s/^([^\(]*?)\)=//
2214             ) and $p2 = patternText2Dom($1);
2215              
2216 27 50       74 if ($action ne 'move') {
2217 27         57 my $i = index($patch, '/=>');
2218 27 100       54 if ($i ==-1 ) {
2219 19 100 66     101 ($action eq 'add') && ($v2 = $patch) or ($v1 = $patch);
2220             }
2221             else {
2222 8         39 $v1 = substr($patch, 0, $i);
2223 8         22 $v2 = substr($patch, $i+3);
2224             }
2225             }
2226 27         832 my $a = eval($v1);
2227 27 50       91 ($@) and die "textPatch2DOM() error in eval($v1) : ".$@;
2228              
2229 27         839 my $b = eval($v2);
2230 27 50       88 ($@) and die "textPatch2DOM() error in eval($v2) : ".$@;
2231              
2232 27         69 push @res,$patchDOM->($action, $p1, $p2, $a, $b);
2233             }
2234              
2235             #
2236 27 50       94 (wantarray()) and return @res;
2237 0           return [@res];
2238             }
2239              
2240              
2241             =begin end
2242              
2243             =head1 AUTHOR
2244              
2245              
2246             Data::Deep was written by Matthieu Damerose Idamo@cpan.orgE> in 2005.
2247              
2248             =cut
2249              
2250              
2251             ###########################################################################
2252             1;#############################################################################
2253             __END__ Deep::Manip.pm