File Coverage

blib/lib/Text/Same/MatchMap.pm
Criterion Covered Total %
statement 80 82 97.5
branch 13 18 72.2
condition 1 3 33.3
subroutine 14 14 100.0
pod 6 6 100.0
total 114 123 92.6


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Text::Same::MatchMap
4              
5             =head1 DESCRIPTION
6              
7             Objects of this class are returned by Text::Same::compare() and hold the
8             results of comparison in a convenient form.
9              
10             =head1 SYNOPSIS
11              
12             use Text::Same;
13             use Text::Same::TextUI;
14              
15             my $matchmap = compare(\%options, $file1, $file2);
16              
17             if ($options{show_matches}) {
18             my @matches = $matchmap->matches;
19              
20             for my $match (@matches) {
21             if (!defined $options{min_score} or $match->score >= $options{min_score}) {
22             print draw_match(\%options, $match);
23             }
24             }
25             }
26              
27             =head1 METHODS
28              
29             See below. Methods private to this module are prefixed by an
30             underscore.
31              
32             =cut
33              
34             package Text::Same::MatchMap;
35              
36 3     3   17 use warnings;
  3         6  
  3         93  
37 3     3   16 use strict;
  3         13  
  3         130  
38 3     3   15 use Carp;
  3         5  
  3         197  
39              
40 3     3   15 use vars qw($VERSION);
  3         7  
  3         235  
41             $VERSION = '0.07';
42              
43 3     3   1949 use Text::Same::Range;
  3         8  
  3         2918  
