File Coverage

blib/lib/XML/LibXML/Enhanced.pm
Criterion Covered Total %
statement 22 42 52.3
branch 0 4 0.0
condition 0 3 0.0
subroutine 8 15 53.3
pod n/a
total 30 64 46.8


line stmt bran cond sub pod time code
1 2     2   19686 use strict;
  2         4  
  2         63  
2 2     2   9 use warnings;
  2         3  
  2         115  
3              
4             our $VERSION = "0.01";
5              
6             =head1 NAME
7              
8             XML::LibXML::Enhanced - adds convenience methods to XML::LibXML and LibXSLT
9              
10             =head1 SYNOPSIS
11              
12             use XML::LibXML::Enhanced;
13            
14             my $xml = XML::LibXML::Singleton->instance;
15             my $xsl = XML::LibXSLT::Singleton->instance;
16            
17             my $doc = $xml->parse_xml_string("");
18            
19             my $root = $doc->getDocumentElement;
20            
21             $root->appendHash({ name => 'Michael', email => 'mjs@beebo.org' });
22              
23             =head1 DESCRIPTION
24              
25             =head1 ADDED FUNCTIONS
26              
27             =over 4
28              
29             =item $xml = parse_xml_file($filename)
30              
31             =item $xml = parse_xml_string($string)
32              
33             =item $xml = parse_xml_chunk($string)
34              
35             Parses a file or string, and returns an XML::LibXML::Document
36             (parse_xml_file(), parse_xml_string()) or
37             XML::LibXML::DocumentFragment (parse_xml_chunk()).
38              
39             That is, the equivalent of
40             XML::LibXML::Singleton->instance->parse_file($filename), etc.
41              
42             =item $xsl = parse_xslt_file($filename)
43              
44             =item $xsl = parse_xslt_string($string)
45              
46             Parses a file or string, and returns an XML::LibXSLT::Stylesheet.
47             (L.)
48              
49             That is, the equivalent of
50             XML::LibXSLT::Singleton->instance->parse_stylesheet(XML::LibXML::Singleton->instance->parse_file($filename)),
51             etc.
52              
53             =back
54              
55             =cut
56              
57             package XML::LibXML::Enhanced;
58              
59 2     2   7 use base qw(Exporter);
  2         6  
  2         179  
60 2     2   9 use vars qw(@EXPORT_OK);
  2         3  
  2         654  
61              
62             @EXPORT_OK = qw(
63             parse_xml_file parse_xml_string parse_xml_chunk
64             parse_xslt_file parse_xslt_string promote
65             );
66              
67             sub parse_xml_file {
68 0 0   0     if (wantarray) {
69 0           return map {
70 0           XML::LibXML::Singleton->instance->parse_file($_)
71             } @_;
72             }
73             else {
74 0           return XML::LibXML::Singleton->instance->parse_file($_[0]);
75             }
76             }
77              
78             sub parse_xml_string {
79 0     0     return XML::LibXML::Singleton->instance->parse_string(@_);
80             }
81              
82             sub parse_xml_chunk {
83 0     0     return XML::LibXML::Singleton->instance->parse_balanced_chunk(@_);
84             }
85              
86             sub parse_xslt_file {
87 0 0   0     if (wantarray) {
88 0           return map {
89 0           XML::LibXSLT::Singleton->instance->parse_stylesheet(parse_xml_file($_))
90             } @_;
91             }
92             else {
93 0           return XML::LibXSLT::Singleton->instance->parse_stylesheet(parse_xml_file($_[0]));
94             }
95             }
96              
97             sub parse_xslt_string {
98 0     0     return XML::LibXSLT::Singleton->instance->parse_stylesheet(parse_xml_string(@_));
99             }
100              
101             sub promote {
102 0     0     return XML::LibXSLT::xpath_to_string(@_);
103             }
104              
105             =head1 ADDED CLASSES
106              
107             =head2 XML::LibXML::Singleton
108              
109             Singleton version of XML::LibXML; get an instance via
110             Cinstance>.
111              
112             Note that the methods C and C have
113             been called on the returned object; see C for futher
114             details.
115              
116             =cut
117              
118             package XML::LibXML::Singleton;
119              
120 2     2   10 use base qw(XML::LibXML Class::Singleton);
  2         3  
  2         2362  
121              
122             sub _new_instance {
123 0     0     my ($proto, @args) = @_;
124            
125 0   0       my $class = ref($proto) || $proto;
126            
127 0           my $self = $class->SUPER::new(@args);
128            
129 0           $self->load_ext_dtd(0);
130 0           $self->validation(0);
131            
132 0           $self;
133             }
134              
135             =head2 XML::LibXSLT::Singleton
136              
137             Singleton version of XML::LibXSLT; get an instance via
138             Cinstance>.
139              
140             =cut
141              
142             package XML::LibXSLT::Singleton;
143              
144 2     2   834 use base qw(XML::LibXSLT Class::Singleton);
  2         3  
  2         859  
145              
146             =head1 ADDED METHODS
147              
148             =head2 TO XML::LibXML::Node
149              
150             =over 4
151              
152             =cut
153              
154             package XML::LibXML::Node;
155              
156 2     2   9 use Carp;
  2         2  
  2         151  
157 2     2   2969 use XML::LibXML qw(XML_ELEMENT_NODE XML_TEXT_NODE);
  0            
  0            
