File Coverage

blib/lib/Data/Diff.pm
Criterion Covered Total %
statement 24 400 6.0
branch 0 178 0.0
condition 0 42 0.0
subroutine 8 25 32.0
pod 4 4 100.0
total 36 649 5.5


line stmt bran cond sub pod time code
1             package Data::Diff;
2              
3 1     1   6348 use 5.006;
  1         3  
  1         40  
4 1     1   5 use strict;
  1         2  
  1         30  
5 1     1   5 use warnings;
  1         5  
  1         48  
6              
7             require Exporter;
8 1     1   856 use AutoLoader qw(AUTOLOAD);
  1         1670  
  1         5  
9 1     1   1070 use Data::Dumper;
  1         11502  
  1         169  
10              
11             our @ISA = qw(Exporter);
12              
13             # Items to export into callers namespace by default. Note: do not export
14             # names by default without a very good reason. Use EXPORT_OK instead.
15             # Do not simply export all your public functions/methods/constants.
16              
17             # This allows declaration use Data::Diff ':all';
18             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
19             # will save memory.
20             our %EXPORT_TAGS = ( 'all' => [ qw(
21             Diff
22             ) ] );
23             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
24             our @EXPORT = qw( );
25             our $VERSION = '0.01';
26              
27             # Preloaded methods go here.
28              
29             # i use this constant to unstick some situations and avoid div by zero
30 1     1   8 use constant NUDGE => 0.0000000001;
  1         2  
  1         61  
31 1     1   5 use constant TO_LEFT => 1;
  1         2  
  1         41  
32 1     1   5 use constant TO_RIGHT => 2;
  1         1  
  1         5858  
