File Coverage

blib/lib/Regexp/Log/DateRange.pm
Criterion Covered Total %
statement 88 91 96.7
branch 45 54 83.3
condition 6 6 100.0
subroutine 7 7 100.0
pod 0 5 0.0
total 146 163 89.5


line stmt bran cond sub pod time code
1             # $Id: DateRange.pm,v 1.2 2005/04/19 15:33:02 dk Exp $
2              
3             package Regexp::Log::DateRange;
4 1     1   919 use strict;
  1         1  
  1         35  
5 1     1   7 use vars qw($VERSION %templates);
  1         1  
  1         1386  
6              
7             $VERSION = '0.02';
8              
9             %templates = (
10             syslog => [
11             [ '\\s+', 1, 12, [ qw(. jan feb mar apr may jun jul aug sep oct nov dec)]],
12             [ '\\s+', 1, 31, undef, '0?'],
13             [ '\\:', 0, 23, undef, '0?' ],
14             [ '\\:', 0, 59, undef, '0?' ],
15             ],
16             );
17              
18             sub new
19             {
20 200     200 0 10897864 my ( $class, $template, $date1, $date2) = @_;
21              
22 200 50       1132 unless ( ref($template)) {
23 200 50       1066 die "Template '$template' doesn't exist\n"
24             unless $templates{$template};
25 200         478 $template = $templates{$template};
26             }
27              
28             # some sanity checks
29 200         407 my $n = @$template;
30 200 50       716 die "template is empty\n" unless $n;
31 200 50       615 die "date [@$date1] is not valid\n" unless $n == @$date1;
32 200 50       609 die "date [@$date2] is not valid\n" unless $n == @$date2;
33              
34 200         866 for ( my $i = 0; $i < $n; $i++) {
35 217 100       703 next if $date1->[$i] == $date2->[$i];
36 200 50       800 last if $date1->[$i] < $date2->[$i];
37 0         0 ( $date2, $date1) = ( $date1, $date2);
38 0         0 last;
39             }
40            
41             # build 'alignment' vectors; for example, for the right range,
42             # 2499 would give 0111, for the left, 1150 would give 0001
43 200         295 my ( @w1, @w2);
44 200         531 my ( $last1, $last2) = ( 1, 1);
45 200         879 for ( my $i = $#$date1; $i > 0; $i--) {
46 600 100       2261 $w1[$i-1] = $last1 & (( $date1->[$i] == $template->[$i]->[1] ) ? 1 : 0);
47 600 100       1842 $w2[$i-1] = $last2 & (( $date2->[$i] == $template->[$i]->[2] ) ? 1 : 0);
48 600         920 $last1 = $w1[$i-1];
49 600         1463 $last2 = $w2[$i-1];
50             }
51 200         1156 my $tree = range2tree( $template, $date1, $date2, \@w1, \@w2, 0);
52 200         780 return tree2re( $template, $tree, 0);
53             }
54              
55             # 1,2,3 => (?:1|2|3)
56             sub re_group
57             {
58 1959 50   1959 0 4953 if ( 0 == @_) {
    100          
59 0         0 return '';
60             } elsif ( 1 == @_) {
61 775         2414 return $_[0];
62             } else {
63 1184         11224 return '(?:'.join('|', @_).')';
64             }
65             }
66              
67             # 8 .. 13 => (?:0?8|9)|1[0123]
68             sub match_range
69             {
70 981     981 0 2250 my ( $from, $to, $digit_prefix) = @_;
71              
72 981         1053 my @tens;
73 981         1897 for my $x ( $from .. $to) {
74 11702         14944 my $ten = int( $x / 10);
75 11702 100       19367 unless ( defined $tens[$ten]) {
76 1931         2702 my $mod = int( $x % 10);
77 1931         5702 $tens[$ten] = [ $mod, $mod];
78             } else {
79 9771         14833 $tens[$ten]->[1]++;
80             }
81             }
82            
83 981         1482 my @q;
84 981         1284 my $last_range = '';
85 981         1138 my @branges;
86 981         2834 for ( my $i = 0; $i < @tens; $i++) {
87 2780 100       7076 next unless defined $tens[$i];
88 1931 100       6818 my $range = ( $tens[$i]->[0] == $tens[$i]->[1] ) ?
89             $tens[$i]->[0] :
90             "[$tens[$i]->[0]-$tens[$i]->[1]]";
91 1931 100       8970 if ( $i) {
92 1439 100       2744 if ( $range eq $last_range) {
93 306         624 push @branges, $i;
94 306         1367 $q[-1] = "[$branges[0]-$branges[-1]]$range";
95             } else {
96 1133         1407 $last_range = $range;
97 1133         2026 push @q, "$i$range";
98 1133         4128 @branges = ($i);
99             }
100             } else {
101 492         1829 push @q, "$digit_prefix$range";
102             }
103             }
104              
105            
106 981         2123 my $ret = re_group(@q);
107 981         2906 $ret =~ s/\[0-9\]/\\d/g;
108 981         4670 return $ret;
109             }
110              
111             # Convert date range into a max-3-branch tree, where each branch is an alternative
112             # expansion rule, and is either a range or a value leaf; the value leaves can
113             # point deeper. For example, if matching date range 1 Apr - 3 June, the corresponding
114             # structure would be something like
115             #
116             # range (Apr-May)
117             # value (June,
118             # range(1-3)
119             # )
120             #
121             sub range2tree
122             {
123 795     795 0 1398 my ( $template, $d1, $d2, $w1, $w2, $depth) = @_;
124              
125 795         1139 my ( $i, $left, $center, $right);
126 795         1494 my ( $r1, $r2) = ( $d1-> [$depth], $d2-> [$depth]);
127              
128             # print +(' ' x $depth), "$depth: $r1 $r2\n";
129              
130 795 100 100     6494 if (
    100 100        
131             ( $w1->[$depth] and $w2->[$depth])
132             or $depth >= $#$d1
133             ) {
134 200         1275 $center = {
135             range => [ $r1 , $r2 ],
136             };
137             # print +(' ' x $depth), "T\n";
138             } elsif ( $r1 < $r2) {
139 569         788 my ( @d1, @d2);
140             # if, say, in '123' vs '145', '2' < '4' where depth = 1,
141             # then d1 = 129 and d2 = 140
142 569         2000 for ( $i = 0; $i <= $depth; $i++) {
143 1144         1877 $d1[$i] = $d1->[$i];
144 1144         2946 $d2[$i] = $d2->[$i];
145             }
146 569         1585 for ( $i = $depth + 1; $i < @$d1; $i++) {
147 1132         1908 $d1[$i] = $template-> [$i]->[2];
148 1132         2989 $d2[$i] = $template-> [$i]->[1];
149             }
150 569 100       1402 if ( $w1->[$depth]) {
151 287         362 $r1--;
152             # print +(' ' x $depth), "LT\n";
153             } else {
154             # print +(' ' x $depth), "$depth L > @$d1 : @d1 [@$w1]\n";
155 282         1947 $left = {
156             next => range2tree( $template, $d1, \@d1, $w1, [(1) x @d1], $depth + 1),
157             value => $r1,
158             };
159             # print +(' ' x $depth), "$depth L <\n";
160             }
161              
162 569 100       1333 if ( $w2->[$depth]) {
163             # print +(' ' x $depth), "RT\n";
164 282         455 $r2++;
165             } else {
166             # print +(' ' x $depth), "$depth R > @d2 : @$d2 [@$w2]\n";
167 287         1609 $right = {
168             next => range2tree( $template, \@d2, $d2, [(1) x @d2], $w2, $depth + 1),
169             value => $r2,
170             };
171             # print +(' ' x $depth), "$depth R <\n";
172             }
173 569 50       1560 if ( $r1 + 1 < $r2) {
174 569         2463 $center = {
175             range => [ $r1 + 1 , $r2 - 1 ],
176             };
177             # print +(' ' x $depth), "$depth CT [ ", $r1+1, ' .. ', $r2-1, " ]\n";
178             }
179             } else {
180 26         119 $center = {
181             next => range2tree( $template, $d1, $d2, $w1, $w2, $depth + 1),
182             value => $r1,
183             }
184             }
185              
186             return [
187 795 100       5226 $left ? $left : (),
    50          
    100          
188             $center ? $center : (),
189             $right ? $right : ()
190             ];
191             }
192              
193             # converts a tree into a regexp
194             sub tree2re
195             {
196 795     795 0 1220 my ( $template, $tree, $depth) = @_;
197 795         853 my @q;
198 795         1206 my $t = $template-> [$depth];
199 795         1582 for my $hash ( @$tree) {
200 1364 100       2947 if ( exists $hash-> {value}) {
201 595 100       2267 my $v = $t->[3] ?
202             $t->[3]->[$hash->{value}] :
203             match_range( $hash->{value}, $hash->{value}, $t->[4]);
204 595         2499 push @q, $v .
205             $t->[0] .
206             tree2re( $template, $hash-> {next}, $depth + 1);
207             } else {
208 1059         2478 my $r = $t->[3] ?
209 586         1655 re_group( map { $t->[3]->[$_] }
210             $hash-> {range}-> [0] .. $hash-> {range}-> [1] ) :
211 769 100       2677 match_range( @{$hash-> {range}}, $t->[4]);
212 769         2946 push @q, $r . $t->[0];
213             }
214             }
215              
216 795         1954 return re_group(@q);
217             }
218              
219             1;
220              
221             __END__