File Coverage

blib/lib/XML/LibXML/Fixup.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package XML::LibXML::Fixup;
2 1     1   19070 use strict;
  1         2  
  1         25  
3 1     1   4 use warnings;
  1         2  
  1         19  
4 1     1   4 use Carp;
  1         5  
  1         83  
5 1     1   293 use XML::LibXML;
  0            
  0            
6             use vars qw ( $VERSION @ISA );
7              
8             $VERSION = '0.03';
9             @ISA = qw ( XML::LibXML );
10              
11              
12             #################################################################
13             # CONSTRUCTOR - calls parent constructor #
14             #################################################################
15             sub new
16             {
17             my $self = XML::LibXML::new(@_);
18             $self->{_throw_ex} = 1;
19             $self->{_errors} = [];
20             $self->{_error_cursor} = 0;
21             $self->{_is_valid} = 0;
22             $self->{_fixup} = [];
23             $self->{_fixup_description} = [];
24             $self->{_fixups_applied} = [];
25             $self->{_fixup_cursor} = 0;
26             return $self;
27             }
28              
29             #################################################################
30             # VALIDATION AND FIXUPS #
31             #################################################################
32             sub valid
33             {
34             return $_[0]->{_is_valid};
35             }
36              
37             sub fixed_up
38             {
39             my $self = shift;
40             if (wantarray){
41             return @{$self->{_fixup_description}}[@{$self->{_fixups_applied}}];
42             } else {
43             return $#{$self->{_fixups_applied}} + 1;
44             }
45             }
46            
47              
48             sub add_fixup
49             {
50             my ($self, $fixup, $desc) = @_;
51             my $filter;
52             if (ref($fixup) eq 'CODE'){
53             $filter = $fixup;
54             } else {
55             eval('$filter = sub { my $xml = shift;'."\n".
56             '$xml =~ '.$fixup.";\n".
57             'return $xml;'."\n}")
58             || croak("not a regex or subroutine reference");
59             }
60             push @{$self->{_fixup}}, $filter;
61             push @{$self->{_fixup_description}}, $desc;
62             }
63              
64             sub clear_fixups
65             {
66             my $self = $_[0];
67             $self->{_fixup} = [];
68             $self->{_fixup_description} = [];
69             $self->{_fixups_applied} = [];
70             $self->{_fixup_cursor} = 0;
71             }
72              
73             sub _do_fixup
74             {
75             my ($self,$xml) = @_;
76             my $fixup = $self->{_fixup}->[$self->{_fixup_cursor}];
77             my $fixed_xml = $fixup->($xml);
78             if ($fixed_xml ne $xml)
79             {
80             push @{$self->{_fixups_applied}}, $self->{_fixup_cursor};
81             }
82             $self->{_fixup_cursor}++;
83             return $fixed_xml;
84             }
85              
86             #################################################################
87             # PARSING - overridden methods of XML::LibXML #
88             #################################################################
89             sub parse_string
90             {
91             my ($self, $string) = @_;
92             $self->_clear_status();
93            
94             my $doc;
95              
96             # first attempt sans fixups
97             $doc = $self->_safe_parse_string($string);
98              
99             # apply fixups one-by-one
100             while(
101             (!$self->valid()) &&
102             ($self->{_fixup_cursor} <= $#{$self->{_fixup}})
103             )
104             {
105             $string = $self->_do_fixup($string);
106             $doc = $self->_safe_parse_string($string);
107             }
108            
109             if($self->throw_exceptions() && !$self->valid()){
110             croak($self->get_last_error());
111             }
112             return $doc;
113             }
114              
115             sub _safe_parse_string
116             {
117             my ($self, $string) = @_;
118             my $doc;
119             eval {$doc = $self->SUPER::parse_string($string)};
120             if ( $@ ) {
121             $self->_add_error( $@ );
122             } else {
123             $self->{_is_valid} = 1;
124             }
125             return $doc;
126             }
127              
128             sub parse_file
129             {
130             die("not yet implimented");
131             }
132              
133             sub parse_fh
134             {
135             die("not yet implemented");
136             }
137              
138             #################################################################
139             # ERROR HANDLING #
140             #################################################################
141             sub throw_exceptions
142             {
143             my ($self, $bool) = @_;
144             if (defined $bool){
145             $self->{_throw_ex} = $bool;
146             }
147             return $self->{_throw_ex};
148             }
149              
150              
151             sub get_errors
152             {
153             return @{$_[0]->{_errors}};
154             }
155              
156             sub _add_error
157             {
158             my ($self, @err) = @_;
159             push @{$self->{_errors}}, @err;
160             }
161              
162             sub first_error
163             {
164             my $self = shift;
165             $self->{_error_cursor} = 0;
166             }
167              
168             sub next_error
169             {
170             my $self = shift;
171             my $last_error_index = $#{$self->{_errors}};
172             my $err;
173             if ($self->{_error_cursor} <= $last_error_index)
174             {
175             $err = $self->{_errors}->[$self->{_error_cursor}];
176             $self->{_error_cursor}++;
177             }
178             return $err;
179             }
180              
181             sub _clear_status
182             {
183             my $self = shift;
184             $self->{_errors} = [];
185             $self->{_is_valid} = 0;
186             $self->{_fixups_applied} = [];
187             $self->{_fixup_cursor} = 0;
188             $self->first_error();
189             }
190              
191             # slightly happier about this, rather than global variable
192             # used in XML::LibXML
193             sub get_last_error
194             {
195             my $self = shift;
196             my $last_error_index = $#{$self->{_errors}};
197             my $err;
198             if ($last_error_index >= 0){
199             $err = $self->{_errors}->[$last_error_index];
200             }
201             return $err;
202             }
203              
204             1;
205              
206             __END__