File Coverage

blib/lib/XML/DOM2.pm
Criterion Covered Total %
statement 84 142 59.1
branch 18 54 33.3
condition 12 25 48.0
subroutine 21 34 61.7
pod 11 11 100.0
total 146 266 54.8


line stmt bran cond sub pod time code
1             package XML::DOM2;
2              
3 3     3   78092 use strict;
  3         9  
  3         105  
4 3     3   68 use warnings;
  3         7  
  3         171  
5              
6             =head1 NAME
7              
8             XML::DOM2 - DOM controlled, strict XML module for extentable xml objects.
9              
10             =head1 VERSION
11              
12             Version 0.06 - 2007-11-28
13              
14             =head1 SYNOPSIS
15              
16             my $xml = XML::DOM2->new( file => 'filename.xml' );
17             my $xml = XML::DOM2->new( data => 'data' );
18             my $xml = XML::DOM2->new( fh => $file_handle );
19              
20             $xml->getChildren();
21              
22             =head1 DESCRIPTION
23              
24             XML::DOM2 is yet _another_ perl XML module.
25              
26             Features:
27              
28             * DOM Level2 Compilence in both document, elements and attributes
29             * NameSpace control for elements and attributes
30             * XPath (it's just one small method once you have a good DOM)
31             * Extendability:
32             * Document, Element or Attribute classes can be used as base class for other
33             kinds of document, element or attribute.
34             * Element and Attribute Handler allows element specific child elements and attribute objects.
35             * Element and Attribute serialisation overiding.
36             * Parsing with SAX (use XML::SAX::PurePerl for low dependancy installs)
37             * Internal serialisation
38              
39             =head1 METHODS
40              
41             =cut
42              
43             our $VERSION = '0.06';
44              
45 3     3   18 use vars qw($VERSION);
  3         8  
  3         765  
46 3     3   24 use base "XML::DOM2::DOM::Document";
  3         6  
  3         4218  
47 3     3   22 use Carp;
  3         36  
  3         174  
48              
49             # All unspecified Elements
50 3     3   1968 use XML::DOM2::Element;
  3         10  
  3         95  
51             # Basic Element Types
52 3     3   2241 use XML::DOM2::Element::Document;
  3         8  
  3         83  
53 3     3   1875 use XML::DOM2::Element::Comment;
  3         9  
  3         84  
54 3     3   19 use XML::DOM2::Element::CDATA;
  3         7  
  3         76  
55              
56             # XML Parsing
57 3     3   1666 use XML::DOM2::Parser;
  3         10  
  3         108  
58 3     3   3155 use XML::SAX::ParserFactory;
  3         15904  
  3         6196  
59              
60             my %default_options = (
61             # processing options
62             printerror => 1, # print error messages to STDERR
63             raiseerror => 1, # die on errors (implies -printerror)
64             # rendering options
65             indent => "\t", # what to indent with
66             seperator => "\n", # element line (vertical) separator
67             nocredits => 0, # enable/disable credit note comment
68             );
69              
70             =head2 $class->new( file|data|fh )
71              
72             Create a new xml object, it will parse a file, data or a file handle if required or will await creation of nodes.
73              
74             =cut
75             sub new
76             {
77 4     4 1 47 my ($proto, %o) = @_;
78 4   33     26 my $class = ref $proto || $proto;
79              
80 4 100 33     48 if($o{'file'} or $o{'fh'} or $o{'data'}) {
      66        
81 2         14 return $class->parseDocument(%o);
82             }
83              
84 2         7 my $self = bless \%o, $class;
85 2         8 return $self;
86             }
87              
88             =head2 $object->parseDocument( %p )
89              
90             Parse existing xml data into a document, inputs taken from ->new;
91              
92             =cut
93             sub parseDocument
94             {
95 2     2 1 8 my ($proto, %p) = @_;
96 2   33     15 my $class = ref $proto || $proto;
97              
98 2         29 my $xml = $proto->createDocument( undef, 'newDocument' );
99 2         20 my $handler = XML::DOM2::Parser->new( document => $xml );
100 2         21 my $parser = XML::SAX::ParserFactory->parser( Handler => $handler );
101              
102 2 50       155968 $parser->parse_uri($p{'file'}) if $p{'file'}; # URI
103 2 50       47 $parser->parse_string($p{'data'}) if $p{'data'}; # STRING DATA
104 2 50       11 $parser->parse_file($p{'fh'}) if $p{'fh'}; # FILE HANDLE
105              
106 2         106 return $xml;
107             }
108              
109             =head2 $object->xmlify( %options )
110             =head2 $object->render( %options )
111             =head2 $object->to_xml( %options )
112             =head2 $object->serialise( %options )
113             =head2 $object->serialize( %options )
114              
115             Returns xml representation of xml document.
116              
117             Options:
118             seperator - default is carage return
119              
120             =cut
121             sub xmlify
122             {
123 1     1 1 7 my ($self,%attrs) = @_;
124 1         3 my ($decl,$ns);
125            
126 1   50     15 my $sep = $attrs{'seperator'} || $self->{'seperator'} || "\n";
127 1 50       5 unless ($self->{'nocredits'}) {
128             #$self->documentElement->appendChild(
129             # $self->createComment( $self->_credit_comment ),
130             #);
131             }
132              
133 1         3 my $xml = '';
134             #write the xml header
135 1         5 $xml .= $self->_serialise_header(
136             seperator => $sep,
137             );
138              
139             #and write the dtd if this is inline
140 1 50       8 $xml .= $self->_serialise_doctype(
141             seperator => $sep,
142             ) unless $self->{'inline'};
143              
144 1 50       4 $self->documentElement->setAttribute('xmlns', $self->namespace) if $self->namespace;
145 1         5 $xml .= $self->documentElement->xmlify(
146             namespace => $self->{'-namespace'},
147             seperator => $sep,
148             indent => $self->{'-indent'},
149             );
150             # Return xml string
151 1         6 return $xml;
152             }
153             *render=\&xmlify;
154             *to_xml=\&xmlify;
155             *serialise=\&xmlify;
156             *serialize=\&xmlify;
157              
158             =head2 I<$class>->adaptation( $name, $structure )
159              
160             Convert a perl structure and create a new xml document of it:
161              
162             $class->adaptation('xml', { foo => [ 'A', 'B', 'C' ], bar => 'D', kou => { 'A' => 1, 'B' => 2 } });
163              
164             Will convert to:
165              
166             "ABCD12"
167              
168             $class->adaptation('xml', { 'foo' => [ { '+' => 'A', '_Letter' => '1' }, { '+' => 'B', '_Letter' => 2 } ] });
169              
170             Will convert to:
171              
172             "AB"
173              
174             =cut
175             sub adaptation
176             {
177 0     0 1 0 my ($class, $baseTag, $structure) = @_;
178 0         0 my $self = $class->new( baseTag => $baseTag );
179 0         0 my $root = $self->documentElement();
180 0         0 $class->_adapt_hash( $root, $structure );
181 0         0 return $self;
182             }
183              
184             # Adapt any kind of child object / data type
185             sub _adapt_child
186             {
187 0     0   0 my ($class, $element, $data, $parent) = @_;
188 0 0       0 if(UNIVERSAL::isa($data, 'HASH')) {
    0          
189 0         0 return $class->_adapt_hash( $element, $data, $parent );
190             } elsif(UNIVERSAL::isa($data, 'ARRAY')) {
191 0         0 return $class->_adapt_array( $element, $data, $parent );
192             } else {
193 0         0 return $class->_adapt_scalar( $element, scalar($data) );
194             }
195             }
196              
197             # Adapt a HASH ref into XML
198             sub _adapt_hash
199             {
200 0     0   0 my ($class, $element, $hash) = @_;
201              
202 0         0 foreach my $name (keys(%{$hash})) {
  0         0  
203 0         0 my $data = $hash->{$name};
204              
205 0 0       0 if($name eq '+') {
    0          
206 0         0 $element->cdata($data)
207             } elsif($name =~ /^_(.+)$/) {
208 0         0 $element->setAttribute($1, $data);
209             } else {
210 0         0 my $isa = UNIVERSAL::isa($data, 'ARRAY');
211 0 0       0 my $child = $isa ? $name : $element->createElement( $name );
212 0         0 $class->_adapt_child( $child, $data, $element );
213             }
214             }
215 0         0 return $element;
216             }
217              
218             # Adapt an ARRAY ref into XML
219             sub _adapt_array
220             {
221 0     0   0 my ($class, $name, $array, $parent) = @_;
222              
223 0         0 foreach my $data (@{$array}) {
  0         0  
224 0         0 my $isa = UNIVERSAL::isa($data, 'ARRAY');
225 0 0       0 my $child = $isa ? $name : $parent->createElement( $name );
226 0         0 $class->_adapt_child( $child, $data, $parent );
227             }
228 0         0 return $parent;
229             }
230              
231             # Adapt a SCALAR into XML
232             sub _adapt_scalar
233             {
234 0     0   0 my ($self, $element, $scalar) = @_;
235 0 0       0 if(defined($scalar)) {
236 0         0 my $result = $element->createElement( '#cdata-entity', text => scalar( $scalar ) );
237             }
238 0         0 return $element;
239             }
240              
241             =head2 $object->extension()
242            
243             $extention = $xml->extention();
244              
245             Does not work, legacy option maybe enabled in later versions.
246              
247             =cut
248             sub extension
249             {
250 0     0 1 0 my ($self) = @_;
251 0         0 return $self->{'-extension'};
252             }
253              
254              
255             =head1 OPTIONS
256              
257             =head2 $object->namespace( $set )
258              
259             Default document name space
260              
261             =cut
262 1     1 1 5 sub namespace { shift->_option('namespace', @_); }
263              
264             =head2 $object->name( $set )
265              
266             Document localName
267              
268             =cut
269 0     0 1 0 sub name { shift->_option('name', @_); }
270              
271             =head2 $object->doctype()
272              
273             Document Type object
274              
275             =cut
276 22     22 1 57 sub doctype { shift->_option('doctype', @_); }
277              
278             =head2 $object->version()
279              
280             XML Version
281              
282             =cut
283 0     0 1 0 sub version { shift->_option('version', @_); }
284              
285             =head2 $object->encoding()
286              
287             XML Encoding
288              
289             =cut
290 0     0 1 0 sub encoding { shift->_option('encoding', @_); }
291              
292             =head2 $object->standalone()
293              
294             XML Standalone
295              
296             =cut
297 0     0 1 0 sub standalone { shift->_option('standalone', @_); }
298              
299             =head1 INTERNAL METHODS
300              
301             =head2 _serialise_doctype
302              
303             $xml->_serialise_doctype( seperator => "\n" );
304              
305             Returns the document type in an xml header form.
306              
307             =cut
308             sub _serialise_doctype
309             {
310 1     1   3 my ($self, %p) = @_;
311 1         3 my $sep = $p{'seperator'};
312 1         10 my $type = $self->documentType();
313 1 50       5 return '' if not $type;
314              
315 1         2 my $id;
316 1 50       5 if ($type->publicId) {
317 0         0 $id = 'PUBLIC "'.$type->publicId.'"';
318 0 0       0 $id .= ($type->systemId ? $sep.' "'.$type->systemId.'"' : '');
319             } else {
320             #warn "I'm not returning a doctype because there is n public id: ".$type->publicId;
321 1         5 return '';
322             }
323              
324 0         0 my $extension = $self->_serialise_extension( seperator => $sep );
325 0         0 $type = $type->name;
326 0 0       0 warn "no TYPE defined!" if not defined($type);
327 0 0       0 warn "no id!" if not defined($id);
328 0         0 return $sep."";
329             }
330              
331             =head2 _serialise_extention
332              
333             $xml->_serialise_extention( seperator => "\n" );
334              
335             Returns the document extentions.
336              
337             =cut
338             sub _serialise_extension
339             {
340 0     0   0 my ($self, %p) = @_;
341 0         0 my $sep = $p{'seperator'};
342 0         0 my $ex = '';
343 0 0       0 if ($self->extension) {
344 0         0 $ex .= $sep.$self->extension.$sep;
345 0         0 $ex = " [".$sep.$ex."]";
346             }
347 0         0 return $ex;
348             }
349              
350             =head2 _serialise_header
351              
352             $xml->_serialise_header( );
353              
354             The XML header, with version, encoding and standalone options.
355              
356             =cut
357             sub _serialise_header
358             {
359 1     1   4 my ($self, %p) = @_;
360              
361 1   50     9 my $version= $self->{'version'} || '1.0';
362 1   50     8 my $encoding = $self->{'encoding'} || 'UTF-8';
363 1   50     6 my $standalone = $self->{'stand_alone'} ||'yes';
364              
365 1         7 return '';
366             }
367              
368             =head2 _element_handle
369              
370             $xml->_element_handle( $type, %element-options );
371              
372             Returns an XML element based on $type, use to extentd element capabilties.
373              
374             =cut
375             sub _element_handle
376             {
377 20     20   50 my ($self, $type, %opts) = @_;
378 20 50       46 confess "Element handler with no bleedin type!!" if not $type;
379 20 100 66     81 if($type eq '#document' or $type eq $self->_document_name) {
    50          
    50          
380 2 50       12 $opts{'documentTag'} = $type if $type ne '#document';
381 2         62 return XML::DOM2::Element::Document->new(%opts);
382             } elsif($type eq '#comment') {
383 0         0 return XML::DOM2::Element::Comment->new( delete($opts{'text'}), %opts);
384             } elsif($type eq '#cdata-entity') {
385 0         0 return XML::DOM2::Element::CDATA->new(delete($opts{'text'}), %opts);
386             }
387 18         82 return XML::DOM2::Element->new( $type, %opts );
388             }
389              
390             =head2 $object->_option( $name[, $data] )
391              
392             Set or get the required option.
393              
394             =cut
395             sub _option
396             {
397 23     23   36 my ($self, $option, $set) = @_;
398 23 50       58 if(defined($set)) {
399 0         0 $self->{$option} = $set;
400             }
401 23         114 return $self->{$option};
402             }
403              
404             =head2 $object->_can_contain_element()
405              
406             Does this node support element children.
407              
408             =cut
409 0     0   0 sub _can_contain_element { 1 }
410              
411              
412             =head2 $object->_document_name()
413              
414             Returns the doctype name or 'xml' as default, can be extended.
415              
416             =cut
417             sub _document_name {
418 20     20   28 my ($self) = @_;
419 20 50       63 if($self->{'baseTag'}) {
420 0         0 return $self->{'baseTag'};
421             }
422 20   50     41 return $self->doctype()->name() || 'xml';
423             }
424              
425             =head2 $object->_credit_comment()
426              
427             Returns the comment credit used in the output
428              
429             =cut
430 0     0     sub _credit_comment { "\nGenerated using the Perl XML::DOM2 Module V$VERSION\nWritten by Martin Owens\n" }
431              
432              
433             =head1 COPYRIGHT
434              
435             Martin Owens, doctormo@cpan.org
436              
437             =head1 CREDITS
438              
439             Based on SVG.pm by Ronan Oger, ronan@roasp.com
440              
441             =head1 SEE ALSO
442              
443             perl(1),L,L
444              
445             =cut
446             1;