33              
34             sub new {
35 0     0 1   my( $proto, $class, $self, $a, $b, $opt );
36              
37 0           ($proto, $a, $b) = @_;
38 0   0       $class = ref($proto) || $proto;
39 0           $self = { };
40              
41 0           $self->{a} = $a;
42 0           $self->{b} = $b;
43 0           $self->{opt} = $opt;
44              
45             # this is for debug print outs
46 0           $self->{debug} = [0];
47 0           $self->{depth} = 0;
48              
49 0           bless( $self, $class );
50              
51 0           $self->{out} = $self->_diff( $a, $b );
52 0           delete $self->{out}->{score};
53              
54 0           return $self;
55             }
56              
57             ################################################################################
58             # non-oo function wrapper.
59              
60             sub Diff {
61 0     0 1   my( $a, $b, $opt ) = @_;
62 0           my( $diff );
63              
64 0           $diff = Data::Diff->new( $a, $b, $opt );
65 0           return $diff->raw();
66             }
67              
68             ################################################################################
69             # public methods
70              
71             sub raw {
72 0     0 1   my( $self ) = @_;
73 0           return $self->{out};
74             }
75              
76             sub apply {
77 0     0 1   my( $self, $options ) = @_;
78 0           my( %opt );
79              
80 0 0 0       $options->{Direction} = TO_LEFT if( ! defined $options->{Direction} ||
      0        
81             $options->{Direction} != TO_LEFT ||
82             $options->{Direction} != TO_RIGHT );
83              
84 0           $opt{Filter} = ['Same', 'Uniq_A', 'Uniq_B' ];
85 0 0         push( @{$opt{Filter}}, 'Diff_A' ) if( $options->{Direction} == TO_LEFT );
  0            
86 0 0         push( @{$opt{Filter}}, 'Diff_B' ) if( $options->{Direction} == TO_RIGHT );
  0            
87              
88 0           $self->{depth} = 0;
89 0           return $self->_slice( $self->{out}, \%opt );
90             }
91              
92             ################################################################################
93             ################################################################################
94             # private-ish methods
95              
96             sub _slice {
97 0     0     my( $self, $data, $opt ) = @_;
98 0           my( $out );
99              
100 0 0         return undef if( ref( $data ) ne 'HASH' );
101              
102 0           $self->{depth}++;
103              
104 0 0         if( $data->{type} eq 'HASH' ) {
    0          
    0          
105 0           $out = $self->_slice_hash( $data, $opt );
106             }
107             elsif( $data->{type} eq 'ARRAY' ) {
108 0           $out = $self->_slice_array( $data, $opt );
109             }
110             elsif( $data->{type} =~ /^(REF:(.*))$/ ) {
111 0           $data->{type} = $2;
112 0           $out = \$self->_slice( $data, $opt );
113 0           $data->{type} = $1;
114             }
115             else {
116 0 0 0       $out = $data->{same} if( defined $data->{same} && scalar grep( /Same/, @{$opt->{Filter}} ) );
  0            
117 0 0 0       $out = $data->{diff} if( defined $data->{diff} && scalar grep( /Diff/, @{$opt->{Filter}} ) );
  0            
118 0 0 0       $out = $data->{diff_a} if( defined $data->{diff_a} && scalar grep( /Diff_A/, @{$opt->{Filter}} ) );
  0            
119 0 0 0       $out = $data->{diff_b} if( defined $data->{diff_b} && scalar grep( /Diff_B/, @{$opt->{Filter}} ) );
  0            
120 0 0 0       $out = $data->{uniq_a} if( defined $data->{uniq_a} && scalar grep( /Uniq_A/, @{$opt->{Filter}} ) );
  0            
121 0 0 0       $out = $data->{uniq_b} if( defined $data->{uniq_b} && scalar grep( /Uniq_B/, @{$opt->{Filter}} ) );
  0            
122             }
123              
124             # print( " "x$self->{depth}, "_slice()\n", Dumper( $data, $opt, $out ) );
125 0           $self->{depth}--;
126              
127 0           return $out;
128             }
129              
130             sub _slice_hash {
131 0     0     my( $self, $data, $opt ) = @_;
132 0           my( $out );
133              
134 0 0         if( scalar grep( /Same/, @{$opt->{Filter}} ) ) {
  0            
135 0           foreach my $key (keys( %{$data->{same}} )) {
  0            
136 0           $out->{$key} = $self->_slice( $data->{same}->{$key}, $opt );
137             }
138             }
139              
140 0 0         if( scalar grep( /Diff/, @{$opt->{Filter}} ) ) {
  0            
141 0           foreach my $key (keys( %{$data->{diff}} )) {
  0            
142 0           $out->{$key} = $self->_slice( $data->{diff}->{$key}, $opt );
143             }
144             }
145              
146 0 0         if( scalar grep( /Uniq_A/, @{$opt->{Filter}} ) ) {
  0            
147 0           foreach my $key (keys( %{$data->{uniq_a}} )) {
  0            
148 0           $out->{$key} = $data->{uniq_a}->{$key};
149             }
150             }
151              
152 0 0         if( scalar grep( /Uniq_B/, @{$opt->{Filter}} ) ) {
  0            
153 0           foreach my $key (keys( %{$data->{uniq_b}} )) {
  0            
154 0           $out->{$key} = $data->{uniq_b}->{$key};
155             }
156             }
157              
158 0           return $out;
159             }
160              
161             sub _slice_array {
162 0     0     my( $self, $data, $opt ) = @_;
163 0           my( $out );
164              
165 0 0 0       if( defined $data->{same} && scalar grep( /Same/, @{$opt->{Filter}} ) ) {
  0            
166 0           push( @$out, map( {$self->_slice( $_, $opt )} @{$data->{same}} ) );
  0            
  0            
167             }
168              
169 0 0 0       if( defined $data->{diff} && scalar grep( /Diff/, @{$opt->{Filter}} ) ) {
  0            
170 0           push( @$out, map( {$self->_slice( $_, $opt )} @{$data->{diff}} ) );
  0            
  0            
171             }
172              
173 0 0 0       if( defined $data->{uniq_a} && scalar grep( /Uniq_A/, @{$opt->{Filter}} ) ) {
  0            
174 0           push( @$out, @{$data->{uniq_a}} );
  0            
175             }
176              
177 0 0 0       if( defined $data->{uniq_b} && scalar grep( /Uniq_B/, @{$opt->{Filter}} ) ) {
  0            
178 0           push( @$out, @{$data->{uniq_b}} );
  0            
179             }
180              
181 0           return $out;
182             }
183              
184             ################################################################################
185              
186             sub _diff {
187 0     0     my( $self, $a, $b ) = @_;
188 0           my( $out );
189              
190 0 0         print( " "x$self->{depth}, "_diff( ",($a?$a:"undef"),", ",($b?$b:"undef")," )\n" ) if( ${$self->{debug}}[0] );
  0 0          
    0          
191 0           $self->{depth}++;
192              
193 0           $out = {
194             orig_a => $a,
195             orig_b => $b,
196             type => ref( $a ),
197             };
198              
199 0 0         if( ref( $a ) ne ref( $b ) ) {
200 0           $out->{score} = ref( $a ) cmp ref( $b );
201 0           delete( $out->{orig_a} );
202 0           delete( $out->{orig_b} );
203              
204 0           $out->{diff_a} = $a;
205 0           $out->{diff_b} = $b;
206 0           $out->{type} = 'MIXED:'. ref( $a ) .':'. ref( $b );
207              
208 0           $self->{depth}--;
209 0 0         print( " "x$self->{depth}, "_diff( ",($a?$a:"undef"),", ",($b?$b:"undef")," ) = $out->{score}\n" ) if( ${$self->{debug}}[0] );
  0 0          
    0          
210              
211 0           return $out;
212             }
213              
214 0 0         if( ! ref( $a ) ) { $self->_diff_( $out ); }
  0 0          
    0          
    0          
    0          
215 0           elsif( ref( $a ) eq 'SCALAR' ) { $self->_diff_scalar( $out ); }
216 0           elsif( ref( $a ) eq 'HASH' ) { $self->_diff_hash( $out ); }
217 0           elsif( ref( $a ) eq 'ARRAY' ) { $self->_diff_array( $out ); }
218             elsif( ref( $a ) eq 'REF' ) {
219 0           $out = $self->_diff( $$a, $$b );
220 0           $out->{type} = 'REF:'. ref( $$a );
221              
222             # ok i thought i knew enough to do this but a can seems to get
223             # perl to change $out->{.*} to references of there current values.
224             # at least it kinda works. oh and now its vunlerable to loops too.
225             # there is a little bit of a work around up in _split sub.
226             }
227             else {
228 0 0         if( $a eq $b ) {
229 0           $out->{same} = $a;
230             }
231             else {
232 0           $out->{diff_a} = $a;
233 0           $out->{diff_b} = $b;
234             }
235             }
236              
237 0 0         if( ! defined $out->{score} ) {
238 0           $out->{score} = $a cmp $b;
239             }
240              
241 0           delete( $out->{orig_a} );
242 0           delete( $out->{orig_b} );
243              
244 0           $self->{depth}--;
245 0 0         print( " "x$self->{depth}, "_diff( ", ($a?$a:"undef"), ", ", ($b?$b:"undef"), " ) = $out->{score}\n" ) if( ${$self->{debug}}[0] );
  0 0          
    0          
246 0           return $out;
247             }
248              
249             sub _diff_ {
250 0     0     my( $self, $out ) = @_;
251              
252 0 0         print( " "x$self->{depth}, "_diff_( '", ($out->{orig_a}?$out->{orig_a}:"undef"), "', '", ($out->{orig_b}?$out->{orig_b}:"undef"),"' )\n" ) if( ${$self->{debug}}[0] );
  0 0          
    0          
253 0           $self->{depth}++;
254              
255 0 0 0       if( ! defined $out->{orig_a} || ! defined $out->{orig_b} ) {
256 0 0 0       $out->{score} = 0 if( ! $out->{orig_a} && ! $out->{orig_b} );
257 0 0         $out->{score} = 1 if( $out->{orig_a} );
258 0 0         $out->{score} =-1 if( $out->{orig_b} );
259             }
260             else {
261 0           $out->{score} = $out->{orig_a} cmp $out->{orig_b};
262             }
263              
264 0 0         if( $out->{score} ) {
265 0           $out->{diff_a} = $out->{orig_a};
266 0           $out->{diff_b} = $out->{orig_b};
267             }
268             else {
269 0           $out->{same} = $out->{orig_a};
270             }
271              
272 0           $self->{depth}--;
273 0 0         print( " "x$self->{depth}, "_diff_( '", ($out->{orig_a}?$out->{orig_a}:"undef"), "', '", ($out->{orig_b}?$out->{orig_b}:"undef"),"' ) = $out->{score}\n" ) if( ${$self->{debug}}[0] );
  0 0          
    0          
274             }
275              
276             sub _diff_scalar {
277 0     0     my( $self, $out ) = @_;
278              
279 0           print( " "x$self->{depth}, "_diff_scalar( ",
280 0           join(",",map({$_ ."=". $out->{orig_a}-{$_}} keys(%{$out->{orig_a}}))),
  0            
281 0           join(",",map({$_ ."=". $out->{orig_b}-{$_}} keys(%{$out->{orig_b}}))),
  0            
282 0 0         " )\n" ) if( ${$self->{debug}}[0] );
283 0           $self->{depth}++;
284              
285 0           $out->{score} = ${$out->{orig_a}} cmp ${$out->{orig_b}};
  0            
  0            
286 0 0         if( $out->{score} ) {
287 0           $out->{diff_a} = $out->{orig_a};
288 0           $out->{diff_b} = $out->{orig_b};
289             }
290             else {
291 0           $out->{same} = $out->{orig_a};
292             }
293 0           $self->{depth}--;
294             }
295              
296             sub _diff_hash {
297 0     0     my( $self, $out ) = @_;
298 0           my( $match, $total, $sign );
299 0           my( @keys );
300              
301 0 0         print( " "x$self->{depth}, "_diff_hash( {",
302 0 0         join(",",map({$_ ."=". ($out->{orig_a}->{$_}?$out->{orig_a}->{$_}:"undef")} keys(%{$out->{orig_a}}))),"}, {",
  0            
303 0           join(",",map({$_ ."=". ($out->{orig_b}->{$_}?$out->{orig_b}->{$_}:"undef")} keys(%{$out->{orig_b}}))),
  0            
304 0 0         "}, )\n" ) if( ${$self->{debug}}[0] );
305 0           $self->{depth}++;
306              
307 0           $sign = NUDGE;
308 0           $match = 0;
309 0           $total = 0;
310              
311 0           foreach my $key (sort( keys( %{$out->{orig_a}} ) )) {
  0            
312 0           $total++;
313 0 0         if( exists $out->{orig_b}->{$key} ) {
314 0           my $diff = $self->_diff( $out->{orig_a}->{$key}, $out->{orig_b}->{$key} );
315              
316 0 0         if( abs( $diff->{score} ) > NUDGE ) {
317 0           $out->{diff}->{$key} = $diff;
318 0           $sign += $diff->{score}<=>0;
319             }
320             else {
321 0           $out->{same}->{$key} = $diff;
322 0 0         $match++ if( abs( $diff->{score} ) < 1 );
323             }
324 0           delete( $diff->{score} );
325             }
326             else {
327 0           $total--;
328 0           $sign++;
329 0           $out->{uniq_a}->{$key} = $out->{orig_a}->{$key};
330             }
331             }
332 0           foreach my $key_b (sort( keys( %{$out->{orig_b}} ) )) {
  0            
333 0 0         if( ! exists $out->{orig_a}->{$key_b} ) {
334 0           $sign--;
335 0           $out->{uniq_b}->{$key_b} = $out->{orig_b}->{$key_b};
336             }
337             }
338              
339 0           $out->{score} = ($sign<=>0) * ($total - $match) / abs(($match - NUDGE));
340 0           $self->{depth}--;
341 0 0         print( " "x$self->{depth}, "_diff_hash( {",
342 0 0         join(",",map({$_ ."=". ($out->{orig_a}->{$_}?$out->{orig_a}->{$_}:"undef")} keys(%{$out->{orig_a}}))),"}, {",
  0            
343 0           join(",",map({$_ ."=". ($out->{orig_b}->{$_}?$out->{orig_b}->{$_}:"undef")} keys(%{$out->{orig_b}}))),
  0            
344 0 0         "} ) = $out->{score} ($sign,$match/$total) \n" ) if( ${$self->{debug}}[0] );
345             }
346              
347             sub _diff_array {
348 0     0     my( $self, $out ) = @_;
349              
350 0           return $self->_diff_array_unordered( $out );
351             }
352              
353             sub _diff_array_ordered {
354 0     0     my( $self, $out ) = @_;
355 0           my( @ai, @bi, @table );
356              
357             # initalize the table size
358              
359             # print( "init ", $#{$out->{orig_a}}, ",", $#{$out->{orig_b}}, "\n" );
360              
361 0           $#table = $#{$out->{orig_a}} + 1;
  0            
362 0           for( my $ai = $#{$out->{orig_a}}; $ai >= 0; $ai-- ) {
  0            
363 0           $#{$table[$ai]} = $#{$out->{orig_b}} + 1;
  0            
  0            
364             }
365              
366 0           print( "equality\n" );
367              
368             # i have to keep the following code here because this is the last place in
369             # the call stack that knows about both arrays.
370              
371             # special case where we are dealing with an array of hashes.
372             # we have to sort the two arrays on the common sub hash keys.
373 0           my %key_count = ();
374 0           foreach my $item (@{$out->{orig_a}}, @{$out->{orig_b}}) {
  0            
  0            
375 0 0         next if( ref( $item ) ne 'HASH' );
376 0           foreach my $key (keys(%$item)) {
377 0 0         $key_count{$key} += (defined $$item{$key})?1:0;
378             }
379             }
380             # sort the keys by the frequence of occurance. that way any common keys have a higher sort priority.
381 0           my @key_order = sort( {$key_count{$b} <=> $key_count{$a}} keys(%key_count) );
  0            
382              
383             # print( "key_order: ", join(",",map({$_ ."(". $key_count{$_} .")"} @key_order)), "\n" );
384 0           my %sort_key;
385 0           foreach my $item (@{$out->{orig_a}}, @{$out->{orig_b}}) {
  0            
  0            
386 0           $sort_key{$item} = $self->_key( $item, \@key_order );
387             }
388              
389 0           @ai = sort( {$sort_key{${$out->{orig_a}}[$a]} cmp $sort_key{${$out->{orig_a}}[$b]}} (0..$#{$out->{orig_a}}) );
  0            
  0            
  0            
  0            
390 0           @bi = sort( {$sort_key{${$out->{orig_b}}[$a]} cmp $sort_key{${$out->{orig_b}}[$b]}} (0..$#{$out->{orig_b}}) );
  0            
  0            
  0            
  0            
391 0           print( "_pre ", join( ",", (0..$#{$out->{orig_a}}) ), "\n" );
  0            
392 0           print( "_post ", join( ",", @ai ), "\n" );
393              
394 0   0       while( (scalar @ai) && (scalar @bi) ) {
395 0           my( $diff, $a, $b );
396 0           $a = ${$out->{orig_a}}[$ai[0]];
  0            
397 0           $b = ${$out->{orig_b}}[$bi[0]];
  0            
398 0           $diff = $self->_diff( $a, $b );
399              
400 0 0         if( abs( $diff->{score} ) < 1 ) {
401 0           $table[$ai[0]][$bi[0]] = 1;
402 0           shift( @ai );
403 0           shift( @bi );
404             }
405             else {
406 0           $table[$ai[0]][$bi[0]] = 0;
407 0           my $sort_cmp = $sort_key{$a} cmp $sort_key{$b};
408 0 0         if( $sort_cmp < 0 ) {
409 0           shift( @ai );
410             }
411             else {
412 0           shift( @bi );
413             }
414             }
415             }
416              
417 0 0         print( "_table\n", join("\n", map( {$_?"[". join(",", map( {$_?$_:0;} @$_ )) ."]":"[undef]";} @table ) ), "\n" );
  0 0          
  0            
418              
419 0           for( my $ai = $#{$out->{orig_a}}; $ai >= 0; $ai-- ) {
  0            
420 0           $#{$table[$ai]} = $#{$out->{orig_b}};
  0            
  0            
421 0           for( my $bi = $#{$out->{orig_b}}; $bi >= 0; $bi-- ) {
  0            
422 0 0 0       if( $ai == $#{$out->{orig_b}} || $bi == $#{$out->{orig_b}} ) {
  0            
  0            
423 0           $table[$ai][$bi] = 0;
424 0           next;
425             }
426 0 0         if( $table[$ai][$bi] ) {
427 0           $table[$ai][$bi] = 1 + $table[$ai + 1][$bi + 1];
428             }
429             else {
430 0           my( $r, $l ) = ($table[$ai + 1][$bi], $table[$ai][$bi + 1]);
431 0 0         $table[$ai][$bi] = ($r > $l) ? $r : $l;
432             }
433             }
434             }
435 0           print( "finish\n" );
436 0 0         print( "_table\n", join("\n", map( {$_?"[". join(",",map( {$_?$_:0;} @$_)) ."]":"[undef]";} @table ) ), "\n" );
  0 0          
  0            
437              
438             # at some point add code to link to Algorithm::Diff to do the LCS
439             }
440              
441             sub _diff_array_unordered {
442 0     0     my( $self, $out ) = @_;
443 0           my( $match, $total, $sign );
444 0           my( @a, @b );
445              
446 0 0         print( " "x$self->{depth}, "_diff_array( [", join(",",@{$out->{orig_a}}), "], [", join(",",@{$out->{orig_b}}),"] )\n" ) if( ${$self->{debug}}[0] );
  0            
  0            
  0            
447 0           $self->{depth}++;
448              
449             # i have to keep the following code here because this is the last place in
450             # the call stack that knows about both arrays.
451              
452             # special case where we are dealing with an array of hashes.
453             # we have to sort the two arrays on the common sub hash keys.
454 0           my %key_count = ();
455 0           foreach my $item (@{$out->{orig_a}}, @{$out->{orig_b}}) {
  0            
  0            
456 0 0         next if( ref( $item ) ne 'HASH' );
457 0           foreach my $key (keys(%$item)) {
458 0 0         $key_count{$key} += (defined $$item{$key})?1:0;
459             }
460             }
461             # sort the keys by the frequence of occurance. that way any common keys have a higher sort priority.
462 0           my @key_order = sort( {$key_count{$b} <=> $key_count{$a}} keys(%key_count) );
  0            
463              
464             # print( "key_order: ", join(",",map({$_ ."(". $key_count{$_} .")"} @key_order)), "\n" );
465 0           my %sort_key;
466 0           foreach my $item (@{$out->{orig_a}}, @{$out->{orig_b}}) {
  0            
  0            
467 0           $sort_key{$item} = $self->_key( $item, \@key_order );
468             }
469              
470 0           @a = sort( {$sort_key{$a} cmp $sort_key{$b}} @{$out->{orig_a}} );
  0            
  0            
471 0           @b = sort( {$sort_key{$a} cmp $sort_key{$b}} @{$out->{orig_b}} );
  0            
  0            
472              
473             # now that the ugly bussines of sort the two arrays is done we can find the common element easier.
474              
475 0 0         print( "reorder\n", Dumper( \@a, \@b ) ) if( ${$self->{debug}}[0] );
  0            
476              
477 0           $sign = NUDGE;
478 0           $match = 0;
479 0           $total = 0;
480 0   0       while( scalar @a && scalar @b ) {
481 0           $total++;
482 0           my( $diff );
483 0           $diff = $self->_diff( $a[0], $b[0] );
484              
485 0 0         if( abs( $diff->{score} ) < 1 ) {
486 0           shift( @a );
487 0           shift( @b );
488              
489 0 0         if( abs( $diff->{score} ) > NUDGE ) {
490 0           push( @{$out->{diff}}, $diff );
  0            
491             }
492             else {
493 0           push( @{$out->{same}}, $diff );
  0            
494 0           $match++;
495             }
496             }
497             else {
498 0           my $sort_cmp = $sort_key{$a[0]} cmp $sort_key{$b[0]};
499             # print( "sort_key cmp: $sort_key{$a[0]} cmp $sort_key{$b[0]} = ", $sort_key{$a[0]} cmp $sort_key{$b[0]}, "\n" );
500              
501 0 0         if( $sort_cmp < 0 ) {
502 0           push( @{$out->{uniq_a}}, $a[0] );
  0            
503 0           shift( @a );
504 0           $sign--;
505             }
506             else {
507 0           push( @{$out->{uniq_b}}, $b[0] );
  0            
508 0           shift( @b );
509 0           $sign++;
510             }
511             }
512              
513 0           delete( $diff->{score} );
514             }
515 0 0         push( @{$out->{uniq_a}}, @a ) if( scalar @a );
  0            
516 0 0         push( @{$out->{uniq_b}}, @b ) if( scalar @b );
  0            
517              
518 0           $out->{score} = ($sign<=>0) * ($total + NUDGE - $match) / ($match + NUDGE);
519              
520 0           $self->{depth}--;
521 0 0         print( " "x$self->{depth}, "_diff_array( [", join(",",@{$out->{orig_a}}), "], [", join(",",@{$out->{orig_b}}),"] ) = $out->{score} ($sign,$match/$total)\n" ) if( ${$self->{debug}}[0] );
  0            
  0            
  0            
522             }
523              
524             ################################################################################
525              
526             sub _key {
527 0     0     my( $self, $data, $key_order ) = @_;
528              
529             # i've often thought about escaping some of the chars so as to not
530             # mix up things like undefined values and scalars that happen to eq
531             # undef but i figure as long as it always sorts that same i can't
532             # really come up with a situation where it could be a problem.
533              
534 0 0         return "undef" if( ! defined $data );
535 0 0         return $data if( ! ref( $data ) );
536 0 0         return "\\". $$data if( ref( $data ) eq "SCALAR" );
537 0 0         return $self->_key_hash( $data, $key_order ) if( ref( $data ) eq "HASH" );
538 0 0         return $self->_key_array( $data ) if( ref( $data ) eq "ARRAY" );
539              
540             # if its not one of the above types i'm not really sure what to do with it.
541 0           return $data;
542             }
543              
544             sub _key_hash {
545 0     0     my( $self, $data, $key_order ) = @_;
546 0           my( @sort_key );
547 0           @sort_key = ();
548              
549 0           foreach my $key (@$key_order) {
550 0           push( @sort_key, $self->_key( $data->{$key}, $key_order ) );
551             }
552              
553 0           return "{". join(",",@sort_key) ."}";
554             }
555              
556             sub _key_array {
557 0     0     my( $self, $data ) = @_;
558 0           my( @sort_key );
559 0           @sort_key = ();
560              
561             # special case where we are dealing with an array of hashes.
562             # we have to sort the array on the most common sub hash keys.
563             # the difference with this case is that we don't have the other
564             # array of hashes so things could get messy if we are given an
565             # array of arrayes of hashes.
566              
567 0           my %key_count = ();
568 0           foreach my $item (@$data) {
569 0 0         next if( ref( $item ) ne 'HASH' );
570 0           foreach my $key (keys(%$item)) {
571 0 0         $key_count{$key} += (defined $$item{$key})?1:0;
572             }
573             }
574             # sort the keys by the frequence of occurance. that way any common keys have a higher sort priority.
575 0           my @key_order = sort( {$key_count{$b} <=> $key_count{$a}} keys(%key_count) );
  0            
576              
577 0           foreach my $item (@$data) {
578 0           push( @sort_key, $self->_key( $item, \@key_order ) );
579             }
580              
581             # i do one final sort of the sort_keys before returning it just in case.
582 0           return "[". join(",",sort(@sort_key)) ."]";
583             }
584              
585             # Autoload methods go after =cut, and are processed by the autosplit program.
586              
587             1;
588             __END__