File Coverage

blib/lib/M3/ServerView/Parser.pm
Criterion Covered Total %
statement 121 123 98.3
branch 48 54 88.8
condition 25 33 75.7
subroutine 20 20 100.0
pod 2 2 100.0
total 216 232 93.1


line stmt bran cond sub pod time code
1             package M3::ServerView::Parser;
2              
3 7     7   43 use strict;
  7         12  
  7         404  
4 7     7   41 use warnings;
  7         20  
  7         278  
5              
6 7     7   34 use Carp qw(croak);
  7         16  
  7         548  
7 7     7   35 use Scalar::Util qw(looks_like_number);
  7         13  
  7         346  
8 7     7   3960 use URI;
  7         31154  
  7         197  
9 7     7   2752 use Time::Local qw(timelocal);
  7         5889  
  7         513  
10              
11 7     7   44 use base qw(HTML::Parser);
  7         12  
  7         7457  
12            
13             {
14             my %Parser;
15              
16             sub new {
17 6     6 1 44 my ($pkg, $view) = @_;
18            
19 6   33     29 my $view_pkg = ref $view || $view;
20 6 100       39 _setup_parser_for_view($view_pkg) unless exists $Parser{$view_pkg};
21            
22 6         54 my $self = $pkg->SUPER::new(api_version => 3);
23 6         257 $self->handler(start => "_enter_tag", "self, tagname, attr");
24 6         31 $self->handler(end => "_leave_tag", "self, tagname");
25 6         39 $self->handler(text => "_text", "self, text");
26 6         34 $self->unbroken_text(1);
27 6         58 $self->report_tags(qw(table tr td th a));
28              
29 6         34 $self->{target_view} = $view;
30 6         15 $self->{target_view_pkg} = $view_pkg;
31            
32 6         19 return $self;
33             }
34            
35             sub _setup_parser_for_view {
36 4     4   11 my ($view_pkg) = @_;
37            
38 4         10 my %desc;
39 4         12 $desc{column_name} = {};
40 4         13 $desc{column_id} = {};
41 4         14 $desc{column_setter} = {};
42            
43 4         34 $desc{entry_class} = $view_pkg->_entry_class;
44            
45 4         29 my $i = 1;
46 4         25 my @columns = $view_pkg->_entry_columns;
47 4         49 while (my ($column, $desc) = splice @columns, 0, 2) {
48 30         48 $column = lc $column;
49 30         75 $desc{column_name}->{$i} = $column;
50 30         75 $desc{column_id}->{$column} = $i;
51            
52 30         30 my $setter;
53            
54 30 100       88 if (ref $desc eq "ARRAY") {
    50          
55 23         41 my ($key, $type) = @$desc;
56 23 100       65 if ($type eq "text") {
    100          
    50          
57             $setter = sub {
58 204     204   360 my ($view, $entry, $value) = @_;
59 204         1439 $entry->{$key} = $value;
60             }
61 11         53 }
62             elsif ($type eq "numeric") {
63             $setter = sub {
64 230     230   341 my ($view, $entry, $value) = @_;
65 230 100       676 if (looks_like_number($value)) {
66 226         1364 $entry->{$key} = $value;
67             }
68             else {
69 4         28 $entry->{$key} = undef;
70             }
71             }
72 11         51 }
73             elsif ($type eq "datetime") {
74             $setter = sub {
75 21     21   33 my ($view, $entry, $value) = @_;
76 21 50       91 if ($value =~ /^ (\d\d\d\d)(\d\d)(\d\d) - (\d\d) : (\d\d) : (\d\d) $/x) {
77 21         215 $entry->{$key} = timelocal($6, $5, $4, $3, $2 - 1, $1);
78             }
79             }
80 1         6 }
81             else {
82 0         0 croak "Unkown type '$type'";
83             }
84             }
85             elsif (ref $desc eq "CODE") {
86 7         12 $setter = $desc;
87             }
88             else {
89 0         0 croak "Unkown column handler type for '${column}'";
90             }
91            
92 30         804 $desc{column_setter}->{$i++} = $setter;
93             }
94            
95 4         15 $desc{column_count} = $i - 1;
96            
97 4         16 $Parser{$view_pkg} = \%desc;
98             }
99            
100             sub _has_column_named {
101 95     95   132 my ($self, $name) = @_;
102 95         126 my $view_pkg = $self->{target_view_pkg};
103 95         309 return $Parser{$view_pkg}->{column_id}->{$name};
104             }
105            
106             sub _column_id {
107 28     28   47 my ($self, $name) = @_;
108 28         210 return $Parser{$self->{target_view_pkg}}->{column_id}->{$name};
109             }
110            
111             sub _column_setter {
112 877     877   1183 my ($self, $id) = @_;
113 877         2486 return $Parser{$self->{target_view_pkg}}->{column_setter}->{$id};
114             }
115            
116             sub _entry_class {
117 71     71   231 my ($self) = @_;
118 71         380 return $Parser{$self->{target_view_pkg}}->{entry_class};
119             }
120             }
121              
122             sub parse {
123 6     6 1 2045 my ($self, $document) = @_;
124              
125             # Clean object
126 6         16 delete @{$self}{qw(in_table row table_is_data column in_table_cell in_table_row)};
  6         92  
127 6         113 $self->SUPER::parse($document);
128 6 100       29 if ($self->{current_entry}) {
129 4         57 $self->{target_view}->_add_entry(delete $self->{current_entry});
130             }
131             }
132              
133             sub _enter_tag {
134 1051     1051   1634 my ($self, $tagname, $attr) = @_;
135            
136 1051 100 66     11561 if ($tagname eq "table") {
    100 100        
    100 100        
    100 66        
      66        
      100        
      100        
      66        
137 11         22 $self->{in_table} = 1;
138 11         17 $self->{row} = 0;
139 11         20 $self->{table_is_data} = 1;
140            
141             # Some pages don't send a initial tr
142 11         73 $self->{_handle_corrupt_open_row} = 1;
143             }
144             elsif ($tagname eq "tr" && $self->{in_table}) {
145 81         128 delete $self->{_handle_corrupt_open_row};
146            
147 81         169 $self->{in_table_row} = 1;
148 81         114 $self->{row}++;
149 81         106 $self->{column} = 0;
150            
151 81 100       188 if ($self->{current_entry}) {
152 67         334 $self->{target_view}->_add_entry(delete $self->{current_entry});
153             }
154              
155 81 100 66     454 if ($self->{row} > 1 && $self->{table_is_data}) {
156             # We expect to be data
157 71         164 my $entry = $self->_entry_class->new;
158 71         523 $self->{current_entry} = $entry;
159             }
160             }
161             elsif (($tagname eq "td" || $tagname eq "th") && ($self->{in_table_row} || $self->{_handle_corrupt_open_row})) {
162 697 100       1420 if ($self->{_handle_corrupt_open_row}) {
163 8         9 $self->{in_table_row} = 1;
164 8         8 $self->{row}++;
165 8         9 $self->{column} = 0;
166             }
167            
168 697         858 $self->{column}++;
169 697         4682 $self->{in_table_cell} = 1;
170             }
171             elsif ($tagname eq "a" && $self->{in_table_cell} && $self->{table_is_data} && $self->{row} > 1 && $self->{current_entry}) {
172 187         294 my $href = $attr->{href};
173 187 50       353 if ($href) {
174 187         462 my $setter = $self->_column_setter($self->{column});
175 187 50       437 if ($setter) {
176 187         699 $setter->($self->{target_view}, $self->{current_entry}, URI->new($href));
177             }
178             }
179             }
180             }
181              
182             sub _text {
183 968     968   16452 my ($self, $text) = @_;
184            
185 968 100       2931 if ($self->{in_table_cell}) {
186             # Check if this is header
187 792 100 66     11823 if ($self->{row} == 1) {
    100          
188 95         479 my ($column) = lc($text) =~ /^\s*(.*?)\s*$/;
189 95 100       1185 if ($self->_has_column_named($column)) {
190 28 100       57 if ($self->_column_id($column) != $self->{column}) {
191 2         11 $self->{table_is_data} = 0;
192             }
193             }
194             else {
195 67         347 $self->{table_is_data} = 0;
196             }
197             }
198             elsif ($self->{row} > 1 && $self->{current_entry}) {
199             # Set key for entry
200 690         1608 my $setter = $self->_column_setter($self->{column});
201 690 50       1619 if ($setter) {
202 690         1760 $setter->($self->{target_view}, $self->{current_entry}, $text);
203             }
204             }
205             }
206             }
207              
208             sub _leave_tag {
209 1069     1069   3054 my ($self, $tagname) = @_;
210              
211 1069 100       4263 delete $self->{in_table_cell} if $tagname =~ /^table|tr|td$/;
212 1069 100       2862 delete $self->{in_table_row} if $tagname =~ /^table|tr$/;
213 1069 100       6713 delete $self->{in_table} if $tagname eq "table";
214             }
215              
216             1;
217             __END__