File Coverage

blib/lib/Markup/MatchNode.pm
Criterion Covered Total %
statement 57 94 60.6
branch 18 50 36.0
condition 7 18 38.8
subroutine 9 10 90.0
pod 1 3 33.3
total 92 175 52.5


line stmt bran cond sub pod time code
1             package Markup::MatchNode;
2             $VERSION = '1.0.0';
3            
4             ####################################################
5             # This module is protected under the terms of the
6             # GNU GPL. Please see
7             # http://www.opensource.org/licenses/gpl-license.php
8             # for more information.
9             ####################################################
10            
11 1     1   75807 use strict;
  1         3  
  1         40  
12 1     1   6 use Carp;
  1         1  
  1         6031  
13            
14             require Markup::TreeNode;
15            
16             our @ISA = qw(Markup::TreeNode);
17             our $empty = '(empty)';
18            
19             sub new {
20 1     1 0 3760 my $invocant = shift();
21 1   33     11 my $class = ref($invocant) || $invocant;
22 1         14 $class = bless {
23             element_type => 'tag',
24             tagname => '',
25             attr => { },
26             level => 0,
27             parent => $empty,
28             child_num => 0,
29             children => [ ],
30             text => '',
31             options => { }
32             }, $class;
33 1         5 $class->init (@_);
34 1         3 return $class;
35             }
36            
37             sub init {
38 1     1 0 2 my $self = shift();
39 1         6 my %args = @_;
40            
41 1         4 foreach (keys %args) {
42             # enforce integrity
43 2 50 33     8 if ($_ eq 'parent' && $args{$_} ne $empty) {
44 0         0 $self->attach_parent($args{$_});
45 0         0 next;
46             }
47            
48             # enforce integrity
49 2 50       5 if ($_ eq 'children') {
50 0         0 $self->attach_children($args{$_});
51 0         0 next;
52             }
53            
54 2 50       17 if (exists $self->{$_}) {
55 2         7 $self->{$_} = $args{$_};
56             }
57             else {
58 0         0 croak ("unrecognized node option $_");
59             }
60             }
61             }
62            
63             sub _ignore_me {
64 1     1   3 my $self = shift();
65            
66 1         6 while (($self = $self->{'parent'}) ne $empty) {
67 0 0       0 return 1 if ($self->{'options'}->{'ignore_children'});
68             }
69            
70 1         6 return 0;
71             }
72            
73             sub compare_to {
74 1     1 1 1415 my ($self, $treenode) = @_;
75            
76 1 50       5 return undef if ($self->_ignore_me());
77            
78 1 50       3 if (scalar(@{ $self->{'options'}->{'call_filter'} || [] })) {
  1 50       20  
79 0         0 foreach (@{$self->{'options'}->{'call_filter'}}) {
  0         0  
80 0         0 $_->($self);
81             }
82            
83 0         0 return undef;
84             }
85            
86 1         3 my (@minor, @major);
87 1         4 @minor = (0, 0, 0, '');
88 1         3 @major = (0, 0, 0, '');
89             # array positions: [0] == possible errors : [1] == correct points : [2] percent correct : [3] errors
90             my $similar = sub {
91 1     1   3 my ($nodeA, $nodeB) = @_;
92            
93 1 50 33     7 if ($nodeA eq $empty && $nodeB eq $empty) {
94 0         0 return (1);
95             }
96            
97 1 50 33     10 if ($nodeA eq $empty || $nodeB eq $empty) {
98 0         0 return (0);
99             }
100            
101 1   33     25 return ((($nodeA->{'element_type'} eq $nodeB->{'element_type'}) &&
102             ($nodeA->{'tagname'} eq $nodeB->{'tagname'})));
103 1         6 };
104             my $calc = sub {
105 1     1   4 my ($minor, $major, $list) = @_;
106            
107 1         4 $minor->[3] =~ s/^://;
108 1         3 $major->[3] =~ s/^://;
109            
110 1         4 $minor->[2] = _percent($minor->[1], $minor->[0]);
111 1         4 $major->[2] = _percent($major->[1], $major->[0]);
112 1 50       31 return $list ? ($minor, $major) : int((($minor->[2] + $major->[2]) / 2));
113 1         7 };
114            
115 1 50       5 if ($self->{'text'}) {
116 1         2 $minor[0]++;
117 1 50       12 if ($self->{'options'}->{'text_not_null'}) {
    50          
    50          
118 0 0       0 if ($treenode->{'text'} =~ m/(?:\S+)/) {
119 0         0 $minor[1]++;
120             }
121             else {
122 0         0 $minor[3] .= ':text is null where option text_not_null was specified';
123             }
124             }
125             elsif ($self->{'text'} =~ m/^{!(.+)!}/) {
126 0 0       0 if ($treenode->{'text'} =~ m/$1/) {
127 0         0 $minor[1]++;
128             }
129             else {
130 0         0 $minor[3] .= ":your regular expression /$1/ did not match the text";
131             }
132             }
133             elsif ($self->{'text'} eq $treenode->{'text'}) {
134 1         2 $minor[1]++;
135             }
136             else {
137 0         0 $minor[3] .= ':unmatched text';
138             }
139             }
140            
141 1         2 $_ = \@minor;
142            
143             # these are tags which, if out of place, I find to be major errors
144 1         4 foreach my $j (qw(table td tr tbody map object img body head title html)) {
145 11 50       28 if ($self->{'tagname'} eq $j) {
146 0         0 $_ = \@major; last;
  0         0  
147             }
148             }
149            
150 1         3 $_->[0]++;
151 1 50       6 if ($similar->($self, $treenode)) {
152 1         11 $_->[1]++;
153             }
154             else {
155             # optional nodes get special treatment :)
156 0 0       0 if ($self->{'options'}->{'optional'}) {
157 0         0 return 'optional';
158             }
159            
160 0         0 $_->[3] .= ':nodes are not simliar';
161            
162             # no point in going on - this node is out of place!
163 0         0 return $calc->(\@minor, \@major, wantarray);
164             }
165            
166 1 50       3690 unless ($self->{'options'}->{'ignore_attrs'}) {
167 1         11 foreach (keys %{ $self->{'attr'} }) {
  1         27  
168 0         0 $minor[0]++;
169 0 0       0 if ($self->{'attr'}->{$_} =~ m/^{!(.+)!}/) {
    0          
170 0 0       0 if ($treenode->{'attr'}->{$_} =~ m/$1/) {
171 0         0 $minor[1]++;
172             }
173             else {
174 0         0 $minor[3] .= ":attribute '$_' could not be matched with /$1/";
175             }
176             }
177             elsif ($self->{'attr'}->{$_} eq $treenode->{'attr'}->{$_}) {
178 0         0 $minor[1]++;
179             }
180             else {
181 0         0 $minor[3] .= ":attribute '$_' could not be matched";
182             }
183             }
184             }
185            
186 1         9 return $calc->(\@minor, \@major, wantarray);
187             }
188            
189             sub _percent {
190 2     2   4 my ($div, $by) = @_;
191 2 50 66     12 return 100 if (!$div && !$by);
192 1 50       9 return (int(($div /((!$by) ? 1 : $by)) * 100));
193             }
194            
195             sub _avg {
196 0     0     my ($res, $cnt);
197 0           foreach (@_) {
198 0           $res += $_;
199 0           $cnt++;
200             }
201 0           return (int($res / $cnt));
202             }
203            
204             1;
205            
206             __END__