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

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

Parent Directory Parent Directory | Revision Log Revision Log


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