File Coverage

blib/lib/Array/Window.pm
Criterion Covered Total %
statement 81 122 66.3
branch 32 62 51.6
condition 1 9 11.1
subroutine 25 29 86.2
pod 18 20 90.0
total 157 242 64.8


line stmt bran cond sub pod time code
1             package Array::Window;
2              
3 2     2   38264 use 5.005;
  2         8  
  2         80  
4 2     2   12 use strict;
  2         4  
  2         76  
5 2     2   2116 use Params::Util '_ARRAYLIKE';
  2         9444  
  2         176  
6              
7 2     2   20 use vars qw{$VERSION};
  2         4  
  2         91  
8             BEGIN {
9 2     2   3080 $VERSION = '1.02';
10             }
11              
12             # A description of the properties
13             #
14             # source_start - The lowest index of the source array
15             # source_end - The highest index of the source array
16             # source_length - The total length of the source
17             # window_start - The lowest index of the data window
18             # window_end - The highest index of the data window
19             # window_length - The length of the window ( number of items inclusive )
20             # window_length_desired - The length of the window they would LIKE to have
21             # previous_start - The index number of window_start for the "Previous" window
22             # next_start - The index number of window_start for the "Next" window
23              
24             sub new {
25 11     11 1 708 my $class = shift;
26 11         95 my %options = @_;
27              
28             # Create the new object
29 11         86 my $self = bless {
30             source_start => undef,
31             source_end => undef,
32             source_length => undef,
33             window_start => undef,
34             window_end => undef,
35             window_length => undef,
36             window_length_desired => undef,
37             previous_start => undef,
38             next_start => undef,
39             }, $class;
40              
41             # Check for a specific source
42 11 50 33     76 if ( $options{source} ) {
    50 0        
    0 0        
    0          
    0          
43 0 0       0 _ARRAYLIKE($options{source}) or return undef;
44 0         0 $self->{source_start} = 0;
45 0         0 $self->{source_end} = $#{$options{source}};
  0         0  
46 0         0 $self->{source_length} = $self->{source_end} + 1;
47              
48             } elsif ( defined $options{source_start} and defined $options{source_end} ) {
49 11         28 $self->{source_start} = $options{source_start};
50 11         22 $self->{source_end} = $options{source_end};
51 11         28 $self->{source_length} = $options{source_end} - $options{source_start} + 1;
52              
53             } elsif ( defined $options{source_start} and defined $options{source_length} ) {
54 0 0       0 return undef unless $options{source_length} > 0;
55 0         0 $self->{source_start} = $options{source_start};
56 0         0 $self->{source_end} = $options{source_start} + $options{source_length} - 1;
57 0         0 $self->{source_length} = $options{source_length};
58              
59             } elsif ( defined $options{source_end} and defined $options{source_length} ) {
60 0 0       0 return undef unless $options{source_length} > 0;
61 0         0 $self->{source_start} = $options{source_end} - $options{source_length} + 1;
62 0         0 $self->{source_end} = $options{source_end};
63 0         0 $self->{source_length} = $options{source_length};
64              
65             } elsif ( defined $options{source_length} ) {
66 0 0       0 return undef unless $options{source_length} > 0;
67 0         0 $self->{source_start} = 0;
68 0         0 $self->{source_end} = $options{source_length} - 1;
69 0         0 $self->{source_length} = $options{source_length};
70              
71             } else {
72             # Source not defined
73 0         0 return undef;
74             }
75              
76             # Do we have the window start?
77 11 50       21 if ( defined $options{window_start} ) {
78             # We can't be before the beginning
79 11         21 $self->{window_start} = $options{window_start};
80             } else {
81 0         0 return undef;
82             }
83              
84             # Do we have the window length?
85 11 100       25 if ( defined $options{window_length} ) {
    50          
86 9 50       22 return undef unless $options{window_length} > 0;
87 9         18 $self->{window_length} = $options{window_length};
88 9         13 $self->{window_length_desired} = $options{window_length};
89             } elsif ( defined $options{window_end} ) {
90 2 50       6 return undef if $options{window_end} < $self->{window_start};
91 2         5 $self->{window_end} = $options{window_end};
92             } else {
93             # Not enough data to do the math
94 0         0 return undef;
95             }
96              
97             # Do the math
98 11         25 $self->_calculate;
99              
100 11         72 return $self;
101             }
102              
103             # Do the calculations to set things as required.
104             # We also support incremental calculations.
105             sub _calculate {
106 11     11   15 my $self = shift;
107              
108             # First, finish the third of the window_ values.
109             # This will be either window_length or window_end.
110 11 100       41 $self->_calculate_window_end unless defined $self->{window_end};
111 11 100       43 $self->_calculate_window_length unless defined $self->{window_length};
112              
113             # Adjust the window back into the source if needed
114 11 50       31 if ( $self->{window_start} < $self->{source_start} ) {
115 0         0 $self->{window_start} += ($self->{source_start} - $self->{window_start});
116 0         0 $self->_calculate_window_end;
117              
118             # If this move puts window_end after source_end, fix it
119 0 0       0 if ( $self->{window_end} > $self->{source_end} ) {
120 0         0 $self->{window_end} = $self->{source_end};
121 0         0 $self->_calculate_window_length;
122             }
123             }
124 11 100       27 if ( $self->{window_end} > $self->{source_end} ) {
125 1         4 $self->{window_start} -= ($self->{window_end} - $self->{source_end});
126 1         4 $self->_calculate_window_end;
127              
128             # If this move puts window_start before source_start, fix it
129 1 50       4 if ( $self->{window_start} < $self->{source_start} ) {
130 0         0 $self->{window_start} = $self->{source_start};
131 0         0 $self->_calculate_window_length;
132             }
133             }
134              
135             # Calculate the next window_start
136 11 100       27 if ( $self->{window_end} == $self->{source_end} ) {
137 3         5 $self->{next_start} = undef;
138             } else {
139 8         17 $self->{next_start} = $self->{window_end} + 1;
140             }
141              
142             # Calculate the previous window_start
143 11 100       26 if ( $self->{window_start} == $self->{source_start} ) {
144 4         7 $self->{previous_start} = undef;
145             } else {
146 7         15 $self->{previous_start} = $self->{window_start} - $self->{window_length};
147 7 50       18 if ( $self->{previous_start} < $self->{source_start} ) {
148 0         0 $self->{previous_start} = $self->{source_start};
149             }
150             }
151              
152 11         17 return 1;
153             }
154              
155             # Smaller calculation componants
156             sub _calculate_window_start {
157 0     0   0 my $self = shift;
158 0         0 $self->{window_start} = $self->{window_end} - $self->{window_length} + 1;
159             }
160             sub _calculate_window_end {
161 10     10   13 my $self = shift;
162 10         29 $self->{window_end} = $self->{window_start} + $self->{window_length} - 1;
163             }
164             sub _calculate_window_length {
165 2     2   3 my $self = shift;
166 2         6 $self->{window_length} = $self->{window_end} - $self->{window_start} + 1;
167             }
168              
169              
170              
171              
172              
173             #####################################################################
174             # Access methods
175              
176 0     0 1 0 sub source_start { $_[0]->{source_start} }
177 0     0 1 0 sub source_end { $_[0]->{source_end} }
178 3     3 0 1928 sub human_source_start { $_[0]->{source_start} + 1 }
179 3     3 0 832 sub human_source_end { $_[0]->{source_end} + 1 }
180 3     3 1 832 sub source_length { $_[0]->{source_length} }
181 3     3 1 876 sub window_start { $_[0]->{window_start} }
182 3     3 1 821 sub window_end { $_[0]->{window_end} }
183 3     3 1 802 sub human_window_start { $_[0]->{window_start} + 1 }
184 3     3 1 842 sub human_window_end { $_[0]->{window_end} + 1 }
185 3     3 1 784 sub window_length { $_[0]->{window_length} }
186 3     3 1 800 sub window_length_desired { $_[0]->{window_length_desired} }
187 3     3 1 764 sub previous_start { $_[0]->{previous_start} }
188 3     3 1 759 sub next_start { $_[0]->{next_start} }
189              
190             # Get an object representing the first window.
191             # Returns 0 if we are currently the first window
192             sub first {
193 3     3 1 819 my $self = shift;
194 3         6 my $class = ref $self;
195              
196             # If the window_start is equal to the source_start, return false
197 3 100       19 return '' if $self->{source_start} == $self->{window_start};
198              
199             # Create the first window
200 2         9 return $class->new(
201             source_start => $self->{source_start},
202             source_end => $self->{source_end},
203             window_length => $self->{window_length_desired},
204             window_start => $self->{source_start},
205             );
206             }
207              
208             # Get an object representing the last window.
209             # Returns false if we are already the last window.
210             sub last {
211 3     3 1 6 my $self = shift;
212 3         6 my $class = ref $self;
213              
214             # If the window_end is equal to the source_end, return false
215 3 100       16 return '' if $self->{source_end} == $self->{window_end};
216              
217             # Create the last window
218 2         6 my $window_start = $self->{source_end} - $self->{window_length_desired} + 1;
219 2         10 return $class->new(
220             source_start => $self->{source_start},
221             source_end => $self->{source_end},
222             window_start => $window_start,
223             window_end => $self->{source_end},
224             );
225             }
226              
227             # Get an object representing the next window.
228             # Returns 0 if there is no next window.
229             sub next {
230 3     3 1 6 my $self = shift;
231 3         5 my $class = ref $self;
232              
233             # If there is no next, return false
234 3 100       14 return '' unless defined $self->{next_start};
235              
236             # Create the next window
237 2         10 return $class->new(
238             source_start => $self->{source_start},
239             source_end => $self->{source_end},
240             window_length => $self->{window_length_desired},
241             window_start => $self->{next_start},
242             );
243             }
244              
245             sub previous {
246 3     3 1 6 my $self = shift;
247 3         5 my $class = ref $self;
248              
249             # If there is no previou, return false
250 3 100       17 return '' unless defined $self->{previous_start};
251              
252             # Create the previous window
253 2         9 return $class->new(
254             source_start => $self->{source_start},
255             source_end => $self->{source_end},
256             window_length => $self->{window_length_desired},
257             window_start => $self->{previous_start},
258             );
259             }
260              
261             # Method to determine if we need to do windowing.
262             # The method returns false if the subset is the entire set,
263             # and true if the subset is smaller than the set
264             sub required {
265 3     3 1 756 my $self = shift;
266 3 100       18 return 1 unless $self->{source_start} == $self->{window_start};
267 1 50       7 return 1 unless $self->{source_end} == $self->{window_end};
268 0           return '';
269             }
270              
271             # $window->extract( \@array );
272             # Method takes a set that matches the window parameters, and extracts
273             # the specified window
274             # Returns a reference to the sub array on success
275             # Returns undef if the array does not match the window
276             sub extract {
277 0     0 1   my $self = shift;
278 0           my $arrayref = shift;
279              
280             # Check that they match
281 0 0         return undef unless $self->{source_start} == 0;
282 0 0         return undef unless $self->{source_end} == $#$arrayref;
283              
284             # Create the sub array
285 0           my @subarray = ();
286 0           @subarray = @{$arrayref}[$self->window_start .. $self->window_end];
  0            
287              
288             # Return a reference to the sub array
289 0           return \@subarray;
290             }
291            
292             1;
293              
294             __END__