File Coverage

blib/lib/XML/Filter/XInclude.pm
Criterion Covered Total %
statement 21 116 18.1
branch 0 36 0.0
condition 0 30 0.0
subroutine 7 23 30.4
pod 10 11 90.9
total 38 216 17.5


line stmt bran cond sub pod time code
1             # $Id: XInclude.pm,v 1.1.1.1 2002/01/21 08:22:26 matt Exp $
2              
3             package XML::Filter::XInclude;
4 2     2   26122 use strict;
  2         5  
  2         62  
5              
6 2     2   1803 use URI;
  2         12872  
  2         52  
7 2     2   3066 use XML::SAX::Base;
  2         46901  
  2         72  
8 2     2   26 use Cwd;
  2         2  
  2         199  
9              
10 2     2   13 use vars qw($VERSION @ISA);
  2         4  
  2         151  
11             @ISA = qw(XML::SAX::Base);
12             $VERSION = '1.0';
13              
14 2     2   11 use constant XINCLUDE_NAMESPACE => 'http://www.w3.org/2001/XInclude';
  2         3  
  2         171  
15 2     2   9 use constant NS_XML => 'http://www.w3.org/XML/1998/namespace';
  2         4  
  2         2772  
16              
17             sub new {
18 0     0 0   my $class = shift;
19 0           my $self = $class->SUPER::new(@_);
20 0           $self->{depth} = 0;
21 0           $self->{level} = 0;
22 0           $self->{locators} = [];
23 0           $self->{bases} = [];
24 0           return $self;
25             }
26              
27             sub set_document_locator {
28 0     0 1   my ($self, $locator) = @_;
29 0           push @{$self->{locators}}, $locator;
  0            
30 0           my $cwd = cwd . "/";
31 0   0       my $uri = URI->new($locator->{SystemId})->abs($cwd) ||
32             throw XML::SAX::Exception::NotSupported(
33             Message => "Unrecognized SYSTEM ID: $locator->{SystemId}"
34             );
35 0           push @{$self->{bases}}, $uri;
  0            
36 0           $self->SUPER::set_document_locator($locator);
37             }
38              
39             sub _inside_xinclude_element {
40 0     0     return shift->{level} != 0;
41             }
42              
43             sub start_element {
44 0     0 1   my ($self, $el) = @_;
45 0 0         if ($self->{level} == 0) {
46 0           my $atts = $el->{Attributes};
47              
48             # handle xml:base stuff
49 0           my $parent_base = $self->{bases}[-1];
50 0           my $current_base = $parent_base;
51 0 0         if (exists $atts->{"{".NS_XML."}base"}) {
52 0           my $base = $atts->{"{".NS_XML."}base"}{Value};
53 0   0       $current_base = URI->new_abs($base, $parent_base) ||
54             throw XML::SAX::Exception(
55             Message => "Malformed base URL: $base"
56             );
57             }
58 0           push @{$self->{bases}}, $current_base;
  0            
59              
60             # handle xincludes
61 0 0 0       if ( ($el->{NamespaceURI} eq XINCLUDE_NAMESPACE)
62             && ($el->{LocalName} eq "include") )
63             {
64 0   0       my $href = $atts->{"{}href"}{Value} ||
65             throw XML::SAX::Exception(
66             Message => "Missing href attribute"
67             );
68            
69             # don't care about auto-vivication here - xinclude element vanishes
70 0   0       my $parse = $atts->{"{}parse"}{Value} || "xml";
71            
72 0 0         if ($parse eq "text") {
    0          
73 0           $self->_include_text_document($href, $atts->{"{}encoding"}{Value});
74             }
75             elsif ($parse eq "xml") {
76 0           $self->_include_xml_document($href);
77             }
78             else {
79 0           throw XML::SAX::Exception(
80             Message => "Illegal value for parse attribute: $parse"
81             );
82             }
83 0           $self->{level}++;
84             }
85             else {
86 0           $self->SUPER::start_element($el);
87             }
88             }
89             }
90              
91             sub end_element {
92 0     0 1   my ($self, $el) = @_;
93 0 0 0       if ( ($el->{NamespaceURI} eq XINCLUDE_NAMESPACE)
    0          
94             && ($el->{LocalName} eq "include") )
95             {
96 0           $self->{level}--;
97             }
98             elsif ($self->{level} == 0) {
99 0           pop @{$self->{bases}};
  0            
100 0           $self->SUPER::end_element($el);
101             }
102             }
103              
104             sub start_document {
105 0     0 1   my ($self, $doc) = @_;
106 0           $self->{level} = 0;
107 0 0         $self->SUPER::start_document($doc) if $self->{depth} == 0;
108 0           $self->{depth}++;
109             }
110              
111             sub end_document {
112 0     0 1   my ($self, $doc) = @_;
113 0           pop @{$self->{locators}};
  0            
114 0           $self->{depth}--;
115 0 0         return $self->SUPER::end_document($doc) if $self->{depth} == 0;
116             }
117              
118             sub start_prefix_mapping {
119 0     0 1   my ($self, $mapping) = @_;
120 0 0         $self->SUPER::start_prefix_mapping($mapping) if $self->{level} == 0;
121             }
122              
123             sub end_prefix_mapping {
124 0     0 1   my ($self, $mapping) = @_;
125 0 0         $self->SUPER::end_prefix_mapping($mapping) if $self->{level} == 0;
126             }
127              
128             sub characters {
129 0     0 1   my ($self, $chars) = @_;
130 0 0         $self->SUPER::characters($chars) if $self->{level} == 0;
131             }
132              
133             sub ignorable_whitespace {
134 0     0 1   my ($self, $chars) = @_;
135 0 0         $self->SUPER::ignorable_whitespace($chars) if $self->{level} == 0;
136             }
137              
138             sub processing_instruction {
139 0     0 1   my ($self, $pi) = @_;
140 0 0         $self->SUPER::processing_instruction($pi) if $self->{level} == 0;
141             }
142              
143             sub _get_location {
144 0     0     my $self = shift;
145 0   0       my $locator = $self->{locators}[-1] || {};
146 0   0       return " in document included from " .
      0        
      0        
      0        
147             ($locator->{PublicId} || "") .
148             " at " .
149             ($locator->{SystemId} || "") .
150             " at line " .
151             ($locator->{LineNumber} || -1) .
152             ", column " .
153             ($locator->{ColumnNumber} || -1);
154             }
155              
156             sub _include_text_document {
157 0     0     my ($self, $url, $encoding) = @_;
158 0           my $base = $self->{bases}[-1];
159 0           my $source = URI->new_abs($url, $base);
160            
161 0 0 0       if (-e $source && -f _) {
162 0 0         open(SOURCE, "<$source") ||
163             throw XML::SAX::Exception(
164             Message => "Unable to open $source: $!"
165             );
166             # TODO binmode encoding on 5.7.2
167 0           while() {
168 0           $self->characters({ Data => $_ });
169             }
170 0           close SOURCE;
171             }
172             else {
173 0           require LWP::UserAgent;
174 0           my $ua = LWP::UserAgent->new;
175 0           $ua->agent("Perl/XML/Filter/XInclude/1.0 " . $ua->agent);
176            
177 0           my $req = HTTP::Request->new(GET => $source);
178            
179             my $callback = sub {
180 0     0     my ($data, $response, $protocol) = @_;
181 0           $self->characters({Data => $data});
182 0           };
183            
184 0           my $res = $ua->request($req, $callback, 4096);
185            
186 0 0         if (!$res->is_success) {
187 0           throw XML::SAX::Exception(
188             Message => "LWP Request Failed"
189             );
190             }
191             }
192             }
193              
194             sub _include_xml_document {
195 0     0     my ($self, $url) = @_;
196 0           my $base = $self->{bases}[-1];
197 0           my $source = URI->new_abs($url, $base);
198              
199             # This should work, but doesn't
200             # $self->parse(
201             # { Source => { SystemId => $source } }
202             # );
203            
204 0           my $parser = XML::SAX::ParserFactory->parser(
205             Handler => $self
206             );
207 0           local $self->{level} = 0;
208 0 0         if (grep { $_ eq $source } @{$self->{bases}}) {
  0            
  0            
209 0           throw XML::SAX::Exception(
210             Message => "Circular XInclude Reference to $source ".
211             $self->_get_location
212             );
213             }
214 0           push @{$self->{bases}}, $source;
  0            
215 0           $parser->parse(
216             { Source => { SystemId => $source } }
217             );
218 0           pop @{$self->{bases}};
  0            
219            
220             }
221              
222             1;
223             __END__