1 |
############################################################################## |
2 |
# |
3 |
# Perl module: XML::XSLT |
4 |
# |
5 |
# By Geert Josten, gjosten@sci.kun.nl |
6 |
# and Egon Willighagen, egonw@sci.kun.nl |
7 |
# |
8 |
# $Log: XSLT.pm,v $ |
9 |
# Revision 1.19 2002/02/18 09:05:14 gellyfish |
10 |
# Refactoring |
11 |
# |
12 |
# Revision 1.18 2002/01/16 21:05:27 gellyfish |
13 |
# * Added the manpage as an example |
14 |
# * Started to properly implement omit-xml-declaration |
15 |
# |
16 |
# Revision 1.17 2002/01/13 10:35:00 gellyfish |
17 |
# Updated pod |
18 |
# |
19 |
# Revision 1.16 2002/01/09 09:17:40 gellyfish |
20 |
# * added test for <xsl:text> |
21 |
# * Stylesheet whitespace stripping as per spec and altered tests ... |
22 |
# |
23 |
# Revision 1.15 2002/01/08 10:11:47 gellyfish |
24 |
# * First cut at cdata-section-element |
25 |
# * test for above |
26 |
# |
27 |
# Revision 1.14 2001/12/24 16:00:19 gellyfish |
28 |
# * Version released to CPAN |
29 |
# |
30 |
# Revision 1.13 2001/12/20 09:21:42 gellyfish |
31 |
# More refactoring |
32 |
# |
33 |
# Revision 1.12 2001/12/19 21:06:31 gellyfish |
34 |
# * Some refactoring and style changes |
35 |
# |
36 |
# Revision 1.11 2001/12/19 09:11:14 gellyfish |
37 |
# * Added more accessors for object attributes |
38 |
# * Fixed potentially broken usage of $variables in _evaluate_template |
39 |
# |
40 |
# Revision 1.10 2001/12/18 09:10:10 gellyfish |
41 |
# Implemented attribute-sets |
42 |
# |
43 |
# Revision 1.9 2001/12/17 22:32:12 gellyfish |
44 |
# * Added Test::More to Makefile.PL |
45 |
# * Added _indent and _outdent methods |
46 |
# * Placed __get_attribute_sets in transform() |
47 |
# |
48 |
# Revision 1.8 2001/12/17 11:32:08 gellyfish |
49 |
# * Rolled in various patches |
50 |
# * Added new tests |
51 |
# |
52 |
# |
53 |
############################################################################### |
54 |
|
55 |
=head1 NAME |
56 |
|
57 |
XML::XSLT - A perl module for processing XSLT |
58 |
|
59 |
=cut |
60 |
|
61 |
|
62 |
###################################################################### |
63 |
package XML::XSLT; |
64 |
###################################################################### |
65 |
|
66 |
use strict; |
67 |
|
68 |
use XML::DOM 1.25; |
69 |
use LWP::Simple qw(get); |
70 |
use URI; |
71 |
use Cwd; |
72 |
use File::Basename qw(dirname); |
73 |
use Carp; |
74 |
|
75 |
# Namespace constants |
76 |
|
77 |
use constant NS_XSLT => 'http://www.w3.org/1999/XSL/Transform'; |
78 |
use constant NS_XHTML => 'http://www.w3.org/TR/xhtml1/strict'; |
79 |
|
80 |
use vars qw ( $VERSION @ISA @EXPORT_OK $AUTOLOAD ); |
81 |
|
82 |
$VERSION = '0.40'; |
83 |
|
84 |
@ISA = qw( Exporter ); |
85 |
@EXPORT_OK = qw( &transform &serve ); |
86 |
|
87 |
|
88 |
|
89 |
my %deprecation_used; |
90 |
|
91 |
|
92 |
###################################################################### |
93 |
# PUBLIC DEFINITIONS |
94 |
|
95 |
sub new { |
96 |
my $class = shift; |
97 |
my $self = bless {}, $class; |
98 |
my %args = $self->__parse_args(@_); |
99 |
|
100 |
$self->{DEBUG} = defined $args{debug} ? $args{debug} : ""; |
101 |
$self->{PARSER} = XML::DOM::Parser->new; |
102 |
$self->{PARSER_ARGS} = defined $args{DOMparser_args} |
103 |
? $args{DOMparser_args} : {}; |
104 |
$self->{VARIABLES} = defined $args{variables} |
105 |
? $args{variables} : {}; |
106 |
$self->{WARNINGS} = defined $args{warnings} |
107 |
? $args{warnings} : 0; |
108 |
$self->{INDENT} = defined $args{indent} |
109 |
? $args{indent} : 0; |
110 |
$self->{INDENT_INCR} = defined $args{indent_incr} |
111 |
? $args{indent_incr} : 1; |
112 |
$self->{XSL_BASE} = defined $args{base} |
113 |
? $args{base} : 'file://' . cwd . '/'; |
114 |
$self->{XML_BASE} = defined $args{base} |
115 |
? $args{base} : 'file://' . cwd . '/'; |
116 |
|
117 |
$self->use_deprecated($args{use_deprecated}) if exists $args{use_deprecated}; |
118 |
|
119 |
$self->debug("creating parser object:"); |
120 |
|
121 |
$self->_indent(); |
122 |
$self->open_xsl(%args); |
123 |
$self->_outdent(); |
124 |
|
125 |
return $self; |
126 |
} |
127 |
|
128 |
sub use_deprecated |
129 |
{ |
130 |
my ( $self, $use_deprecated ) = @_; |
131 |
|
132 |
if ( defined $use_deprecated ) |
133 |
{ |
134 |
$self->{USE_DEPRECATED} = $use_deprecated; |
135 |
} |
136 |
|
137 |
return $self->{USE_DEPRECATED} || 0; |
138 |
} |
139 |
|
140 |
sub DESTROY {} # Cuts out random dies on includes |
141 |
|
142 |
sub default_xml_version |
143 |
{ |
144 |
my ( $self, $xml_version ) = @_; |
145 |
|
146 |
if ( defined $xml_version ) |
147 |
{ |
148 |
$self->{DEFAULT_XML_VERSION} = $xml_version; |
149 |
} |
150 |
|
151 |
return $self->{DEFAULT_XML_VERSION} ||= '1.0'; |
152 |
} |
153 |
|
154 |
sub serve { |
155 |
my $self = shift; |
156 |
my $class = ref $self || croak "Not a method call"; |
157 |
my %args = $self->__parse_args(@_); |
158 |
my $ret; |
159 |
|
160 |
$args{http_headers} = 1 unless defined $args{http_headers}; |
161 |
$args{xml_declaration} = 1 unless defined $args{xml_declaration}; |
162 |
$args{xml_version} = $self->default_xml_version() |
163 |
unless defined $args{xml_version}; |
164 |
$args{doctype} = 'SYSTEM' unless defined $args{doctype}; |
165 |
$args{clean} = 0 unless defined $args{clean}; |
166 |
|
167 |
$ret = $self->transform($args{Source})->toString; |
168 |
|
169 |
if($args{clean}) { |
170 |
eval {require HTML::Clean}; |
171 |
|
172 |
if($@) { |
173 |
CORE::warn("Not passing through HTML::Clean -- install the module"); |
174 |
} else { |
175 |
my $hold = HTML::Clean->new(\$ret); |
176 |
$hold->strip; |
177 |
$ret = ${$hold->data}; |
178 |
} |
179 |
} |
180 |
|
181 |
|
182 |
|
183 |
if (my $doctype = $self->doctype()) |
184 |
{ |
185 |
$ret = $doctype . "\n" . $ret; |
186 |
} |
187 |
|
188 |
|
189 |
if($args{xml_declaration}) |
190 |
{ |
191 |
$ret = $self->xml_declaration() . "\n" . $ret; |
192 |
} |
193 |
|
194 |
if($args{http_headers}) { |
195 |
$ret = "Content-Type: " . $self->media_type . "\n" . |
196 |
"Content-Length: " . length($ret) . "\n\n" . $ret; |
197 |
} |
198 |
|
199 |
return $ret; |
200 |
} |
201 |
|
202 |
|
203 |
sub xml_declaration |
204 |
{ |
205 |
my ( $self, $xml_version, $output_encoding ) = @_; |
206 |
|
207 |
$xml_version ||= $self->default_xml_version(); |
208 |
$output_encoding ||= $self->output_encoding(); |
209 |
|
210 |
return qq{<?xml version="$xml_version" encoding="$output_encoding"?>}; |
211 |
} |
212 |
|
213 |
|
214 |
|
215 |
sub output_encoding |
216 |
{ |
217 |
my ( $self,$encoding ) = @_; |
218 |
|
219 |
if ( defined $encoding ) |
220 |
{ |
221 |
$self->{OUTPUT_ENCODING} = $encoding; |
222 |
} |
223 |
|
224 |
return exists $self->{OUTPUT_ENCODING} ? $self->{OUTPUT_ENCODING} : 'UTF-8'; |
225 |
} |
226 |
|
227 |
sub doctype_system |
228 |
{ |
229 |
my ( $self, $doctype ) = @_; |
230 |
|
231 |
if ( defined $doctype ) |
232 |
{ |
233 |
$self->{DOCTYPE_SYSTEM} = $doctype; |
234 |
} |
235 |
|
236 |
return $self->{DOCTYPE_SYSTEM}; |
237 |
} |
238 |
|
239 |
sub doctype_public |
240 |
{ |
241 |
my ( $self, $doctype ) = @_; |
242 |
|
243 |
if ( defined $doctype ) |
244 |
{ |
245 |
$self->{DOCTYPE_PUBLIC} = $doctype; |
246 |
} |
247 |
|
248 |
return $self->{DOCTYPE_PUBLIC}; |
249 |
} |
250 |
|
251 |
sub result_document() |
252 |
{ |
253 |
my ( $self, $document ) = @_; |
254 |
|
255 |
if ( defined $document ) |
256 |
{ |
257 |
$self->{RESULT_DOCUMENT} = $document; |
258 |
} |
259 |
|
260 |
return $self->{RESULT_DOCUMENT}; |
261 |
} |
262 |
|
263 |
sub debug { |
264 |
my $self = shift; |
265 |
my $arg = shift || ""; |
266 |
|
267 |
print STDERR " "x$self->{INDENT},"$arg\n" |
268 |
if $self->{DEBUG}; |
269 |
} |
270 |
|
271 |
sub warn { |
272 |
my $self = shift; |
273 |
my $arg = shift || ""; |
274 |
|
275 |
print STDERR " "x$self->{INDENT},"$arg\n" |
276 |
if $self->{DEBUG}; |
277 |
print STDERR "$arg\n" |
278 |
if $self->{WARNINGS} && ! $self->{DEBUG}; |
279 |
} |
280 |
|
281 |
sub open_xml { |
282 |
my $self = shift; |
283 |
my $class = ref $self || croak "Not a method call"; |
284 |
my %args = $self->__parse_args(@_); |
285 |
|
286 |
if(defined $self->xml_document() && not $self->{XML_PASSED_AS_DOM}) { |
287 |
$self->debug("flushing old XML::DOM::Document object..."); |
288 |
$self->xml_document()->dispose; |
289 |
} |
290 |
|
291 |
$self->{XML_PASSED_AS_DOM} = 1 |
292 |
if ref $args{Source} eq 'XML::DOM::Document'; |
293 |
|
294 |
if (defined $self->result_document()) { |
295 |
$self->debug("flushing result..."); |
296 |
$self->result_document()->dispose (); |
297 |
} |
298 |
|
299 |
$self->debug("opening xml..."); |
300 |
|
301 |
$args{parser_args} ||= {}; |
302 |
|
303 |
my $xml_document = $self->__open_document (Source => $args{Source}, |
304 |
base => $self->{XML_BASE}, |
305 |
parser_args => |
306 |
{%{$self->{PARSER_ARGS}}, |
307 |
%{$args{parser_args}}}, |
308 |
); |
309 |
|
310 |
$self->xml_document($xml_document); |
311 |
|
312 |
$self->{XML_BASE} = |
313 |
dirname(URI->new_abs($args{Source}, $self->{XML_BASE})->as_string) . '/'; |
314 |
$self->result_document($self->xml_document()->createDocumentFragment); |
315 |
} |
316 |
|
317 |
sub xml_document |
318 |
{ |
319 |
my ( $self, $xml_document ) = @_; |
320 |
|
321 |
if ( defined $xml_document ) |
322 |
{ |
323 |
$self->{XML_DOCUMENT} = $xml_document; |
324 |
} |
325 |
|
326 |
return $self->{XML_DOCUMENT}; |
327 |
} |
328 |
|
329 |
sub open_xsl { |
330 |
my $self = shift; |
331 |
my $class = ref $self || croak "Not a method call"; |
332 |
my %args = $self->__parse_args(@_); |
333 |
|
334 |
$self->xsl_document()->dispose |
335 |
if not $self->{XSL_PASSED_AS_DOM} and defined $self->xsl_document(); |
336 |
|
337 |
$self->{XSL_PASSED_AS_DOM} = 1 |
338 |
if ref $args{Source} eq 'XML::DOM::Document'; |
339 |
|
340 |
# open new document # open new document |
341 |
$self->debug("opening xsl..."); |
342 |
|
343 |
$args{parser_args} ||= {}; |
344 |
|
345 |
my $xsl_document = $self->__open_document (Source => $args{Source}, |
346 |
base => $self->{XSL_BASE}, |
347 |
parser_args => |
348 |
{%{$self->{PARSER_ARGS}}, |
349 |
%{$args{parser_args}}}, |
350 |
); |
351 |
|
352 |
$self->xsl_document($xsl_document); |
353 |
|
354 |
$self->{XSL_BASE} = |
355 |
dirname(URI->new_abs($args{Source}, $self->{XSL_BASE})->as_string) . '/'; |
356 |
|
357 |
$self->__preprocess_stylesheet; |
358 |
} |
359 |
|
360 |
sub xsl_document |
361 |
{ |
362 |
my ( $self, $xsl_document ) = @_; |
363 |
|
364 |
if ( defined $xsl_document ) |
365 |
{ |
366 |
$self->{XSL_DOCUMENT} = $xsl_document; |
367 |
} |
368 |
|
369 |
return $self->{XSL_DOCUMENT}; |
370 |
} |
371 |
|
372 |
# Argument parsing with backwards compatibility. |
373 |
sub __parse_args { |
374 |
my $self = shift; |
375 |
my %args; |
376 |
|
377 |
if(@_ % 2 ) { |
378 |
$args{Source} = shift; |
379 |
%args = (%args, @_); |
380 |
} else { |
381 |
%args = @_; |
382 |
if(not exists $args{Source}) { |
383 |
my $name = [caller(1)]->[3]; |
384 |
carp "Argument syntax of call to $name deprecated. See the documentation for $name" |
385 |
unless $self->use_deprecated() |
386 |
or exists $deprecation_used{$name}; |
387 |
$deprecation_used{$name} = 1; |
388 |
%args = (); |
389 |
$args{Source} = shift; |
390 |
shift; |
391 |
%args = (%args, @_); |
392 |
} |
393 |
} |
394 |
|
395 |
return %args; |
396 |
} |
397 |
|
398 |
# private auxiliary function # |
399 |
sub __my_tag_compression { |
400 |
my ($tag, $elem) = @_; |
401 |
|
402 |
=begin internal_docs |
403 |
|
404 |
__my_tag_compression__( $tag, $elem ) |
405 |
|
406 |
A function for DOM::XML::setTagCompression to determine the style for printing |
407 |
of empty tags and empty container tags. |
408 |
|
409 |
XML::XSLT implements an XHTML-friendly style. |
410 |
|
411 |
Allow tag to be preceded by a namespace: ([\w\.]+\:){0,1} |
412 |
|
413 |
<br> -> <br /> |
414 |
|
415 |
or |
416 |
|
417 |
<myns:hr> -> <myns:hr /> |
418 |
|
419 |
Empty tag list obtained from: |
420 |
|
421 |
http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd |
422 |
|
423 |
According to "Appendix C. HTML Compatibility Guidelines", |
424 |
C.3 Element Minimization and Empty Element Content |
425 |
|
426 |
Given an empty instance of an element whose content model is not EMPTY |
427 |
(for example, an empty title or paragraph) do not use the minimized form |
428 |
(e.g. use <p> </p> and not <p />). |
429 |
|
430 |
However, the <p> tag is processed like an empty tag here! |
431 |
|
432 |
Tags allowed: |
433 |
|
434 |
base meta link hr br param img area input col |
435 |
|
436 |
Special Case: p (even though it violates C.3) |
437 |
|
438 |
The tags are matched in order of expected common occurence. |
439 |
|
440 |
=end internal_docs |
441 |
|
442 |
=cut |
443 |
|
444 |
$tag = [split ':', $tag]->[1] if index($tag, ':') >= 0; |
445 |
return 2 if $tag =~ m/^(p|br|img|hr|input|meta|base|link|param|area|col)$/i; |
446 |
|
447 |
# Print other empty tags like this: <empty></empty> |
448 |
return 1; |
449 |
} |
450 |
|
451 |
|
452 |
# private auxiliary function # |
453 |
sub __preprocess_stylesheet { |
454 |
my $self = $_[0]; |
455 |
|
456 |
$self->debug("preprocessing stylesheet..."); |
457 |
|
458 |
$self->__get_first_element; |
459 |
$self->__extract_namespaces; |
460 |
$self->__get_stylesheet; |
461 |
|
462 |
# Why is this here when __get_first_element does, apparently, the same thing? |
463 |
# Because, in __get_stylesheet we warp the document. |
464 |
$self->top_xsl_node($self->xsl_document()->getFirstChild); |
465 |
$self->__expand_xsl_includes; |
466 |
$self->__extract_top_level_variables; |
467 |
|
468 |
$self->__add_default_templates; |
469 |
$self->__cache_templates; # speed optim |
470 |
|
471 |
$self->__set_xsl_output; |
472 |
} |
473 |
|
474 |
sub top_xsl_node |
475 |
{ |
476 |
my ( $self, $top_xsl_node) = @_; |
477 |
|
478 |
if ( defined $top_xsl_node ) |
479 |
{ |
480 |
$self->{TOP_XSL_NODE} = $top_xsl_node; |
481 |
} |
482 |
|
483 |
return $self->{TOP_XSL_NODE}; |
484 |
} |
485 |
|
486 |
# private auxiliary function # |
487 |
|
488 |
sub __get_stylesheet { |
489 |
my $self = shift; |
490 |
my $stylesheet; |
491 |
my $xsl_ns = $self->xsl_ns(); |
492 |
my $xsl = $self->xsl_document(); |
493 |
|
494 |
foreach my $child ($xsl->getElementsByTagName ('*', 0)) |
495 |
{ |
496 |
my ($ns, $tag) = split(':', $child->getTagName()); |
497 |
if(not defined $tag) |
498 |
{ |
499 |
$tag = $ns; |
500 |
$ns = $self->default_ns(); |
501 |
} |
502 |
if ($tag eq 'stylesheet' || $tag eq 'transform') |
503 |
{ |
504 |
if ( my $attributes = $child->getAttributes()) |
505 |
{ |
506 |
my $version = $attributes->getNamedItem('version'); |
507 |
|
508 |
$self->xslt_version($version->getNodeValue()) if $version; |
509 |
} |
510 |
|
511 |
$stylesheet = $child; |
512 |
last; |
513 |
} |
514 |
} |
515 |
|
516 |
if (! $stylesheet) { |
517 |
# stylesheet is actually one complete template! |
518 |
# put it in a template-element |
519 |
|
520 |
$stylesheet = $xsl->createElement ("$ {xsl_ns}stylesheet"); |
521 |
my $template = $xsl->createElement ("$ {xsl_ns}template"); |
522 |
$template->setAttribute ('match', "/"); |
523 |
|
524 |
my $template_content = $xsl->getElementsByTagName ('*', 0)->item (0); |
525 |
$xsl->replaceChild ($stylesheet, $template_content); |
526 |
$stylesheet->appendChild ($template); |
527 |
$template->appendChild ($template_content); |
528 |
} |
529 |
|
530 |
$self->xsl_document($stylesheet); |
531 |
} |
532 |
|
533 |
sub xslt_version |
534 |
{ |
535 |
my ( $self, $xslt_version ) = @_; |
536 |
|
537 |
if ( defined $xslt_version ) |
538 |
{ |
539 |
$self->{XSLT_VERSION} = $xslt_version; |
540 |
} |
541 |
|
542 |
return $self->{XSLT_VERSION} ||= '1.0'; |
543 |
} |
544 |
|
545 |
# private auxiliary function # |
546 |
sub __get_first_element { |
547 |
my ($self) = @_; |
548 |
my $node = $self->xsl_document()->getFirstChild(); |
549 |
|
550 |
$node = $node->getNextSibling |
551 |
until ref $node eq 'XML::DOM::Element'; |
552 |
$self->top_xsl_node($node); |
553 |
} |
554 |
|
555 |
# private auxiliary function # |
556 |
sub __extract_namespaces { |
557 |
my ($self) = @_; |
558 |
|
559 |
my $attr = $self->top_xsl_node()->getAttributes; |
560 |
if(defined $attr) { |
561 |
foreach my $attribute ($self->top_xsl_node()->getAttributes->getValues) { |
562 |
my ($pre, $post) = split(":", $attribute->getName, 2); |
563 |
my $value = $attribute->getValue; |
564 |
|
565 |
# Take care of namespaces |
566 |
if ($pre eq 'xmlns' and not defined $post) { |
567 |
$self->default_ns(''); |
568 |
|
569 |
$self->{NAMESPACE}->{$self->default_ns()}->{namespace} = $value; |
570 |
$self->xsl_ns('') |
571 |
if $value eq NS_XSLT; |
572 |
$self->debug("Namespace `" . $self->default_ns() . "' = `$value'"); |
573 |
} elsif ($pre eq 'xmlns') { |
574 |
$self->{NAMESPACE}->{$post}->{namespace} = $value; |
575 |
$self->xsl_ns("$post:") |
576 |
if $value eq NS_XSLT; |
577 |
$self->debug("Namespace `$post:' = `$value'"); |
578 |
} else { |
579 |
$self->default_ns(''); |
580 |
} |
581 |
|
582 |
# Take care of versions |
583 |
if ($pre eq "version" and not defined $post) { |
584 |
$self->{NAMESPACE}->{$self->default_ns()}->{version} = $value; |
585 |
$self->debug("Version for namespace `" . $self->default_ns() . |
586 |
"' = `$value'"); |
587 |
} elsif ($pre eq "version") { |
588 |
$self->{NAMESPACE}->{$post}->{version} = $value; |
589 |
$self->debug("Version for namespace `$post:' = `$value'"); |
590 |
} |
591 |
} |
592 |
} |
593 |
if (not defined $self->default_ns()) { |
594 |
my ($dns) = split(':', $self->top_xsl_node()->getTagName); |
595 |
$self->default_ns($dns); |
596 |
} |
597 |
$self->debug("Default Namespace: `" . $self->default_ns() . "'"); |
598 |
$self->xsl_ns($self->default_ns()) unless $self->xsl_ns(); |
599 |
|
600 |
$self->debug("XSL Namespace: `" .$self->xsl_ns() ."'"); |
601 |
# ** FIXME: is this right? |
602 |
$self->{NAMESPACE}->{$self->default_ns()}->{namespace} ||= NS_XHTML; |
603 |
} |
604 |
|
605 |
sub default_ns |
606 |
{ |
607 |
my ( $self, $default_ns ) = @_; |
608 |
|
609 |
if ( defined $default_ns ) |
610 |
{ |
611 |
$self->{DEFAULT_NS} = $default_ns; |
612 |
} |
613 |
return exists $self->{DEFAULT_NS} ? $self->{DEFAULT_NS} : undef; |
614 |
} |
615 |
|
616 |
sub xsl_ns |
617 |
{ |
618 |
my ( $self, $prefix ) = @_; |
619 |
|
620 |
if ( defined $prefix ) |
621 |
{ |
622 |
$prefix .= ':' unless $prefix =~ /:$/; |
623 |
$self->{XSL_NS} = $prefix; |
624 |
} |
625 |
return $self->{XSL_NS}; |
626 |
} |
627 |
|
628 |
# private auxiliary function # |
629 |
sub __expand_xsl_includes { |
630 |
my $self = shift; |
631 |
|
632 |
foreach my $include_node |
633 |
($self->top_xsl_node()->getElementsByTagName($self->xsl_ns() . "include")) |
634 |
{ |
635 |
my $include_file = $include_node->getAttribute('href'); |
636 |
|
637 |
die "include tag carries no selection!" |
638 |
unless defined $include_file; |
639 |
|
640 |
my $include_doc; |
641 |
eval { |
642 |
my $tmp_doc = |
643 |
$self->__open_by_filename($include_file, $self->{XSL_BASE}); |
644 |
$include_doc = $tmp_doc->getFirstChild->cloneNode(1); |
645 |
$tmp_doc->dispose; |
646 |
}; |
647 |
die "parsing of $include_file failed: $@" |
648 |
if $@; |
649 |
|
650 |
$self->debug("inserting `$include_file'"); |
651 |
$include_doc->setOwnerDocument($self->xsl_document()); |
652 |
$self->top_xsl_node()->replaceChild($include_doc, $include_node); |
653 |
$include_doc->dispose; |
654 |
} |
655 |
} |
656 |
|
657 |
# private auxiliary function # |
658 |
sub __extract_top_level_variables { |
659 |
my $self = $_[0]; |
660 |
|
661 |
$self->debug("Extracting variables"); |
662 |
foreach my $child ($self->top_xsl_node()->getElementsByTagName ('*',0)) { |
663 |
my ($ns, $tag) = split(':', $child); |
664 |
|
665 |
if(($tag eq '' && $self->xsl_ns() eq '') || |
666 |
$self->xsl_ns() eq $ns) { |
667 |
$tag = $ns if $tag eq ''; |
668 |
|
669 |
if ($tag eq 'variable' || $tag eq 'param') { |
670 |
|
671 |
my $name = $child->getAttribute("name"); |
672 |
if ($name) { |
673 |
my $value = $child->getAttribute("select"); |
674 |
if (!$value) { |
675 |
my $result = $self->xml_document()->createDocumentFragment; |
676 |
$self->_evaluate_template ($child, $self->xml_document(), '', |
677 |
$result); |
678 |
$value = $self->_string ($result); |
679 |
$result->dispose(); |
680 |
} |
681 |
$self->debug("Setting $tag `$name' = `$value'"); |
682 |
$self->{VARIABLES}->{$name} = $value; |
683 |
} else { |
684 |
# Required, so we die (http://www.w3.org/TR/xslt#variables) |
685 |
die "$tag tag carries no name!"; |
686 |
} |
687 |
} |
688 |
} |
689 |
} |
690 |
} |
691 |
|
692 |
# private auxiliary function # |
693 |
sub __add_default_templates { |
694 |
my $self = $_[0]; |
695 |
my $doc = $self->top_xsl_node()->getOwnerDocument; |
696 |
|
697 |
# create template for '*' and '/' |
698 |
my $elem_template = |
699 |
$doc->createElement |
700 |
($self->xsl_ns() . "template"); |
701 |
$elem_template->setAttribute('match','*|/'); |
702 |
|
703 |
# <xsl:apply-templates /> |
704 |
$elem_template->appendChild |
705 |
($doc->createElement |
706 |
($self->xsl_ns() . "apply-templates")); |
707 |
|
708 |
# create template for 'text()' and '@*' |
709 |
my $attr_template = |
710 |
$doc->createElement |
711 |
($self->xsl_ns() . "template"); |
712 |
$attr_template->setAttribute('match','text()|@*'); |
713 |
|
714 |
# <xsl:value-of select="." /> |
715 |
$attr_template->appendChild |
716 |
($doc->createElement |
717 |
($self->xsl_ns() . "value-of")); |
718 |
$attr_template->getFirstChild->setAttribute('select','.'); |
719 |
|
720 |
# create template for 'processing-instruction()' and 'comment()' |
721 |
my $pi_template = |
722 |
$doc->createElement($self->xsl_ns() . "template"); |
723 |
$pi_template->setAttribute('match','processing-instruction()|comment()'); |
724 |
|
725 |
$self->debug("adding default templates to stylesheet"); |
726 |
# add them to the stylesheet |
727 |
$self->xsl_document()->insertBefore($pi_template, |
728 |
$self->top_xsl_node); |
729 |
$self->xsl_document()->insertBefore($attr_template, |
730 |
$self->top_xsl_node()); |
731 |
$self->xsl_document()->insertBefore($elem_template, |
732 |
$self->top_xsl_node()); |
733 |
} |
734 |
|
735 |
|
736 |
sub templates |
737 |
{ |
738 |
my ( $self, $templates ) = @_; |
739 |
|
740 |
if ( defined $templates ) |
741 |
{ |
742 |
$self->{TEMPLATE} = $templates; |
743 |
} |
744 |
|
745 |
unless ( exists $self->{TEMPLATE} ) |
746 |
{ |
747 |
$self->{TEMPLATE} = []; |
748 |
my $xsld = $self->xsl_document(); |
749 |
my $tag = $self->xsl_ns() . 'template'; |
750 |
|
751 |
@{$self->{TEMPLATE}} = $xsld->getElementsByTagName($tag); |
752 |
} |
753 |
|
754 |
return wantarray ? @{$self->{TEMPLATE}} : $self->{TEMPLATE}; |
755 |
} |
756 |
|
757 |
# private auxiliary function # |
758 |
sub __cache_templates { |
759 |
my $self = $_[0]; |
760 |
|
761 |
|
762 |
# pre-cache template names and matches # |
763 |
# reversing the template order is much more efficient # |
764 |
|
765 |
foreach my $template (reverse $self->templates()) { |
766 |
if ($template->getParentNode->getTagName =~ |
767 |
/^([\w\.\-]+\:){0,1}(stylesheet|transform|include)/) { |
768 |
my $match = $template->getAttribute ('match'); |
769 |
my $name = $template->getAttribute ('name'); |
770 |
if ($match && $name) { |
771 |
$self->warn(qq{defining a template with both a "name" and a "match" attribute is not allowed!}); |
772 |
push (@{$self->{TEMPLATE_MATCH}}, ""); |
773 |
push (@{$self->{TEMPLATE_NAME}}, ""); |
774 |
} elsif ($match) { |
775 |
push (@{$self->{TEMPLATE_MATCH}}, $match); |
776 |
push (@{$self->{TEMPLATE_NAME}}, ""); |
777 |
} elsif ($name) { |
778 |
push (@{$self->{TEMPLATE_MATCH}}, ""); |
779 |
push (@{$self->{TEMPLATE_NAME}}, $name); |
780 |
} else { |
781 |
push (@{$self->{TEMPLATE_MATCH}}, ""); |
782 |
push (@{$self->{TEMPLATE_NAME}}, ""); |
783 |
} |
784 |
} |
785 |
} |
786 |
} |
787 |
|
788 |
# private auxiliary function # |
789 |
sub __set_xsl_output { |
790 |
my $self = $_[0]; |
791 |
|
792 |
# default settings |
793 |
$self->{METHOD} = 'xml'; |
794 |
$self->media_type('text/xml'); |
795 |
|
796 |
# extraction of top-level xsl:output tag |
797 |
my ($output) = |
798 |
$self->xsl_document()->getElementsByTagName($self->xsl_ns() . "output",0); |
799 |
|
800 |
if (defined $output) { |
801 |
# extraction and processing of the attributes |
802 |
my $attribs = $output->getAttributes; |
803 |
my $media = $attribs->getNamedItem('media-type'); |
804 |
my $method = $attribs->getNamedItem('method'); |
805 |
$self->media_type($media->getNodeValue) if defined $media; |
806 |
$self->{METHOD} = $method->getNodeValue if defined $method; |
807 |
|
808 |
if (my $omit = $attribs->getNamedItem('omit-xml-declaration')) |
809 |
{ |
810 |
if ($omit->getNodeValue() =~ /^(yes|no)$/) |
811 |
{ |
812 |
$self->omit_xml_declaration($1); |
813 |
} |
814 |
else |
815 |
{ |
816 |
|
817 |
# I would say that this should be fatal |
818 |
# Perhaps there should be a 'strict' option to the constructor |
819 |
|
820 |
my $m = qq{Wrong value for attribute "omit-xml-declaration" in\n\t} . |
821 |
$self->xsl_ns() . qq{output, should be "yes" or "no"}; |
822 |
$self->warn($m); |
823 |
} |
824 |
} |
825 |
|
826 |
unless ( $self->omit_xml_declaration()) |
827 |
{ |
828 |
my $output_ver = $attribs->getNamedItem('version'); |
829 |
my $output_enc = $attribs->getNamedItem('encoding'); |
830 |
$self->output_version($output_ver->getNodeValue) |
831 |
if defined $output_ver; |
832 |
$self->output_encoding($output_enc->getNodeValue) |
833 |
if defined $output_enc; |
834 |
|
835 |
if (not $self->output_version() || not $self->output_encoding()) |
836 |
{ |
837 |
$self->warn(qq{Expected attributes "version" and "encoding" in\n\t} . |
838 |
$self->xsl_ns() . "output"); |
839 |
} |
840 |
} |
841 |
my $doctype_public = $attribs->getNamedItem('doctype-public'); |
842 |
my $doctype_system = $attribs->getNamedItem('doctype-system'); |
843 |
|
844 |
my $dp = defined $doctype_public ? $doctype_public->getNodeValue : ''; |
845 |
|
846 |
$self->doctype_public($dp); |
847 |
|
848 |
my $ds = defined $doctype_system ? $doctype_system->getNodeValue : ''; |
849 |
$self->doctype_system($ds); |
850 |
|
851 |
# cdata-section-elements should only be used if the output type |
852 |
# is XML but as we are not checking that right now ... |
853 |
|
854 |
my $cdata_section = $attribs->getNamedItem('cdata-section-elements'); |
855 |
|
856 |
if ( defined $cdata_section ) |
857 |
{ |
858 |
my $cdata_sections = []; |
859 |
@{$cdata_sections} = split /\s+/, $cdata_section->getNodeValue(); |
860 |
$self->cdata_sections($cdata_sections); |
861 |
} |
862 |
} else { |
863 |
$self->debug("Default Output options being used"); |
864 |
} |
865 |
} |
866 |
|
867 |
sub omit_xml_declaration |
868 |
{ |
869 |
my ( $self, $omit_xml_declaration ) = @_; |
870 |
|
871 |
if ( defined $omit_xml_declaration ) |
872 |
{ |
873 |
if ( $omit_xml_declaration =~ /^(yes|no)$/ ) |
874 |
{ |
875 |
$self->{OMIT_XML_DECL} = ($1 eq 'yes'); |
876 |
} |
877 |
else |
878 |
{ |
879 |
$self->{OMIT_XML_DECL} = $omit_xml_declaration ? 1 : 0; |
880 |
} |
881 |
} |
882 |
|
883 |
return exists $self->{OMIT_XML_DECL} ? $self->{OMIT_XML_DECL} : 0; |
884 |
} |
885 |
|
886 |
sub cdata_sections |
887 |
{ |
888 |
my ( $self, $cdata_sections ) = @_; |
889 |
|
890 |
if ( defined $cdata_sections ) |
891 |
{ |
892 |
$self->{CDATA_SECTIONS} = $cdata_sections; |
893 |
} |
894 |
|
895 |
$self->{CDATA_SECTIONS} = [] unless exists $self->{CDATA_SECTIONS}; |
896 |
|
897 |
return wantarray() ? @{$self->{CDATA_SECTIONS}} : $self->{CDATA_SECTIONS}; |
898 |
} |
899 |
|
900 |
|
901 |
sub is_cdata_section |
902 |
{ |
903 |
my ( $self, $element ) = @_; |
904 |
|
905 |
my %cdata_sections; |
906 |
|
907 |
my @cdata_temp = $self->cdata_sections(); |
908 |
@cdata_sections{@cdata_temp} = (1) x @cdata_temp; |
909 |
|
910 |
my $tagname; |
911 |
|
912 |
if ( defined $element and ref($element) and ref($element) eq 'XML::DOM' ) |
913 |
{ |
914 |
$tagname = $element->getTagName(); |
915 |
} |
916 |
else |
917 |
{ |
918 |
$tagname = $element; |
919 |
} |
920 |
|
921 |
# Will need to do namespace checking on this really |
922 |
|
923 |
return exists $cdata_sections{$tagname} ? 1 : 0; |
924 |
} |
925 |
|
926 |
|
927 |
sub output_version |
928 |
{ |
929 |
my ( $self, $output_version ) = @_; |
930 |
|
931 |
if ( defined $output_version ) |
932 |
{ |
933 |
$self->{OUTPUT_VERSION} = $output_version; |
934 |
} |
935 |
|
936 |
return exists $self->{OUTPUT_VERSION} ? $self->{OUTPUT_VERSION} : |
937 |
$self->default_xml_version(); |
938 |
} |
939 |
|
940 |
sub __get_attribute_sets |
941 |
{ |
942 |
my ( $self ) = @_; |
943 |
|
944 |
my $doc = $self->xsl_document(); |
945 |
my $nsp = $self->xsl_ns(); |
946 |
my $tagname = $nsp . 'attribute-set'; |
947 |
foreach my $attribute_set ( $doc->getElementsByTagName($tagname,0)) |
948 |
{ |
949 |
my $attribs = $attribute_set->getAttributes(); |
950 |
next unless defined $attribs; |
951 |
my $name_attr = $attribs->getNamedItem('name'); |
952 |
next unless defined $name_attr; |
953 |
my $name = $name_attr->getValue(); |
954 |
$self->debug("processing attribute-set $name"); |
955 |
|
956 |
my $attr_set = {}; |
957 |
|
958 |
my $tagname = $nsp . 'attribute'; |
959 |
|
960 |
foreach my $attribute ( $attribute_set->getElementsByTagName($tagname,0)) |
961 |
{ |
962 |
my $attribs = $attribute->getAttributes(); |
963 |
next unless defined $attribs; |
964 |
my $name_attr = $attribs->getNamedItem('name'); |
965 |
next unless defined $name_attr; |
966 |
my $attr_name = $name_attr->getValue(); |
967 |
$self->debug("Processing attribute $attr_name"); |
968 |
if ( $attr_name ) |
969 |
{ |
970 |
my $result = $self->xml_document()->createDocumentFragment(); |
971 |
$self->_evaluate_template($attribute, |
972 |
$self->xml_document(), |
973 |
'/', |
974 |
$result); # might need variables |
975 |
my $value = $self->fix_attribute_value($self->__string__($result)); |
976 |
$attr_set->{$attr_name} = $value; |
977 |
$result->dispose(); |
978 |
$self->debug("Adding attribute $attr_name with value $value"); |
979 |
} |
980 |
} |
981 |
|
982 |
$self->__attribute_set_($name,$attr_set); |
983 |
} |
984 |
} |
985 |
|
986 |
# Accessor for attribute sets |
987 |
|
988 |
sub __attribute_set_ |
989 |
{ |
990 |
my ($self,$name,$attr_hash) = @_; |
991 |
|
992 |
if ( defined $attr_hash && defined $name) |
993 |
{ |
994 |
$self->{ATTRIBUTE_SETS}->{$name} = $attr_hash; |
995 |
} |
996 |
|
997 |
return defined $name && exists $self->{ATTRIBUTE_SETS}->{$name} ? |
998 |
$self->{ATTRIBUTE_SETS}->{$name} : undef; |
999 |
} |
1000 |
|
1001 |
sub open_project { |
1002 |
my $self = shift; |
1003 |
my $xml = shift; |
1004 |
my $xsl = shift; |
1005 |
my ($xmlflag, $xslflag, %args) = @_; |
1006 |
|
1007 |
carp "open_project is deprecated." |
1008 |
unless $self->use_deprecated() |
1009 |
or exists $deprecation_used{open_project}; |
1010 |
$deprecation_used{open_project} = 1; |
1011 |
|
1012 |
$self->debug("opening project:"); |
1013 |
$self->_indent(); |
1014 |
|
1015 |
$self->open_xml ($xml, %args); |
1016 |
$self->open_xsl ($xsl, %args); |
1017 |
|
1018 |
$self->debug("done..."); |
1019 |
$self->_outdent(); |
1020 |
} |
1021 |
|
1022 |
sub transform { |
1023 |
my $self = shift; |
1024 |
my %topvariables = $self->__parse_args(@_); |
1025 |
|
1026 |
$self->debug("transforming document:"); |
1027 |
$self->_indent(); |
1028 |
|
1029 |
$self->open_xml (%topvariables); |
1030 |
|
1031 |
|
1032 |
$self->debug("done..."); |
1033 |
$self->_outdent(); |
1034 |
|
1035 |
# The _get_attribute_set needs an open XML document |
1036 |
|
1037 |
$self->_indent(); |
1038 |
$self->__get_attribute_sets(); |
1039 |
$self->_outdent(); |
1040 |
|
1041 |
$self->debug("processing project:"); |
1042 |
$self->_indent(); |
1043 |
|
1044 |
$self->process(%topvariables); |
1045 |
|
1046 |
$self->debug("done!"); |
1047 |
$self->_outdent(); |
1048 |
$self->result_document()->normalize(); |
1049 |
return $self->result_document(); |
1050 |
} |
1051 |
|
1052 |
sub process { |
1053 |
my ($self, %topvariables) = @_; |
1054 |
|
1055 |
$self->debug("processing project:"); |
1056 |
$self->_indent(); |
1057 |
|
1058 |
my $root_template = $self->_match_template ("match", '/', 1, ''); |
1059 |
|
1060 |
%topvariables = (%topvariables, |
1061 |
defined $self->{VARIABLES} && ref $self->{VARIABLES} && |
1062 |
ref $self->{VARIABLES} eq 'ARRAY' ? |
1063 |
@{$self->{VARIABLES}} : ()); |
1064 |
|
1065 |
$self->_evaluate_template ( |
1066 |
$root_template, # starting template: the root template |
1067 |
$self->xml_document(), |
1068 |
'', # current XML selection path: the root |
1069 |
$self->result_document(), # current result tree node: the root |
1070 |
{()}, # current known variables: none |
1071 |
\%topvariables # previously known variables: top level variables |
1072 |
); |
1073 |
|
1074 |
$self->debug("done!"); |
1075 |
$self->_outdent(); |
1076 |
} |
1077 |
|
1078 |
# Handles deprecations. |
1079 |
sub AUTOLOAD { |
1080 |
my $self = shift; |
1081 |
my $type = ref($self) || croak "Not a method call"; |
1082 |
my $name = $AUTOLOAD; |
1083 |
$name =~ s/.*://; |
1084 |
|
1085 |
my %deprecation = ('output_string' => 'toString', |
1086 |
'result_string' => 'toString', |
1087 |
'output' => 'toString', |
1088 |
'result' => 'toString', |
1089 |
'result_mime_type' => 'media_type', |
1090 |
'output_mime_type' => 'media_type', |
1091 |
'result_tree' => 'to_dom', |
1092 |
'output_tree' => 'to_dom', |
1093 |
'transform_document' => 'transform', |
1094 |
'process_project' => 'process' |
1095 |
); |
1096 |
|
1097 |
if (exists $deprecation{$name}) { |
1098 |
carp "$name is deprecated. Use $deprecation{$name}" |
1099 |
unless $self->use_deprecated() |
1100 |
or exists $deprecation_used{$name}; |
1101 |
$deprecation_used{$name} = 1; |
1102 |
eval qq{return \$self->$deprecation{$name}(\@_)}; |
1103 |
} else { |
1104 |
croak "$name: No such method name"; |
1105 |
} |
1106 |
} |
1107 |
|
1108 |
sub _my_print_text { |
1109 |
my ($self, $FILE) = @_; |
1110 |
|
1111 |
if (UNIVERSAL::isa($self, "XML::DOM::CDATASection")) { |
1112 |
$FILE->print ($self->getData()); |
1113 |
} else { |
1114 |
$FILE->print (XML::DOM::encodeText($self->getData(), "<&")); |
1115 |
} |
1116 |
} |
1117 |
|
1118 |
sub toString { |
1119 |
my $self = $_[0]; |
1120 |
|
1121 |
local *XML::DOM::Text::print = \&_my_print_text; |
1122 |
|
1123 |
my $string = $self->result_document()->toString(); |
1124 |
|
1125 |
return $string; |
1126 |
} |
1127 |
|
1128 |
sub to_dom { |
1129 |
my ($self) = @_; |
1130 |
|
1131 |
return $self->result_document(); |
1132 |
} |
1133 |
|
1134 |
sub media_type { |
1135 |
my ( $self, $media_type ) = @_; |
1136 |
|
1137 |
if ( defined $media_type ) |
1138 |
{ |
1139 |
$self->{MEDIA_TYPE} = $media_type; |
1140 |
} |
1141 |
|
1142 |
return $self->{MEDIA_TYPE}; |
1143 |
} |
1144 |
|
1145 |
sub print_output { |
1146 |
my ($self, $file, $mime) = @_; |
1147 |
$file ||= ''; # print to STDOUT by default |
1148 |
$mime = 1 unless defined $mime; |
1149 |
|
1150 |
# print mime-type header etc by default |
1151 |
|
1152 |
# $self->{RESULT_DOCUMENT}->printToFileHandle (\*STDOUT); |
1153 |
# or $self->{RESULT_DOCUMENT}->print (\*STDOUT); ??? |
1154 |
# exit; |
1155 |
|
1156 |
carp "print_output is deprecated. Use serve." |
1157 |
unless $self->use_deprecated() |
1158 |
or exists $deprecation_used{print_output}; |
1159 |
$deprecation_used{print_output} = 1; |
1160 |
|
1161 |
if ($mime) { |
1162 |
print "Content-type: " . $self->media_type() . "\n\n"; |
1163 |
|
1164 |
if ($self->{METHOD} eq 'xml' || $self->{METHOD} eq 'html') { |
1165 |
unless ($self->omit_xml_declaration()) |
1166 |
{ |
1167 |
print $self->xml_declaration(),"\n"; |
1168 |
} |
1169 |
} |
1170 |
|
1171 |
if ( my $doctype = $self->doctype() ) |
1172 |
{ |
1173 |
print "$doctype\n"; |
1174 |
} |
1175 |
} |
1176 |
|
1177 |
if ($file) { |
1178 |
if (ref (\$file) eq 'SCALAR') { |
1179 |
print $file $self->output_string,"\n" |
1180 |
} else { |
1181 |
if (open (FILE, ">$file")) { |
1182 |
print FILE $self->output_string,"\n"; |
1183 |
if (! close (FILE)) { |
1184 |
die ("Error writing $file: $!. Nothing written...\n"); |
1185 |
} |
1186 |
} else { |
1187 |
die ("Error opening $file: $!. Nothing done...\n"); |
1188 |
} |
1189 |
} |
1190 |
} else { |
1191 |
print $self->output_string,"\n"; |
1192 |
} |
1193 |
} |
1194 |
|
1195 |
*print_result = *print_output; |
1196 |
|
1197 |
sub doctype |
1198 |
{ |
1199 |
my ( $self ) = @_; |
1200 |
|
1201 |
my $doctype = ""; |
1202 |
|
1203 |
if ($self->doctype_public() || $self->doctype_system()) |
1204 |
{ |
1205 |
my $root_name = $self->result_document() |
1206 |
->getElementsByTagName('*',0)->item(0)->getTagName; |
1207 |
|
1208 |
if ($self->doctype_public()) |
1209 |
{ |
1210 |
$doctype = qq{<!DOCTYPE $root_name PUBLIC "} . |
1211 |
$self->doctype_public() . |
1212 |
qq{" "} . $self->doctype_system() . qq{">}; |
1213 |
} |
1214 |
else |
1215 |
{ |
1216 |
$doctype = qq{<!DOCTYPE $root_name SYSTEM "} . |
1217 |
$self->doctype_system() |
1218 |
. qq{">}; |
1219 |
} |
1220 |
} |
1221 |
|
1222 |
$self->debug("returning doctype of $doctype"); |
1223 |
return $doctype; |
1224 |
} |
1225 |
|
1226 |
sub dispose { |
1227 |
#my $self = $_[0]; |
1228 |
|
1229 |
#$_[0]->[PARSER] = undef if (defined $_[0]->[PARSER]); |
1230 |
$_[0]->result_document()->dispose if (defined $_[0]->result_document()); |
1231 |
|
1232 |
# only dispose xml and xsl when they were not passed as DOM |
1233 |
if (not defined $_[0]->{XML_PASSED_AS_DOM} && defined $_-[0]->xml_document()) { |
1234 |
$_[0]->xml_document()->dispose; |
1235 |
} |
1236 |
if (not defined $_[0]->{XSL_PASSED_AS_DOM} && defined $_-[0]->xsl_document()) { |
1237 |
$_[0]->xsl_document()->dispose; |
1238 |
} |
1239 |
|
1240 |
$_[0] = undef; |
1241 |
} |
1242 |
|
1243 |
|
1244 |
###################################################################### |
1245 |
# PRIVATE DEFINITIONS |
1246 |
|
1247 |
sub __open_document { |
1248 |
my $self = shift; |
1249 |
my %args = @_; |
1250 |
%args = (%{$self->{PARSER_ARGS}}, %args); |
1251 |
my $doc; |
1252 |
|
1253 |
$self->debug("opening document"); |
1254 |
|
1255 |
eval |
1256 |
{ |
1257 |
my $ref = ref($args{Source}); |
1258 |
if(!$ref && length $args{Source} < 255 && |
1259 |
(-f $args{Source} || |
1260 |
lc(substr($args{Source}, 0, 5)) eq 'http:' || |
1261 |
lc(substr($args{Source}, 0, 6)) eq 'https:' || |
1262 |
lc(substr($args{Source}, 0, 4)) eq 'ftp:' || |
1263 |
lc(substr($args{Source}, 0, 5)) eq 'file:')) { |
1264 |
# Filename |
1265 |
$self->debug("Opening URL"); |
1266 |
$doc = $self->__open_by_filename($args{Source}, $args{base}); |
1267 |
} elsif(!$ref) { |
1268 |
# String |
1269 |
$self->debug("Opening String"); |
1270 |
$doc = $self->{PARSER}->parse ($args{Source}); |
1271 |
} elsif($ref eq "SCALAR") { |
1272 |
# Stringref |
1273 |
$self->debug("Opening Stringref"); |
1274 |
$doc = $self->{PARSER}->parse (${$args{Source}}); |
1275 |
} elsif($ref eq "XML::DOM::Document") { |
1276 |
# DOM object |
1277 |
$self->debug("Opening XML::DOM"); |
1278 |
$doc = $args{Source}; |
1279 |
} elsif ($ref eq "GLOB") { # This is a file glob |
1280 |
$self->debug("Opening GLOB"); |
1281 |
my $ioref = *{$args{Source}}{IO}; |
1282 |
$doc = $self->{PARSER}->parse($ioref); |
1283 |
} elsif (UNIVERSAL::isa($args{Source}, 'IO::Handle')) { # IO::Handle |
1284 |
$self->debug("Opening IO::Handle"); |
1285 |
$doc = $self->{PARSER}->parse($args{Source}); |
1286 |
} |
1287 |
else { |
1288 |
$doc = undef; |
1289 |
} |
1290 |
}; |
1291 |
die "Error while parsing: $@\n". $args{Source} if $@; |
1292 |
return $doc; |
1293 |
} |
1294 |
|
1295 |
# private auxiliary function # |
1296 |
sub __open_by_filename { |
1297 |
my ($self, $filename, $base) = @_; |
1298 |
my $doc; |
1299 |
|
1300 |
# ** FIXME: currently reads the whole document into memory |
1301 |
# might not be avoidable |
1302 |
|
1303 |
# LWP should be able to deal with files as well as links |
1304 |
$ENV{DOMAIN} ||= "example.com"; # hide complaints from Net::Domain |
1305 |
|
1306 |
my $file = get(URI->new_abs($filename, $base)); |
1307 |
|
1308 |
return $self->{PARSER}->parse($file, %{$self->{PARSER_ARGS}}); |
1309 |
} |
1310 |
|
1311 |
sub _match_template { |
1312 |
my ($self, $attribute_name, $select_value, $xml_count, $xml_selection_path, |
1313 |
$mode) = @_; |
1314 |
$mode ||= ""; |
1315 |
|
1316 |
my $template = ""; |
1317 |
my @template_matches = (); |
1318 |
|
1319 |
$self->debug(qq{matching template for "$select_value" with count $xml_count\n\t} . |
1320 |
qq{and path "$xml_selection_path":}); |
1321 |
|
1322 |
if ($attribute_name eq "match" && ref $self->{TEMPLATE_MATCH}) { |
1323 |
push @template_matches, @{$self->{TEMPLATE_MATCH}}; |
1324 |
} elsif ($attribute_name eq "name" && ref $self->{TEMPLATE_NAME}) { |
1325 |
push @template_matches, @{$self->{TEMPLATE_NAME}}; |
1326 |
} |
1327 |
|
1328 |
# note that the order of @template_matches is the reverse of $self->{TEMPLATE} |
1329 |
my $count = @template_matches; |
1330 |
foreach my $original_match (@template_matches) { |
1331 |
# templates with no match or name or with both simultaniuously |
1332 |
# have no $template_match value |
1333 |
if ($original_match) { |
1334 |
my $full_match = $original_match; |
1335 |
|
1336 |
# multipe match? (for example: match="*|/") |
1337 |
while ($full_match =~ s/^(.+?)\|//) { |
1338 |
my $match = $1; |
1339 |
if (&__template_matches__ ($match, $select_value, $xml_count, |
1340 |
$xml_selection_path)) { |
1341 |
$self->debug(qq{ found #$count with "$match" in "$original_match"}); |
1342 |
|
1343 |
$template = ($self->templates())[$count-1]; |
1344 |
return $template; |
1345 |
# last; |
1346 |
} |
1347 |
} |
1348 |
|
1349 |
# last match? |
1350 |
if (!$template) { |
1351 |
if (&__template_matches__ ($full_match, $select_value, $xml_count, |
1352 |
$xml_selection_path)) { |
1353 |
$self->debug(qq{ found #$count with "$full_match" in "$original_match"}); |
1354 |
$template = ($self->templates())[$count-1]; |
1355 |
return $template; |
1356 |
# last; |
1357 |
} else { |
1358 |
$self->debug(qq{ #$count "$original_match" did not match}); |
1359 |
} |
1360 |
} |
1361 |
} |
1362 |
$count--; |
1363 |
} |
1364 |
|
1365 |
if (! $template) { |
1366 |
$self->warn(qq{No template matching `$xml_selection_path' found !!}); |
1367 |
} |
1368 |
|
1369 |
return $template; |
1370 |
} |
1371 |
|
1372 |
# auxiliary function # |
1373 |
sub __template_matches__ { |
1374 |
my ($template, $select, $count, $path) = @_; |
1375 |
|
1376 |
my $nocount_path = $path; |
1377 |
$nocount_path =~ s/\[.*?\]//g; |
1378 |
|
1379 |
if (($template eq $select) || ($template eq $path) |
1380 |
|| ($template eq "$select\[$count\]") || ($template eq "$path\[$count\]")) { |
1381 |
# perfect match or path ends with templates match |
1382 |
#print "perfect match","\n"; |
1383 |
return "True"; |
1384 |
} elsif ( ($template eq substr ($path, - length ($template))) |
1385 |
|| ($template eq substr ($nocount_path, - length ($template))) |
1386 |
|| ("$template\[$count\]" eq substr ($path, - length ($template))) |
1387 |
|| ("$template\[$count\]" eq substr ($nocount_path, - length ($template))) |
1388 |
) { |
1389 |
# template matches tail of path matches perfectly |
1390 |
#print "perfect tail match","\n"; |
1391 |
return "True"; |
1392 |
} elsif ($select =~ /\[\s*(\@.*?)\s*=\s*(.*?)\s*\]$/) { |
1393 |
# match attribute test |
1394 |
my $attribute = $1; |
1395 |
my $value = $2; |
1396 |
return ""; # False, no test evaluation yet # |
1397 |
} elsif ($select =~ /\[\s*(.*?)\s*=\s*(.*?)\s*\]$/) { |
1398 |
# match test |
1399 |
my $element = $1; |
1400 |
my $value = $2; |
1401 |
return ""; # False, no test evaluation yet # |
1402 |
} elsif ($select =~ /(\@\*|\@[\w\.\-\:]+)$/) { |
1403 |
# match attribute |
1404 |
my $attribute = $1; |
1405 |
#print "attribute match?\n"; |
1406 |
return (($template eq '@*') || ($template eq $attribute) |
1407 |
|| ($template eq "\@*\[$count\]") || ($template eq "$attribute\[$count\]")); |
1408 |
} elsif ($select =~ /(\*|[\w\.\-\:]+)$/) { |
1409 |
# match element |
1410 |
my $element = $1; |
1411 |
#print "element match?\n"; |
1412 |
return (($template eq "*") || ($template eq $element) |
1413 |
|| ($template eq "*\[$count\]") || ($template eq "$element\[$count\]")); |
1414 |
} else { |
1415 |
return ""; # False # |
1416 |
} |
1417 |
} |
1418 |
|
1419 |
sub _evaluate_test { |
1420 |
my ($self, $test, $current_xml_node, $current_xml_selection_path, |
1421 |
$variables) = @_; |
1422 |
|
1423 |
if ($test =~ /^(.+)\/\[(.+)\]$/) { |
1424 |
my $path = $1; |
1425 |
$test = $2; |
1426 |
|
1427 |
$self->debug("evaluating test $test at path $path:");; |
1428 |
|
1429 |
$self->_indent(); |
1430 |
my $node = $self->_get_node_set ($path, $self->xml_document(), |
1431 |
$current_xml_selection_path, |
1432 |
$current_xml_node, $variables); |
1433 |
if (@$node) { |
1434 |
$current_xml_node = $$node[0]; |
1435 |
} else { |
1436 |
return ""; |
1437 |
} |
1438 |
$self->_outdent(); |
1439 |
} else { |
1440 |
$self->debug("evaluating path or test $test:");; |
1441 |
my $node = $self->_get_node_set ($test, $self->xml_document(), |
1442 |
$current_xml_selection_path, |
1443 |
$current_xml_node, $variables, "silent"); |
1444 |
$self->_indent(); |
1445 |
if (@$node) { |
1446 |
$self->debug("path exists!");; |
1447 |
return "true"; |
1448 |
} else { |
1449 |
$self->debug("not a valid path, evaluating as test");; |
1450 |
} |
1451 |
$self->_outdent(); |
1452 |
} |
1453 |
|
1454 |
$self->_indent(); |
1455 |
my $result = &__evaluate_test__ ($self,$test, $current_xml_selection_path,$current_xml_node,$variables); |
1456 |
if ($result) { |
1457 |
$self->debug("test evaluates true.."); |
1458 |
} else { |
1459 |
$self->debug("test evaluates false.."); |
1460 |
} |
1461 |
$self->_outdent(); |
1462 |
return $result; |
1463 |
} |
1464 |
|
1465 |
sub _evaluate_template { |
1466 |
my ($self, $template, $current_xml_node, $current_xml_selection_path, |
1467 |
$current_result_node, $variables, $oldvariables) = @_; |
1468 |
|
1469 |
$self->debug(qq{evaluating template content with current path } |
1470 |
. qq{"$current_xml_selection_path": }); |
1471 |
$self->_indent(); |
1472 |
|
1473 |
die "No Template" |
1474 |
unless defined $template && ref $template; |
1475 |
$template->normalize; |
1476 |
|
1477 |
foreach my $child ($template->getChildNodes) { |
1478 |
my $ref = ref $child; |
1479 |
|
1480 |
$self->debug("$ref"); |
1481 |
$self->_indent(); |
1482 |
my $node_type = $child->getNodeType; |
1483 |
if ($node_type == ELEMENT_NODE) { |
1484 |
$self->_evaluate_element ($child, $current_xml_node, |
1485 |
$current_xml_selection_path, |
1486 |
$current_result_node, $variables, |
1487 |
$oldvariables); |
1488 |
} elsif ($node_type == TEXT_NODE) { |
1489 |
my $value = $child->getNodeValue; |
1490 |
if ( length($value) and $value !~ /^[\x20\x09\x0D\x0A]+$/s ) |
1491 |
{ |
1492 |
$self->_add_node ($child, $current_result_node); |
1493 |
} |
1494 |
} elsif ($node_type == CDATA_SECTION_NODE) { |
1495 |
my $text = $self->xml_document()->createTextNode ($child->getData); |
1496 |
$self->_add_node($text, $current_result_node); |
1497 |
} elsif ($node_type == ENTITY_REFERENCE_NODE) { |
1498 |
$self->_add_node($child, $current_result_node); |
1499 |
} elsif ($node_type == DOCUMENT_TYPE_NODE) { |
1500 |
# skip # |
1501 |
$self->debug("Skipping Document Type node..."); |
1502 |
} elsif ($node_type == COMMENT_NODE) { |
1503 |
# skip # |
1504 |
$self->debug("Skipping Comment node..."); |
1505 |
} else { |
1506 |
$self->warn("evaluate-template: Dunno what to do with node of type $ref !!!\n\t" . |
1507 |
"($current_xml_selection_path)"); |
1508 |
} |
1509 |
|
1510 |
$self->_outdent(); |
1511 |
} |
1512 |
|
1513 |
$self->debug("done!"); |
1514 |
$self->_outdent(); |
1515 |
} |
1516 |
|
1517 |
sub _add_node { |
1518 |
my ($self, $node, $parent, $deep, $owner) = @_; |
1519 |
$owner ||= $self->xml_document(); |
1520 |
|
1521 |
my $what = defined $deep ? 'deep' : 'non-deep'; |
1522 |
|
1523 |
$self->debug("adding node ($what).."); |
1524 |
|
1525 |
$node = $node->cloneNode($deep); |
1526 |
$node->setOwnerDocument($owner); |
1527 |
if ($node->getNodeType == ATTRIBUTE_NODE) { |
1528 |
$parent->setAttributeNode($node); |
1529 |
} else { |
1530 |
$parent->appendChild($node); |
1531 |
} |
1532 |
} |
1533 |
|
1534 |
sub _apply_templates { |
1535 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
1536 |
$current_result_node, $variables, $oldvariables) = @_; |
1537 |
my $children; |
1538 |
my $params = {}; |
1539 |
my $newvariables = defined $variables ? {%$variables}: {}; |
1540 |
|
1541 |
my $select = $xsl_node->getAttribute ('select'); |
1542 |
|
1543 |
if ($select =~ /\$/ and defined $variables) { |
1544 |
# replacing occurences of variables: |
1545 |
foreach my $varname (keys (%$variables)) { |
1546 |
$select =~ s/[^\\]\$$varname/$$variables{$varname}/g; |
1547 |
} |
1548 |
} |
1549 |
|
1550 |
if ($select) { |
1551 |
$self->debug(qq{applying templates on children $select of "$current_xml_selection_path":}); |
1552 |
$children = $self->_get_node_set ($select, $self->xml_document(), |
1553 |
$current_xml_selection_path, |
1554 |
$current_xml_node, $variables); |
1555 |
} else { |
1556 |
$self->debug(qq{applying templates on all children of "$current_xml_selection_path":}); |
1557 |
$children = [ $current_xml_node->getChildNodes ]; |
1558 |
} |
1559 |
|
1560 |
$self->_process_with_params ($xsl_node, $current_xml_node, |
1561 |
$current_xml_selection_path, |
1562 |
$variables, $params); |
1563 |
|
1564 |
# process xsl:sort here |
1565 |
|
1566 |
$self->_indent(); |
1567 |
|
1568 |
my $count = 1; |
1569 |
foreach my $child (@$children) { |
1570 |
my $node_type = $child->getNodeType; |
1571 |
|
1572 |
if ($node_type == DOCUMENT_TYPE_NODE) { |
1573 |
# skip # |
1574 |
$self->debug("Skipping Document Type node..."); |
1575 |
} elsif ($node_type == DOCUMENT_FRAGMENT_NODE) { |
1576 |
# skip # |
1577 |
$self->debug("Skipping Document Fragment node..."); |
1578 |
} elsif ($node_type == NOTATION_NODE) { |
1579 |
# skip # |
1580 |
$self->debug("Skipping Notation node..."); |
1581 |
} else { |
1582 |
|
1583 |
my $newselect = ""; |
1584 |
my $newcount = $count; |
1585 |
if (!$select || ($select eq '.')) { |
1586 |
if ($node_type == ELEMENT_NODE) { |
1587 |
$newselect = $child->getTagName; |
1588 |
} elsif ($node_type == ATTRIBUTE_NODE) { |
1589 |
$newselect = "@$child->getName"; |
1590 |
} elsif (($node_type == TEXT_NODE) || ($node_type == ENTITY_REFERENCE_NODE)) { |
1591 |
$newselect = "text()"; |
1592 |
} elsif ($node_type == PROCESSING_INSTRUCTION_NODE) { |
1593 |
$newselect = "processing-instruction()"; |
1594 |
} elsif ($node_type == COMMENT_NODE) { |
1595 |
$newselect = "comment()"; |
1596 |
} else { |
1597 |
my $ref = ref $child; |
1598 |
$self->debug("Unknown node encountered: `$ref'"); |
1599 |
} |
1600 |
} else { |
1601 |
$newselect = $select; |
1602 |
if ($newselect =~ s/\[(\d+)\]$//) { |
1603 |
$newcount = $1; |
1604 |
} |
1605 |
} |
1606 |
|
1607 |
$self->_select_template ($child, $newselect, $newcount, |
1608 |
$current_xml_node, |
1609 |
$current_xml_selection_path, |
1610 |
$current_result_node, $newvariables, $params); |
1611 |
} |
1612 |
$count++; |
1613 |
} |
1614 |
|
1615 |
$self->_indent(); |
1616 |
} |
1617 |
|
1618 |
sub _for_each { |
1619 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
1620 |
$current_result_node, $variables, $oldvariables) = @_; |
1621 |
|
1622 |
my $select = $xsl_node->getAttribute ('select') || die "No `select' attribute in for-each element"; |
1623 |
|
1624 |
if ($select =~ /\$/) { |
1625 |
# replacing occurences of variables: |
1626 |
foreach my $varname (keys (%$variables)) { |
1627 |
$select =~ s/[^\\]\$$varname/$$variables{$varname}/g; |
1628 |
} |
1629 |
} |
1630 |
|
1631 |
if (defined $select) { |
1632 |
$self->debug(qq{applying template for each child $select of "$current_xml_selection_path":}); |
1633 |
my $children = $self->_get_node_set ($select, $self->xml_document(), |
1634 |
$current_xml_selection_path, |
1635 |
$current_xml_node, $variables); |
1636 |
$self->_indent(); |
1637 |
my $count = 1; |
1638 |
foreach my $child (@$children) { |
1639 |
my $node_type = $child->getNodeType; |
1640 |
|
1641 |
if ($node_type == DOCUMENT_TYPE_NODE) { |
1642 |
# skip # |
1643 |
$self->debug("Skipping Document Type node...");; |
1644 |
} elsif ($node_type == DOCUMENT_FRAGMENT_NODE) { |
1645 |
# skip # |
1646 |
$self->debug("Skipping Document Fragment node...");; |
1647 |
} elsif ($node_type == NOTATION_NODE) { |
1648 |
# skip # |
1649 |
$self->debug("Skipping Notation node...");; |
1650 |
} else { |
1651 |
|
1652 |
$self->_evaluate_template ($xsl_node, $child, |
1653 |
"$current_xml_selection_path/$select\[$count\]", |
1654 |
$current_result_node, $variables, $oldvariables); |
1655 |
} |
1656 |
$count++; |
1657 |
} |
1658 |
|
1659 |
$self->_outdent(); |
1660 |
} else { |
1661 |
my $ns = $self->xsl_ns(); |
1662 |
$self->warn(qq%expected attribute "select" in <${ns}for-each>%); |
1663 |
} |
1664 |
|
1665 |
} |
1666 |
|
1667 |
sub _select_template { |
1668 |
my ($self, $child, $select, $count, $current_xml_node, $current_xml_selection_path, |
1669 |
$current_result_node, $variables, $oldvariables) = @_; |
1670 |
|
1671 |
my $ref = ref $child; |
1672 |
$self->debug(qq{selecting template $select for child type $ref of "$current_xml_selection_path":}); |
1673 |
|
1674 |
$self->_indent(); |
1675 |
|
1676 |
my $child_xml_selection_path = "$current_xml_selection_path/$select"; |
1677 |
my $template = $self->_match_template ("match", $select, $count, |
1678 |
$child_xml_selection_path); |
1679 |
|
1680 |
if ($template) { |
1681 |
|
1682 |
$self->_evaluate_template ($template, |
1683 |
$child, |
1684 |
"$child_xml_selection_path\[$count\]", |
1685 |
$current_result_node, $variables, $oldvariables); |
1686 |
} else { |
1687 |
$self->debug("skipping template selection...");; |
1688 |
} |
1689 |
|
1690 |
$self->_outdent(); |
1691 |
} |
1692 |
|
1693 |
sub _evaluate_element { |
1694 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
1695 |
$current_result_node, $variables, $oldvariables) = @_; |
1696 |
my ($ns, $xsl_tag) = split(':', $xsl_node->getTagName); |
1697 |
|
1698 |
if(not defined $xsl_tag) { |
1699 |
$xsl_tag = $ns; |
1700 |
$ns = $self->default_ns(); |
1701 |
} else { |
1702 |
$ns .= ':'; |
1703 |
} |
1704 |
$self->debug(qq{evaluating element `$xsl_tag' from `$current_xml_selection_path': }); |
1705 |
$self->_indent(); |
1706 |
|
1707 |
if ($ns eq $self->xsl_ns()) { |
1708 |
my @attributes = $xsl_node->getAttributes->getValues; |
1709 |
$self->debug(qq{This is an xsl tag}); |
1710 |
if ($xsl_tag eq 'apply-templates') { |
1711 |
$self->_apply_templates ($xsl_node, $current_xml_node, |
1712 |
$current_xml_selection_path, |
1713 |
$current_result_node, $variables, $oldvariables); |
1714 |
|
1715 |
} elsif ($xsl_tag eq 'attribute') { |
1716 |
$self->_attribute ($xsl_node, $current_xml_node, |
1717 |
$current_xml_selection_path, |
1718 |
$current_result_node, $variables, $oldvariables); |
1719 |
|
1720 |
} elsif ($xsl_tag eq 'call-template') { |
1721 |
$self->_call_template ($xsl_node, $current_xml_node, |
1722 |
$current_xml_selection_path, |
1723 |
$current_result_node, $variables, $oldvariables); |
1724 |
|
1725 |
} elsif ($xsl_tag eq 'choose') { |
1726 |
$self->_choose ($xsl_node, $current_xml_node, |
1727 |
$current_xml_selection_path, |
1728 |
$current_result_node, $variables, $oldvariables); |
1729 |
|
1730 |
} elsif ($xsl_tag eq 'comment') { |
1731 |
$self->_comment ($xsl_node, $current_xml_node, |
1732 |
$current_xml_selection_path, |
1733 |
$current_result_node, $variables, $oldvariables); |
1734 |
|
1735 |
} elsif ($xsl_tag eq 'copy') { |
1736 |
$self->_copy ($xsl_node, $current_xml_node, |
1737 |
$current_xml_selection_path, |
1738 |
$current_result_node, $variables, $oldvariables); |
1739 |
|
1740 |
} elsif ($xsl_tag eq 'copy-of') { |
1741 |
$self->_copy_of ($xsl_node, $current_xml_node, |
1742 |
$current_xml_selection_path, |
1743 |
$current_result_node, $variables); |
1744 |
} elsif ($xsl_tag eq 'element') { |
1745 |
$self->_element ($xsl_node, $current_xml_node, |
1746 |
$current_xml_selection_path, |
1747 |
$current_result_node, $variables, $oldvariables); |
1748 |
} elsif ($xsl_tag eq 'for-each') { |
1749 |
$self->_for_each ($xsl_node, $current_xml_node, |
1750 |
$current_xml_selection_path, |
1751 |
$current_result_node, $variables, $oldvariables); |
1752 |
|
1753 |
} elsif ($xsl_tag eq 'if') { |
1754 |
$self->_if ($xsl_node, $current_xml_node, |
1755 |
$current_xml_selection_path, |
1756 |
$current_result_node, $variables, $oldvariables); |
1757 |
|
1758 |
# } elsif ($xsl_tag eq 'output') { |
1759 |
|
1760 |
} elsif ($xsl_tag eq 'param') { |
1761 |
$self->_variable ($xsl_node, $current_xml_node, |
1762 |
$current_xml_selection_path, |
1763 |
$current_result_node, $variables, $oldvariables, 1); |
1764 |
|
1765 |
} elsif ($xsl_tag eq 'processing-instruction') { |
1766 |
$self->_processing_instruction ($xsl_node, $current_result_node); |
1767 |
|
1768 |
} elsif ($xsl_tag eq 'text') { |
1769 |
$self->_text ($xsl_node, $current_result_node); |
1770 |
|
1771 |
} elsif ($xsl_tag eq 'value-of') { |
1772 |
$self->_value_of ($xsl_node, $current_xml_node, |
1773 |
$current_xml_selection_path, |
1774 |
$current_result_node, $variables); |
1775 |
|
1776 |
} elsif ($xsl_tag eq 'variable') { |
1777 |
$self->_variable ($xsl_node, $current_xml_node, |
1778 |
$current_xml_selection_path, |
1779 |
$current_result_node, $variables, $oldvariables, 0); |
1780 |
|
1781 |
} elsif ( $xsl_tag eq 'sort' ) { |
1782 |
$self->_sort ($xsl_node, $current_xml_node, |
1783 |
$current_xml_selection_path, |
1784 |
$current_result_node, $variables, $oldvariables, 0); |
1785 |
} elsif ( $xsl_tag eq 'fallback' ) { |
1786 |
$self->_fallback ($xsl_node, $current_xml_node, |
1787 |
$current_xml_selection_path, |
1788 |
$current_result_node, $variables, $oldvariables, 0); |
1789 |
} elsif ( $xsl_tag eq 'attribute-set' ) { |
1790 |
$self->_attribute_set ($xsl_node, $current_xml_node, |
1791 |
$current_xml_selection_path, |
1792 |
$current_result_node, $variables, |
1793 |
$oldvariables, 0); |
1794 |
} else { |
1795 |
$self->_add_and_recurse ($xsl_node, $current_xml_node, |
1796 |
$current_xml_selection_path, |
1797 |
$current_result_node, $variables, $oldvariables); |
1798 |
} |
1799 |
} else { |
1800 |
$self->debug($ns ." does not match ". $self->xsl_ns()); |
1801 |
|
1802 |
# not entirely sure if this right but the spec is a bit vague |
1803 |
|
1804 |
if ( $self->is_cdata_section($xsl_tag) ) |
1805 |
{ |
1806 |
$self->debug("This is a CDATA section element"); |
1807 |
$self->_add_cdata_section($xsl_node, $current_xml_node, |
1808 |
$current_xml_selection_path, |
1809 |
$current_result_node, $variables, |
1810 |
$oldvariables); |
1811 |
} |
1812 |
else |
1813 |
{ |
1814 |
$self->debug("This is a literal element"); |
1815 |
$self->_check_attributes_and_recurse ($xsl_node, $current_xml_node, |
1816 |
$current_xml_selection_path, |
1817 |
$current_result_node, $variables, |
1818 |
$oldvariables); |
1819 |
} |
1820 |
} |
1821 |
|
1822 |
$self->_outdent(); |
1823 |
} |
1824 |
|
1825 |
sub _add_cdata_section |
1826 |
{ |
1827 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
1828 |
$current_result_node, $variables, $oldvariables) = @_; |
1829 |
|
1830 |
my $node = $self->xml_document()->createElement($xsl_node->getTagName); |
1831 |
|
1832 |
my $cdata = ''; |
1833 |
|
1834 |
foreach my $child_node ( $xsl_node->getChildNodes() ) |
1835 |
{ |
1836 |
if ($child_node->can('asString') ) |
1837 |
{ |
1838 |
$cdata .= $child_node->asString(); |
1839 |
} |
1840 |
else |
1841 |
{ |
1842 |
$cdata .= $child_node->getNodeValue(); |
1843 |
} |
1844 |
} |
1845 |
|
1846 |
$node->addCDATA($cdata); |
1847 |
|
1848 |
$current_result_node->appendChild($node); |
1849 |
|
1850 |
} |
1851 |
|
1852 |
sub _add_and_recurse { |
1853 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
1854 |
$current_result_node, $variables, $oldvariables) = @_; |
1855 |
|
1856 |
# the addition is commented out to prevent unknown xsl: commands to be printed in the result |
1857 |
$self->_add_node ($xsl_node, $current_result_node); |
1858 |
$self->_evaluate_template ($xsl_node, $current_xml_node, |
1859 |
$current_xml_selection_path, |
1860 |
$current_result_node, $variables, $oldvariables); #->getLastChild); |
1861 |
} |
1862 |
|
1863 |
sub _check_attributes_and_recurse { |
1864 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
1865 |
$current_result_node, $variables, $oldvariables) = @_; |
1866 |
|
1867 |
$self->_add_node ($xsl_node, $current_result_node); |
1868 |
$self->_attribute_value_of ($current_result_node->getLastChild, |
1869 |
$current_xml_node, |
1870 |
$current_xml_selection_path, $variables); |
1871 |
$self->_evaluate_template ($xsl_node, $current_xml_node, |
1872 |
$current_xml_selection_path, |
1873 |
$current_result_node->getLastChild, |
1874 |
$variables, $oldvariables); |
1875 |
} |
1876 |
|
1877 |
|
1878 |
sub _element { |
1879 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
1880 |
$current_result_node, $variables, $oldvariables) = @_; |
1881 |
|
1882 |
my $name = $xsl_node->getAttribute ('name'); |
1883 |
$self->debug(qq{inserting Element named "$name":}); |
1884 |
$self->_indent(); |
1885 |
|
1886 |
if (defined $name) { |
1887 |
my $result = $self->xml_document()->createElement($name); |
1888 |
|
1889 |
$self->_evaluate_template ($xsl_node, |
1890 |
$current_xml_node, |
1891 |
$current_xml_selection_path, |
1892 |
$result, $variables, $oldvariables); |
1893 |
|
1894 |
my $attr_set = $xsl_node->getAttribute('use-attribute-sets'); |
1895 |
|
1896 |
if ( $attr_set ) |
1897 |
{ |
1898 |
$self->_indent(); |
1899 |
my $set_name = $attr_set; |
1900 |
|
1901 |
if ( my $set = $self->__attribute_set_($set_name) ) |
1902 |
{ |
1903 |
$self->debug("Adding attribute-set '$set_name'"); |
1904 |
|
1905 |
foreach my $attr_name ( keys %{$set} ) |
1906 |
{ |
1907 |
$self->debug("Adding attribute $attr_name ->" . $set->{$attr_name}); |
1908 |
$result->setAttribute($attr_name,$set->{$attr_name}); |
1909 |
} |
1910 |
} |
1911 |
$self->_outdent(); |
1912 |
} |
1913 |
$current_result_node->appendChild($result); |
1914 |
} else { |
1915 |
$self->warn(q{expected attribute "name" in <} . |
1916 |
$self->xsl_ns() . q{element>}); |
1917 |
} |
1918 |
$self->_outdent(); |
1919 |
} |
1920 |
|
1921 |
{ |
1922 |
###################################################################### |
1923 |
# Auxiliary package for disable-output-escaping |
1924 |
###################################################################### |
1925 |
|
1926 |
package XML::XSLT::DOM::TextDOE; |
1927 |
use vars qw( @ISA ); |
1928 |
@ISA = qw( XML::DOM::Text ); |
1929 |
|
1930 |
sub print { |
1931 |
my ($self, $FILE) = @_; |
1932 |
$FILE->print ($self->getData); |
1933 |
} |
1934 |
} |
1935 |
|
1936 |
|
1937 |
sub _value_of { |
1938 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
1939 |
$current_result_node, $variables) = @_; |
1940 |
|
1941 |
my $select = $xsl_node->getAttribute('select'); |
1942 |
|
1943 |
# Need to determine here whether the value is an XPath expression |
1944 |
# and act accordingly |
1945 |
|
1946 |
my $xml_node; |
1947 |
|
1948 |
if (defined $select) { |
1949 |
$xml_node = $self->_get_node_set ($select, $self->xml_document(), |
1950 |
$current_xml_selection_path, |
1951 |
$current_xml_node, $variables); |
1952 |
|
1953 |
$self->debug("stripping node to text:"); |
1954 |
|
1955 |
$self->_indent(); |
1956 |
my $text = ''; |
1957 |
$text = $self->__string__ ($xml_node->[0]) if @{$xml_node}; |
1958 |
$self->_outdent(); |
1959 |
|
1960 |
if ($text ne '') { |
1961 |
my $node = $self->xml_document()->createTextNode ($text); |
1962 |
if ($xsl_node->getAttribute ('disable-output-escaping') eq 'yes') { |
1963 |
$self->debug("disabling output escaping"); |
1964 |
bless $node,'XML::XSLT::DOM::TextDOE' ; |
1965 |
} |
1966 |
$self->_move_node ($node, $current_result_node); |
1967 |
} else { |
1968 |
$self->debug("nothing left.."); |
1969 |
} |
1970 |
} else { |
1971 |
$self->warn(qq{expected attribute "select" in <} . |
1972 |
$self->xsl_ns() . q{value-of>}); |
1973 |
} |
1974 |
} |
1975 |
|
1976 |
sub __strip_node_to_text__ { |
1977 |
my ($self, $node) = @_; |
1978 |
|
1979 |
my $result = ""; |
1980 |
|
1981 |
my $node_type = $node->getNodeType; |
1982 |
if ($node_type == TEXT_NODE) { |
1983 |
$result = $node->getData; |
1984 |
} elsif (($node_type == ELEMENT_NODE) |
1985 |
|| ($node_type == DOCUMENT_FRAGMENT_NODE)) { |
1986 |
$self->_indent(); |
1987 |
foreach my $child ($node->getChildNodes) { |
1988 |
$result .= &__strip_node_to_text__ ($self, $child); |
1989 |
} |
1990 |
$self->_outdent(); |
1991 |
} |
1992 |
return $result; |
1993 |
} |
1994 |
|
1995 |
sub __string__ { |
1996 |
my ($self, $node,$depth) = @_; |
1997 |
|
1998 |
my $result = ""; |
1999 |
|
2000 |
if (defined $node) { |
2001 |
my $ref = (ref ($node) || "not a reference"); |
2002 |
$self->debug("stripping child nodes ($ref):"); |
2003 |
|
2004 |
$self->_indent(); |
2005 |
|
2006 |
if ($ref eq "ARRAY") { |
2007 |
return $self->__string__ ($$node[0], $depth); |
2008 |
} else { |
2009 |
my $node_type = $node->getNodeType; |
2010 |
|
2011 |
if (($node_type == ELEMENT_NODE) |
2012 |
|| ($node_type == DOCUMENT_FRAGMENT_NODE) |
2013 |
|| ($node_type == DOCUMENT_NODE)) { |
2014 |
foreach my $child ($node->getChildNodes) { |
2015 |
$result .= &__string__ ($self, $child,1); |
2016 |
} |
2017 |
} elsif ($node_type == ATTRIBUTE_NODE) { |
2018 |
$result .= $node->getValue; |
2019 |
} elsif (($node_type == TEXT_NODE) |
2020 |
|| ($node_type == CDATA_SECTION_NODE) |
2021 |
|| ($node_type == ENTITY_REFERENCE_NODE)) { |
2022 |
$result .= $node->getData; |
2023 |
} elsif (!$depth && ( ($node_type == PROCESSING_INSTRUCTION_NODE) |
2024 |
|| ($node_type == COMMENT_NODE) )) { |
2025 |
$result .= $node->getData; # COM,PI - only in 'top-level' call |
2026 |
} else { |
2027 |
# just to be consistent |
2028 |
$self->warn("Can't get string-value for node of type $ref !"); |
2029 |
} |
2030 |
} |
2031 |
|
2032 |
$self->debug(qq{ "$result"}); |
2033 |
$self->_outdent(); |
2034 |
} else { |
2035 |
$self->debug(" no result"); |
2036 |
} |
2037 |
|
2038 |
return $result; |
2039 |
} |
2040 |
|
2041 |
sub _move_node { |
2042 |
my ($self, $node, $parent) = @_; |
2043 |
|
2044 |
$self->debug("moving node..");; |
2045 |
|
2046 |
$parent->appendChild($node); |
2047 |
} |
2048 |
|
2049 |
sub _get_node_set { |
2050 |
my ($self, $path, $root_node, $current_path, $current_node, $variables, |
2051 |
$silent) = @_; |
2052 |
$current_path ||= "/"; |
2053 |
$current_node ||= $root_node; |
2054 |
$silent ||= 0; |
2055 |
|
2056 |
$self->debug(qq{getting node-set "$path" from "$current_path"}); |
2057 |
|
2058 |
$self->_indent(); |
2059 |
|
2060 |
# expand abbriviated syntax |
2061 |
$path =~ s/\@/attribute\:\:/g; |
2062 |
$path =~ s/\.\./parent\:\:node\(\)/g; |
2063 |
$path =~ s/\./self\:\:node\(\)/g; |
2064 |
$path =~ s/\/\//\/descendant\-or\-self\:\:node\(\)\//g; |
2065 |
#$path =~ s/\/[^\:\/]*?\//attribute::/g; |
2066 |
|
2067 |
if ($path =~ /^\$([\w\.\-]+)$/) { |
2068 |
my $varname = $1; |
2069 |
my $var = $$variables{$varname}; |
2070 |
if (defined $var) { |
2071 |
if (ref ($$variables{$varname}) eq 'ARRAY') { |
2072 |
# node-set array-ref |
2073 |
return $$variables{$varname}; |
2074 |
} elsif (ref ($$variables{$varname}) eq 'XML::DOM::NodeList') { |
2075 |
# node-set nodelist |
2076 |
return [@{$$variables{$varname}}]; |
2077 |
} elsif (ref ($$variables{$varname}) eq 'XML::DOM::DocumentFragment') { |
2078 |
# node-set documentfragment |
2079 |
return [$$variables{$varname}->getChildNodes]; |
2080 |
} else { |
2081 |
# string or number? |
2082 |
return [$self->xml_document()->createTextNode ($$variables{$varname})]; |
2083 |
} |
2084 |
} else { |
2085 |
# var does not exist |
2086 |
return []; |
2087 |
} |
2088 |
} elsif ($path eq $current_path || $path eq 'self::node()') { |
2089 |
$self->debug("direct hit!");; |
2090 |
return [$current_node]; |
2091 |
} else { |
2092 |
# open external documents first # |
2093 |
if ($path =~ /^\s*document\s*\(["'](.*?)["']\s*(,\s*(.*)\s*){0,1}\)\s*(.*)$/) { |
2094 |
my $filename = $1; |
2095 |
my $sec_arg = $3; |
2096 |
$path = ($4 || ""); |
2097 |
|
2098 |
$self->debug(qq{external selection ("$filename")!}); |
2099 |
|
2100 |
if ($sec_arg) { |
2101 |
$self->warn("Ignoring second argument of $path"); |
2102 |
} |
2103 |
|
2104 |
($root_node) = $self->__open_by_filename ($filename, $self->{XSL_BASE}); |
2105 |
} |
2106 |
|
2107 |
if ($path =~ /^\//) { |
2108 |
# start from the root # |
2109 |
$current_node = $root_node; |
2110 |
} elsif ($path =~ /^self\:\:node\(\)\//) { #'#"#'#" |
2111 |
# remove preceding dot from './etc', which is expanded to 'self::node()' |
2112 |
# at the top of this subroutine # |
2113 |
$path =~ s/^self\:\:node\(\)//; |
2114 |
} else { |
2115 |
# to facilitate parsing, precede path with a '/' # |
2116 |
$path = "/$path"; |
2117 |
} |
2118 |
|
2119 |
$self->debug(qq{using "$path":}); |
2120 |
|
2121 |
if ($path eq '/') { |
2122 |
$current_node = [$current_node]; |
2123 |
} else { |
2124 |
$current_node = &__get_node_set__ ($self, $path, [$current_node], $silent); |
2125 |
} |
2126 |
|
2127 |
$self->_outdent(); |
2128 |
|
2129 |
return $current_node; |
2130 |
} |
2131 |
} |
2132 |
|
2133 |
|
2134 |
# auxiliary function # |
2135 |
sub __get_node_set__ { |
2136 |
my ($self, $path, $node, $silent) = @_; |
2137 |
|
2138 |
# a Qname (?) should actually be: [a-Z_][\w\.\-]*\:[a-Z_][\w\.\-]* |
2139 |
|
2140 |
if ($path eq "") { |
2141 |
|
2142 |
$self->debug("node found!");; |
2143 |
return $node; |
2144 |
|
2145 |
} else { |
2146 |
my $list = []; |
2147 |
foreach my $item (@$node) { |
2148 |
my $sublist = &__try_a_step__ ($self, $path, $item, $silent); |
2149 |
push (@$list, @$sublist); |
2150 |
} |
2151 |
return $list; |
2152 |
} |
2153 |
} |
2154 |
|
2155 |
sub __try_a_step__ { |
2156 |
my ($self, $path, $node, $silent) = @_; |
2157 |
|
2158 |
study ($path); |
2159 |
if ($path =~ s/^\/parent\:\:node\(\)//) { |
2160 |
# /.. # |
2161 |
$self->debug(qq{getting parent ("$path")}); |
2162 |
return &__parent__ ($self, $path, $node, $silent); |
2163 |
|
2164 |
} elsif ($path =~ s/^\/attribute\:\:(\*|[\w\.\:\-]+)//) { |
2165 |
# /@attr # |
2166 |
$self->debug(qq{getting attribute `$1' ("$path")}); |
2167 |
return &__attribute__ ($self, $1, $path, $node, $silent); |
2168 |
|
2169 |
} elsif ($path =~ s/^\/descendant\-or\-self\:\:node\(\)\/(child\:\:|)(\*|[\w\.\:\-]+)\[(\S+?)\]//) { |
2170 |
# //elem[n] # |
2171 |
$self->debug(qq{getting deep indexed element `$1' `$2' ("$path")}); |
2172 |
return &__indexed_element__ ($self, $1, $2, $path, $node, $silent, "deep"); |
2173 |
|
2174 |
} elsif ($path =~ s/^\/descendant\-or\-self\:\:node\(\)\/(\*|[\w\.\:\-]+)//) { |
2175 |
# //elem # |
2176 |
$self->debug(qq{getting deep element `$1' ("$path")}); |
2177 |
return &__element__ ($self, $1, $path, $node, $silent, "deep"); |
2178 |
|
2179 |
} elsif ($path =~ s/^\/(child\:\:|)(\*|[\w\.\:\-]+)\[(\S+?)\]//) { |
2180 |
# /elem[n] # |
2181 |
$self->debug(qq{getting indexed element `$2' `$3' ("$path")}); |
2182 |
return &__indexed_element__ ($self, $2, $3, $path, $node, $silent); |
2183 |
|
2184 |
} elsif ($path =~ s/^\/(child\:\:|)(\*|[\w\.\:\-]+)//) { |
2185 |
# /elem # |
2186 |
$self->debug(qq{getting element `$2' ("$path")}); |
2187 |
return &__element__ ($self, $2, $path, $node, $silent); |
2188 |
|
2189 |
} elsif ($path =~ s/^\/(child\:\:|)text\(\)//) { |
2190 |
# /text() # |
2191 |
$self->debug(qq{getting text ("$path")}); |
2192 |
return &__get_nodes__ ($self, TEXT_NODE, $path, $node, $silent); |
2193 |
|
2194 |
} elsif ($path =~ s/^\/(child\:\:|)processing-instruction\(\)//) { |
2195 |
# /processing-instruction() # |
2196 |
$self->debug(qq{getting processing instruction ("$path")}); |
2197 |
return &__get_nodes__ ($self, PROCESSING_INSTRUCTION_NODE, $path, $node, $silent); |
2198 |
|
2199 |
} elsif ($path =~ s/^\/(child\:\:|)comment\(\)//) { |
2200 |
# /comment() # |
2201 |
$self->debug(qq{getting comment ("$path")}); |
2202 |
return &__get_nodes__ ($self, COMMENT_NODE, $path, $node, $silent); |
2203 |
|
2204 |
} else { |
2205 |
$self->warn("get-node-from-path: Don't know what to do with path $path !!!"); |
2206 |
return []; |
2207 |
} |
2208 |
} |
2209 |
|
2210 |
sub __parent__ { |
2211 |
my ($self, $path, $node, $silent) = @_; |
2212 |
|
2213 |
$self->_indent(); |
2214 |
if (($node->getNodeType == DOCUMENT_NODE) |
2215 |
|| ($node->getNodeType == DOCUMENT_FRAGMENT_NODE)) { |
2216 |
$self->debug("no parent!");; |
2217 |
$node = []; |
2218 |
} else { |
2219 |
$node = $node->getParentNode; |
2220 |
|
2221 |
$node = &__get_node_set__ ($self, $path, [$node], $silent); |
2222 |
} |
2223 |
$self->_outdent(); |
2224 |
|
2225 |
return $node; |
2226 |
} |
2227 |
|
2228 |
sub __indexed_element__ { |
2229 |
my ($self, $element, $index, $path, $node, $silent, $deep) = @_; |
2230 |
$index ||= 0; |
2231 |
$deep ||= ""; # False # |
2232 |
|
2233 |
if ($index =~ /^first\s*\(\)/) { |
2234 |
$index = 0; |
2235 |
} elsif ($index =~ /^last\s*\(\)/) { |
2236 |
$index = -1; |
2237 |
} else { |
2238 |
$index--; |
2239 |
} |
2240 |
|
2241 |
my @list = $node->getElementsByTagName($element, $deep); |
2242 |
|
2243 |
if (@list) { |
2244 |
$node = $list[$index]; |
2245 |
} else { |
2246 |
$node = ""; |
2247 |
} |
2248 |
|
2249 |
$self->_indent(); |
2250 |
if ($node) { |
2251 |
$node = &__get_node_set__ ($self, $path, [$node], $silent); |
2252 |
} else { |
2253 |
$self->debug("failed!");; |
2254 |
$node = []; |
2255 |
} |
2256 |
$self->_outdent(); |
2257 |
|
2258 |
return $node; |
2259 |
} |
2260 |
|
2261 |
sub __element__ { |
2262 |
my ($self, $element, $path, $node, $silent, $deep) = @_; |
2263 |
$deep ||= ""; # False # |
2264 |
|
2265 |
$node = [$node->getElementsByTagName($element, $deep)]; |
2266 |
|
2267 |
$self->_indent(); |
2268 |
if (@$node) { |
2269 |
$node = &__get_node_set__($self, $path, $node, $silent); |
2270 |
} else { |
2271 |
$self->debug("failed!");; |
2272 |
} |
2273 |
$self->_outdent(); |
2274 |
|
2275 |
return $node; |
2276 |
} |
2277 |
|
2278 |
sub __attribute__ { |
2279 |
my ($self, $attribute, $path, $node, $silent) = @_; |
2280 |
if ($attribute eq '*') { |
2281 |
$node = [$node->getAttributes->getValues]; |
2282 |
|
2283 |
$self->_indent(); |
2284 |
if ($node) { |
2285 |
$node = &__get_node_set__ ($self, $path, $node, $silent); |
2286 |
} else { |
2287 |
$self->debug("failed!");; |
2288 |
} |
2289 |
$self->_outdent(); |
2290 |
} else { |
2291 |
$node = $node->getAttributeNode($attribute); |
2292 |
|
2293 |
$self->_indent(); |
2294 |
if ($node) { |
2295 |
$node = &__get_node_set__ ($self, $path, [$node], $silent); |
2296 |
} else { |
2297 |
$self->debug("failed!");; |
2298 |
$node = []; |
2299 |
} |
2300 |
$self->_outdent(); |
2301 |
} |
2302 |
|
2303 |
return $node; |
2304 |
} |
2305 |
|
2306 |
sub __get_nodes__ { |
2307 |
my ($self, $node_type, $path, $node, $silent) = @_; |
2308 |
|
2309 |
my $result = []; |
2310 |
|
2311 |
$self->_indent(); |
2312 |
foreach my $child ($node->getChildNodes) { |
2313 |
if ($child->getNodeType == $node_type) { |
2314 |
$result = [@$result, &__get_node_set__ ($self, $path, [$child], $silent)]; |
2315 |
} |
2316 |
} |
2317 |
$self->_outdent(); |
2318 |
|
2319 |
if (! @$result) { |
2320 |
$self->debug("failed!");; |
2321 |
} |
2322 |
|
2323 |
return $result; |
2324 |
} |
2325 |
|
2326 |
|
2327 |
sub _attribute_value_of { |
2328 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2329 |
$variables) = @_; |
2330 |
|
2331 |
foreach my $attribute ($xsl_node->getAttributes->getValues) { |
2332 |
my $value = $attribute->getValue; |
2333 |
study ($value); |
2334 |
#$value =~ s/(\*|\$|\@|\&|\?|\+|\\)/\\$1/g; |
2335 |
$value =~ s/(\*|\?|\+)/\\$1/g; |
2336 |
study ($value); |
2337 |
while ($value =~ /\G[^\\]?\{(.*?[^\\]?)\}/) { |
2338 |
my $node = $self->_get_node_set ($1, $self->xml_document(), |
2339 |
$current_xml_selection_path, |
2340 |
$current_xml_node, $variables); |
2341 |
if (@$node) { |
2342 |
$self->_indent(); |
2343 |
my $text = $self->__string__ ($$node[0]); |
2344 |
$self->_outdent(); |
2345 |
$value =~ s/(\G[^\\]?)\{(.*?)[^\\]?\}/$1$text/; |
2346 |
} else { |
2347 |
$value =~ s/(\G[^\\]?)\{(.*?)[^\\]?\}/$1/; |
2348 |
} |
2349 |
} |
2350 |
#$value =~ s/\\(\*|\$|\@|\&|\?|\+|\\)/$1/g; |
2351 |
$value =~ s/\\(\*|\?|\+)/$1/g; |
2352 |
$value =~ s/\\(\{|\})/$1/g; |
2353 |
$attribute->setValue ($value); |
2354 |
} |
2355 |
} |
2356 |
|
2357 |
sub _processing_instruction { |
2358 |
my ($self, $xsl_node, $current_result_node, $variables, $oldvariables) = @_; |
2359 |
|
2360 |
my $new_PI_name = $xsl_node->getAttribute('name'); |
2361 |
|
2362 |
if ($new_PI_name eq "xml") { |
2363 |
$self->warn("<" . $self->xsl_ns() . "processing-instruction> may not be used to create XML"); |
2364 |
$self->warn("declaration. Use <" . $self->xsl_ns() . "output> instead..."); |
2365 |
} elsif ($new_PI_name) { |
2366 |
my $text = $self->__string__ ($xsl_node); |
2367 |
my $new_PI = $self->xml_document()->createProcessingInstruction($new_PI_name, $text); |
2368 |
|
2369 |
if ($new_PI) { |
2370 |
$self->_move_node ($new_PI, $current_result_node); |
2371 |
} |
2372 |
} else { |
2373 |
$self->warn(q{Expected attribute "name" in <} . |
2374 |
$self->xsl_ns() . "processing-instruction> !"); |
2375 |
} |
2376 |
} |
2377 |
|
2378 |
sub _process_with_params { |
2379 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2380 |
$variables, $params) = @_; |
2381 |
|
2382 |
my @params = $xsl_node->getElementsByTagName($self->xsl_ns() . "with-param"); |
2383 |
foreach my $param (@params) { |
2384 |
my $varname = $param->getAttribute('name'); |
2385 |
|
2386 |
if ($varname) { |
2387 |
my $value = $param->getAttribute('select'); |
2388 |
|
2389 |
if (!$value) { |
2390 |
# process content as template |
2391 |
$value = $self->xml_document()->createDocumentFragment; |
2392 |
|
2393 |
$self->_evaluate_template ($param, |
2394 |
$current_xml_node, |
2395 |
$current_xml_selection_path, |
2396 |
$value, $variables, {} ); |
2397 |
$$params{$varname} = $value; |
2398 |
|
2399 |
} else { |
2400 |
# *** FIXME - should evaluate this as an expression! |
2401 |
$$params{$varname} = $value; |
2402 |
} |
2403 |
} else { |
2404 |
$self->warn(q{Expected attribute "name" in <} . |
2405 |
$self->xsl_ns() . q{with-param> !}); |
2406 |
} |
2407 |
} |
2408 |
|
2409 |
} |
2410 |
|
2411 |
sub _call_template { |
2412 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2413 |
$current_result_node, $variables, $oldvariables) = @_; |
2414 |
|
2415 |
my $params={}; |
2416 |
my $newvariables = defined $variables ? {%$variables} : {} ; |
2417 |
my $name = $xsl_node->getAttribute('name'); |
2418 |
|
2419 |
if ($name) { |
2420 |
$self->debug(qq{calling template named "$name"}); |
2421 |
|
2422 |
$self->_process_with_params ($xsl_node, $current_xml_node, |
2423 |
$current_xml_selection_path, |
2424 |
$variables, $params); |
2425 |
|
2426 |
$self->_indent(); |
2427 |
my $template = $self->_match_template ("name", $name, 0, ''); |
2428 |
|
2429 |
if ($template) { |
2430 |
$self->_evaluate_template ($template, $current_xml_node, |
2431 |
$current_xml_selection_path, |
2432 |
$current_result_node, $newvariables, $params); |
2433 |
} else { |
2434 |
$self->warn("no template named $name found!"); |
2435 |
} |
2436 |
$self->_outdent(); |
2437 |
} else { |
2438 |
$self->warn(q{Expected attribute "name" in <} . |
2439 |
$self->xsl_ns() . q{call-template/>}); |
2440 |
} |
2441 |
} |
2442 |
|
2443 |
sub _choose { |
2444 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2445 |
$current_result_node, $variables, $oldvariables) = @_; |
2446 |
|
2447 |
$self->debug("evaluating choose:");; |
2448 |
|
2449 |
$self->_indent(); |
2450 |
|
2451 |
my $notdone = "true"; |
2452 |
my $testwhen = "active"; |
2453 |
foreach my $child ($xsl_node->getElementsByTagName ('*', 0)) { |
2454 |
if ($notdone && $testwhen && ($child->getTagName eq $self->xsl_ns() ."when")) { |
2455 |
my $test = $child->getAttribute ('test'); |
2456 |
|
2457 |
if ($test) { |
2458 |
my $test_succeeds = $self->_evaluate_test ($test, $current_xml_node, |
2459 |
$current_xml_selection_path, |
2460 |
$variables); |
2461 |
if ($test_succeeds) { |
2462 |
$self->_evaluate_template ($child, $current_xml_node, |
2463 |
$current_xml_selection_path, |
2464 |
$current_result_node, $variables, $oldvariables); |
2465 |
$testwhen = ""; |
2466 |
$notdone = ""; |
2467 |
} |
2468 |
} else { |
2469 |
$self->warn(q{expected attribute "test" in <} . |
2470 |
$self->xsl_ns() . q{when>}); |
2471 |
} |
2472 |
} elsif ($notdone && ($child->getTagName eq $self->xsl_ns() . "otherwise")) { |
2473 |
$self->_evaluate_template ($child, $current_xml_node, |
2474 |
$current_xml_selection_path, |
2475 |
$current_result_node, $variables, $oldvariables); |
2476 |
$notdone = ""; |
2477 |
} |
2478 |
} |
2479 |
|
2480 |
if ($notdone) { |
2481 |
$self->debug("nothing done!");; |
2482 |
} |
2483 |
|
2484 |
$self->_outdent(); |
2485 |
} |
2486 |
|
2487 |
sub _if { |
2488 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2489 |
$current_result_node, $variables, $oldvariables) = @_; |
2490 |
|
2491 |
$self->debug("evaluating if:");; |
2492 |
|
2493 |
$self->_indent(); |
2494 |
|
2495 |
my $test = $xsl_node->getAttribute ('test'); |
2496 |
|
2497 |
if ($test) { |
2498 |
my $test_succeeds = $self->_evaluate_test ($test, $current_xml_node, |
2499 |
$current_xml_selection_path, |
2500 |
$variables); |
2501 |
if ($test_succeeds) { |
2502 |
$self->_evaluate_template ($xsl_node, $current_xml_node, |
2503 |
$current_xml_selection_path, |
2504 |
$current_result_node, $variables, $oldvariables); |
2505 |
} |
2506 |
} else { |
2507 |
$self->warn(q{expected attribute "test" in <} . |
2508 |
$self->xsl_ns() . q{if>}); |
2509 |
} |
2510 |
|
2511 |
$self->_outdent(); |
2512 |
} |
2513 |
|
2514 |
sub __evaluate_test__ { |
2515 |
my ($self,$test, $path,$node,$variables) = @_; |
2516 |
|
2517 |
my $tagname = eval { $node->getTagName() } || ''; |
2518 |
|
2519 |
$self->debug(qq{testing with "$test" and $tagname}); |
2520 |
|
2521 |
if ($test =~ /^\s*\@([\w\.\:\-]+)\s*(<=|>=|!=|<|>|=)?\s*['"]?([^'"]*?)['"]?\s*$/) { |
2522 |
my $attr = $node->getAttribute($1); |
2523 |
|
2524 |
my $test = $2 ; |
2525 |
$test =~ s/\s+//g; |
2526 |
my $expval = $3; |
2527 |
my $numeric = ($attr =~ /^\d+$/ && $expval =~ /^\d+$/ ? 1 : 0); |
2528 |
|
2529 |
$self->debug("evaluating $attr $test $expval " ); |
2530 |
|
2531 |
if ( $test eq '!=' ) |
2532 |
{ |
2533 |
$self->debug("$numeric ? $attr != $expval : $attr ne $expval"); |
2534 |
return $numeric ? $attr != $expval : $attr ne $expval; |
2535 |
} |
2536 |
elsif ( $test eq '=' ) |
2537 |
{ |
2538 |
$self->debug("$numeric ? $attr == $expval : $attr eq $expval"); |
2539 |
return $numeric ? $attr == $expval : $attr eq $expval; |
2540 |
} |
2541 |
elsif ( $test eq '<' ) |
2542 |
{ |
2543 |
$self->debug("$numeric ? $attr < $expval : $attr lt $expval"); |
2544 |
return $numeric ? $attr < $expval : $attr lt $expval; |
2545 |
} |
2546 |
elsif ( $test eq '>' ) |
2547 |
{ |
2548 |
$self->debug("$numeric ? $attr > $expval : $attr gt $expval"); |
2549 |
return $numeric ? $attr > $expval : $attr gt $expval; |
2550 |
} |
2551 |
elsif ( $test eq '>=' ) |
2552 |
{ |
2553 |
$self->debug("$numeric ? $attr >= $expval : $attr ge $expval"); |
2554 |
return $numeric ? $attr >= $expval : $attr ge $expval; |
2555 |
} |
2556 |
elsif ( $test eq '<=' ) |
2557 |
{ |
2558 |
$self->debug("$numeric ? $attr <= $expval : $attr le $expval"); |
2559 |
return $numeric ? $attr <= $expval : $attr le $expval; |
2560 |
} |
2561 |
else |
2562 |
{ |
2563 |
$self->debug("no test matches"); |
2564 |
return 0; |
2565 |
} |
2566 |
} elsif ($test =~ /^\s*([\w\.\:\-]+)\s*(<=|>=|!=|=|<|>)\s*['"]?([^'"]*)['"]?\s*$/) { |
2567 |
my $expval = $3; |
2568 |
my $test = $2; |
2569 |
my $nodeset=&_get_node_set($self,$1,$self->xml_document(),$path,$node,$variables); |
2570 |
return ($expval ne '') unless @$nodeset; |
2571 |
my $content = &__string__($self,$$nodeset[0]); |
2572 |
my $numeric = $content =~ /^\d+$/ && $expval =~ /^\d+$/ ? 1 : 0; |
2573 |
|
2574 |
$self->debug("evaluating $content $test $expval"); |
2575 |
|
2576 |
if ( $test eq '!=' ) |
2577 |
{ |
2578 |
return $numeric ? $content != $expval : $content ne $expval; |
2579 |
} |
2580 |
elsif ( $test eq '=' ) |
2581 |
{ |
2582 |
return $numeric ? $content == $expval : $content eq $expval; |
2583 |
} |
2584 |
elsif ( $test eq '<' ) |
2585 |
{ |
2586 |
return $numeric ? $content < $expval : $content lt $expval; |
2587 |
} |
2588 |
elsif ( $test eq '>' ) |
2589 |
{ |
2590 |
return $numeric ? $content > $expval : $content gt $expval; |
2591 |
} |
2592 |
elsif ( $test eq '>=' ) |
2593 |
{ |
2594 |
return $numeric ? $content >= $expval : $content ge $expval; |
2595 |
} |
2596 |
elsif ( $test eq '<=' ) |
2597 |
{ |
2598 |
return $numeric ? $content <= $expval : $content le $expval; |
2599 |
} |
2600 |
else |
2601 |
{ |
2602 |
$self->debug("no test matches"); |
2603 |
return 0; |
2604 |
} |
2605 |
|
2606 |
# tests for variables|parameters |
2607 |
} elsif ($test =~ /^\s*{*\$([\w\.\:\-]+)}*\s*(<=|>=|!=|=|<|>)\s*['"]?([^'"]*)['"]?\s*$/) { |
2608 |
my $expval = $3; |
2609 |
my $test = $2; |
2610 |
=pod |
2611 |
my $nodeset=&_get_node_set($self,$1,$self->xml_document(),$path,$node,$variables); |
2612 |
return ($expval ne '') unless @$nodeset; |
2613 |
my $content = &__string__($self,$$nodeset[0]); |
2614 |
=cut |
2615 |
my $variable_name = $1; |
2616 |
my $content = &__string__($self,$variables->{$variable_name}); |
2617 |
my $numeric = $content =~ /^\d+$/ && $expval =~ /^\d+$/ ? 1 : 0; |
2618 |
|
2619 |
$self->debug("evaluating $content $test $expval"); |
2620 |
|
2621 |
if ( $test eq '!=' ) |
2622 |
{ |
2623 |
return $numeric ? $content != $expval : $content ne $expval; |
2624 |
} |
2625 |
elsif ( $test eq '=' ) |
2626 |
{ |
2627 |
return $numeric ? $content == $expval : $content eq $expval; |
2628 |
} |
2629 |
elsif ( $test eq '<' ) |
2630 |
{ |
2631 |
return $numeric ? $content < $expval : $content lt $expval; |
2632 |
} |
2633 |
elsif ( $test eq '>' ) |
2634 |
{ |
2635 |
return $numeric ? $content > $expval : $content gt $expval; |
2636 |
} |
2637 |
elsif ( $test eq '>=' ) |
2638 |
{ |
2639 |
return $numeric ? $content >= $expval : $content ge $expval; |
2640 |
} |
2641 |
elsif ( $test eq '<=' ) |
2642 |
{ |
2643 |
return $numeric ? $content <= $expval : $content le $expval; |
2644 |
} |
2645 |
else |
2646 |
{ |
2647 |
$self->warn("no test matches while evaluating parameter comparison: $test"); |
2648 |
return 0; |
2649 |
} |
2650 |
} else { |
2651 |
$self->debug("no match for test"); |
2652 |
return ""; |
2653 |
} |
2654 |
} |
2655 |
|
2656 |
sub _copy_of { |
2657 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2658 |
$current_result_node, $variables) = @_; |
2659 |
|
2660 |
my $nodelist; |
2661 |
my $select = $xsl_node->getAttribute('select'); |
2662 |
$self->debug(qq{evaluating copy-of with select "$select":});; |
2663 |
|
2664 |
$self->_indent(); |
2665 |
if ($select) { |
2666 |
$nodelist = $self->_get_node_set ($select, $self->xml_document(), |
2667 |
$current_xml_selection_path, |
2668 |
$current_xml_node, $variables); |
2669 |
} else { |
2670 |
$self->warn(q{expected attribute "select" in <} . |
2671 |
$self->xsl_ns() . q{copy-of>}); |
2672 |
} |
2673 |
foreach my $node (@$nodelist) { |
2674 |
$self->_add_node ($node, $current_result_node, "deep"); |
2675 |
} |
2676 |
|
2677 |
$self->_outdent(); |
2678 |
} |
2679 |
|
2680 |
sub _copy { |
2681 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2682 |
$current_result_node, $variables, $oldvariables) = @_; |
2683 |
|
2684 |
|
2685 |
$self->debug("evaluating copy:");; |
2686 |
|
2687 |
$self->_indent(); |
2688 |
if ($current_xml_node->getNodeType == ATTRIBUTE_NODE) { |
2689 |
my $attribute = $current_xml_node->cloneNode(0); |
2690 |
$current_result_node->setAttributeNode($attribute); |
2691 |
} elsif (($current_xml_node->getNodeType == COMMENT_NODE) |
2692 |
|| ($current_xml_node->getNodeType == PROCESSING_INSTRUCTION_NODE)) { |
2693 |
$self->_add_node ($current_xml_node, $current_result_node); |
2694 |
} else { |
2695 |
$self->_add_node ($current_xml_node, $current_result_node); |
2696 |
$self->_evaluate_template ($xsl_node, |
2697 |
$current_xml_node, |
2698 |
$current_xml_selection_path, |
2699 |
$current_result_node->getLastChild, |
2700 |
$variables, $oldvariables); |
2701 |
} |
2702 |
$self->_outdent(); |
2703 |
} |
2704 |
|
2705 |
sub _text { |
2706 |
#=item addText (text) |
2707 |
# |
2708 |
#Appends the specified string to the last child if it is a Text node, or else |
2709 |
#appends a new Text node (with the specified text.) |
2710 |
# |
2711 |
#Return Value: the last child if it was a Text node or else the new Text node. |
2712 |
my ($self, $xsl_node, $current_result_node) = @_; |
2713 |
|
2714 |
$self->debug("inserting text:"); |
2715 |
|
2716 |
$self->_indent(); |
2717 |
|
2718 |
$self->debug("stripping node to text:"); |
2719 |
|
2720 |
$self->_indent(); |
2721 |
my $text = $self->__string__ ($xsl_node); |
2722 |
$self->_outdent(); |
2723 |
|
2724 |
if ($text ne '') { |
2725 |
my $node = $self->xml_document()->createTextNode ($text); |
2726 |
if ($xsl_node->getAttribute ('disable-output-escaping') eq 'yes') |
2727 |
{ |
2728 |
$self->debug("disabling output escaping"); |
2729 |
bless $node,'XML::XSLT::DOM::TextDOE' ; |
2730 |
} |
2731 |
$self->_move_node ($node, $current_result_node); |
2732 |
} else { |
2733 |
$self->debug("nothing left.."); |
2734 |
} |
2735 |
|
2736 |
$current_result_node->normalize(); |
2737 |
|
2738 |
$self->_outdent(); |
2739 |
} |
2740 |
|
2741 |
sub _attribute { |
2742 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2743 |
$current_result_node, $variables, $oldvariables) = @_; |
2744 |
|
2745 |
my $name = $xsl_node->getAttribute ('name'); |
2746 |
$self->debug(qq{inserting attribute named "$name":}); |
2747 |
$self->_indent(); |
2748 |
|
2749 |
if ($name) { |
2750 |
if ( $name =~ /^xmlns:/ ) |
2751 |
{ |
2752 |
$self->debug("Won't create namespace declaration"); |
2753 |
} |
2754 |
else |
2755 |
{ |
2756 |
my $result = $self->xml_document()->createDocumentFragment; |
2757 |
|
2758 |
$self->_evaluate_template ($xsl_node, |
2759 |
$current_xml_node, |
2760 |
$current_xml_selection_path, |
2761 |
$result, $variables, $oldvariables); |
2762 |
|
2763 |
$self->_indent(); |
2764 |
my $text = $self->fix_attribute_value($self->__string__ ($result)); |
2765 |
|
2766 |
|
2767 |
$self->_outdent(); |
2768 |
|
2769 |
$current_result_node->setAttribute($name, $text); |
2770 |
$result->dispose(); |
2771 |
} |
2772 |
} else { |
2773 |
$self->warn(q{expected attribute "name" in <} . |
2774 |
$self->xsl_ns() . q{attribute>}); |
2775 |
} |
2776 |
$self->_outdent(); |
2777 |
} |
2778 |
|
2779 |
sub _comment { |
2780 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2781 |
$current_result_node, $variables, $oldvariables) = @_; |
2782 |
|
2783 |
$self->debug("inserting comment:"); |
2784 |
|
2785 |
$self->_indent(); |
2786 |
|
2787 |
my $result = $self->xml_document()->createDocumentFragment; |
2788 |
|
2789 |
$self->_evaluate_template ($xsl_node, |
2790 |
$current_xml_node, |
2791 |
$current_xml_selection_path, |
2792 |
$result, $variables, $oldvariables); |
2793 |
|
2794 |
$self->_indent(); |
2795 |
my $text = $self->__string__ ($result); |
2796 |
$self->_outdent(); |
2797 |
|
2798 |
$self->_move_node ($self->xml_document()->createComment ($text), $current_result_node); |
2799 |
$result->dispose(); |
2800 |
|
2801 |
$self->_outdent(); |
2802 |
} |
2803 |
|
2804 |
sub _variable { |
2805 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2806 |
$current_result_node, $variables, $params, $is_param) = @_; |
2807 |
|
2808 |
my $varname = $xsl_node->getAttribute ('name'); |
2809 |
|
2810 |
if ($varname) { |
2811 |
$self->debug("definition of variable \$$varname:");; |
2812 |
|
2813 |
$self->_indent(); |
2814 |
|
2815 |
if ( $is_param and exists $$params{$varname} ) { |
2816 |
# copy from parent-template |
2817 |
|
2818 |
$$variables{$varname} = $$params{$varname}; |
2819 |
|
2820 |
} else { |
2821 |
# new variable definition |
2822 |
|
2823 |
my $value = $xsl_node->getAttribute ('select'); |
2824 |
|
2825 |
if (! $value) { |
2826 |
#tough case, evaluate content as template |
2827 |
|
2828 |
$value = $self->xml_document()->createDocumentFragment; |
2829 |
|
2830 |
$self->_evaluate_template ($xsl_node, |
2831 |
$current_xml_node, |
2832 |
$current_xml_selection_path, |
2833 |
$value, $variables, $params); |
2834 |
} |
2835 |
|
2836 |
$$variables{$varname} = $value; |
2837 |
} |
2838 |
|
2839 |
$self->_outdent(); |
2840 |
} else { |
2841 |
$self->warn(q{expected attribute "name" in <} . |
2842 |
$self->xsl_ns() . q{param> or <} . |
2843 |
$self->xsl_ns() . q{variable>}); |
2844 |
} |
2845 |
} |
2846 |
|
2847 |
# not implemented - but log it and make it go away |
2848 |
|
2849 |
sub _sort |
2850 |
{ |
2851 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2852 |
$current_result_node, $variables, $params, $is_param) = @_; |
2853 |
|
2854 |
$self->debug("dummy process for sort"); |
2855 |
} |
2856 |
|
2857 |
# Not quite sure how fallback should be implemented as the spec seems a |
2858 |
# little vague to me |
2859 |
|
2860 |
sub _fallback |
2861 |
{ |
2862 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2863 |
$current_result_node, $variables, $params, $is_param) = @_; |
2864 |
|
2865 |
$self->debug("dummy process for fallback"); |
2866 |
} |
2867 |
|
2868 |
# This is a no-op - attribute-sets should not appear within templates and |
2869 |
# we have already processed the stylesheet wide ones. |
2870 |
|
2871 |
sub _attribute_set |
2872 |
{ |
2873 |
my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path, |
2874 |
$current_result_node, $variables, $params, $is_param) = @_; |
2875 |
|
2876 |
$self->debug("in _attribute_set"); |
2877 |
} |
2878 |
|
2879 |
sub _indent |
2880 |
{ |
2881 |
my ( $self ) = @_; |
2882 |
$self->{INDENT} += $self->{INDENT_INCR}; |
2883 |
|
2884 |
} |
2885 |
|
2886 |
sub _outdent |
2887 |
{ |
2888 |
my ( $self ) = @_; |
2889 |
$self->{INDENT} -= $self->{INDENT_INCR}; |
2890 |
} |
2891 |
|
2892 |
sub fix_attribute_value |
2893 |
{ |
2894 |
my ( $self, $text ) = @_; |
2895 |
|
2896 |
# The spec say's that there can't be a literal line break in the |
2897 |
# attributes value - white space at the beginning or the end is |
2898 |
# almost certainly an mistake. |
2899 |
|
2900 |
$text =~ s/^\s+//g; |
2901 |
$text =~ s/\s+$//g; |
2902 |
|
2903 |
if ( $text ) |
2904 |
{ |
2905 |
$text =~ s/([\x0A\x0D])/sprintf("\&#%02X;",ord $1)/eg; |
2906 |
} |
2907 |
|
2908 |
return $text; |
2909 |
} |
2910 |
|
2911 |
1; |
2912 |
|
2913 |
__DATA__ |
2914 |
|
2915 |
=head1 SYNOPSIS |
2916 |
|
2917 |
use XML::XSLT; |
2918 |
|
2919 |
my $xslt = XML::XSLT->new ($xsl, warnings => 1); |
2920 |
|
2921 |
$xslt->transform ($xmlfile); |
2922 |
print $xslt->toString; |
2923 |
|
2924 |
$xslt->dispose(); |
2925 |
|
2926 |
=head1 DESCRIPTION |
2927 |
|
2928 |
This module implements the W3C's XSLT specification. The goal is full |
2929 |
implementation of this spec, but we have not yet achieved |
2930 |
that. However, it already works well. See L<XML::XSLT Commands> for |
2931 |
the current status of each command. |
2932 |
|
2933 |
XML::XSLT makes use of XML::DOM and LWP::Simple, while XML::DOM |
2934 |
uses XML::Parser. Therefore XML::Parser, XML::DOM and LWP::Simple |
2935 |
have to be installed properly for XML::XSLT to run. |
2936 |
|
2937 |
=head1 Specifying Sources |
2938 |
|
2939 |
The stylesheets and the documents may be passed as filenames, file |
2940 |
handles regular strings, string references or DOM-trees. Functions |
2941 |
that require sources (e.g. new), will accept either a named parameter |
2942 |
or simply the argument. |
2943 |
|
2944 |
Either of the following are allowed: |
2945 |
|
2946 |
my $xslt = XML::XSLT->new($xsl); |
2947 |
my $xslt = XML::XSLT->new(Source => $xsl); |
2948 |
|
2949 |
In documentation, the named parameter `Source' is always shown, but it |
2950 |
is never required. |
2951 |
|
2952 |
=head2 METHODS |
2953 |
|
2954 |
=over 4 |
2955 |
|
2956 |
=item new(Source => $xml [, %args]) |
2957 |
|
2958 |
Returns a new XSLT parser object. Valid flags are: |
2959 |
|
2960 |
=over 2 |
2961 |
|
2962 |
=item DOMparser_args |
2963 |
|
2964 |
Hashref of arguments to pass to the XML::DOM::Parser object's parse |
2965 |
method. |
2966 |
|
2967 |
=item variables |
2968 |
|
2969 |
Hashref of variables and their values for the stylesheet. |
2970 |
|
2971 |
=item base |
2972 |
|
2973 |
Base of URL for file inclusion. |
2974 |
|
2975 |
=item debug |
2976 |
|
2977 |
Turn on debugging messages. |
2978 |
|
2979 |
=item warnings |
2980 |
|
2981 |
Turn on warning messages. |
2982 |
|
2983 |
=item indent |
2984 |
|
2985 |
Starting amount of indention for debug messages. Defaults to 0. |
2986 |
|
2987 |
=item indent_incr |
2988 |
|
2989 |
Amount to indent each level of debug message. Defaults to 1. |
2990 |
|
2991 |
=back |
2992 |
|
2993 |
=item open_xml(Source => $xml [, %args]) |
2994 |
|
2995 |
Gives the XSLT object new XML to process. Returns an XML::DOM object |
2996 |
corresponding to the XML. |
2997 |
|
2998 |
=over 4 |
2999 |
|
3000 |
=item base |
3001 |
|
3002 |
The base URL to use for opening documents. |
3003 |
|
3004 |
=item parser_args |
3005 |
|
3006 |
Arguments to pase to the parser. |
3007 |
|
3008 |
=back |
3009 |
|
3010 |
=item open_xsl(Source => $xml, [, %args]) |
3011 |
|
3012 |
Gives the XSLT object a new stylesheet to use in processing XML. |
3013 |
Returns an XML::DOM object corresponding to the stylesheet. Any |
3014 |
arguments present are passed to the XML::DOM::Parser. |
3015 |
|
3016 |
=over 4 |
3017 |
|
3018 |
=item base |
3019 |
|
3020 |
The base URL to use for opening documents. |
3021 |
|
3022 |
=item parser_args |
3023 |
|
3024 |
Arguments to pase to the parser. |
3025 |
|
3026 |
=back |
3027 |
|
3028 |
=item process(%variables) |
3029 |
|
3030 |
Processes the previously loaded XML through the stylesheet using the |
3031 |
variables set in the argument. |
3032 |
|
3033 |
=item transform(Source => $xml [, %args]) |
3034 |
|
3035 |
Processes the given XML through the stylesheet. Returns an XML::DOM |
3036 |
object corresponding to the transformed XML. Any arguments present |
3037 |
are passed to the XML::DOM::Parser. |
3038 |
|
3039 |
=item serve(Source => $xml [, %args]) |
3040 |
|
3041 |
Processes the given XML through the stylesheet. Returns a string |
3042 |
containg the result. Example: |
3043 |
|
3044 |
use XML::XSLT qw(serve); |
3045 |
|
3046 |
$xslt = XML::XSLT->new($xsl); |
3047 |
print $xslt->serve $xml; |
3048 |
|
3049 |
=over 4 |
3050 |
|
3051 |
=item http_headers |
3052 |
|
3053 |
If true, then prepends the appropriate HTTP headers (e.g. Content-Type, |
3054 |
Content-Length); |
3055 |
|
3056 |
Defaults to true. |
3057 |
|
3058 |
=item xml_declaration |
3059 |
|
3060 |
If true, then the result contains the appropriate <?xml?> header. |
3061 |
|
3062 |
Defaults to true. |
3063 |
|
3064 |
=item xml_version |
3065 |
|
3066 |
The version of the XML. |
3067 |
|
3068 |
Defaults to 1.0. |
3069 |
|
3070 |
=item doctype |
3071 |
|
3072 |
The type of DOCTYPE this document is. Defaults to SYSTEM. |
3073 |
|
3074 |
=back |
3075 |
|
3076 |
=item toString |
3077 |
|
3078 |
Returns the result of transforming the XML with the stylesheet as a |
3079 |
string. |
3080 |
|
3081 |
=item to_dom |
3082 |
|
3083 |
Returns the result of transforming the XML with the stylesheet as an |
3084 |
XML::DOM object. |
3085 |
|
3086 |
=item media_type |
3087 |
|
3088 |
Returns the media type (aka mime type) of the object. |
3089 |
|
3090 |
=item dispose |
3091 |
|
3092 |
Executes the C<dispose> method on each XML::DOM object. |
3093 |
|
3094 |
=back |
3095 |
|
3096 |
=head1 XML::XSLT Commands |
3097 |
|
3098 |
=over 4 |
3099 |
|
3100 |
=item xsl:apply-imports no |
3101 |
|
3102 |
Not supported yet. |
3103 |
|
3104 |
=item xsl:apply-templates limited |
3105 |
|
3106 |
Attribute 'select' is supported to the same extent as xsl:value-of |
3107 |
supports path selections. |
3108 |
|
3109 |
Not supported yet: |
3110 |
- attribute 'mode' |
3111 |
- xsl:sort and xsl:with-param in content |
3112 |
|
3113 |
=item xsl:attribute partially |
3114 |
|
3115 |
Adds an attribute named to the value of the attribute 'name' and as value |
3116 |
the stringified content-template. |
3117 |
|
3118 |
Not supported yet: |
3119 |
- attribute 'namespace' |
3120 |
|
3121 |
=item xsl:attribute-set yes |
3122 |
|
3123 |
Partially |
3124 |
|
3125 |
=item xsl:call-template yes |
3126 |
|
3127 |
Takes attribute 'name' which selects xsl:template's by name. |
3128 |
|
3129 |
Weak support: |
3130 |
- xsl:with-param (select attrib not supported) |
3131 |
|
3132 |
Not supported yet: |
3133 |
- xsl:sort |
3134 |
|
3135 |
=item xsl:choose yes |
3136 |
|
3137 |
Tests sequentially all xsl:whens until one succeeds or |
3138 |
until an xsl:otherwise is found. Limited test support, see xsl:when |
3139 |
|
3140 |
=item xsl:comment yes |
3141 |
|
3142 |
Supported. |
3143 |
|
3144 |
=item xsl:copy partially |
3145 |
|
3146 |
=item xsl:copy-of limited |
3147 |
|
3148 |
Attribute 'select' functions as well as with |
3149 |
xsl:value-of |
3150 |
|
3151 |
=item xsl:decimal-format no |
3152 |
|
3153 |
Not supported yet. |
3154 |
|
3155 |
=item xsl:element yes |
3156 |
|
3157 |
=item xsl:fallback no |
3158 |
|
3159 |
Not supported yet. |
3160 |
|
3161 |
=item xsl:for-each limited |
3162 |
|
3163 |
Attribute 'select' functions as well as with |
3164 |
xsl:value-of |
3165 |
|
3166 |
Not supported yet: |
3167 |
- xsl:sort in content |
3168 |
|
3169 |
=item xsl:if limited |
3170 |
|
3171 |
Identical to xsl:when, but outside xsl:choose context. |
3172 |
|
3173 |
=item xsl:import no |
3174 |
|
3175 |
Not supported yet. |
3176 |
|
3177 |
=item xsl:include yes |
3178 |
|
3179 |
Takes attribute href, which can be relative-local, |
3180 |
absolute-local as well as an URL (preceded by |
3181 |
identifier http:). |
3182 |
|
3183 |
=item xsl:key no |
3184 |
|
3185 |
Not supported yet. |
3186 |
|
3187 |
=item xsl:message no |
3188 |
|
3189 |
Not supported yet. |
3190 |
|
3191 |
=item xsl:namespace-alias no |
3192 |
|
3193 |
Not supported yet. |
3194 |
|
3195 |
=item xsl:number no |
3196 |
|
3197 |
Not supported yet. |
3198 |
|
3199 |
=item xsl:otherwise yes |
3200 |
|
3201 |
Supported. |
3202 |
|
3203 |
=item xsl:output limited |
3204 |
|
3205 |
Only the initial xsl:output element is used. The "text" output method |
3206 |
is not supported, but shouldn't be difficult to implement. Only the |
3207 |
"doctype-public", "doctype-system", "omit-xml-declaration", "method", |
3208 |
and "encoding" attributes have any support. |
3209 |
|
3210 |
=item xsl:param experimental |
3211 |
|
3212 |
Synonym for xsl:variable (currently). See xsl:variable for support. |
3213 |
|
3214 |
=item xsl:preserve-space no |
3215 |
|
3216 |
Not supported yet. Whitespace is always preserved. |
3217 |
|
3218 |
=item xsl:processing-instruction yes |
3219 |
|
3220 |
Supported. |
3221 |
|
3222 |
=item xsl:sort no |
3223 |
|
3224 |
Not supported yet. |
3225 |
|
3226 |
=item xsl:strip-space no |
3227 |
|
3228 |
Not supported yet. No whitespace is stripped. |
3229 |
|
3230 |
=item xsl:stylesheet limited |
3231 |
|
3232 |
Minor namespace support: other namespace than 'xsl:' for xsl-commands |
3233 |
is allowed if xmlns-attribute is present. xmlns URL is verified. |
3234 |
Other attributes are ignored. |
3235 |
|
3236 |
=item xsl:template limited |
3237 |
|
3238 |
Attribute 'name' and 'match' are supported to minor extend. |
3239 |
('name' must match exactly and 'match' must match with full |
3240 |
path or no path) |
3241 |
|
3242 |
Not supported yet: |
3243 |
- attributes 'priority' and 'mode' |
3244 |
|
3245 |
=item xsl:text yes |
3246 |
|
3247 |
Supported. |
3248 |
|
3249 |
=item xsl:transform limited |
3250 |
|
3251 |
Synonym for xsl:stylesheet |
3252 |
|
3253 |
=item xsl:value-of limited |
3254 |
|
3255 |
Inserts attribute or element values. Limited support: |
3256 |
|
3257 |
<xsl:value-of select="."/> |
3258 |
|
3259 |
<xsl:value-of select="/root-elem"/> |
3260 |
|
3261 |
<xsl:value-of select="elem"/> |
3262 |
|
3263 |
<xsl:value-of select="//elem"/> |
3264 |
|
3265 |
<xsl:value-of select="elem[n]"/> |
3266 |
|
3267 |
<xsl:value-of select="//elem[n]"/> |
3268 |
|
3269 |
<xsl:value-of select="@attr"/> |
3270 |
|
3271 |
<xsl:value-of select="text()"/> |
3272 |
|
3273 |
<xsl:value-of select="processing-instruction()"/> |
3274 |
|
3275 |
<xsl:value-of select="comment()"/> |
3276 |
|
3277 |
and combinations of these. |
3278 |
|
3279 |
Not supported yet: |
3280 |
- attribute 'disable-output-escaping' |
3281 |
|
3282 |
=item xsl:variable experimental |
3283 |
|
3284 |
Very limited. It should be possible to define a variable and use it with |
3285 |
<xsl:value select="$varname" /> within the same template. |
3286 |
|
3287 |
=item xsl:when limited |
3288 |
|
3289 |
Only inside xsl:choose. Limited test support: |
3290 |
|
3291 |
<xsl:when test="@attr='value'"> |
3292 |
|
3293 |
<xsl:when test="elem='value'"> |
3294 |
|
3295 |
<xsl:when test="path/[@attr='value']"> |
3296 |
|
3297 |
<xsl:when test="path/[elem='value']"> |
3298 |
|
3299 |
<xsl:when test="path"> |
3300 |
|
3301 |
path is supported to the same extend as with xsl:value-of |
3302 |
|
3303 |
=item xsl:with-param experimental |
3304 |
|
3305 |
It is currently not functioning. (or is it?) |
3306 |
|
3307 |
=back |
3308 |
|
3309 |
=head1 SUPPORT |
3310 |
|
3311 |
General information, bug reporting tools, the latest version, mailing |
3312 |
lists, etc. can be found at the XML::XSLT homepage: |
3313 |
|
3314 |
http://xmlxslt.sourceforge.net/ |
3315 |
|
3316 |
=head1 DEPRECATIONS |
3317 |
|
3318 |
Methods and interfaces from previous versions that are not documented in this |
3319 |
version are deprecated. Each of these deprecations can still be used |
3320 |
but will produce a warning when the deprecation is first used. You |
3321 |
can use the old interfaces without warnings by passing C<new()> the |
3322 |
flag C<use_deprecated>. Example: |
3323 |
|
3324 |
$parser = XML::XSLT->new($xsl, "FILE", |
3325 |
use_deprecated => 1); |
3326 |
|
3327 |
The deprecated methods will disappear by the time a 1.0 release is made. |
3328 |
|
3329 |
The deprecated methods are : |
3330 |
|
3331 |
=over 2 |
3332 |
|
3333 |
=item output_string |
3334 |
|
3335 |
use toString instead |
3336 |
|
3337 |
=item result_string |
3338 |
|
3339 |
use toString instead |
3340 |
|
3341 |
=item output |
3342 |
|
3343 |
use toString instead |
3344 |
|
3345 |
=item result |
3346 |
|
3347 |
use toString instead |
3348 |
|
3349 |
=item result_mime_type |
3350 |
|
3351 |
use media_type instead |
3352 |
|
3353 |
=item output_mime_type |
3354 |
|
3355 |
use media_type instead |
3356 |
|
3357 |
=item result_tree |
3358 |
|
3359 |
use to_dom instead |
3360 |
|
3361 |
=item output_tree |
3362 |
|
3363 |
use to_dom instead |
3364 |
|
3365 |
=item transform_document |
3366 |
|
3367 |
use transform instead |
3368 |
|
3369 |
=item process_project |
3370 |
|
3371 |
use process instead |
3372 |
|
3373 |
=item open_project |
3374 |
|
3375 |
use C<Source> argument to B<new()> and B<transform> instead. |
3376 |
|
3377 |
=item print_output |
3378 |
|
3379 |
use B<serve()> instead. |
3380 |
|
3381 |
=back |
3382 |
|
3383 |
=head1 BUGS |
3384 |
|
3385 |
Yes. |
3386 |
|
3387 |
=head1 HISTORY |
3388 |
|
3389 |
Geert Josten and Egon Willighagen developed and maintained XML::XSLT |
3390 |
up to version 0.22. At that point, Mark Hershberger started moving |
3391 |
the project to Sourceforge and began working on it with Bron Gondwana. |
3392 |
|
3393 |
=head1 LICENCE |
3394 |
|
3395 |
Copyright (c) 1999 Geert Josten & Egon Willighagen. All Rights |
3396 |
Reserverd. This module is free software, and may be distributed under |
3397 |
the same terms and conditions as Perl. |
3398 |
|
3399 |
=head1 AUTHORS |
3400 |
|
3401 |
Geert Josten <gjosten@sci.kun.nl> |
3402 |
|
3403 |
Egon Willighagen <egonw@sci.kun.nl> |
3404 |
|
3405 |
Mark A. Hershberger <mah@everybody.org> |
3406 |
|
3407 |
Bron Gondwana <perlcode@brong.net> |
3408 |
|
3409 |
Jonathan Stowe <jns@gellyfish.com> |
3410 |
|
3411 |
=head1 SEE ALSO |
3412 |
|
3413 |
L<XML::DOM>, L<LWP::Simple>, L<XML::Parser> |
3414 |
|
3415 |
=cut |