2 Copyright (C) 2004,2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu
.xml
.transform
;
40 import gnu
.java
.lang
.CPStringBuilder
;
42 import java
.text
.DecimalFormat
;
43 import java
.text
.DecimalFormatSymbols
;
44 import java
.util
.ArrayList
;
45 import java
.util
.Collection
;
46 import java
.util
.Collections
;
47 import java
.util
.HashSet
;
48 import java
.util
.Iterator
;
49 import java
.util
.LinkedHashMap
;
50 import java
.util
.LinkedHashSet
;
51 import java
.util
.LinkedList
;
52 import java
.util
.List
;
55 import java
.util
.StringTokenizer
;
56 import javax
.xml
.XMLConstants
;
57 import javax
.xml
.namespace
.NamespaceContext
;
58 import javax
.xml
.namespace
.QName
;
59 import javax
.xml
.transform
.Source
;
60 import javax
.xml
.transform
.TransformerConfigurationException
;
61 import javax
.xml
.transform
.TransformerException
;
62 import javax
.xml
.xpath
.XPathFunction
;
63 import javax
.xml
.xpath
.XPathFunctionResolver
;
64 import javax
.xml
.xpath
.XPathExpressionException
;
65 import org
.w3c
.dom
.Attr
;
66 import org
.w3c
.dom
.Document
;
67 import org
.w3c
.dom
.DOMException
;
68 import org
.w3c
.dom
.Element
;
69 import org
.w3c
.dom
.NamedNodeMap
;
70 import org
.w3c
.dom
.Node
;
71 import org
.w3c
.dom
.Text
;
72 import org
.w3c
.dom
.UserDataHandler
;
73 import gnu
.xml
.xpath
.Expr
;
74 import gnu
.xml
.xpath
.NameTest
;
75 import gnu
.xml
.xpath
.NodeTypeTest
;
76 import gnu
.xml
.xpath
.Pattern
;
77 import gnu
.xml
.xpath
.Selector
;
78 import gnu
.xml
.xpath
.Root
;
79 import gnu
.xml
.xpath
.Test
;
80 import gnu
.xml
.xpath
.XPathImpl
;
85 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
88 implements NamespaceContext
, XPathFunctionResolver
, UserDataHandler
, Cloneable
91 static final String XSL_NS
= "http://www.w3.org/1999/XSL/Transform";
92 private static final NameTest STYLESHEET_PRESERVE_TEXT
=
93 new NameTest(new QName(XSL_NS
, "text"), false, false);
95 static final int OUTPUT_XML
= 0;
96 static final int OUTPUT_HTML
= 1;
97 static final int OUTPUT_TEXT
= 2;
99 final TransformerFactoryImpl factory
;
100 TransformerImpl transformer
;
102 final XPathImpl xpath
;
103 final String systemId
;
104 final int precedence
;
113 Collection
<String
> extensionElementPrefixes
;
114 Collection
<String
> excludeResultPrefixes
;
117 * Set of element names for which we should strip whitespace.
119 Set
<StrippingInstruction
> stripSpace
;
122 * Set of element names for which we should preserve whitespace.
124 Set
<StrippingInstruction
> preserveSpace
;
131 String outputVersion
;
132 String outputEncoding
;
133 boolean outputOmitXmlDeclaration
;
134 boolean outputStandalone
;
135 String outputPublicId
;
136 String outputSystemId
;
137 Collection
<String
> outputCdataSectionElements
;
138 boolean outputIndent
;
139 String outputMediaType
;
144 Collection
<Key
> keys
;
149 Map
<String
,DecimalFormat
> decimalFormats
;
154 Map
<String
,String
> namespaceAliases
;
159 List
<AttributeSet
> attributeSets
;
164 List
<ParameterNode
> variables
;
167 * Variable and parameter bindings.
174 LinkedList
<Template
> templates
;
176 TemplateNode builtInNodeTemplate
;
177 TemplateNode builtInTextTemplate
;
180 * Holds the current node while parsing.
181 * Necessary to associate the document function with its declaring node,
182 * to resolve namespaces, and to maintain the current node for the
183 * current() function.
188 * Set by a terminating message.
190 transient boolean terminated
;
193 * Current template in force.
195 transient Template currentTemplate
;
197 Stylesheet(TransformerFactoryImpl factory
,
202 throws TransformerConfigurationException
204 this.factory
= factory
;
205 this.systemId
= systemId
;
206 this.precedence
= precedence
;
207 this.parent
= parent
;
208 extensionElementPrefixes
= new HashSet
<String
>();
209 excludeResultPrefixes
= new HashSet
<String
>();
210 stripSpace
= new LinkedHashSet
<StrippingInstruction
>();
211 preserveSpace
= new LinkedHashSet
<StrippingInstruction
>();
212 outputCdataSectionElements
= new LinkedHashSet
<String
>();
213 xpath
= (XPathImpl
) factory
.xpathFactory
.newXPath();
214 xpath
.setNamespaceContext(this);
217 bindings
= new Bindings(this);
218 attributeSets
= new LinkedList
<AttributeSet
>();
219 variables
= new LinkedList
<ParameterNode
>();
220 namespaceAliases
= new LinkedHashMap
<String
,String
>();
221 templates
= new LinkedList
<Template
>();
222 keys
= new LinkedList
<Key
>();
223 decimalFormats
= new LinkedHashMap
<String
,DecimalFormat
>();
224 initDefaultDecimalFormat();
225 xpath
.setXPathFunctionResolver(this);
229 /* Test for import circularity */
230 for (Stylesheet ctx
= this; ctx
.parent
!= null; ctx
= ctx
.parent
)
232 if (systemId
!= null && systemId
.equals(ctx
.parent
.systemId
))
234 String msg
= "circularity importing " + systemId
;
235 throw new TransformerConfigurationException(msg
);
239 Stylesheet root
= getRootStylesheet();
240 bindings
= root
.bindings
;
241 attributeSets
= root
.attributeSets
;
242 variables
= root
.variables
;
243 namespaceAliases
= root
.namespaceAliases
;
244 templates
= root
.templates
;
246 decimalFormats
= root
.decimalFormats
;
247 xpath
.setXPathFunctionResolver(root
);
249 xpath
.setXPathVariableResolver(bindings
);
251 Test anyNode
= new NodeTypeTest((short) 0);
252 List
<Test
> tests
= Collections
.singletonList(anyNode
);
253 builtInNodeTemplate
=
254 new ApplyTemplatesNode(new Selector(Selector
.CHILD
, tests
),
255 null, null, null, true);
256 builtInTextTemplate
=
257 new ValueOfNode(new Selector(Selector
.SELF
, tests
),
260 parse(doc
.getDocumentElement(), true);
261 current
= doc
; // Alow namespace resolution during processing
263 debug
= ("yes".equals(System
.getProperty("xsl.debug")));
267 System
.err
.println("Stylesheet: " + doc
.getDocumentURI());
268 for (Template t
: templates
)
271 System
.err
.println("--------------------");
276 Stylesheet
getRootStylesheet()
278 Stylesheet stylesheet
= this;
279 while (stylesheet
.parent
!= null)
280 stylesheet
= stylesheet
.parent
;
284 void initDefaultDecimalFormat()
286 DecimalFormat defaultDecimalFormat
= new DecimalFormat();
287 DecimalFormatSymbols symbols
= new DecimalFormatSymbols();
288 symbols
.setDecimalSeparator('.');
289 symbols
.setGroupingSeparator(',');
290 symbols
.setPercent('%');
291 symbols
.setPerMill('\u2030');
292 symbols
.setZeroDigit('0');
293 symbols
.setDigit('#');
294 symbols
.setPatternSeparator(';');
295 symbols
.setInfinity("Infinity");
296 symbols
.setNaN("NaN");
297 symbols
.setMinusSign('-');
298 defaultDecimalFormat
.setDecimalFormatSymbols(symbols
);
299 decimalFormats
.put(null, defaultDecimalFormat
);
304 public Object
clone()
308 Stylesheet clone
= (Stylesheet
) super.clone();
309 clone
.bindings
= (Bindings
) bindings
.clone();
311 LinkedList
<Template
> templates2
= new LinkedList
<Template
>();
312 for (Template t
: templates
)
314 templates2
.add(t
.clone(clone
));
316 clone
.templates
= templates2
;
318 LinkedList
<AttributeSet
> attributeSets2
= new LinkedList
<AttributeSet
>();
319 for (AttributeSet as
: attributeSets
)
321 attributeSets2
.add(as
.clone(clone
));
323 clone
.attributeSets
= attributeSets2
;
325 LinkedList
<ParameterNode
> variables2
= new LinkedList
<ParameterNode
>();
326 for (ParameterNode var
: variables
)
328 variables2
.add(var
.clone(clone
));
330 clone
.variables
= variables2
;
332 LinkedList
<Key
> keys2
= new LinkedList
<Key
>();
335 keys2
.add(k
.clone(clone
));
341 catch (CloneNotSupportedException e
)
343 throw new Error(e
.getMessage());
347 // -- Variable evaluation --
349 void initTopLevelVariables(Node context
)
350 throws TransformerException
353 // Sort the variables into order
354 // See XSLT 11.4: "If the template or expression specifying the value of
355 // a global variable x references a global variable y, then the value
356 // for y must be computed before the value of x."
357 List
<ParameterNode
> topLevel
= new ArrayList
<ParameterNode
>(variables
);
358 Collections
.sort(topLevel
);
359 for (ParameterNode var
: topLevel
)
361 bindings
.set(var
.name
,
362 var
.getValue(this, null, context
, 1, 1),
368 // -- NamespaceContext --
370 public String
getNamespaceURI(String prefix
)
372 return (current
== null) ?
null : current
.lookupNamespaceURI(prefix
);
375 public String
getPrefix(String namespaceURI
)
377 return (current
== null) ?
null : current
.lookupPrefix(namespaceURI
);
380 public Iterator
<String
> getPrefixes(String namespaceURI
)
383 return Collections
.singleton(getPrefix(namespaceURI
)).iterator();
386 final QName
getQName(String name
)
388 String localName
= name
, uri
= null, prefix
= null;
389 int ci
= name
.indexOf(':');
392 prefix
= name
.substring(0, ci
);
393 localName
= name
.substring(ci
+ 1);
394 uri
= getNamespaceURI(prefix
);
396 return new QName(uri
, localName
, prefix
);
399 // -- Template selection --
401 TemplateNode
getTemplate(QName mode
, Node context
, boolean applyImports
)
402 throws TransformerException
405 System
.err
.println("getTemplate: mode="+mode
+" context="+context
);
406 Template selected
= null;
407 for (Template t
: templates
)
409 boolean isMatch
= t
.matches(mode
, context
);
412 if (currentTemplate
== null)
414 String msg
= "current template may not be null " +
415 "during apply-imports";
416 throw new TransformerException(msg
);
418 if (!currentTemplate
.imports(t
))
421 //System.err.println("\t"+context+" "+t+"="+isMatch);
424 // Conflict resolution
425 // @see http://www.w3.org/TR/xslt#conflict
426 if (selected
== null)
430 if (t
.precedence
< selected
.precedence
||
431 t
.priority
< selected
.priority
)
437 if (selected
== null)
439 // Apply built-in template
440 // Current template is unchanged
442 System
.err
.println("\tbuiltInTemplate context="+context
);
443 switch (context
.getNodeType())
445 case Node
.ELEMENT_NODE
:
446 case Node
.DOCUMENT_NODE
:
447 case Node
.DOCUMENT_FRAGMENT_NODE
:
448 case Node
.PROCESSING_INSTRUCTION_NODE
:
449 case Node
.COMMENT_NODE
:
450 return builtInNodeTemplate
;
452 case Node
.CDATA_SECTION_NODE
:
453 case Node
.ATTRIBUTE_NODE
:
454 return builtInTextTemplate
;
459 // Set current template
460 currentTemplate
= selected
;
462 System
.err
.println("\ttemplate="+currentTemplate
+" context="+context
);
463 return currentTemplate
.node
;
466 TemplateNode
getTemplate(QName mode
, QName name
)
467 throws TransformerException
469 Template selected
= null;
470 for (Template t
: templates
)
472 boolean isMatch
= t
.matches(name
);
475 // Conflict resolution
476 // @see http://www.w3.org/TR/xslt#conflict
477 if (selected
== null)
481 if (t
.precedence
< selected
.precedence
||
482 t
.priority
< selected
.priority
)
488 if (selected
== null)
490 return selected
.node
;
496 final Template
parseTemplate(Node node
, NamedNodeMap attrs
)
497 throws TransformerConfigurationException
, XPathExpressionException
499 String n
= getAttribute(attrs
, "name");
500 QName name
= (n
== null) ?
null : getQName(n
);
501 String m
= getAttribute(attrs
, "match");
502 Pattern match
= null;
507 match
= (Pattern
) xpath
.compile(m
);
509 catch (ClassCastException e
)
511 String msg
= "illegal pattern: " + m
;
512 throw new TransformerConfigurationException(msg
);
515 String p
= getAttribute(attrs
, "priority");
516 String mm
= getAttribute(attrs
, "mode");
517 QName mode
= (mm
== null) ?
null : getQName(mm
);
518 Node children
= node
.getFirstChild();
519 return new Template(this, name
, match
, parse(children
),
520 precedence
, p
, mode
);
526 final void parseOutput(Node node
, NamedNodeMap attrs
)
527 throws TransformerConfigurationException
530 String method
= getAttribute(attrs
, "method");
531 if ("xml".equals(method
) || method
== null)
532 outputMethod
= OUTPUT_XML
;
533 else if ("html".equals(method
))
534 outputMethod
= OUTPUT_HTML
;
535 else if ("text".equals(method
))
536 outputMethod
= OUTPUT_TEXT
;
539 String msg
= "unsupported output method: " + method
;
540 DOMSourceLocator l
= new DOMSourceLocator(node
);
541 throw new TransformerConfigurationException(msg
, l
);
543 outputPublicId
= getAttribute(attrs
, "doctype-public");
544 outputSystemId
= getAttribute(attrs
, "doctype-system");
545 outputEncoding
= getAttribute(attrs
, "encoding");
546 String indent
= getAttribute(attrs
, "indent");
548 outputIndent
= "yes".equals(indent
);
549 outputVersion
= getAttribute(attrs
, "version");
550 String omitXmlDecl
= getAttribute(attrs
, "omit-xml-declaration");
551 if (omitXmlDecl
!= null)
552 outputOmitXmlDeclaration
= "yes".equals(omitXmlDecl
);
553 String standalone
= getAttribute(attrs
, "standalone");
554 if (standalone
!= null)
555 outputStandalone
= "yes".equals(standalone
);
556 outputMediaType
= getAttribute(attrs
, "media-type");
557 String cdataSectionElements
=
558 getAttribute(attrs
, "cdata-section-elements");
559 if (cdataSectionElements
!= null)
561 StringTokenizer st
= new StringTokenizer(cdataSectionElements
, " ");
562 while (st
.hasMoreTokens())
563 outputCdataSectionElements
.add(st
.nextToken());
570 final void parseKey(Node node
, NamedNodeMap attrs
)
571 throws TransformerConfigurationException
, XPathExpressionException
573 String n
= getRequiredAttribute(attrs
, "name", node
);
574 String m
= getRequiredAttribute(attrs
, "match", node
);
575 String u
= getRequiredAttribute(attrs
, "use", node
);
576 QName name
= getQName(n
);
577 Expr use
= (Expr
) xpath
.compile(u
);
580 Pattern match
= (Pattern
) xpath
.compile(m
);
581 Key key
= new Key(name
, match
, use
);
584 catch (ClassCastException e
)
586 throw new TransformerConfigurationException("invalid pattern: " + m
);
593 final void parseDecimalFormat(Node node
, NamedNodeMap attrs
)
594 throws TransformerConfigurationException
596 String dfName
= getAttribute(attrs
, "name");
597 DecimalFormat df
= new DecimalFormat();
598 DecimalFormatSymbols symbols
= new DecimalFormatSymbols();
599 symbols
.setDecimalSeparator(parseDFChar(attrs
, "decimal-separator", '.'));
600 symbols
.setGroupingSeparator(parseDFChar(attrs
, "grouping-separator", ','));
601 symbols
.setInfinity(parseDFString(attrs
, "infinity", "Infinity"));
602 symbols
.setMinusSign(parseDFChar(attrs
, "minus-sign", '-'));
603 symbols
.setNaN(parseDFString(attrs
, "NaN", "NaN"));
604 symbols
.setPercent(parseDFChar(attrs
, "percent", '%'));
605 symbols
.setPerMill(parseDFChar(attrs
, "per-mille", '\u2030'));
606 symbols
.setZeroDigit(parseDFChar(attrs
, "zero-digit", '0'));
607 symbols
.setDigit(parseDFChar(attrs
, "digit", '#'));
608 symbols
.setPatternSeparator(parseDFChar(attrs
, "pattern-separator", ';'));
609 df
.setDecimalFormatSymbols(symbols
);
610 decimalFormats
.put(dfName
, df
);
613 private final char parseDFChar(NamedNodeMap attrs
, String name
, char def
)
614 throws TransformerConfigurationException
616 Node attr
= attrs
.getNamedItem(name
);
619 return (attr
== null) ? def
: attr
.getNodeValue().charAt(0);
621 catch (StringIndexOutOfBoundsException e
)
623 throw new TransformerConfigurationException("empty attribute '" +
625 "' in decimal-format", e
);
629 private final String
parseDFString(NamedNodeMap attrs
, String name
,
632 Node attr
= attrs
.getNamedItem(name
);
633 return (attr
== null) ? def
: attr
.getNodeValue();
639 final void parseNamespaceAlias(Node node
, NamedNodeMap attrs
)
640 throws TransformerConfigurationException
642 String sp
= getRequiredAttribute(attrs
, "stylesheet-prefix", node
);
643 String rp
= getRequiredAttribute(attrs
, "result-prefix", node
);
644 namespaceAliases
.put(sp
, rp
);
650 final void parseAttributeSet(Node node
, NamedNodeMap attrs
)
651 throws TransformerConfigurationException
, XPathExpressionException
653 TemplateNode children
= parse(node
.getFirstChild());
654 String name
= getRequiredAttribute(attrs
, "name", node
);
655 String uas
= getAttribute(attrs
, "use-attribute-sets");
656 attributeSets
.add(new AttributeSet(children
, name
, uas
));
660 * Parse top-level elements.
662 void parse(Node node
, boolean root
)
663 throws TransformerConfigurationException
669 node
= node
.getNextSibling();
673 void doParse(Node node
, boolean root
)
674 throws TransformerConfigurationException
678 String namespaceUri
= node
.getNamespaceURI();
679 if (XSL_NS
.equals(namespaceUri
) &&
680 node
.getNodeType() == Node
.ELEMENT_NODE
)
682 String name
= node
.getLocalName();
683 NamedNodeMap attrs
= node
.getAttributes();
684 if ("stylesheet".equals(name
))
686 version
= getAttribute(attrs
, "version");
687 String eep
= getAttribute(attrs
, "extension-element-prefixes");
690 StringTokenizer st
= new StringTokenizer(eep
);
691 while (st
.hasMoreTokens())
693 extensionElementPrefixes
.add(st
.nextToken());
696 String erp
= getAttribute(attrs
, "exclude-result-prefixes");
699 StringTokenizer st
= new StringTokenizer(erp
);
700 while (st
.hasMoreTokens())
702 excludeResultPrefixes
.add(st
.nextToken());
705 parse(node
.getFirstChild(), false);
707 else if ("template".equals(name
))
708 templates
.add(parseTemplate(node
, attrs
));
709 else if ("param".equals(name
) ||
710 "variable".equals(name
))
712 int type
= "variable".equals(name
) ?
713 Bindings
.VARIABLE
: Bindings
.PARAM
;
714 TemplateNode content
= parse(node
.getFirstChild());
716 getQName(getRequiredAttribute(attrs
, "name", node
));
717 String select
= getAttribute(attrs
, "select");
719 if (select
!= null && select
.length() > 0)
723 String msg
= "parameter '" + paramName
+
724 "' has both select and content";
725 DOMSourceLocator l
= new DOMSourceLocator(node
);
726 throw new TransformerConfigurationException(msg
, l
);
728 Expr expr
= (Expr
) xpath
.compile(select
);
729 param
= new ParameterNode(paramName
, expr
, type
);
733 param
= new ParameterNode(paramName
, null, type
);
734 param
.children
= content
;
736 variables
.add(param
);
738 else if ("include".equals(name
) || "import".equals(name
))
740 int delta
= "import".equals(name
) ?
-1 : 0;
741 String href
= getRequiredAttribute(attrs
, "href", node
);
743 synchronized (factory
.resolver
)
745 if (transformer
!= null)
748 .setUserResolver(transformer
.getURIResolver());
750 .setUserListener(transformer
.getErrorListener());
752 source
= factory
.resolver
.resolve(systemId
, href
);
754 factory
.newStylesheet(source
, precedence
+ delta
, this);
756 else if ("output".equals(name
))
757 parseOutput(node
, attrs
);
758 else if ("preserve-space".equals(name
))
761 getRequiredAttribute(attrs
, "elements", node
);
762 StringTokenizer st
= new StringTokenizer(elements
,
764 while (st
.hasMoreTokens())
766 NameTest element
= parseNameTest(st
.nextToken());
767 preserveSpace
.add(new StrippingInstruction(element
,
771 else if ("strip-space".equals(name
))
774 getRequiredAttribute(attrs
, "elements", node
);
775 StringTokenizer st
= new StringTokenizer(elements
,
777 while (st
.hasMoreTokens())
779 NameTest element
= parseNameTest(st
.nextToken());
780 stripSpace
.add(new StrippingInstruction(element
,
784 else if ("key".equals(name
))
785 parseKey(node
, attrs
);
786 else if ("decimal-format".equals(name
))
787 parseDecimalFormat(node
, attrs
);
788 else if ("namespace-alias".equals(name
))
789 parseNamespaceAlias(node
, attrs
);
790 else if ("attribute-set".equals(name
))
791 parseAttributeSet(node
, attrs
);
795 // Literal document element
797 ((Element
)node
).getAttributeNodeNS(XSL_NS
, "version");
798 if (versionNode
== null)
800 String msg
= "no xsl:version attribute on literal result node";
801 DOMSourceLocator l
= new DOMSourceLocator(node
);
802 throw new TransformerConfigurationException(msg
, l
);
804 version
= versionNode
.getValue();
805 Node rootClone
= node
.cloneNode(true);
806 NamedNodeMap attrs
= rootClone
.getAttributes();
807 attrs
.removeNamedItemNS(XSL_NS
, "version");
808 templates
.add(new Template(this, null, new Root(),
816 // Skip unknown elements, text, comments, etc
819 catch (TransformerException e
)
821 DOMSourceLocator l
= new DOMSourceLocator(node
);
822 throw new TransformerConfigurationException(e
.getMessage(), l
, e
);
824 catch (DOMException e
)
826 DOMSourceLocator l
= new DOMSourceLocator(node
);
827 throw new TransformerConfigurationException(e
.getMessage(), l
, e
);
829 catch (XPathExpressionException e
)
831 DOMSourceLocator l
= new DOMSourceLocator(node
);
832 throw new TransformerConfigurationException(e
.getMessage(), l
, e
);
836 final NameTest
parseNameTest(String token
)
838 if ("*".equals(token
))
839 return new NameTest(null, true, true);
840 else if (token
.endsWith(":*"))
842 QName qName
= getQName(token
);
843 return new NameTest(qName
, true, false);
847 QName qName
= getQName(token
);
848 return new NameTest(qName
, false, false);
852 final TemplateNode
parseAttributeValueTemplate(String value
, Node source
)
853 throws TransformerConfigurationException
, XPathExpressionException
857 int len
= value
.length();
859 List
<String
> tokens
= new ArrayList
<String
>(); // text tokens
860 List
<Boolean
> types
= new ArrayList
<Boolean
>(); // literal or expression
862 for (int i
= 0; i
< len
; i
++)
864 char c
= value
.charAt(i
);
867 if (i
< (len
- 1) && value
.charAt(i
+ 1) == '{')
869 tokens
.add(value
.substring(off
, i
+ 1));
870 types
.add(Boolean
.FALSE
);
879 tokens
.add(value
.substring(off
, i
));
880 types
.add(Boolean
.FALSE
);
888 if (i
< (len
- 1) && value
.charAt(i
+ 1) == '}')
890 tokens
.add(value
.substring(off
, i
+ 1));
891 types
.add(Boolean
.FALSE
);
900 tokens
.add(value
.substring(off
, i
));
901 types
.add(Boolean
.TRUE
);
905 String msg
= "attribute value template " +
906 "must contain expression: " + value
;
907 DOMSourceLocator l
= new DOMSourceLocator(source
);
908 throw new TransformerConfigurationException(msg
, l
);
917 String msg
= "invalid attribute value template: " + value
;
918 throw new TransformerConfigurationException(msg
);
923 tokens
.add(value
.substring(off
));
924 types
.add(Boolean
.FALSE
);
927 // Construct template node tree
928 TemplateNode ret
= null;
929 Document doc
= source
.getOwnerDocument();
931 for (int i
= len
- 1; i
>= 0; i
--)
933 String token
= tokens
.get(i
);
934 Boolean type
= types
.get(i
);
935 if (type
== Boolean
.TRUE
)
938 Expr select
= (Expr
) xpath
.compile(token
);
939 TemplateNode ret2
= new ValueOfNode(select
, false);
946 TemplateNode ret2
= new LiteralNode(doc
.createTextNode(token
));
955 * Whitespace stripping.
956 * @param text the text node
957 * @param source true if a source node, false if a stylesheet text node
958 * @see http://www.w3.org/TR/xslt#strip
960 boolean isPreserved(Text text
, boolean source
)
961 throws TransformerConfigurationException
963 // Check characters in text
964 String value
= text
.getData();
967 int len
= value
.length();
968 for (int i
= 0; i
< len
; i
++)
970 char c
= value
.charAt(i
);
971 if (c
!= 0x20 && c
!= 0x09 && c
!= 0x0a && c
!= 0x0d)
976 Node ctx
= text
.getParentNode();
979 // Source document text node
980 boolean preserve
= true;
981 float psPriority
= 0.0f
, ssPriority
= 0.0f
;
982 if (!stripSpace
.isEmpty())
984 // Conflict resolution
985 StrippingInstruction ssi
= null, psi
= null;
986 for (StrippingInstruction si
: stripSpace
)
988 if (si
.element
.matches(ctx
, 1, 1))
992 if (si
.precedence
< ssi
.precedence
)
994 float p
= si
.getPriority();
1001 for (StrippingInstruction si
: preserveSpace
)
1003 if (si
.element
.matches(ctx
, 1, 1))
1007 if (si
.precedence
< psi
.precedence
)
1009 float p
= si
.getPriority();
1020 if (psi
.precedence
< ssi
.precedence
)
1022 else if (psPriority
< ssPriority
)
1034 // Stylesheet text node
1035 if (STYLESHEET_PRESERVE_TEXT
.matches(ctx
, 1, 1))
1038 // Check whether any ancestor specified xml:space
1041 if (ctx
.getNodeType() == Node
.ELEMENT_NODE
)
1043 Element element
= (Element
) ctx
;
1044 String xmlSpace
= element
.getAttribute("xml:space");
1045 if ("default".equals(xmlSpace
))
1047 else if ("preserve".equals(xmlSpace
))
1049 else if (xmlSpace
.length() > 0)
1051 String msg
= "Illegal value for xml:space: " + xmlSpace
;
1052 throw new TransformerConfigurationException(msg
);
1055 ctx
= ctx
.getParentNode();
1060 public XPathFunction
resolveFunction(QName name
, int arity
)
1062 String uri
= name
.getNamespaceURI();
1063 if (XSL_NS
.equals(uri
) || uri
== null || uri
.length() == 0)
1065 String localName
= name
.getLocalPart();
1066 if ("document".equals(localName
) && (arity
== 1 || arity
== 2))
1068 if (current
== null)
1069 throw new RuntimeException("current is null");
1070 return new DocumentFunction(getRootStylesheet(), current
);
1072 else if ("key".equals(localName
) && (arity
== 2))
1073 return new KeyFunction(getRootStylesheet());
1074 else if ("format-number".equals(localName
) &&
1075 (arity
== 2 || arity
== 3))
1076 return new FormatNumberFunction(getRootStylesheet());
1077 else if ("current".equals(localName
) && (arity
== 0))
1078 return new CurrentFunction(getRootStylesheet());
1079 else if ("unparsed-entity-uri".equals(localName
) && (arity
== 1))
1080 return new UnparsedEntityUriFunction();
1081 else if ("generate-id".equals(localName
) &&
1082 (arity
== 1 || arity
== 0))
1083 return new GenerateIdFunction();
1084 else if ("system-property".equals(localName
) && (arity
== 1))
1085 return new SystemPropertyFunction();
1086 else if ("element-available".equals(localName
) && (arity
== 1))
1087 return new ElementAvailableFunction(new NamespaceProxy(current
));
1088 else if ("function-available".equals(localName
) && (arity
== 1))
1089 return new FunctionAvailableFunction(new NamespaceProxy(current
));
1099 final TemplateNode
parseApplyTemplates(Node node
)
1100 throws TransformerConfigurationException
, XPathExpressionException
1102 NamedNodeMap attrs
= node
.getAttributes();
1103 String m
= getAttribute(attrs
, "mode");
1104 QName mode
= (m
== null) ?
null : getQName(m
);
1105 String s
= getAttribute(attrs
, "select");
1107 s
= "child::node()";
1108 Node children
= node
.getFirstChild();
1109 List
<SortKey
> sortKeys
= parseSortKeys(children
);
1110 List
<WithParam
> withParams
= parseWithParams(children
);
1111 Expr select
= (Expr
) xpath
.compile(s
);
1112 return new ApplyTemplatesNode(select
, mode
,
1113 sortKeys
, withParams
, false);
1119 final TemplateNode
parseCallTemplate(Node node
)
1120 throws TransformerConfigurationException
, XPathExpressionException
1122 NamedNodeMap attrs
= node
.getAttributes();
1123 String n
= getRequiredAttribute(attrs
, "name", node
);
1124 QName name
= getQName(n
);
1125 Node children
= node
.getFirstChild();
1126 List
<WithParam
> withParams
= parseWithParams(children
);
1127 return new CallTemplateNode(name
, withParams
);
1133 final TemplateNode
parseValueOf(Node node
)
1134 throws TransformerConfigurationException
, XPathExpressionException
1136 NamedNodeMap attrs
= node
.getAttributes();
1137 String s
= getRequiredAttribute(attrs
, "select", node
);
1138 String doe
= getAttribute(attrs
, "disable-output-escaping");
1139 boolean d
= "yes".equals(doe
);
1140 Expr select
= (Expr
) xpath
.compile(s
);
1141 return new ValueOfNode(select
, d
);
1147 final TemplateNode
parseForEach(Node node
)
1148 throws TransformerConfigurationException
, XPathExpressionException
1150 NamedNodeMap attrs
= node
.getAttributes();
1151 String s
= getRequiredAttribute(attrs
, "select", node
);
1152 Node children
= node
.getFirstChild();
1153 List
<SortKey
> sortKeys
= parseSortKeys(children
);
1154 Expr select
= (Expr
) xpath
.compile(s
);
1155 ForEachNode ret
= new ForEachNode(select
, sortKeys
);
1156 ret
.children
= parse(children
);
1163 final TemplateNode
parseIf(Node node
)
1164 throws TransformerConfigurationException
, XPathExpressionException
1166 NamedNodeMap attrs
= node
.getAttributes();
1167 String t
= getRequiredAttribute(attrs
, "test", node
);
1168 Expr test
= (Expr
) xpath
.compile(t
);
1169 Node children
= node
.getFirstChild();
1170 IfNode ret
= new IfNode(test
);
1171 ret
.children
= parse(children
);
1178 final TemplateNode
parseWhen(Node node
)
1179 throws TransformerConfigurationException
, XPathExpressionException
1181 NamedNodeMap attrs
= node
.getAttributes();
1182 String t
= getRequiredAttribute(attrs
, "test", node
);
1183 Expr test
= (Expr
) xpath
.compile(t
);
1184 Node children
= node
.getFirstChild();
1185 WhenNode ret
= new WhenNode(test
);
1186 ret
.children
= parse(children
);
1193 final TemplateNode
parseElement(Node node
)
1194 throws TransformerConfigurationException
, XPathExpressionException
1196 NamedNodeMap attrs
= node
.getAttributes();
1197 String name
= getRequiredAttribute(attrs
, "name", node
);
1198 String namespace
= getAttribute(attrs
, "namespace");
1199 String uas
= getAttribute(attrs
, "use-attribute-sets");
1200 TemplateNode n
= parseAttributeValueTemplate(name
, node
);
1201 TemplateNode ns
= (namespace
== null) ?
null :
1202 parseAttributeValueTemplate(namespace
, node
);
1203 Node children
= node
.getFirstChild();
1204 ElementNode ret
= new ElementNode(n
, ns
, uas
, node
);
1205 ret
.children
= parse(children
);
1212 final TemplateNode
parseAttribute(Node node
)
1213 throws TransformerConfigurationException
, XPathExpressionException
1215 NamedNodeMap attrs
= node
.getAttributes();
1216 String name
= getRequiredAttribute(attrs
, "name", node
);
1217 String namespace
= getAttribute(attrs
, "namespace");
1218 TemplateNode n
= parseAttributeValueTemplate(name
, node
);
1219 TemplateNode ns
= (namespace
== null) ?
null :
1220 parseAttributeValueTemplate(namespace
, node
);
1221 Node children
= node
.getFirstChild();
1222 AttributeNode ret
= new AttributeNode(n
, ns
, node
);
1223 ret
.children
= parse(children
);
1230 final TemplateNode
parseText(Node node
)
1231 throws TransformerConfigurationException
, XPathExpressionException
1233 NamedNodeMap attrs
= node
.getAttributes();
1234 String doe
= getAttribute(attrs
, "disable-output-escaping");
1235 boolean d
= "yes".equals(doe
);
1236 Node children
= node
.getFirstChild();
1237 TextNode ret
= new TextNode(d
);
1238 ret
.children
= parse(children
);
1245 final TemplateNode
parseCopy(Node node
)
1246 throws TransformerConfigurationException
, XPathExpressionException
1248 NamedNodeMap attrs
= node
.getAttributes();
1249 String uas
= getAttribute(attrs
, "use-attribute-sets");
1250 Node children
= node
.getFirstChild();
1251 CopyNode ret
= new CopyNode(uas
);
1252 ret
.children
= parse(children
);
1257 * processing-instruction
1259 final TemplateNode
parseProcessingInstruction(Node node
)
1260 throws TransformerConfigurationException
, XPathExpressionException
1262 NamedNodeMap attrs
= node
.getAttributes();
1263 String name
= getRequiredAttribute(attrs
, "name", node
);
1264 Node children
= node
.getFirstChild();
1265 ProcessingInstructionNode ret
= new ProcessingInstructionNode(name
);
1266 ret
.children
= parse(children
);
1273 final TemplateNode
parseNumber(Node node
)
1274 throws TransformerConfigurationException
, XPathExpressionException
1276 NamedNodeMap attrs
= node
.getAttributes();
1277 String v
= getAttribute(attrs
, "value");
1278 String ff
= getAttribute(attrs
, "format");
1283 TemplateNode format
= parseAttributeValueTemplate(ff
, node
);
1284 String lang
= getAttribute(attrs
, "lang");
1285 String lv
= getAttribute(attrs
, "letter-value");
1286 int letterValue
= "traditional".equals(lv
) ?
1287 AbstractNumberNode
.TRADITIONAL
:
1288 AbstractNumberNode
.ALPHABETIC
;
1289 String gs
= getAttribute(attrs
, "grouping-separator");
1290 String gz
= getAttribute(attrs
, "grouping-size");
1291 int gz2
= (gz
!= null && gz
.length() > 0) ?
1292 Integer
.parseInt(gz
) : 1;
1293 Node children
= node
.getFirstChild();
1295 if (v
!= null && v
.length() > 0)
1297 Expr value
= (Expr
) xpath
.compile(v
);
1298 ret
= new NumberNode(value
, format
, lang
,
1299 letterValue
, gs
, gz2
);
1303 String l
= getAttribute(attrs
, "level");
1305 "multiple".equals(l
) ? NodeNumberNode
.MULTIPLE
:
1306 "any".equals(l
) ? NodeNumberNode
.ANY
:
1307 NodeNumberNode
.SINGLE
;
1308 String c
= getAttribute(attrs
, "count");
1309 String f
= getAttribute(attrs
, "from");
1310 Pattern count
= null;
1311 Pattern from
= null;
1316 count
= (Pattern
) xpath
.compile(c
);
1318 catch (ClassCastException e
)
1320 String msg
= "invalid pattern: " + c
;
1321 throw new TransformerConfigurationException(msg
);
1328 from
= (Pattern
) xpath
.compile(f
);
1330 catch (ClassCastException e
)
1332 String msg
= "invalid pattern: " + f
;
1333 throw new TransformerConfigurationException(msg
);
1336 ret
= new NodeNumberNode(level
, count
, from
,
1338 letterValue
, gs
, gz2
);
1340 ret
.children
= parse(children
);
1347 final TemplateNode
parseCopyOf(Node node
)
1348 throws TransformerConfigurationException
, XPathExpressionException
1350 NamedNodeMap attrs
= node
.getAttributes();
1351 String s
= getRequiredAttribute(attrs
, "select", node
);
1352 Expr select
= (Expr
) xpath
.compile(s
);
1353 Node children
= node
.getFirstChild();
1354 CopyOfNode ret
= new CopyOfNode(select
);
1355 ret
.children
= parse(children
);
1362 final TemplateNode
parseMessage(Node node
)
1363 throws TransformerConfigurationException
, XPathExpressionException
1365 NamedNodeMap attrs
= node
.getAttributes();
1366 String t
= getAttribute(attrs
, "terminate");
1367 boolean terminate
= "yes".equals(t
);
1368 Node children
= node
.getFirstChild();
1369 MessageNode ret
= new MessageNode(terminate
);
1370 ret
.children
= parse(children
);
1375 * Parse template-level elements.
1377 final TemplateNode
parse(Node node
)
1378 throws TransformerConfigurationException
1380 TemplateNode first
= null;
1381 TemplateNode previous
= null;
1382 while (node
!= null)
1384 Node next
= node
.getNextSibling();
1385 TemplateNode tnode
= doParse(node
);
1390 if (previous
!= null)
1391 previous
.next
= tnode
;
1399 private final TemplateNode
doParse(Node node
)
1400 throws TransformerConfigurationException
1402 // Hack to associate the document function with its declaring node
1406 String namespaceUri
= node
.getNamespaceURI();
1407 if (Stylesheet
.XSL_NS
.equals(namespaceUri
) &&
1408 Node
.ELEMENT_NODE
== node
.getNodeType())
1410 String name
= node
.getLocalName();
1411 if ("apply-templates".equals(name
))
1412 return parseApplyTemplates(node
);
1413 else if ("call-template".equals(name
))
1414 return parseCallTemplate(node
);
1415 else if ("value-of".equals(name
))
1416 return parseValueOf(node
);
1417 else if ("for-each".equals(name
))
1418 return parseForEach(node
);
1419 else if ("if".equals(name
))
1420 return parseIf(node
);
1421 else if ("choose".equals(name
))
1423 Node children
= node
.getFirstChild();
1424 ChooseNode ret
= new ChooseNode();
1425 ret
.children
= parse(children
);
1428 else if ("when".equals(name
))
1429 return parseWhen(node
);
1430 else if ("otherwise".equals(name
))
1432 Node children
= node
.getFirstChild();
1433 OtherwiseNode ret
= new OtherwiseNode();
1434 ret
.children
= parse(children
);
1437 else if ("element".equals(name
))
1438 return parseElement(node
);
1439 else if ("attribute".equals(name
))
1440 return parseAttribute(node
);
1441 else if ("text".equals(name
))
1442 return parseText(node
);
1443 else if ("copy".equals(name
))
1444 return parseCopy(node
);
1445 else if ("processing-instruction".equals(name
))
1446 return parseProcessingInstruction(node
);
1447 else if ("comment".equals(name
))
1449 Node children
= node
.getFirstChild();
1450 CommentNode ret
= new CommentNode();
1451 ret
.children
= parse(children
);
1454 else if ("number".equals(name
))
1455 return parseNumber(node
);
1456 else if ("param".equals(name
) ||
1457 "variable".equals(name
))
1459 int type
= "variable".equals(name
) ?
1460 Bindings
.VARIABLE
: Bindings
.PARAM
;
1461 NamedNodeMap attrs
= node
.getAttributes();
1462 Node children
= node
.getFirstChild();
1463 TemplateNode content
= parse(children
);
1465 getQName(getRequiredAttribute(attrs
, "name", node
));
1466 String select
= getAttribute(attrs
, "select");
1470 if (content
!= null)
1472 String msg
= "parameter '" + paramName
+
1473 "' has both select and content";
1474 DOMSourceLocator l
= new DOMSourceLocator(node
);
1475 throw new TransformerConfigurationException(msg
, l
);
1477 Expr expr
= (Expr
) xpath
.compile(select
);
1478 ret
= new ParameterNode(paramName
, expr
, type
);
1482 ret
= new ParameterNode(paramName
, null, type
);
1483 ret
.children
= content
;
1487 else if ("copy-of".equals(name
))
1488 return parseCopyOf(node
);
1489 else if ("message".equals(name
))
1490 return parseMessage(node
);
1491 else if ("apply-imports".equals(name
))
1493 Node children
= node
.getFirstChild();
1494 ApplyImportsNode ret
= new ApplyImportsNode();
1495 ret
.children
= parse(children
);
1501 // Pass over any other XSLT nodes
1505 String prefix
= node
.getPrefix();
1506 if (extensionElementPrefixes
.contains(prefix
))
1508 // Check for xsl:fallback
1509 for (Node ctx
= node
.getFirstChild(); ctx
!= null;
1510 ctx
= ctx
.getNextSibling())
1512 String ctxUri
= ctx
.getNamespaceURI();
1513 if (XSL_NS
.equals(ctxUri
) &&
1514 "fallback".equals(ctx
.getLocalName()))
1516 ctx
= ctx
.getFirstChild();
1517 return (ctx
== null) ?
null : parse(ctx
);
1520 // Otherwise pass over extension element
1523 switch (node
.getNodeType())
1525 case Node
.TEXT_NODE
:
1526 case Node
.CDATA_SECTION_NODE
:
1527 // Determine whether to strip whitespace
1528 Text text
= (Text
) node
;
1529 if (!isPreserved(text
, false))
1532 text
.getParentNode().removeChild(text
);
1536 case Node
.COMMENT_NODE
:
1539 case Node
.ELEMENT_NODE
:
1540 // Check for attribute value templates and use-attribute-sets
1541 NamedNodeMap attrs
= node
.getAttributes();
1542 boolean convert
= false;
1543 String useAttributeSets
= null;
1544 int len
= attrs
.getLength();
1545 for (int i
= 0; i
< len
; i
++)
1547 Node attr
= attrs
.item(i
);
1548 String value
= attr
.getNodeValue();
1549 if (Stylesheet
.XSL_NS
.equals(attr
.getNamespaceURI()) &&
1550 "use-attribute-sets".equals(attr
.getLocalName()))
1552 useAttributeSets
= value
;
1556 int start
= value
.indexOf('{');
1557 int end
= value
.indexOf('}');
1558 if (start
!= -1 || end
!= -1)
1566 // Create an element-producing template node instead
1567 // with appropriate attribute-producing child template nodes
1568 Node children
= node
.getFirstChild();
1569 TemplateNode child
= parse(children
);
1570 for (int i
= 0; i
< len
; i
++)
1572 Node attr
= attrs
.item(i
);
1573 String ans
= attr
.getNamespaceURI();
1574 String aname
= attr
.getNodeName();
1575 if (Stylesheet
.XSL_NS
.equals(ans
) &&
1576 "use-attribute-sets".equals(attr
.getLocalName()))
1578 String value
= attr
.getNodeValue();
1579 TemplateNode grandchild
=
1580 parseAttributeValueTemplate(value
, node
);
1582 parseAttributeValueTemplate(aname
, node
);
1583 TemplateNode ns
= (ans
== null) ?
null :
1584 parseAttributeValueTemplate(ans
, node
);
1585 TemplateNode newChild
= new AttributeNode(n
, ns
, attr
);
1586 newChild
.children
= grandchild
;
1587 newChild
.next
= child
;
1590 String ename
= node
.getNodeName();
1591 TemplateNode n
= parseAttributeValueTemplate(ename
, node
);
1592 //TemplateNode ns = (namespaceUri == null) ? null :
1593 // parseAttributeValueTemplate(namespaceUri, node);
1594 TemplateNode ns
= null;
1595 ElementNode ret
= new ElementNode(n
, ns
, useAttributeSets
,
1597 ret
.children
= child
;
1600 // Otherwise fall through
1604 catch (XPathExpressionException e
)
1606 DOMSourceLocator l
= new DOMSourceLocator(node
);
1607 throw new TransformerConfigurationException(e
.getMessage(), l
, e
);
1609 Node children
= node
.getFirstChild();
1610 LiteralNode ret
= new LiteralNode(node
);
1611 ret
.children
= parse(children
);
1615 final List
<SortKey
> parseSortKeys(Node node
)
1616 throws TransformerConfigurationException
, XPathExpressionException
1618 List
<SortKey
> ret
= new LinkedList
<SortKey
>();
1619 while (node
!= null)
1621 String namespaceUri
= node
.getNamespaceURI();
1622 if (Stylesheet
.XSL_NS
.equals(namespaceUri
) &&
1623 Node
.ELEMENT_NODE
== node
.getNodeType() &&
1624 "sort".equals(node
.getLocalName()))
1626 NamedNodeMap attrs
= node
.getAttributes();
1627 String s
= getAttribute(attrs
, "select");
1630 Expr select
= (Expr
) xpath
.compile(s
);
1631 String l
= getAttribute(attrs
, "lang");
1632 TemplateNode lang
= (l
== null) ?
null :
1633 parseAttributeValueTemplate(l
, node
);
1634 String dt
= getAttribute(attrs
, "data-type");
1635 TemplateNode dataType
= (dt
== null) ?
null :
1636 parseAttributeValueTemplate(dt
, node
);
1637 String o
= getAttribute(attrs
, "order");
1638 TemplateNode order
= (o
== null) ?
null :
1639 parseAttributeValueTemplate(o
, node
);
1640 String co
= getAttribute(attrs
, "case-order");
1641 TemplateNode caseOrder
= (co
== null) ?
null :
1642 parseAttributeValueTemplate(co
, node
);
1643 ret
.add(new SortKey(select
, lang
, dataType
, order
, caseOrder
));
1645 node
= node
.getNextSibling();
1650 final List
<WithParam
> parseWithParams(Node node
)
1651 throws TransformerConfigurationException
, XPathExpressionException
1653 List
<WithParam
> ret
= new LinkedList
<WithParam
>();
1654 while (node
!= null)
1656 String namespaceUri
= node
.getNamespaceURI();
1657 if (Stylesheet
.XSL_NS
.equals(namespaceUri
) &&
1658 Node
.ELEMENT_NODE
== node
.getNodeType() &&
1659 "with-param".equals(node
.getLocalName()))
1661 NamedNodeMap attrs
= node
.getAttributes();
1662 TemplateNode content
= parse(node
.getFirstChild());
1664 getQName(getRequiredAttribute(attrs
, "name", node
));
1665 String select
= getAttribute(attrs
, "select");
1668 if (content
!= null)
1670 String msg
= "parameter '" + name
+
1671 "' has both select and content";
1672 DOMSourceLocator l
= new DOMSourceLocator(node
);
1673 throw new TransformerConfigurationException(msg
, l
);
1675 Expr expr
= (Expr
) xpath
.compile(select
);
1676 ret
.add(new WithParam(name
, expr
));
1679 ret
.add(new WithParam(name
, content
));
1681 node
= node
.getNextSibling();
1687 * Created element nodes have a copy of the namespace nodes in the
1688 * stylesheet, except the XSLT namespace, extension namespaces, and
1689 * exclude-result-prefixes.
1691 final void addNamespaceNodes(Node source
, Node target
, Document doc
,
1692 Collection
<String
> elementExcludeResultPrefixes
)
1694 NamedNodeMap attrs
= source
.getAttributes();
1697 int len
= attrs
.getLength();
1698 for (int i
= 0; i
< len
; i
++)
1700 Node attr
= attrs
.item(i
);
1701 String uri
= attr
.getNamespaceURI();
1702 if (uri
== XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
)
1704 String prefix
= attr
.getLocalName();
1705 if (XMLConstants
.XMLNS_ATTRIBUTE
.equals(prefix
))
1706 prefix
= "#default";
1707 String ns
= attr
.getNodeValue();
1708 // Should the namespace be excluded?
1709 if (XSL_NS
.equals(ns
) ||
1710 extensionElementPrefixes
.contains(prefix
) ||
1711 elementExcludeResultPrefixes
.contains(prefix
) ||
1712 excludeResultPrefixes
.contains(prefix
))
1714 // Is the namespace already defined on the target?
1715 if (prefix
== "#default")
1717 if (target
.lookupNamespaceURI(prefix
) != null)
1719 attr
= attr
.cloneNode(true);
1720 attr
= doc
.adoptNode(attr
);
1721 target
.getAttributes().setNamedItemNS(attr
);
1725 Node parent
= source
.getParentNode();
1727 addNamespaceNodes(parent
, target
, doc
, elementExcludeResultPrefixes
);
1730 static final String
getAttribute(NamedNodeMap attrs
, String name
)
1732 Node attr
= attrs
.getNamedItem(name
);
1735 String ret
= attr
.getNodeValue();
1736 if (ret
.length() == 0)
1741 static final String
getRequiredAttribute(NamedNodeMap attrs
, String name
,
1743 throws TransformerConfigurationException
1745 String value
= getAttribute(attrs
, name
);
1746 if (value
== null || value
.length() == 0)
1749 name
+ " attribute is required on " + source
.getNodeName();
1750 DOMSourceLocator l
= new DOMSourceLocator(source
);
1751 throw new TransformerConfigurationException(msg
, l
);
1756 // Handle user data changes when nodes are cloned etc
1758 public void handle(short op
, String key
, Object data
, Node src
, Node dst
)
1760 dst
.setUserData(key
, data
, this);
1763 public String
toString()
1765 CPStringBuilder b
= new CPStringBuilder(getClass().getName());
1766 b
.append("[templates=");
1767 b
.append(templates
);
1769 return b
.toString();