44              
45             =head2 new
46              
47             Title : new
48             Usage : $matchmap = new Text::Same::MatchMap(options=>$options,
49             source1=>$source1,
50             source2=>$source2,
51             seen_pairs=>\%seen_pairs);
52             Function: Creates a new MatchMap object for a comparison
53             Returns : A Text::Same::MatchMap object
54             Args : options - the options used by Text::Same::compare();
55             source1 - a ChunkedSource for the first source
56             source2 - a ChunkedSource for the second source
57             seen_pairs - a hash from ChunkPair to Match object, used during
58             comparison to record which pairs of chunks (ie.
59             pairs of lines) have been assigned to a Match
60              
61             =cut
62              
63             sub new
64             {
65 37     37 1 57 my $arg = shift;
66 37   33     150 my $class = ref($arg) || $arg;
67 37         158 my %args = @_;
68              
69 37         189 my $self = {options=>$args{options}, source1=>$args{source1}, source2=>$args{source2}};
70              
71 128 50       411 my @sorted_matches =
72             sort {
73 37         207 $a->{min1} <=> $b->{min1}
74             ||
75             $a->{min2} <=> $b->{min2};
76 37         66 } @{$args{matches}};
77              
78 37         83 $self->{matches} = \@sorted_matches;
79              
80 37         99 bless $self, $class;
81              
82 37         390 return $self;
83             }
84              
85             =head2 source1
86              
87             Title : source1
88             Usage : $source = $matchmap->source1();
89             Function: returns the source1 argument to new()
90              
91             =cut
92              
93             sub source1
94             {
95 12     12 1 815 my $self = shift;
96 12         59 return $self->{source1};
97             }
98              
99              
100             =head2 source2
101              
102             Title : source2
103             Usage : $source = $matchmap->source2();
104             Function: returns the source2 argument to new()
105              
106             =cut
107              
108             sub source2
109             {
110 18     18 1 5392 my $self = shift;
111 18         65 return $self->{source2};
112             }
113              
114             sub _find_matched_ranges
115             {
116 36     36   46 my $self = shift;
117 36         77 my @matches = $self->matches;
118              
119 36         79 my %indx_to_matches1 = ();
120 36         60 my %indx_to_matches2 = ();
121              
122 36         74 for my $match (@matches) {
123 105         127 my $i;
124 105         282 for ($i = $match->min1; $i <= $match->max1; ++$i) {
125 333         337 push @{$indx_to_matches1{$i}}, $match;
  333         1379  
126             }
127 105         264 for ($i = $match->min2; $i <= $match->max2; ++$i) {
128 336         341 push @{$indx_to_matches2{$i}}, $match;
  336         1381  
129             }
130             }
131              
132 36         141 return (\%indx_to_matches1, \%indx_to_matches2);
133             }
134              
135             sub _find_unmatched_chunks
136             {
137 36     36   48 my $self = shift;
138 36         54 my $source1 = shift;
139 36         47 my $source2 = shift;
140              
141 36         207 my ($indx_to_matches1, $indx_to_matches2) = $self->_find_matched_ranges();
142              
143 36         91 $self->{source1_non_matches} = _get_non_matches($source1, $indx_to_matches1);
144 36         78 $self->{source2_non_matches} = _get_non_matches($source2, $indx_to_matches2);
145             }
146              
147             sub _get_non_matches
148             {
149 72     72   108 my $source = shift;
150 72         84 my $indx_to_matches_ref = shift;
151 72         84 my %indx_to_matches = %{$indx_to_matches_ref};
  72         439  
152 72         141 my @non_matches = ();
153 72         206 my $max_chunk = $source->get_all_chunks_count;
154 72         102 my $current_min = undef;
155              
156 72 50       164 if ($max_chunk == 0) {
157 0         0 return [];
158             }
159              
160 72 100       172 if (!exists $indx_to_matches{0}) {
161 9         15 $current_min = 0;
162             }
163              
164 72         178 for (my $i = 1; $i < $max_chunk; $i++) {
165 507 100       733 if (defined $current_min) {
166 39 50       114 if (exists $indx_to_matches{$i}) {
167 39         187 push @non_matches, new Text::Same::Range($current_min, $i - 1);
168 39         117 $current_min = undef;
169             }
170             } else {
171 468 100       1307 if (!exists $indx_to_matches{$i}) {
172 36         86 $current_min = $i;
173             }
174             }
175             }
176              
177 72 100       150 if (defined $current_min) {
178 6         22 push @non_matches, new Text::Same::Range($current_min, $max_chunk - 1);
179             }
180              
181 72         469 return \@non_matches;
182             }
183              
184              
185             =head2 matches
186              
187             Title : matches
188             Usage : my @matches = $matches->matches();
189             Function: return the Match objects from the seen_pairs argument to new()
190              
191             =cut
192              
193             sub matches
194             {
195 73     73 1 227 my $self = shift;
196 73         79 return @{$self->{matches}};
  73         286  
197             }
198              
199              
200             =head2 source1_non_matches
201              
202             Title : source1_non_matches
203             Usage : my @ranges = $matchmap->source1_non_matches();
204             Function: return the ranges of chunks/lines from source1 that didn't match
205             any lines from source2
206              
207             =cut
208              
209             sub source1_non_matches
210             {
211 36     36 1 10145 my $self = shift;
212 36 50       119 if (!defined $self->{source1_non_matches}) {
213 36         132 $self->_find_unmatched_chunks($self->{source1}, $self->{source2});
214             }
215 36         56 return @{$self->{source1_non_matches}};
  36         135  
216             }
217              
218             =head2 source2_non_matches
219              
220             Title : source2_non_matches
221             Usage : my @ranges = $matchmap->source2_non_matches();
222             Function: return the ranges of chunks/lines from source2 that didn't match
223             any lines from source1
224              
225             =cut
226              
227             sub source2_non_matches
228             {
229 36     36 1 112 my $self = shift;
230 36 50       106 if (!defined $self->{source2_non_matches}) {
231 0         0 $self->_find_unmatched_chunks($self->{source1}, $self->{source2});
232             }
233 36         50 return @{$self->{source2_non_matches}};
  36         133  
234             }
235              
236             =head1 AUTHOR
237              
238             Kim Rutherford
239              
240             =head1 COPYRIGHT & LICENSE
241              
242             Copyright 2005,2006 Kim Rutherford. All rights reserved.
243              
244             This program is free software; you can redistribute it and/or modify it
245             under the same terms as Perl itself.
246              
247             =head1 DISCLAIMER
248              
249             This module is provided "as is" without warranty of any kind. It
250             may redistributed under the same conditions as Perl itself.
251              
252             =cut
253              
254             1;