File Coverage

blib/lib/XML/SAX/Builder.pm
Criterion Covered Total %
statement 212 213 99.5
branch 34 46 73.9
condition 25 42 59.5
subroutine 55 55 100.0
pod 8 8 100.0
total 334 364 91.7


line stmt bran cond sub pod time code
1             # @(#) $Id: Builder.pm,v 1.4 2003/04/24 12:48:43 dom Exp $
2             package XML::SAX::Builder;
3              
4 4     4   135375 use strict;
  4         10  
  4         150  
5 4     4   20 use warnings;
  4         8  
  4         129  
6              
7 4     4   24 use Carp qw( croak );
  4         20  
  4         290  
8 4     4   5027 use XML::NamespaceSupport;
  4         15847  
  4         155  
9 4     4   4155 use XML::SAX::Writer;
  4         206527  
  4         3036  
10              
11             our $VERSION = '0.02';
12             our $AUTOLOAD;
13              
14             sub new {
15 4     4 1 502 my $class = shift;
16             # Escape hatch.
17 4 50       15 return XML::SAX::Builder::Tag->new( $class->{Handler}, 'new', @_ )
18             if ref $class;
19 4         15 my ( $handler, %opts ) = @_;
20              
21             # Default to spitting out XML to STDOUT.
22 4   33     21 $handler ||= XML::SAX::Writer->new;
23 4         28 bless { Handler => $handler, %opts }, $class;
24             }
25              
26             sub AUTOLOAD {
27 41     41   8090 my $self = shift;
28 41         89 my @args = @_;
29 41         70 my $tag = $AUTOLOAD;
30 41         326 $tag =~ s/.*:://;
31 41 50       123 return if $tag eq 'DESTROY';
32 41 100       115 $tag = "$self->{Prefix}:$tag"
33             if $self->{Prefix};
34 41         199 XML::SAX::Builder::Tag->new( $self->{ Handler }, $tag, @args );
35             }
36              
37             # Start a new namespace.
38             sub xmlns {
39 10     10 1 20 my $self = shift;
40 10         278 XML::SAX::Builder::Namespace->new( $self->{ Handler }, @_ );
41             }
42              
43             # Output unescaped stuff.
44             sub xmlcdata {
45 2     2 1 15 my $self = shift;
46 2         34 XML::SAX::Builder::CDATA->new( $self->{ Handler }, @_ );
47             }
48              
49             # Output an XML DOCTYPE
50             sub xmldtd {
51 4     4 1 2873 my $self = shift;
52 4         33 XML::SAX::Builder::Doctype->new( $self->{ Handler }, @_ );
53             }
54              
55             # Output an XML comment
56             sub xmlcomment {
57 1     1 1 2 my $self = shift;
58 1         11 XML::SAX::Builder::Comment->new( $self->{ Handler }, @_ );
59             }
60              
61             # Output an XML Processing Instruction.
62             sub xmlpi {
63 2     2 1 666 my $self = shift;
64 2         29 XML::SAX::Builder::ProcessingInstruction->new( $self->{ Handler }, @_ );
65             }
66              
67             # Return a new generator which will automatically prefix elements.
68             sub xmlprefix {
69 1     1 1 724 my $self = shift;
70 1         3 my ($prefix) = @_;
71 1 50       4 croak "usage: xmlprefix(prefix)"
72             unless $prefix;
73 1         2 my $class = ref $self;
74 1         7 return $class->new( $self->{Handler}, Prefix => $prefix );
75             }
76              
77             sub _only_one_element {
78 23     23   33 my $self = shift;
79 23         44 my ( @builders ) = @_;
80              
81             # A namespace only allows one element child, so this rule is
82             # effectively propogated downwards.
83 28 100       166 my @tag = grep {
84 23         36 ref eq 'XML::SAX::Builder::Tag'
85             || ref eq 'XML::SAX::Builder::Namespace'
86             } @builders;
87 23         368 return @tag == 1;
88             }
89              
90             # Finalise the document.
91             sub xml {
92 23     23 1 20155 my $self = shift;
93 23         49 my ( @builders ) = @_;
94 23 100       134 croak "one and only one root element allowed"
95             unless $self->_only_one_element( @builders );
96 21         208 $self->{ Handler }->start_document( {} );
97 21         6564 my $nsup = XML::NamespaceSupport->new( { xmlns => 1 } );
98 21         354 $nsup->push_context;
99 21         216 foreach ( @builders ) {
100 26 50 33     732 if ( ref && $_->can( 'run' ) ) {
101 26         59 $_->run( $nsup );
102             } else {
103 0         0 $self->{ Handler }->characters( $_ );
104             }
105             }
106 20         344 $self->{ Handler }->end_document( {} );
107             }
108              
109             #---------------------------------------------------------------------
110              
111             package XML::SAX::Builder::Base;
112 4     4   55 use strict;
  4         7  
  4         177  
