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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (hide annotations)
Sat Apr 26 01:44:00 2003 UTC (21 years, 6 months 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 joko 1.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