158             use HTML::Entities;
159             use Data::Eacherator qw(eacherator);
160              
161             =item $n->appendHash( $hash [, $values_are_text ])
162              
163             Converts C<$hash> to an XML element, and adds it to C<$n>. (That is,
164             the opposite of C<$hash = $n-EtoHash>.)
165              
166             If C<$values_are_text> is 1, the values of the hash are treated as
167             text, and are XML-encoded (C<&> to C<&> and so forth) before being
168             added to the node.
169              
170             If C<$values_are_text> is 0 or undefined, the values of the hash are
171             treated as XML, and are parsed before being added to the node. In
172             this case, the key values must be either be well-formed XML, or
173             well-balanced XML chunks. (i.e. "" is okay, but not
174             "".) If neither is true, a comment will be inserted in the place
175             of value--C.
176              
177             For example, if
178              
179             $hash = {
180             name => "Clive",
181             email => "clive@example.com",
182             };
183            
184             and
185              
186             $node
187            
188             is
189              
190            
191            
192            
193             then
194              
195             $node->appendHash($hash);
196            
197             results in the C<$node>
198              
199            
200             Clive
201             clive@example.com
202            
203              
204             NOTE: attribute names (i.e. key values) are lowercased.
205              
206             =cut
207              
208             sub appendHash {
209             my ($self, $data, $values_are_text) = @_;
210            
211             my $doc = $self->getOwnerDocument;
212              
213             my $iter = eacherator($data);
214              
215             while (my ($k, $v) = $iter->()) {
216              
217             my $n = $doc->createElement($k);
218            
219             if ((defined $v) && ($v ne '') && !ref($v)) {
220              
221             if ($values_are_text) {
222             $n->appendChild($doc->createTextNode($v));
223             }
224              
225             else {
226             my $c = eval {
227             XML::LibXML::Singleton->instance->parse_balanced_chunk($v);
228             };
229             if ($@) {
230             $n->appendChild(
231             $doc->createComment("NOT BALANCED XML")
232             );
233             }
234             else {
235             $n->appendChild(
236             $doc->adoptNode($c)
237             );
238             }
239             }
240             }
241              
242             $self->appendChild($n);
243             }
244            
245             $data; # because $n->appendChild($c) returns $c
246             }
247              
248             =item $n->appendAttributeHash( $hash )
249              
250             Adds attributes to node C<$n>.
251              
252             =cut
253              
254             sub appendAttributeHash {
255             my ($self, $data) = @_;
256            
257             my $iter = eacherator($data);
258              
259             while (my ($k, $v) = $iter->()) {
260             $self->setAttribute($k, $v);
261             }
262            
263             $data;
264             }
265              
266             =item $h = $n->toHash( [ $values_are_text ] )
267              
268             Returns a simple hash reference representation of node C<$n>. (That
269             is, the opposite of C<$n-EappendHash($h)>.)
270              
271             If C<$values_are_text> is 1, an XML decoding of the values is
272             performed. (C<&> to C<&>, etc.)
273              
274             If C<$values_are_text> is 0 or undefined, then no transformation of
275             the values are performed.
276              
277             For example, when C is called on the C node
278              
279            
280             Michael
281             mjs@beebo.org
282            
283              
284             the return value will be
285              
286             {
287             name => 'Michael',
288             email => 'mjs@beebo.org',
289             }
290              
291             B:
292              
293             =over 4
294              
295             =item o
296              
297             order is not necessarily preserved, and if two or more tags of the
298             same name are present, only one will be present in the hash.
299              
300             =item o
301              
302             attributes are discarded.
303              
304             =back
305            
306             =cut
307              
308             sub toHash {
309             my ($self, $values_are_text) = @_;
310            
311             # The grep below filters out non element nodes because we need to
312             # skip text nodes in the event of mixed content.
313              
314             my $hash = {
315             map {
316             $_->nodeName, $_->childrenToString
317             }
318             grep {
319             $_->nodeType == XML_ELEMENT_NODE
320             }
321             $self->childNodes
322             };
323            
324             if ($values_are_text) {
325            
326             # createTextNode() encodes its argument; we need to perform
327             # the reverse process here. (& => &, and so on.)
328              
329             foreach (values %$hash) {
330             $_ = decode_entities($_);
331             }
332              
333             }
334            
335             return $hash;
336             }
337              
338             =item $h = $n->toAttributeHash
339              
340             =cut
341              
342             sub toAttributeHash {
343             my ($self) = @_;
344            
345             return { map { $_->nodeName, $_->value } $self->attributes };
346             }
347              
348             =item $n->appendRow($hash [, $name ])
349              
350             Similar to C except that the appended hash is added
351             as child of a C<$name> element. That is, if C<$n> is the node
352             "", C<$n-EappendRow({ name =E 'Michael' }, "row")> results
353             in
354              
355            
356            
357             Michael
358            
359            
360            
361             C<$name> defaults to "row".
362              
363             =cut
364              
365             sub appendRow {
366             my ($self, $hash, $name, $values_are_text) = @_;
367              
368             $name = "row" unless defined $name;
369             $values_are_text = 0 unless defined $values_are_text;
370              
371             my $doc = $self->getOwnerDocument;
372             my $row = $doc->createElement(lc($name));
373            
374             $row->appendHash($hash, $values_are_text);
375            
376             return $self->appendChild($row);
377             }
378              
379             =item $s = $n->childrenToString
380              
381             Like C except that only the node's I are
382             stringified--the opening and closing tags of the node itself are
383             omitted. (This may create a "balanced chunk.")
384              
385             =cut
386              
387             sub childrenToString {
388             my ($self) = @_;
389            
390             return join('', map { defined $_->toString ? $_->toString : '' } $self->childNodes);
391             }
392              
393             =back
394              
395             =head1 AUTHOR
396              
397             Michael Stillwell
398              
399             =cut
400              
401             1;
402              
403             __END__