File Coverage

lib/Template/Plugin/XML/LibXML.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             #============================================================= -*-Perl-*-
2             #
3             # Template::Plugin::XML::LibXML
4             #
5             # DESCRIPTION
6             #
7             # Template Toolkit plugin interfacing to the XML::LibXML.pm module.
8             #
9             # AUTHOR
10             # Mark Fowler
11             #
12             # COPYRIGHT
13             # Copyright (C) 2002-3 Mark Fowler. All Rights Reserved.
14             #
15             # This module is free software; you can redistribute it and/or
16             # modify it under the same terms as Perl itself.
17             #
18             #----------------------------------------------------------------------------
19             #
20             # CVS tag goes here.
21             #
22             #============================================================================
23              
24             package Template::Plugin::XML::LibXML;
25              
26             require 5.004;
27              
28 1     1   84118 use strict;
  1         2  
  1         52  
29 1     1   8 use Template::Exception;
  1         2  
  1         26  
30 1     1   2020 use Template::Plugin;
  1         790  
  1         33  
31 1     1   358 use XML::LibXML;
  0            
  0            
32              
33             # load the recommended (but not manditory) openhandle routine
34             # for filehandle detection.
35             BEGIN { eval "use Scalar::Util qw(openhandle)" }
36              
37             use base qw( Template::Plugin );
38             use vars qw( $VERSION $parser );
39              
40             $VERSION = 1.07; #sprintf("%d.%02d", q$Revision: 2.50 $ =~ /(\d+)\.(\d+)/);
41              
42             # these are a list of combatibilty mappings from names that were used
43             # (or logical extensions of those names for html) in the XML::XPath
44             # plugin. Though we're using existing names, I want you to be able
45             # to still use the old names. Very DWIM
46             use constant TYPE => { 'xml' => 'string',
47             'text' => 'string',
48             'filename' => 'file',
49             'html' => 'html_string',
50             'html_text' => 'html_string',
51             'html_file' => 'html_file',
52             'html_filename' => 'html_file',
53             };
54              
55             #------------------------------------------------------------------------
56             # new($context, \%config)
57             #
58             # Constructor method for XML::LibXML plugin. Creates an XML::LibXML
59             # object and initialises plugin configuration.
60             #------------------------------------------------------------------------
61              
62             sub new {
63              
64             local $_;
65              
66             my $class = shift;
67             my $context = shift;
68              
69             # get the named arguments if there were any
70             my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { };
71              
72             my $type; # how we're going to get out data
73             my $content; # a ref to the data
74              
75             # work out what data we should process
76             if (@_)
77             {
78             # ah, we got positional data.
79             $content = \$_[0]; # remember where it is
80             $type = _guess_type($_[0]); # guess what type we're doing
81             }
82             else
83             {
84             # okay, the data must be in the named parameters
85              
86             # first up we'll just try the method names. You really should
87             # supply the arguments like this you know.
88             foreach (qw(string file fh html_string html_file html_fh))
89             {
90             if ($args->{ $_ })
91             {
92             $content = \$args->{ $_ }; # remember where it is
93             delete $args->{ $_ }; # don't pass on parameter though
94             $type = $_; # remember what type we're doing
95             last; # skip to the end
96             }
97             }
98              
99             unless ($type)
100             {
101             # last ditch effort. In this case we'll try some of the names
102             # that the XML::XPath plugin uses. We might strike lucky
103             foreach (keys %{ &TYPE })
104             {
105             if ($args->{ $_ })
106             {
107             $content = \$args->{ $_ }; # remember where it is
108             delete $args->{ $_ }; # don't pass on parameter though
109             $type = &TYPE->{ $_ }; # remember what type we're doing
110             last; # skip to the end
111             }
112             }
113             }
114             }
115              
116             # return an error if we didn't get a response back
117             return $class->_throw('no filename, handle or text specified')
118             unless $type;
119              
120             # create a parser
121             my $parser = XML::LibXML->new();
122              
123             # set the options
124             foreach my $method (keys %$args)
125             {
126             # try setting the method
127             eval { $parser->$method($args->{$method}) };
128              
129             # if there's a problem throw a Tempalte::Exception
130             if ($@)
131             {
132             die Template::Exception->new("XML.LibXML",
133             "option '$method' not supported");
134             }
135             }
136              
137             # parse
138             my $method = "parse_$type";
139             return $parser->$method($$content);
140             }
141              
142             #------------------------------------------------------------------------
143             # _guess_type($string)
144             #
145             # Guesses what type of data this is
146             #------------------------------------------------------------------------
147              
148             sub _guess_type
149             {
150             # look for a filehandle
151             return "fh" if _openhandle($_[0]);
152              
153             # okay, look for the xml declaration at the start
154             return "string" if $_[0] =~ m/^\<\?xml/;
155              
156             # okay, look for the html declaration anywhere in the doc
157             return "html_string" if $_[0] =~ m//i;
158              
159             # okay, does this contain a "<" symbol, and declare it to be
160             # xml if it's got one, though they should use "
161             return "string" if $_[0] =~ m{\<};
162              
163             # okay, we've tried everything else, return a filename
164             return "file";
165             }
166              
167             #------------------------------------------------------------------------
168             # _throw($errmsg)
169             #
170             # Raise a Template::Exception of type XML.XPath via die().
171             #------------------------------------------------------------------------
172              
173             sub _throw {
174             my ($self, $error) = @_;
175             # print STDERR "about to throw $error\n";
176             die Template::Exception->new('XML.LibXML', $error);
177             }
178              
179             #------------------------------------------------------------------------
180             # _openhandle($scalar)
181             #
182             # Determines if this is probably an open filehandle or not.
183             #
184             # uses openhandle from Scalar::Util if we have it.
185             #------------------------------------------------------------------------
186              
187             sub _openhandle ($)
188             {
189             return openhandle($_[0]) if defined(&openhandle);
190              
191             # poor man's openhandle
192             return defined(fileno $_[0]);
193             }
194              
195             #========================================================================
196             package XML::LibXML::Node;
197             #========================================================================
198              
199             #-----------------------------------------------------------------------
200             # present($view)
201             #
202             # Method to present an node via a view, using the block that has the
203             # same localname.
204             #-----------------------------------------------------------------------
205              
206             # note, should this worry about namespaces? Probably. Hmm.
207              
208             sub present {
209             my ($self, $view) = @_;
210             my $localname = $self->localname();
211              
212             # convert anything that isn't A-Za-z1-9 to _. All those years
213             # of working on i18n and this throws it all away. I suck.
214             $localname =~ s/[^A-Za-z0-9]/_/g;
215              
216             # render out with the block matching the hacked version of localname
217             $view->view($localname, $self);
218             }
219              
220             #-----------------------------------------------------------------------
221             # content($view)
222             #
223             # Method present the node's children via a view
224             #-----------------------------------------------------------------------
225              
226             sub content {
227             my ($self, $view) = @_;
228             my $output = '';
229             foreach my $node ($self->childNodes ) {
230             $output .= $node->present($view);
231             }
232             return $output;
233             }
234              
235             #----------------------------------------------------------------------
236             # starttag(), endtag()
237             #
238             # Methods to output the start & end tag, e.g.
239             # and
240             #----------------------------------------------------------------------
241              
242             sub starttag {
243             my ($self) = @_;
244             my $output = "<". $self->nodeName();
245             foreach my $attr ($self->attributes)
246             {
247             $output .= $attr->toString();
248             }
249             $output .= ">";
250             return $output;
251             }
252              
253             sub endtag {
254             my ($self) = @_;
255             return "nodeName() . ">";
256             }
257              
258             #========================================================================
259             package XML::LibXML::Document;
260             #========================================================================
261              
262             #------------------------------------------------------------------------
263             # present($view)
264             #
265             # Method to present a document node via a view.
266             #------------------------------------------------------------------------
267              
268             sub present {
269             my ($self, $view) = @_;
270             # okay, just start rendering from the first element, ignore the pi
271             # and all that
272             $self->documentElement->present($view);
273             }
274              
275             #========================================================================
276             package XML::LibXML::Text;
277             #========================================================================
278              
279             #------------------------------------------------------------------------
280             # present($view)
281             #
282             # Method to present a text node via a view.
283             #------------------------------------------------------------------------
284              
285             sub present {
286             my ($self, $view) = @_;
287             $view->view('text', $self->data); # same as $self->nodeData
288             }
289              
290             #========================================================================
291             package XML::LibXML::NodeList;
292             #========================================================================
293              
294             #------------------------------------------------------------------------
295             # present($view)
296             #
297             # Method to present a node list via a view. This is only normally useful
298             # when you call outside of TT as findnodes will be called in list context
299             # normally
300             #------------------------------------------------------------------------
301              
302             sub present {
303             my ($self, $view) = @_;
304             my $output = '';
305             foreach my $node ($self->get_nodelist ) {
306             $output .= $node->present($view);
307             }
308             return $output;
309             }
310              
311             #package debug;
312              
313             #sub debug
314             #{
315             # local $^W;
316             # my $nodename;
317             # eval { $nodename = $_[0]->nodeName(); };
318             # my $methodname = (caller(1))[3];
319             # $methodname =~ s/.*:://;
320             #
321             # print STDERR "${nodename}'s $methodname: ".
322             # (join ",", (map { ref } @_)) .
323             # "\n";
324             #}
325              
326             1;
327              
328             __END__