113 4     4   22 use warnings;
  4         9  
  4         1499  
114              
115             sub new {
116 60     60   179 my ( $class, $handler, @args ) = @_;
117 60         224 bless $class->_make_closure( $handler, @args ), $class;
118             }
119              
120 52     52   349 sub run { shift->(@_) }
121              
122             sub is_valid_name {
123 38     38   498 local $_ = $_[1];
124             # This is deliberately very simplistic...
125 38         512 return m/^[\w:][\w:.-]*$/;
126             }
127              
128             sub _is_reserved_name {
129 43     43   81 local $_ = $_[1];
130 43         661 return m/^xml/i;
131             }
132              
133             sub _is_valid_lang {
134 1     1   3 local $_ = $_[1];
135 1         187 return m/^
136             (
137             [a-zA-Z][a-zA-Z] # ISO639Code
138             |
139             i-[a-zA-Z]+ # IanaCode
140             |
141             x-[a-zA-Z]+ # UserCode
142             )
143             (-[a-zA-Z]+)* # Subcode
144             $/x;
145             }
146              
147             #---------------------------------------------------------------------
148              
149             package XML::SAX::Builder::Tag;
150 4     4   22 use strict;
  4         9  
  4         132  
151 4     4   18 use warnings;
  4         7  
  4         117  
152 4     4   61 use base 'XML::SAX::Builder::Base';
  4         7  
  4         6166  
153              
154             sub _make_closure {
155 41     41   182 my $class = shift;
156 41         88 my ( $handler, $tag, @args ) = @_;
157 41 100       129 Carp::croak "names beginning with /xml/i are reserved"
158             if $class->_is_reserved_name( $tag );
159 38         278 Carp::croak "doctype must appear before the first element"
160 39 100       88 if grep { ref eq 'XML::SAX::Builder::Doctype' } @args;
161 38 100       489 Carp::croak "invalid character in name"
162             unless $class->is_valid_name( $tag );
163             return sub {
164 35     35   126 my ($self, $nsup) = @_;
165 35 50 33     331 Carp::croak "usage self->(nsup)"
166             unless $self && $nsup;
167 35         94 my $data = $self->_make_element_data( $nsup, $tag );
168 35         104 $nsup->push_context;
169 35 100 100     1162 $self->_add_attributes( $nsup, $data, shift @args )
170             if $args[0] && ref $args[0] eq 'HASH';
171 34         471 $handler->start_element( $data );
172 34         4097 foreach ( @args ) {
173 32 100 66     248 if ( ref && $_->can( 'run' ) ) {
174 16         207 $_->run( $nsup );
175             } else {
176 16         86 $handler->characters( { Data => $_ } );
177             }
178             }
179 34         495 $handler->end_element( $data );
180 34         5141 $nsup->pop_context;
181 37         522 };
182             }
183              
184             sub _make_element_data {
185 35     35   53 my $self = shift;
186 35         55 my ( $nsup, $tag ) = @_;
187 35         155 my ( $uri, $prefix, $lname ) = $nsup->process_element_name( $tag );
188 35   100     1121 $uri ||= ''; $prefix ||= ''; $lname ||= '';
  35   100     132  
  35   50     76  
189 35         312 my $data = {
190             LocalName => $lname,
191             Name => $tag,
192             NamespaceURI => $uri,
193             Prefix => $prefix,
194             };
195 35         289 $self->_add_namespace_attributes( $nsup, $data );
196 35         108 return $data;
197             }
198              
199             sub _add_namespace_attributes {
200 35     35   50 my $self = shift;
201 35         48 my ( $nsup, $data ) = @_;
202 10         69 my %new_namespaces =
203 35         106 map { $_ => $nsup->get_uri( $_ ) } $nsup->get_declared_prefixes;
204 35         375 foreach my $prefix ( keys %new_namespaces ) {
205 10 100       84 my $xmlns = length( $prefix ) ? "xmlns:$prefix" : "xmlns";
206 10         41 $new_namespaces{ $xmlns } = delete $new_namespaces{ $prefix };
207             }
208 35         104 $self->_add_attributes( $nsup, $data, \%new_namespaces );
209             }
210              
211             sub _add_attributes {
212 40     40   55 my $self = shift;
213 40         414 my ( $nsup, $data, $attr ) = @_;
214 40 100 66     373 Carp::croak "invalid LanguageID"
215             if $attr->{'xml:lang'} && !$self->_is_valid_lang( $attr->{'xml:lang'} );
216 39         310 foreach ( keys %$attr ) {
217 15         49 my ($uri, $prefix, $lname) = $nsup->process_attribute_name( $_ );
218 15   100     282 $uri ||= ''; $prefix ||= ''; $lname ||= '';
  15   100     50  
  15   50     35  
219 15         143 $data->{ Attributes }->{ "{$uri}$_" } = {
220             Name => $_,
221             LocalName => $lname,
222             Prefix => $prefix,
223             NamespaceURI => $uri,
224             Value => $attr->{ $_ },
225             };
226             }
227             }
228              
229             #---------------------------------------------------------------------
230              
231             package XML::SAX::Builder::Namespace;
232 4     4   26 use strict;
  4         13  
  4         145  
