/[cvs]/nfo/perl/libs/XML/XSLT.pm
ViewVC logotype

Contents of /nfo/perl/libs/XML/XSLT.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (show annotations)
Sat Apr 26 01:44:00 2003 UTC (21 years ago) by joko
Branch: MAIN
initial commit - from http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/xmlxslt/XML-XSLT/lib/XML/XSLT.pm?rev=1.19&content-type=text/vnd.viewcvs-markup

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 } else {
2606 $self->debug("no match for test");
2607 return "";
2608 }
2609 }
2610
2611 sub _copy_of {
2612 my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path,
2613 $current_result_node, $variables) = @_;
2614
2615 my $nodelist;
2616 my $select = $xsl_node->getAttribute('select');
2617 $self->debug(qq{evaluating copy-of with select "$select":});;
2618
2619 $self->_indent();
2620 if ($select) {
2621 $nodelist = $self->_get_node_set ($select, $self->xml_document(),
2622 $current_xml_selection_path,
2623 $current_xml_node, $variables);
2624 } else {
2625 $self->warn(q{expected attribute "select" in <} .
2626 $self->xsl_ns() . q{copy-of>});
2627 }
2628 foreach my $node (@$nodelist) {
2629 $self->_add_node ($node, $current_result_node, "deep");
2630 }
2631
2632 $self->_outdent();
2633 }
2634
2635 sub _copy {
2636 my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path,
2637 $current_result_node, $variables, $oldvariables) = @_;
2638
2639
2640 $self->debug("evaluating copy:");;
2641
2642 $self->_indent();
2643 if ($current_xml_node->getNodeType == ATTRIBUTE_NODE) {
2644 my $attribute = $current_xml_node->cloneNode(0);
2645 $current_result_node->setAttributeNode($attribute);
2646 } elsif (($current_xml_node->getNodeType == COMMENT_NODE)
2647 || ($current_xml_node->getNodeType == PROCESSING_INSTRUCTION_NODE)) {
2648 $self->_add_node ($current_xml_node, $current_result_node);
2649 } else {
2650 $self->_add_node ($current_xml_node, $current_result_node);
2651 $self->_evaluate_template ($xsl_node,
2652 $current_xml_node,
2653 $current_xml_selection_path,
2654 $current_result_node->getLastChild,
2655 $variables, $oldvariables);
2656 }
2657 $self->_outdent();
2658 }
2659
2660 sub _text {
2661 #=item addText (text)
2662 #
2663 #Appends the specified string to the last child if it is a Text node, or else
2664 #appends a new Text node (with the specified text.)
2665 #
2666 #Return Value: the last child if it was a Text node or else the new Text node.
2667 my ($self, $xsl_node, $current_result_node) = @_;
2668
2669 $self->debug("inserting text:");
2670
2671 $self->_indent();
2672
2673 $self->debug("stripping node to text:");
2674
2675 $self->_indent();
2676 my $text = $self->__string__ ($xsl_node);
2677 $self->_outdent();
2678
2679 if ($text ne '') {
2680 my $node = $self->xml_document()->createTextNode ($text);
2681 if ($xsl_node->getAttribute ('disable-output-escaping') eq 'yes')
2682 {
2683 $self->debug("disabling output escaping");
2684 bless $node,'XML::XSLT::DOM::TextDOE' ;
2685 }
2686 $self->_move_node ($node, $current_result_node);
2687 } else {
2688 $self->debug("nothing left..");
2689 }
2690
2691 $current_result_node->normalize();
2692
2693 $self->_outdent();
2694 }
2695
2696 sub _attribute {
2697 my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path,
2698 $current_result_node, $variables, $oldvariables) = @_;
2699
2700 my $name = $xsl_node->getAttribute ('name');
2701 $self->debug(qq{inserting attribute named "$name":});
2702 $self->_indent();
2703
2704 if ($name) {
2705 if ( $name =~ /^xmlns:/ )
2706 {
2707 $self->debug("Won't create namespace declaration");
2708 }
2709 else
2710 {
2711 my $result = $self->xml_document()->createDocumentFragment;
2712
2713 $self->_evaluate_template ($xsl_node,
2714 $current_xml_node,
2715 $current_xml_selection_path,
2716 $result, $variables, $oldvariables);
2717
2718 $self->_indent();
2719 my $text = $self->fix_attribute_value($self->__string__ ($result));
2720
2721
2722 $self->_outdent();
2723
2724 $current_result_node->setAttribute($name, $text);
2725 $result->dispose();
2726 }
2727 } else {
2728 $self->warn(q{expected attribute "name" in <} .
2729 $self->xsl_ns() . q{attribute>});
2730 }
2731 $self->_outdent();
2732 }
2733
2734 sub _comment {
2735 my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path,
2736 $current_result_node, $variables, $oldvariables) = @_;
2737
2738 $self->debug("inserting comment:");
2739
2740 $self->_indent();
2741
2742 my $result = $self->xml_document()->createDocumentFragment;
2743
2744 $self->_evaluate_template ($xsl_node,
2745 $current_xml_node,
2746 $current_xml_selection_path,
2747 $result, $variables, $oldvariables);
2748
2749 $self->_indent();
2750 my $text = $self->__string__ ($result);
2751 $self->_outdent();
2752
2753 $self->_move_node ($self->xml_document()->createComment ($text), $current_result_node);
2754 $result->dispose();
2755
2756 $self->_outdent();
2757 }
2758
2759 sub _variable {
2760 my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path,
2761 $current_result_node, $variables, $params, $is_param) = @_;
2762
2763 my $varname = $xsl_node->getAttribute ('name');
2764
2765 if ($varname) {
2766 $self->debug("definition of variable \$$varname:");;
2767
2768 $self->_indent();
2769
2770 if ( $is_param and exists $$params{$varname} ) {
2771 # copy from parent-template
2772
2773 $$variables{$varname} = $$params{$varname};
2774
2775 } else {
2776 # new variable definition
2777
2778 my $value = $xsl_node->getAttribute ('select');
2779
2780 if (! $value) {
2781 #tough case, evaluate content as template
2782
2783 $value = $self->xml_document()->createDocumentFragment;
2784
2785 $self->_evaluate_template ($xsl_node,
2786 $current_xml_node,
2787 $current_xml_selection_path,
2788 $value, $variables, $params);
2789 }
2790
2791 $$variables{$varname} = $value;
2792 }
2793
2794 $self->_outdent();
2795 } else {
2796 $self->warn(q{expected attribute "name" in <} .
2797 $self->xsl_ns() . q{param> or <} .
2798 $self->xsl_ns() . q{variable>});
2799 }
2800 }
2801
2802 # not implemented - but log it and make it go away
2803
2804 sub _sort
2805 {
2806 my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path,
2807 $current_result_node, $variables, $params, $is_param) = @_;
2808
2809 $self->debug("dummy process for sort");
2810 }
2811
2812 # Not quite sure how fallback should be implemented as the spec seems a
2813 # little vague to me
2814
2815 sub _fallback
2816 {
2817 my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path,
2818 $current_result_node, $variables, $params, $is_param) = @_;
2819
2820 $self->debug("dummy process for fallback");
2821 }
2822
2823 # This is a no-op - attribute-sets should not appear within templates and
2824 # we have already processed the stylesheet wide ones.
2825
2826 sub _attribute_set
2827 {
2828 my ($self, $xsl_node, $current_xml_node, $current_xml_selection_path,
2829 $current_result_node, $variables, $params, $is_param) = @_;
2830
2831 $self->debug("in _attribute_set");
2832 }
2833
2834 sub _indent
2835 {
2836 my ( $self ) = @_;
2837 $self->{INDENT} += $self->{INDENT_INCR};
2838
2839 }
2840
2841 sub _outdent
2842 {
2843 my ( $self ) = @_;
2844 $self->{INDENT} -= $self->{INDENT_INCR};
2845 }
2846
2847 sub fix_attribute_value
2848 {
2849 my ( $self, $text ) = @_;
2850
2851 # The spec say's that there can't be a literal line break in the
2852 # attributes value - white space at the beginning or the end is
2853 # almost certainly an mistake.
2854
2855 $text =~ s/^\s+//g;
2856 $text =~ s/\s+$//g;
2857
2858 if ( $text )
2859 {
2860 $text =~ s/([\x0A\x0D])/sprintf("\&#%02X;",ord $1)/eg;
2861 }
2862
2863 return $text;
2864 }
2865
2866 1;
2867
2868 __DATA__
2869
2870 =head1 SYNOPSIS
2871
2872 use XML::XSLT;
2873
2874 my $xslt = XML::XSLT->new ($xsl, warnings => 1);
2875
2876 $xslt->transform ($xmlfile);
2877 print $xslt->toString;
2878
2879 $xslt->dispose();
2880
2881 =head1 DESCRIPTION
2882
2883 This module implements the W3C's XSLT specification. The goal is full
2884 implementation of this spec, but we have not yet achieved
2885 that. However, it already works well. See L<XML::XSLT Commands> for
2886 the current status of each command.
2887
2888 XML::XSLT makes use of XML::DOM and LWP::Simple, while XML::DOM
2889 uses XML::Parser. Therefore XML::Parser, XML::DOM and LWP::Simple
2890 have to be installed properly for XML::XSLT to run.
2891
2892 =head1 Specifying Sources
2893
2894 The stylesheets and the documents may be passed as filenames, file
2895 handles regular strings, string references or DOM-trees. Functions
2896 that require sources (e.g. new), will accept either a named parameter
2897 or simply the argument.
2898
2899 Either of the following are allowed:
2900
2901 my $xslt = XML::XSLT->new($xsl);
2902 my $xslt = XML::XSLT->new(Source => $xsl);
2903
2904 In documentation, the named parameter `Source' is always shown, but it
2905 is never required.
2906
2907 =head2 METHODS
2908
2909 =over 4
2910
2911 =item new(Source => $xml [, %args])
2912
2913 Returns a new XSLT parser object. Valid flags are:
2914
2915 =over 2
2916
2917 =item DOMparser_args
2918
2919 Hashref of arguments to pass to the XML::DOM::Parser object's parse
2920 method.
2921
2922 =item variables
2923
2924 Hashref of variables and their values for the stylesheet.
2925
2926 =item base
2927
2928 Base of URL for file inclusion.
2929
2930 =item debug
2931
2932 Turn on debugging messages.
2933
2934 =item warnings
2935
2936 Turn on warning messages.
2937
2938 =item indent
2939
2940 Starting amount of indention for debug messages. Defaults to 0.
2941
2942 =item indent_incr
2943
2944 Amount to indent each level of debug message. Defaults to 1.
2945
2946 =back
2947
2948 =item open_xml(Source => $xml [, %args])
2949
2950 Gives the XSLT object new XML to process. Returns an XML::DOM object
2951 corresponding to the XML.
2952
2953 =over 4
2954
2955 =item base
2956
2957 The base URL to use for opening documents.
2958
2959 =item parser_args
2960
2961 Arguments to pase to the parser.
2962
2963 =back
2964
2965 =item open_xsl(Source => $xml, [, %args])
2966
2967 Gives the XSLT object a new stylesheet to use in processing XML.
2968 Returns an XML::DOM object corresponding to the stylesheet. Any
2969 arguments present are passed to the XML::DOM::Parser.
2970
2971 =over 4
2972
2973 =item base
2974
2975 The base URL to use for opening documents.
2976
2977 =item parser_args
2978
2979 Arguments to pase to the parser.
2980
2981 =back
2982
2983 =item process(%variables)
2984
2985 Processes the previously loaded XML through the stylesheet using the
2986 variables set in the argument.
2987
2988 =item transform(Source => $xml [, %args])
2989
2990 Processes the given XML through the stylesheet. Returns an XML::DOM
2991 object corresponding to the transformed XML. Any arguments present
2992 are passed to the XML::DOM::Parser.
2993
2994 =item serve(Source => $xml [, %args])
2995
2996 Processes the given XML through the stylesheet. Returns a string
2997 containg the result. Example:
2998
2999 use XML::XSLT qw(serve);
3000
3001 $xslt = XML::XSLT->new($xsl);
3002 print $xslt->serve $xml;
3003
3004 =over 4
3005
3006 =item http_headers
3007
3008 If true, then prepends the appropriate HTTP headers (e.g. Content-Type,
3009 Content-Length);
3010
3011 Defaults to true.
3012
3013 =item xml_declaration
3014
3015 If true, then the result contains the appropriate <?xml?> header.
3016
3017 Defaults to true.
3018
3019 =item xml_version
3020
3021 The version of the XML.
3022
3023 Defaults to 1.0.
3024
3025 =item doctype
3026
3027 The type of DOCTYPE this document is. Defaults to SYSTEM.
3028
3029 =back
3030
3031 =item toString
3032
3033 Returns the result of transforming the XML with the stylesheet as a
3034 string.
3035
3036 =item to_dom
3037
3038 Returns the result of transforming the XML with the stylesheet as an
3039 XML::DOM object.
3040
3041 =item media_type
3042
3043 Returns the media type (aka mime type) of the object.
3044
3045 =item dispose
3046
3047 Executes the C<dispose> method on each XML::DOM object.
3048
3049 =back
3050
3051 =head1 XML::XSLT Commands
3052
3053 =over 4
3054
3055 =item xsl:apply-imports no
3056
3057 Not supported yet.
3058
3059 =item xsl:apply-templates limited
3060
3061 Attribute 'select' is supported to the same extent as xsl:value-of
3062 supports path selections.
3063
3064 Not supported yet:
3065 - attribute 'mode'
3066 - xsl:sort and xsl:with-param in content
3067
3068 =item xsl:attribute partially
3069
3070 Adds an attribute named to the value of the attribute 'name' and as value
3071 the stringified content-template.
3072
3073 Not supported yet:
3074 - attribute 'namespace'
3075
3076 =item xsl:attribute-set yes
3077
3078 Partially
3079
3080 =item xsl:call-template yes
3081
3082 Takes attribute 'name' which selects xsl:template's by name.
3083
3084 Weak support:
3085 - xsl:with-param (select attrib not supported)
3086
3087 Not supported yet:
3088 - xsl:sort
3089
3090 =item xsl:choose yes
3091
3092 Tests sequentially all xsl:whens until one succeeds or
3093 until an xsl:otherwise is found. Limited test support, see xsl:when
3094
3095 =item xsl:comment yes
3096
3097 Supported.
3098
3099 =item xsl:copy partially
3100
3101 =item xsl:copy-of limited
3102
3103 Attribute 'select' functions as well as with
3104 xsl:value-of
3105
3106 =item xsl:decimal-format no
3107
3108 Not supported yet.
3109
3110 =item xsl:element yes
3111
3112 =item xsl:fallback no
3113
3114 Not supported yet.
3115
3116 =item xsl:for-each limited
3117
3118 Attribute 'select' functions as well as with
3119 xsl:value-of
3120
3121 Not supported yet:
3122 - xsl:sort in content
3123
3124 =item xsl:if limited
3125
3126 Identical to xsl:when, but outside xsl:choose context.
3127
3128 =item xsl:import no
3129
3130 Not supported yet.
3131
3132 =item xsl:include yes
3133
3134 Takes attribute href, which can be relative-local,
3135 absolute-local as well as an URL (preceded by
3136 identifier http:).
3137
3138 =item xsl:key no
3139
3140 Not supported yet.
3141
3142 =item xsl:message no
3143
3144 Not supported yet.
3145
3146 =item xsl:namespace-alias no
3147
3148 Not supported yet.
3149
3150 =item xsl:number no
3151
3152 Not supported yet.
3153
3154 =item xsl:otherwise yes
3155
3156 Supported.
3157
3158 =item xsl:output limited
3159
3160 Only the initial xsl:output element is used. The "text" output method
3161 is not supported, but shouldn't be difficult to implement. Only the
3162 "doctype-public", "doctype-system", "omit-xml-declaration", "method",
3163 and "encoding" attributes have any support.
3164
3165 =item xsl:param experimental
3166
3167 Synonym for xsl:variable (currently). See xsl:variable for support.
3168
3169 =item xsl:preserve-space no
3170
3171 Not supported yet. Whitespace is always preserved.
3172
3173 =item xsl:processing-instruction yes
3174
3175 Supported.
3176
3177 =item xsl:sort no
3178
3179 Not supported yet.
3180
3181 =item xsl:strip-space no
3182
3183 Not supported yet. No whitespace is stripped.
3184
3185 =item xsl:stylesheet limited
3186
3187 Minor namespace support: other namespace than 'xsl:' for xsl-commands
3188 is allowed if xmlns-attribute is present. xmlns URL is verified.
3189 Other attributes are ignored.
3190
3191 =item xsl:template limited
3192
3193 Attribute 'name' and 'match' are supported to minor extend.
3194 ('name' must match exactly and 'match' must match with full
3195 path or no path)
3196
3197 Not supported yet:
3198 - attributes 'priority' and 'mode'
3199
3200 =item xsl:text yes
3201
3202 Supported.
3203
3204 =item xsl:transform limited
3205
3206 Synonym for xsl:stylesheet
3207
3208 =item xsl:value-of limited
3209
3210 Inserts attribute or element values. Limited support:
3211
3212 <xsl:value-of select="."/>
3213
3214 <xsl:value-of select="/root-elem"/>
3215
3216 <xsl:value-of select="elem"/>
3217
3218 <xsl:value-of select="//elem"/>
3219
3220 <xsl:value-of select="elem[n]"/>
3221
3222 <xsl:value-of select="//elem[n]"/>
3223
3224 <xsl:value-of select="@attr"/>
3225
3226 <xsl:value-of select="text()"/>
3227
3228 <xsl:value-of select="processing-instruction()"/>
3229
3230 <xsl:value-of select="comment()"/>
3231
3232 and combinations of these.
3233
3234 Not supported yet:
3235 - attribute 'disable-output-escaping'
3236
3237 =item xsl:variable experimental
3238
3239 Very limited. It should be possible to define a variable and use it with
3240 &lt;xsl:value select="$varname" /&gt; within the same template.
3241
3242 =item xsl:when limited
3243
3244 Only inside xsl:choose. Limited test support:
3245
3246 <xsl:when test="@attr='value'">
3247
3248 <xsl:when test="elem='value'">
3249
3250 <xsl:when test="path/[@attr='value']">
3251
3252 <xsl:when test="path/[elem='value']">
3253
3254 <xsl:when test="path">
3255
3256 path is supported to the same extend as with xsl:value-of
3257
3258 =item xsl:with-param experimental
3259
3260 It is currently not functioning. (or is it?)
3261
3262 =back
3263
3264 =head1 SUPPORT
3265
3266 General information, bug reporting tools, the latest version, mailing
3267 lists, etc. can be found at the XML::XSLT homepage:
3268
3269 http://xmlxslt.sourceforge.net/
3270
3271 =head1 DEPRECATIONS
3272
3273 Methods and interfaces from previous versions that are not documented in this
3274 version are deprecated. Each of these deprecations can still be used
3275 but will produce a warning when the deprecation is first used. You
3276 can use the old interfaces without warnings by passing C<new()> the
3277 flag C<use_deprecated>. Example:
3278
3279 $parser = XML::XSLT->new($xsl, "FILE",
3280 use_deprecated => 1);
3281
3282 The deprecated methods will disappear by the time a 1.0 release is made.
3283
3284 The deprecated methods are :
3285
3286 =over 2
3287
3288 =item output_string
3289
3290 use toString instead
3291
3292 =item result_string
3293
3294 use toString instead
3295
3296 =item output
3297
3298 use toString instead
3299
3300 =item result
3301
3302 use toString instead
3303
3304 =item result_mime_type
3305
3306 use media_type instead
3307
3308 =item output_mime_type
3309
3310 use media_type instead
3311
3312 =item result_tree
3313
3314 use to_dom instead
3315
3316 =item output_tree
3317
3318 use to_dom instead
3319
3320 =item transform_document
3321
3322 use transform instead
3323
3324 =item process_project
3325
3326 use process instead
3327
3328 =item open_project
3329
3330 use C<Source> argument to B<new()> and B<transform> instead.
3331
3332 =item print_output
3333
3334 use B<serve()> instead.
3335
3336 =back
3337
3338 =head1 BUGS
3339
3340 Yes.
3341
3342 =head1 HISTORY
3343
3344 Geert Josten and Egon Willighagen developed and maintained XML::XSLT
3345 up to version 0.22. At that point, Mark Hershberger started moving
3346 the project to Sourceforge and began working on it with Bron Gondwana.
3347
3348 =head1 LICENCE
3349
3350 Copyright (c) 1999 Geert Josten & Egon Willighagen. All Rights
3351 Reserverd. This module is free software, and may be distributed under
3352 the same terms and conditions as Perl.
3353
3354 =head1 AUTHORS
3355
3356 Geert Josten <gjosten@sci.kun.nl>
3357
3358 Egon Willighagen <egonw@sci.kun.nl>
3359
3360 Mark A. Hershberger <mah@everybody.org>
3361
3362 Bron Gondwana <perlcode@brong.net>
3363
3364 Jonathan Stowe <jns@gellyfish.com>
3365
3366 =head1 SEE ALSO
3367
3368 L<XML::DOM>, L<LWP::Simple>, L<XML::Parser>
3369
3370 =cut

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed