/[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.2 - (hide annotations)
Wed Apr 30 00:06:55 2003 UTC (21 years ago) by joko
Branch: MAIN
Changes since 1.1: +47 -2 lines
NEW: sub __evaluate_test__ now detects a variable name in an lvalue of an expression (e.g. $var=val, {$var}=val)

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

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