233 4     4   21 use warnings;
  4         7  
  4         144  
234 4     4   19 use base 'XML::SAX::Builder::Base';
  4         7  
  4         2858  
235              
236             sub _make_closure {
237 10     10   15 my $class = shift;
238 10         24 my ( $handler, $prefix, $uri, @args ) = @_;
239 10         16 my $child = $args[0];
240 10 50 33     150 Carp::croak "new(handler,prefix,uri,child)"
      33        
      33        
241             unless $handler && defined $prefix && $uri && $child;
242 10 50       25 Carp::croak "Only one child of a namespace element is permitted"
243             if @args > 1;
244 10 50 66     42 Carp::croak "Namespace child must be element or namespace: $child"
245             unless ref($child) eq 'XML::SAX::Builder::Tag' || ref($child) eq __PACKAGE__;
246             return sub {
247 10     10   17 my ( $self, $nsup ) = @_;
248 10         37 $nsup->declare_prefix( $prefix => $uri );
249 10         219 my $data = {
250             Prefix => $prefix,
251             NamespaceURI => $uri,
252             };
253 10         45 $handler->start_prefix_mapping( $data );
254 10         383 $child->run( $nsup );
255 10         314 $handler->end_prefix_mapping( $data );
256 10         257 };
257             }
258              
259             #---------------------------------------------------------------------
260              
261             package XML::SAX::Builder::CDATA;
262 4     4   48 use strict;
  4         8  
  4         107  
263 4     4   30 use warnings;
  4         6  
  4         126  
264 4     4   27 use base 'XML::SAX::Builder::Base';
  4         7  
  4         2523  
265              
266             sub _make_closure {
267 2     2   7 my ( $class, $handler, @args ) = @_;
268 2         62 Carp::croak "arguments must be character data only"
269 2 50       6 if grep { ref } @args;
270 2         6 @args = grep { defined } @args;
  2         59  
271             return sub {
272 2     2   5 my ( $self, $nsup ) = @_;
273 2         12 $handler->start_cdata( {} );
274 2         113 $handler->characters( { Data => join ( '', @args ) } );
275 2         22 $handler->end_cdata( {} );
276 2         37 };
277             }
278              
279             #---------------------------------------------------------------------
280              
281             package XML::SAX::Builder::Doctype;
282 4     4   25 use strict;
  4         8  
  4         139  
283 4     4   21 use warnings;
  4         5  
  4         126  
284 4     4   22 use base 'XML::SAX::Builder::Base';
  4         7  
  4         2437  
285              
286             sub _make_closure {
287 4     4   9 my ( $class, $handler, $name, $system, $public ) = @_;
288 4 50       13 Carp::croak "doctype: must specify name" unless $name;
289 4 50       11 Carp::croak "doctype: must specify system id" unless $system;
290             return sub {
291 3     3   6 my ( $self, $nsup ) = @_;
292 3         14 my $data = {
293             Name => $name,
294             PublicId => $public,
295             SystemId => $system,
296             };
297 3         23 $handler->start_dtd( $data );
298 3         202 $handler->end_dtd( $data );
299             }
300 4         63 }
301              
302             #---------------------------------------------------------------------
303              
304             package XML::SAX::Builder::Comment;
305 4     4   23 use strict;
  4         5  
  4         136  
306 4     4   26 use warnings;
  4         6  
  4         123  
307 4     4   25 use base 'XML::SAX::Builder::Base';
  4         7  
  4         2143  
308              
309             sub _make_closure {
310 1     1   2 my ( $class, $handler, $data ) = @_;
311             return sub {
312 1     1   3 my ( $self, $nsup ) = @_;
313 1         7 $handler->comment( { Data => $data } );
314             }
315 1         12 }
316              
317             #---------------------------------------------------------------------
318              
319             package XML::SAX::Builder::ProcessingInstruction;
320 4     4   26 use strict;
  4         6  
  4         115  
321 4     4   18 use warnings;
  4         7  
  4         123  
322 4     4   32 use base 'XML::SAX::Builder::Base';
  4         12  
  4         2300  
323              
324             sub _make_closure {
325 2     2   6 my ( $class, $handler, $target, $data ) = @_;
326 2 50       12 Carp::croak "usage: xmlpi(target,data)"
327             unless @_ == 4;
328 2 100       13 Carp::croak "names beginning with /xml/i are reserved"
329             if $class->_is_reserved_name( $target );
330             return sub {
331 1     1   3 my ( $self, $nsup ) = @_;
332 1         10 $handler->processing_instruction( {
333             Target => $target,
334             Data => $data,
335             } );
336             }
337 1         14 }
338              
339             1;
340             __END__