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 java
.text
.DecimalFormat
;
41 import java
.text
.DecimalFormatSymbols
;
42 import java
.util
.ArrayList
;
43 import java
.util
.Collection
;
44 import java
.util
.Collections
;
45 import java
.util
.HashSet
;
46 import java
.util
.Iterator
;
47 import java
.util
.LinkedHashMap
;
48 import java
.util
.LinkedHashSet
;
49 import java
.util
.LinkedList
;
50 import java
.util
.List
;
53 import java
.util
.StringTokenizer
;
54 import java
.util
.TreeSet
;
55 import javax
.xml
.XMLConstants
;
56 import javax
.xml
.namespace
.NamespaceContext
;
57 import javax
.xml
.namespace
.QName
;
58 import javax
.xml
.transform
.Source
;
59 import javax
.xml
.transform
.TransformerConfigurationException
;
60 import javax
.xml
.transform
.TransformerException
;
61 import javax
.xml
.xpath
.XPathFunction
;
62 import javax
.xml
.xpath
.XPathFunctionResolver
;
63 import javax
.xml
.xpath
.XPathExpressionException
;
64 import org
.w3c
.dom
.Attr
;
65 import org
.w3c
.dom
.Document
;
66 import org
.w3c
.dom
.DOMException
;
67 import org
.w3c
.dom
.Element
;
68 import org
.w3c
.dom
.NamedNodeMap
;
69 import org
.w3c
.dom
.Node
;
70 import org
.w3c
.dom
.Text
;
71 import org
.w3c
.dom
.UserDataHandler
;
72 import gnu
.xml
.xpath
.Expr
;
73 import gnu
.xml
.xpath
.NameTest
;
74 import gnu
.xml
.xpath
.NodeTypeTest
;
75 import gnu
.xml
.xpath
.Pattern
;
76 import gnu
.xml
.xpath
.Selector
;
77 import gnu
.xml
.xpath
.Root
;
78 import gnu
.xml
.xpath
.Test
;
79 import gnu
.xml
.xpath
.XPathImpl
;
84 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
87 implements NamespaceContext
, XPathFunctionResolver
, UserDataHandler
, Cloneable
90 static final String XSL_NS
= "http://www.w3.org/1999/XSL/Transform";
91 private static final NameTest STYLESHEET_PRESERVE_TEXT
=
92 new NameTest(new QName(XSL_NS
, "text"), false, false);
94 static final int OUTPUT_XML
= 0;
95 static final int OUTPUT_HTML
= 1;
96 static final int OUTPUT_TEXT
= 2;
98 final TransformerFactoryImpl factory
;
99 TransformerImpl transformer
;
101 final XPathImpl xpath
;
102 final String systemId
;
103 final int precedence
;
112 Collection extensionElementPrefixes
;
113 Collection excludeResultPrefixes
;
116 * Set of element names for which we should strip whitespace.
121 * Set of element names for which we should preserve whitespace.
130 String outputVersion
;
131 String outputEncoding
;
132 boolean outputOmitXmlDeclaration
;
133 boolean outputStandalone
;
134 String outputPublicId
;
135 String outputSystemId
;
136 Collection outputCdataSectionElements
;
137 boolean outputIndent
;
138 String outputMediaType
;
153 Map namespaceAliases
;
166 * Variable and parameter bindings.
173 LinkedList templates
;
175 TemplateNode builtInNodeTemplate
;
176 TemplateNode builtInTextTemplate
;
179 * Holds the current node while parsing.
180 * Necessary to associate the document function with its declaring node,
181 * to resolve namespaces, and to maintain the current node for the
182 * current() function.
187 * Set by a terminating message.
189 transient boolean terminated
;
192 * Current template in force.
194 transient Template currentTemplate
;
196 Stylesheet(TransformerFactoryImpl factory
,
201 throws TransformerConfigurationException
203 this.factory
= factory
;
204 this.systemId
= systemId
;
205 this.precedence
= precedence
;
206 this.parent
= parent
;
207 extensionElementPrefixes
= new HashSet();
208 excludeResultPrefixes
= new HashSet();
209 stripSpace
= new LinkedHashSet();
210 preserveSpace
= new LinkedHashSet();
211 outputCdataSectionElements
= new LinkedHashSet();
212 xpath
= (XPathImpl
) factory
.xpathFactory
.newXPath();
213 xpath
.setNamespaceContext(this);
216 bindings
= new Bindings(this);
217 attributeSets
= new LinkedList();
218 variables
= new LinkedList();
219 namespaceAliases
= new LinkedHashMap();
220 templates
= new LinkedList();
221 keys
= new LinkedList();
222 decimalFormats
= new LinkedHashMap();
223 initDefaultDecimalFormat();
224 xpath
.setXPathFunctionResolver(this);
228 /* Test for import circularity */
229 for (Stylesheet ctx
= this; ctx
.parent
!= null; ctx
= ctx
.parent
)
231 if (systemId
!= null && systemId
.equals(ctx
.parent
.systemId
))
233 String msg
= "circularity importing " + systemId
;
234 throw new TransformerConfigurationException(msg
);
238 Stylesheet root
= getRootStylesheet();
239 bindings
= root
.bindings
;
240 attributeSets
= root
.attributeSets
;
241 variables
= root
.variables
;
242 namespaceAliases
= root
.namespaceAliases
;
243 templates
= root
.templates
;
245 decimalFormats
= root
.decimalFormats
;
246 xpath
.setXPathFunctionResolver(root
);
248 xpath
.setXPathVariableResolver(bindings
);
250 Test anyNode
= new NodeTypeTest((short) 0);
251 List tests
= Collections
.singletonList(anyNode
);
252 builtInNodeTemplate
=
253 new ApplyTemplatesNode(new Selector(Selector
.CHILD
, tests
),
254 null, null, null, true);
255 builtInTextTemplate
=
256 new ValueOfNode(new Selector(Selector
.SELF
, tests
),
259 parse(doc
.getDocumentElement(), true);
260 current
= doc
; // Alow namespace resolution during processing
262 debug
= ("yes".equals(System
.getProperty("xsl.debug")));
266 System
.err
.println("Stylesheet: " + doc
.getDocumentURI());
267 for (Iterator i
= templates
.iterator(); i
.hasNext(); )
269 Template t
= (Template
) i
.next();
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 templates2
= new LinkedList();
312 for (Iterator i
= templates
.iterator(); i
.hasNext(); )
314 Template t
= (Template
) i
.next();
315 templates2
.add(t
.clone(clone
));
317 clone
.templates
= templates2
;
319 LinkedList attributeSets2
= new LinkedList();
320 for (Iterator i
= attributeSets
.iterator(); i
.hasNext(); )
322 AttributeSet as
= (AttributeSet
) i
.next();
323 attributeSets2
.add(as
.clone(clone
));
325 clone
.attributeSets
= attributeSets2
;
327 LinkedList variables2
= new LinkedList();
328 for (Iterator i
= variables
.iterator(); i
.hasNext(); )
330 ParameterNode var
= (ParameterNode
) i
.next();
331 variables2
.add(var
.clone(clone
));
333 clone
.variables
= variables2
;
335 LinkedList keys2
= new LinkedList();
336 for (Iterator i
= keys
.iterator(); i
.hasNext(); )
338 Key k
= (Key
) i
.next();
339 keys2
.add(k
.clone(clone
));
345 catch (CloneNotSupportedException e
)
347 throw new Error(e
.getMessage());
351 // -- Variable evaluation --
353 void initTopLevelVariables(Node context
)
354 throws TransformerException
357 // Sort the variables into order
358 // See XSLT 11.4: "If the template or expression specifying the value of
359 // a global variable x references a global variable y, then the value
360 // for y must be computed before the value of x."
361 List topLevel
= new ArrayList(variables
);
362 Collections
.sort(topLevel
);
363 for (Iterator i
= topLevel
.iterator(); i
.hasNext(); )
365 ParameterNode var
= (ParameterNode
) i
.next();
366 bindings
.set(var
.name
,
367 var
.getValue(this, null, context
, 1, 1),
373 // -- NamespaceContext --
375 public String
getNamespaceURI(String prefix
)
377 return (current
== null) ?
null : current
.lookupNamespaceURI(prefix
);
380 public String
getPrefix(String namespaceURI
)
382 return (current
== null) ?
null : current
.lookupPrefix(namespaceURI
);
385 public Iterator
getPrefixes(String namespaceURI
)
388 return Collections
.singleton(getPrefix(namespaceURI
)).iterator();
391 final QName
getQName(String name
)
393 String localName
= name
, uri
= null, prefix
= null;
394 int ci
= name
.indexOf(':');
397 prefix
= name
.substring(0, ci
);
398 localName
= name
.substring(ci
+ 1);
399 uri
= getNamespaceURI(prefix
);
401 return new QName(uri
, localName
, prefix
);
404 // -- Template selection --
406 TemplateNode
getTemplate(QName mode
, Node context
, boolean applyImports
)
407 throws TransformerException
410 System
.err
.println("getTemplate: mode="+mode
+" context="+context
);
411 Set candidates
= new TreeSet();
412 for (Iterator j
= templates
.iterator(); j
.hasNext(); )
414 Template t
= (Template
) j
.next();
415 boolean isMatch
= t
.matches(mode
, context
);
418 if (currentTemplate
== null)
420 String msg
= "current template may not be null " +
421 "during apply-imports";
422 throw new TransformerException(msg
);
424 if (!currentTemplate
.imports(t
))
427 //System.err.println("\t"+context+" "+t+"="+isMatch);
431 //System.err.println("\tcandidates="+candidates);
432 if (candidates
.isEmpty())
434 // Apply built-in template
435 // Current template is unchanged
437 System
.err
.println("\tbuiltInTemplate context="+context
);
438 switch (context
.getNodeType())
440 case Node
.ELEMENT_NODE
:
441 case Node
.DOCUMENT_NODE
:
442 case Node
.DOCUMENT_FRAGMENT_NODE
:
443 case Node
.PROCESSING_INSTRUCTION_NODE
:
444 case Node
.COMMENT_NODE
:
445 return builtInNodeTemplate
;
447 case Node
.CDATA_SECTION_NODE
:
448 case Node
.ATTRIBUTE_NODE
:
449 return builtInTextTemplate
;
456 Template t
= (Template
) candidates
.iterator().next();
457 // Set current template
460 System
.err
.println("\ttemplate="+t
+" context="+context
);
465 TemplateNode
getTemplate(QName mode
, QName name
)
466 throws TransformerException
468 Set candidates
= new TreeSet();
469 for (Iterator j
= templates
.iterator(); j
.hasNext(); )
471 Template t
= (Template
) j
.next();
472 boolean isMatch
= t
.matches(name
);
476 if (candidates
.isEmpty())
478 Template t
= (Template
) candidates
.iterator().next();
485 final Template
parseTemplate(Node node
, NamedNodeMap attrs
)
486 throws TransformerConfigurationException
, XPathExpressionException
488 String n
= getAttribute(attrs
, "name");
489 QName name
= (n
== null) ?
null : getQName(n
);
490 String m
= getAttribute(attrs
, "match");
491 Pattern match
= null;
496 match
= (Pattern
) xpath
.compile(m
);
498 catch (ClassCastException e
)
500 String msg
= "illegal pattern: " + m
;
501 throw new TransformerConfigurationException(msg
);
504 String p
= getAttribute(attrs
, "priority");
505 String mm
= getAttribute(attrs
, "mode");
506 QName mode
= (mm
== null) ?
null : getQName(mm
);
507 double priority
= (p
== null) ? Template
.DEFAULT_PRIORITY
:
508 Double
.parseDouble(p
);
509 Node children
= node
.getFirstChild();
510 return new Template(this, name
, match
, parse(children
),
511 precedence
, priority
, mode
);
517 final void parseOutput(Node node
, NamedNodeMap attrs
)
518 throws TransformerConfigurationException
521 String method
= getAttribute(attrs
, "method");
522 if ("xml".equals(method
) || method
== null)
523 outputMethod
= OUTPUT_XML
;
524 else if ("html".equals(method
))
525 outputMethod
= OUTPUT_HTML
;
526 else if ("text".equals(method
))
527 outputMethod
= OUTPUT_TEXT
;
530 String msg
= "unsupported output method: " + method
;
531 DOMSourceLocator l
= new DOMSourceLocator(node
);
532 throw new TransformerConfigurationException(msg
, l
);
534 outputPublicId
= getAttribute(attrs
, "doctype-public");
535 outputSystemId
= getAttribute(attrs
, "doctype-system");
536 outputEncoding
= getAttribute(attrs
, "encoding");
537 String indent
= getAttribute(attrs
, "indent");
539 outputIndent
= "yes".equals(indent
);
540 outputVersion
= getAttribute(attrs
, "version");
541 String omitXmlDecl
= getAttribute(attrs
, "omit-xml-declaration");
542 if (omitXmlDecl
!= null)
543 outputOmitXmlDeclaration
= "yes".equals(omitXmlDecl
);
544 String standalone
= getAttribute(attrs
, "standalone");
545 if (standalone
!= null)
546 outputStandalone
= "yes".equals(standalone
);
547 outputMediaType
= getAttribute(attrs
, "media-type");
548 String cdataSectionElements
=
549 getAttribute(attrs
, "cdata-section-elements");
550 if (cdataSectionElements
!= null)
552 StringTokenizer st
= new StringTokenizer(cdataSectionElements
, " ");
553 while (st
.hasMoreTokens())
554 outputCdataSectionElements
.add(st
.nextToken());
561 final void parseKey(Node node
, NamedNodeMap attrs
)
562 throws TransformerConfigurationException
, XPathExpressionException
564 String n
= getRequiredAttribute(attrs
, "name", node
);
565 String m
= getRequiredAttribute(attrs
, "match", node
);
566 String u
= getRequiredAttribute(attrs
, "use", node
);
567 QName name
= getQName(n
);
568 Expr use
= (Expr
) xpath
.compile(u
);
571 Pattern match
= (Pattern
) xpath
.compile(m
);
572 Key key
= new Key(name
, match
, use
);
575 catch (ClassCastException e
)
577 throw new TransformerConfigurationException("invalid pattern: " + m
);
584 final void parseDecimalFormat(Node node
, NamedNodeMap attrs
)
585 throws TransformerConfigurationException
587 String dfName
= getAttribute(attrs
, "name");
588 DecimalFormat df
= new DecimalFormat();
589 DecimalFormatSymbols symbols
= new DecimalFormatSymbols();
590 symbols
.setDecimalSeparator(parseDFChar(attrs
, "decimal-separator", '.'));
591 symbols
.setGroupingSeparator(parseDFChar(attrs
, "grouping-separator", ','));
592 symbols
.setInfinity(parseDFString(attrs
, "infinity", "Infinity"));
593 symbols
.setMinusSign(parseDFChar(attrs
, "minus-sign", '-'));
594 symbols
.setNaN(parseDFString(attrs
, "NaN", "NaN"));
595 symbols
.setPercent(parseDFChar(attrs
, "percent", '%'));
596 symbols
.setPerMill(parseDFChar(attrs
, "per-mille", '\u2030'));
597 symbols
.setZeroDigit(parseDFChar(attrs
, "zero-digit", '0'));
598 symbols
.setDigit(parseDFChar(attrs
, "digit", '#'));
599 symbols
.setPatternSeparator(parseDFChar(attrs
, "pattern-separator", ';'));
600 df
.setDecimalFormatSymbols(symbols
);
601 decimalFormats
.put(dfName
, df
);
604 private final char parseDFChar(NamedNodeMap attrs
, String name
, char def
)
605 throws TransformerConfigurationException
607 Node attr
= attrs
.getNamedItem(name
);
610 return (attr
== null) ? def
: attr
.getNodeValue().charAt(0);
612 catch (StringIndexOutOfBoundsException e
)
614 throw new TransformerConfigurationException("empty attribute '" +
616 "' in decimal-format", e
);
620 private final String
parseDFString(NamedNodeMap attrs
, String name
,
623 Node attr
= attrs
.getNamedItem(name
);
624 return (attr
== null) ? def
: attr
.getNodeValue();
630 final void parseNamespaceAlias(Node node
, NamedNodeMap attrs
)
631 throws TransformerConfigurationException
633 String sp
= getRequiredAttribute(attrs
, "stylesheet-prefix", node
);
634 String rp
= getRequiredAttribute(attrs
, "result-prefix", node
);
635 namespaceAliases
.put(sp
, rp
);
641 final void parseAttributeSet(Node node
, NamedNodeMap attrs
)
642 throws TransformerConfigurationException
, XPathExpressionException
644 TemplateNode children
= parse(node
.getFirstChild());
645 String name
= getRequiredAttribute(attrs
, "name", node
);
646 String uas
= getAttribute(attrs
, "use-attribute-sets");
647 attributeSets
.add(new AttributeSet(children
, name
, uas
));
651 * Parse top-level elements.
653 void parse(Node node
, boolean root
)
654 throws TransformerConfigurationException
660 node
= node
.getNextSibling();
664 void doParse(Node node
, boolean root
)
665 throws TransformerConfigurationException
669 String namespaceUri
= node
.getNamespaceURI();
670 if (XSL_NS
.equals(namespaceUri
) &&
671 node
.getNodeType() == Node
.ELEMENT_NODE
)
673 String name
= node
.getLocalName();
674 NamedNodeMap attrs
= node
.getAttributes();
675 if ("stylesheet".equals(name
))
677 version
= getAttribute(attrs
, "version");
678 String eep
= getAttribute(attrs
, "extension-element-prefixes");
681 StringTokenizer st
= new StringTokenizer(eep
);
682 while (st
.hasMoreTokens())
684 extensionElementPrefixes
.add(st
.nextToken());
687 String erp
= getAttribute(attrs
, "exclude-result-prefixes");
690 StringTokenizer st
= new StringTokenizer(erp
);
691 while (st
.hasMoreTokens())
693 excludeResultPrefixes
.add(st
.nextToken());
696 parse(node
.getFirstChild(), false);
698 else if ("template".equals(name
))
699 templates
.add(parseTemplate(node
, attrs
));
700 else if ("param".equals(name
) ||
701 "variable".equals(name
))
703 int type
= "variable".equals(name
) ?
704 Bindings
.VARIABLE
: Bindings
.PARAM
;
705 TemplateNode content
= parse(node
.getFirstChild());
707 getQName(getRequiredAttribute(attrs
, "name", node
));
708 String select
= getAttribute(attrs
, "select");
710 if (select
!= null && select
.length() > 0)
714 String msg
= "parameter '" + paramName
+
715 "' has both select and content";
716 DOMSourceLocator l
= new DOMSourceLocator(node
);
717 throw new TransformerConfigurationException(msg
, l
);
719 Expr expr
= (Expr
) xpath
.compile(select
);
720 param
= new ParameterNode(paramName
, expr
, type
);
724 param
= new ParameterNode(paramName
, null, type
);
725 param
.children
= content
;
727 variables
.add(param
);
729 else if ("include".equals(name
) || "import".equals(name
))
731 int delta
= "import".equals(name
) ?
-1 : 0;
732 String href
= getRequiredAttribute(attrs
, "href", node
);
734 synchronized (factory
.resolver
)
736 if (transformer
!= null)
739 .setUserResolver(transformer
.getURIResolver());
741 .setUserListener(transformer
.getErrorListener());
743 source
= factory
.resolver
.resolve(systemId
, href
);
745 factory
.newStylesheet(source
, precedence
+ delta
, this);
747 else if ("output".equals(name
))
748 parseOutput(node
, attrs
);
749 else if ("preserve-space".equals(name
))
752 getRequiredAttribute(attrs
, "elements", node
);
753 StringTokenizer st
= new StringTokenizer(elements
,
755 while (st
.hasMoreTokens())
757 NameTest element
= parseNameTest(st
.nextToken());
758 preserveSpace
.add(new StrippingInstruction(element
,
762 else if ("strip-space".equals(name
))
765 getRequiredAttribute(attrs
, "elements", node
);
766 StringTokenizer st
= new StringTokenizer(elements
,
768 while (st
.hasMoreTokens())
770 NameTest element
= parseNameTest(st
.nextToken());
771 stripSpace
.add(new StrippingInstruction(element
,
775 else if ("key".equals(name
))
776 parseKey(node
, attrs
);
777 else if ("decimal-format".equals(name
))
778 parseDecimalFormat(node
, attrs
);
779 else if ("namespace-alias".equals(name
))
780 parseNamespaceAlias(node
, attrs
);
781 else if ("attribute-set".equals(name
))
782 parseAttributeSet(node
, attrs
);
786 // Literal document element
788 ((Element
)node
).getAttributeNodeNS(XSL_NS
, "version");
789 if (versionNode
== null)
791 String msg
= "no xsl:version attribute on literal result node";
792 DOMSourceLocator l
= new DOMSourceLocator(node
);
793 throw new TransformerConfigurationException(msg
, l
);
795 version
= versionNode
.getValue();
796 Node rootClone
= node
.cloneNode(true);
797 NamedNodeMap attrs
= rootClone
.getAttributes();
798 attrs
.removeNamedItemNS(XSL_NS
, "version");
799 templates
.add(new Template(this, null, new Root(),
802 Template
.DEFAULT_PRIORITY
,
807 // Skip unknown elements, text, comments, etc
810 catch (TransformerException e
)
812 DOMSourceLocator l
= new DOMSourceLocator(node
);
813 throw new TransformerConfigurationException(e
.getMessage(), l
, e
);
815 catch (DOMException e
)
817 DOMSourceLocator l
= new DOMSourceLocator(node
);
818 throw new TransformerConfigurationException(e
.getMessage(), l
, e
);
820 catch (XPathExpressionException e
)
822 DOMSourceLocator l
= new DOMSourceLocator(node
);
823 throw new TransformerConfigurationException(e
.getMessage(), l
, e
);
827 final NameTest
parseNameTest(String token
)
829 if ("*".equals(token
))
830 return new NameTest(null, true, true);
831 else if (token
.endsWith(":*"))
833 QName qName
= getQName(token
);
834 return new NameTest(qName
, true, false);
838 QName qName
= getQName(token
);
839 return new NameTest(qName
, false, false);
843 final TemplateNode
parseAttributeValueTemplate(String value
, Node source
)
844 throws TransformerConfigurationException
, XPathExpressionException
848 int len
= value
.length();
850 List tokens
= new ArrayList(); // text tokens
851 List types
= new ArrayList(); // literal or expression
853 for (int i
= 0; i
< len
; i
++)
855 char c
= value
.charAt(i
);
858 if (i
< (len
- 1) && value
.charAt(i
+ 1) == '{')
860 tokens
.add(value
.substring(off
, i
+ 1));
861 types
.add(Boolean
.FALSE
);
870 tokens
.add(value
.substring(off
, i
));
871 types
.add(Boolean
.FALSE
);
879 if (i
< (len
- 1) && value
.charAt(i
+ 1) == '}')
881 tokens
.add(value
.substring(off
, i
+ 1));
882 types
.add(Boolean
.FALSE
);
891 tokens
.add(value
.substring(off
, i
));
892 types
.add(Boolean
.TRUE
);
896 String msg
= "attribute value template " +
897 "must contain expression: " + value
;
898 DOMSourceLocator l
= new DOMSourceLocator(source
);
899 throw new TransformerConfigurationException(msg
, l
);
908 String msg
= "invalid attribute value template: " + value
;
909 throw new TransformerConfigurationException(msg
);
914 tokens
.add(value
.substring(off
));
915 types
.add(Boolean
.FALSE
);
918 // Construct template node tree
919 TemplateNode ret
= null;
920 Document doc
= source
.getOwnerDocument();
922 for (int i
= len
- 1; i
>= 0; i
--)
924 String token
= (String
) tokens
.get(i
);
925 Boolean type
= (Boolean
) types
.get(i
);
926 if (type
== Boolean
.TRUE
)
929 Expr select
= (Expr
) xpath
.compile(token
);
930 TemplateNode ret2
= new ValueOfNode(select
, false);
937 TemplateNode ret2
= new LiteralNode(doc
.createTextNode(token
));
946 * Whitespace stripping.
947 * @param text the text node
948 * @param source true if a source node, false if a stylesheet text node
949 * @see http://www.w3.org/TR/xslt#strip
951 boolean isPreserved(Text text
, boolean source
)
952 throws TransformerConfigurationException
954 // Check characters in text
955 String value
= text
.getData();
958 int len
= value
.length();
959 for (int i
= 0; i
< len
; i
++)
961 char c
= value
.charAt(i
);
962 if (c
!= 0x20 && c
!= 0x09 && c
!= 0x0a && c
!= 0x0d)
967 Node ctx
= text
.getParentNode();
970 // Source document text node
971 boolean preserve
= true;
972 float psPriority
= 0.0f
, ssPriority
= 0.0f
;
973 if (!stripSpace
.isEmpty())
975 // Conflict resolution
976 StrippingInstruction ssi
= null, psi
= null;
977 for (Iterator i
= stripSpace
.iterator(); i
.hasNext(); )
979 StrippingInstruction si
= (StrippingInstruction
) i
.next();
980 if (si
.element
.matches(ctx
, 1, 1))
984 if (si
.precedence
< ssi
.precedence
)
986 float p
= si
.getPriority();
993 for (Iterator i
= preserveSpace
.iterator(); i
.hasNext(); )
995 StrippingInstruction si
= (StrippingInstruction
) i
.next();
996 if (si
.element
.matches(ctx
, 1, 1))
1000 if (si
.precedence
< psi
.precedence
)
1002 float p
= si
.getPriority();
1013 if (psi
.precedence
< ssi
.precedence
)
1015 else if (psPriority
< ssPriority
)
1027 // Stylesheet text node
1028 if (STYLESHEET_PRESERVE_TEXT
.matches(ctx
, 1, 1))
1031 // Check whether any ancestor specified xml:space
1034 if (ctx
.getNodeType() == Node
.ELEMENT_NODE
)
1036 Element element
= (Element
) ctx
;
1037 String xmlSpace
= element
.getAttribute("xml:space");
1038 if ("default".equals(xmlSpace
))
1040 else if ("preserve".equals(xmlSpace
))
1042 else if (xmlSpace
.length() > 0)
1044 String msg
= "Illegal value for xml:space: " + xmlSpace
;
1045 throw new TransformerConfigurationException(msg
);
1048 ctx
= ctx
.getParentNode();
1053 public XPathFunction
resolveFunction(QName name
, int arity
)
1055 String uri
= name
.getNamespaceURI();
1056 if (XSL_NS
.equals(uri
) || uri
== null || uri
.length() == 0)
1058 String localName
= name
.getLocalPart();
1059 if ("document".equals(localName
) && (arity
== 1 || arity
== 2))
1061 if (current
== null)
1062 throw new RuntimeException("current is null");
1063 return new DocumentFunction(getRootStylesheet(), current
);
1065 else if ("key".equals(localName
) && (arity
== 2))
1066 return new KeyFunction(getRootStylesheet());
1067 else if ("format-number".equals(localName
) &&
1068 (arity
== 2 || arity
== 3))
1069 return new FormatNumberFunction(getRootStylesheet());
1070 else if ("current".equals(localName
) && (arity
== 0))
1071 return new CurrentFunction(getRootStylesheet());
1072 else if ("unparsed-entity-uri".equals(localName
) && (arity
== 1))
1073 return new UnparsedEntityUriFunction();
1074 else if ("generate-id".equals(localName
) &&
1075 (arity
== 1 || arity
== 0))
1076 return new GenerateIdFunction();
1077 else if ("system-property".equals(localName
) && (arity
== 1))
1078 return new SystemPropertyFunction();
1079 else if ("element-available".equals(localName
) && (arity
== 1))
1080 return new ElementAvailableFunction(new NamespaceProxy(current
));
1081 else if ("function-available".equals(localName
) && (arity
== 1))
1082 return new FunctionAvailableFunction(new NamespaceProxy(current
));
1092 final TemplateNode
parseApplyTemplates(Node node
)
1093 throws TransformerConfigurationException
, XPathExpressionException
1095 NamedNodeMap attrs
= node
.getAttributes();
1096 String m
= getAttribute(attrs
, "mode");
1097 QName mode
= (m
== null) ?
null : getQName(m
);
1098 String s
= getAttribute(attrs
, "select");
1100 s
= "child::node()";
1101 Node children
= node
.getFirstChild();
1102 List sortKeys
= parseSortKeys(children
);
1103 List withParams
= parseWithParams(children
);
1104 Expr select
= (Expr
) xpath
.compile(s
);
1105 return new ApplyTemplatesNode(select
, mode
,
1106 sortKeys
, withParams
, false);
1112 final TemplateNode
parseCallTemplate(Node node
)
1113 throws TransformerConfigurationException
, XPathExpressionException
1115 NamedNodeMap attrs
= node
.getAttributes();
1116 String n
= getRequiredAttribute(attrs
, "name", node
);
1117 QName name
= getQName(n
);
1118 Node children
= node
.getFirstChild();
1119 List withParams
= parseWithParams(children
);
1120 return new CallTemplateNode(name
, withParams
);
1126 final TemplateNode
parseValueOf(Node node
)
1127 throws TransformerConfigurationException
, XPathExpressionException
1129 NamedNodeMap attrs
= node
.getAttributes();
1130 String s
= getRequiredAttribute(attrs
, "select", node
);
1131 String doe
= getAttribute(attrs
, "disable-output-escaping");
1132 boolean d
= "yes".equals(doe
);
1133 Expr select
= (Expr
) xpath
.compile(s
);
1134 return new ValueOfNode(select
, d
);
1140 final TemplateNode
parseForEach(Node node
)
1141 throws TransformerConfigurationException
, XPathExpressionException
1143 NamedNodeMap attrs
= node
.getAttributes();
1144 String s
= getRequiredAttribute(attrs
, "select", node
);
1145 Node children
= node
.getFirstChild();
1146 List sortKeys
= parseSortKeys(children
);
1147 Expr select
= (Expr
) xpath
.compile(s
);
1148 ForEachNode ret
= new ForEachNode(select
, sortKeys
);
1149 ret
.children
= parse(children
);
1156 final TemplateNode
parseIf(Node node
)
1157 throws TransformerConfigurationException
, XPathExpressionException
1159 NamedNodeMap attrs
= node
.getAttributes();
1160 String t
= getRequiredAttribute(attrs
, "test", node
);
1161 Expr test
= (Expr
) xpath
.compile(t
);
1162 Node children
= node
.getFirstChild();
1163 IfNode ret
= new IfNode(test
);
1164 ret
.children
= parse(children
);
1171 final TemplateNode
parseWhen(Node node
)
1172 throws TransformerConfigurationException
, XPathExpressionException
1174 NamedNodeMap attrs
= node
.getAttributes();
1175 String t
= getRequiredAttribute(attrs
, "test", node
);
1176 Expr test
= (Expr
) xpath
.compile(t
);
1177 Node children
= node
.getFirstChild();
1178 WhenNode ret
= new WhenNode(test
);
1179 ret
.children
= parse(children
);
1186 final TemplateNode
parseElement(Node node
)
1187 throws TransformerConfigurationException
, XPathExpressionException
1189 NamedNodeMap attrs
= node
.getAttributes();
1190 String name
= getRequiredAttribute(attrs
, "name", node
);
1191 String namespace
= getAttribute(attrs
, "namespace");
1192 String uas
= getAttribute(attrs
, "use-attribute-sets");
1193 TemplateNode n
= parseAttributeValueTemplate(name
, node
);
1194 TemplateNode ns
= (namespace
== null) ?
null :
1195 parseAttributeValueTemplate(namespace
, node
);
1196 Node children
= node
.getFirstChild();
1197 ElementNode ret
= new ElementNode(n
, ns
, uas
, node
);
1198 ret
.children
= parse(children
);
1205 final TemplateNode
parseAttribute(Node node
)
1206 throws TransformerConfigurationException
, XPathExpressionException
1208 NamedNodeMap attrs
= node
.getAttributes();
1209 String name
= getRequiredAttribute(attrs
, "name", node
);
1210 String namespace
= getAttribute(attrs
, "namespace");
1211 TemplateNode n
= parseAttributeValueTemplate(name
, node
);
1212 TemplateNode ns
= (namespace
== null) ?
null :
1213 parseAttributeValueTemplate(namespace
, node
);
1214 Node children
= node
.getFirstChild();
1215 AttributeNode ret
= new AttributeNode(n
, ns
, node
);
1216 ret
.children
= parse(children
);
1223 final TemplateNode
parseText(Node node
)
1224 throws TransformerConfigurationException
, XPathExpressionException
1226 NamedNodeMap attrs
= node
.getAttributes();
1227 String doe
= getAttribute(attrs
, "disable-output-escaping");
1228 boolean d
= "yes".equals(doe
);
1229 Node children
= node
.getFirstChild();
1230 TextNode ret
= new TextNode(d
);
1231 ret
.children
= parse(children
);
1238 final TemplateNode
parseCopy(Node node
)
1239 throws TransformerConfigurationException
, XPathExpressionException
1241 NamedNodeMap attrs
= node
.getAttributes();
1242 String uas
= getAttribute(attrs
, "use-attribute-sets");
1243 Node children
= node
.getFirstChild();
1244 CopyNode ret
= new CopyNode(uas
);
1245 ret
.children
= parse(children
);
1250 * processing-instruction
1252 final TemplateNode
parseProcessingInstruction(Node node
)
1253 throws TransformerConfigurationException
, XPathExpressionException
1255 NamedNodeMap attrs
= node
.getAttributes();
1256 String name
= getRequiredAttribute(attrs
, "name", node
);
1257 Node children
= node
.getFirstChild();
1258 ProcessingInstructionNode ret
= new ProcessingInstructionNode(name
);
1259 ret
.children
= parse(children
);
1266 final TemplateNode
parseNumber(Node node
)
1267 throws TransformerConfigurationException
, XPathExpressionException
1269 NamedNodeMap attrs
= node
.getAttributes();
1270 String v
= getAttribute(attrs
, "value");
1271 String ff
= getAttribute(attrs
, "format");
1276 TemplateNode format
= parseAttributeValueTemplate(ff
, node
);
1277 String lang
= getAttribute(attrs
, "lang");
1278 String lv
= getAttribute(attrs
, "letter-value");
1279 int letterValue
= "traditional".equals(lv
) ?
1280 AbstractNumberNode
.TRADITIONAL
:
1281 AbstractNumberNode
.ALPHABETIC
;
1282 String gs
= getAttribute(attrs
, "grouping-separator");
1283 String gz
= getAttribute(attrs
, "grouping-size");
1284 int gz2
= (gz
!= null && gz
.length() > 0) ?
1285 Integer
.parseInt(gz
) : 1;
1286 Node children
= node
.getFirstChild();
1288 if (v
!= null && v
.length() > 0)
1290 Expr value
= (Expr
) xpath
.compile(v
);
1291 ret
= new NumberNode(value
, format
, lang
,
1292 letterValue
, gs
, gz2
);
1296 String l
= getAttribute(attrs
, "level");
1298 "multiple".equals(l
) ? NodeNumberNode
.MULTIPLE
:
1299 "any".equals(l
) ? NodeNumberNode
.ANY
:
1300 NodeNumberNode
.SINGLE
;
1301 String c
= getAttribute(attrs
, "count");
1302 String f
= getAttribute(attrs
, "from");
1303 Pattern count
= null;
1304 Pattern from
= null;
1309 count
= (Pattern
) xpath
.compile(c
);
1311 catch (ClassCastException e
)
1313 String msg
= "invalid pattern: " + c
;
1314 throw new TransformerConfigurationException(msg
);
1321 from
= (Pattern
) xpath
.compile(f
);
1323 catch (ClassCastException e
)
1325 String msg
= "invalid pattern: " + f
;
1326 throw new TransformerConfigurationException(msg
);
1329 ret
= new NodeNumberNode(level
, count
, from
,
1331 letterValue
, gs
, gz2
);
1333 ret
.children
= parse(children
);
1340 final TemplateNode
parseCopyOf(Node node
)
1341 throws TransformerConfigurationException
, XPathExpressionException
1343 NamedNodeMap attrs
= node
.getAttributes();
1344 String s
= getRequiredAttribute(attrs
, "select", node
);
1345 Expr select
= (Expr
) xpath
.compile(s
);
1346 Node children
= node
.getFirstChild();
1347 CopyOfNode ret
= new CopyOfNode(select
);
1348 ret
.children
= parse(children
);
1355 final TemplateNode
parseMessage(Node node
)
1356 throws TransformerConfigurationException
, XPathExpressionException
1358 NamedNodeMap attrs
= node
.getAttributes();
1359 String t
= getAttribute(attrs
, "terminate");
1360 boolean terminate
= "yes".equals(t
);
1361 Node children
= node
.getFirstChild();
1362 MessageNode ret
= new MessageNode(terminate
);
1363 ret
.children
= parse(children
);
1368 * Parse template-level elements.
1370 final TemplateNode
parse(Node node
)
1371 throws TransformerConfigurationException
1373 TemplateNode first
= null;
1374 TemplateNode previous
= null;
1375 while (node
!= null)
1377 Node next
= node
.getNextSibling();
1378 TemplateNode tnode
= doParse(node
);
1383 if (previous
!= null)
1384 previous
.next
= tnode
;
1392 private final TemplateNode
doParse(Node node
)
1393 throws TransformerConfigurationException
1395 // Hack to associate the document function with its declaring node
1399 String namespaceUri
= node
.getNamespaceURI();
1400 if (Stylesheet
.XSL_NS
.equals(namespaceUri
) &&
1401 Node
.ELEMENT_NODE
== node
.getNodeType())
1403 String name
= node
.getLocalName();
1404 if ("apply-templates".equals(name
))
1405 return parseApplyTemplates(node
);
1406 else if ("call-template".equals(name
))
1407 return parseCallTemplate(node
);
1408 else if ("value-of".equals(name
))
1409 return parseValueOf(node
);
1410 else if ("for-each".equals(name
))
1411 return parseForEach(node
);
1412 else if ("if".equals(name
))
1413 return parseIf(node
);
1414 else if ("choose".equals(name
))
1416 Node children
= node
.getFirstChild();
1417 ChooseNode ret
= new ChooseNode();
1418 ret
.children
= parse(children
);
1421 else if ("when".equals(name
))
1422 return parseWhen(node
);
1423 else if ("otherwise".equals(name
))
1425 Node children
= node
.getFirstChild();
1426 OtherwiseNode ret
= new OtherwiseNode();
1427 ret
.children
= parse(children
);
1430 else if ("element".equals(name
))
1431 return parseElement(node
);
1432 else if ("attribute".equals(name
))
1433 return parseAttribute(node
);
1434 else if ("text".equals(name
))
1435 return parseText(node
);
1436 else if ("copy".equals(name
))
1437 return parseCopy(node
);
1438 else if ("processing-instruction".equals(name
))
1439 return parseProcessingInstruction(node
);
1440 else if ("comment".equals(name
))
1442 Node children
= node
.getFirstChild();
1443 CommentNode ret
= new CommentNode();
1444 ret
.children
= parse(children
);
1447 else if ("number".equals(name
))
1448 return parseNumber(node
);
1449 else if ("param".equals(name
) ||
1450 "variable".equals(name
))
1452 int type
= "variable".equals(name
) ?
1453 Bindings
.VARIABLE
: Bindings
.PARAM
;
1454 NamedNodeMap attrs
= node
.getAttributes();
1455 Node children
= node
.getFirstChild();
1456 TemplateNode content
= parse(children
);
1458 getQName(getRequiredAttribute(attrs
, "name", node
));
1459 String select
= getAttribute(attrs
, "select");
1463 if (content
!= null)
1465 String msg
= "parameter '" + paramName
+
1466 "' has both select and content";
1467 DOMSourceLocator l
= new DOMSourceLocator(node
);
1468 throw new TransformerConfigurationException(msg
, l
);
1470 Expr expr
= (Expr
) xpath
.compile(select
);
1471 ret
= new ParameterNode(paramName
, expr
, type
);
1475 ret
= new ParameterNode(paramName
, null, type
);
1476 ret
.children
= content
;
1480 else if ("copy-of".equals(name
))
1481 return parseCopyOf(node
);
1482 else if ("message".equals(name
))
1483 return parseMessage(node
);
1484 else if ("apply-imports".equals(name
))
1486 Node children
= node
.getFirstChild();
1487 ApplyImportsNode ret
= new ApplyImportsNode();
1488 ret
.children
= parse(children
);
1494 // Pass over any other XSLT nodes
1498 String prefix
= node
.getPrefix();
1499 if (extensionElementPrefixes
.contains(prefix
))
1501 // Check for xsl:fallback
1502 for (Node ctx
= node
.getFirstChild(); ctx
!= null;
1503 ctx
= ctx
.getNextSibling())
1505 String ctxUri
= ctx
.getNamespaceURI();
1506 if (XSL_NS
.equals(ctxUri
) &&
1507 "fallback".equals(ctx
.getLocalName()))
1509 ctx
= ctx
.getFirstChild();
1510 return (ctx
== null) ?
null : parse(ctx
);
1513 // Otherwise pass over extension element
1516 switch (node
.getNodeType())
1518 case Node
.TEXT_NODE
:
1519 case Node
.CDATA_SECTION_NODE
:
1520 // Determine whether to strip whitespace
1521 Text text
= (Text
) node
;
1522 if (!isPreserved(text
, false))
1525 text
.getParentNode().removeChild(text
);
1529 case Node
.COMMENT_NODE
:
1532 case Node
.ELEMENT_NODE
:
1533 // Check for attribute value templates and use-attribute-sets
1534 NamedNodeMap attrs
= node
.getAttributes();
1535 boolean convert
= false;
1536 String useAttributeSets
= null;
1537 int len
= attrs
.getLength();
1538 for (int i
= 0; i
< len
; i
++)
1540 Node attr
= attrs
.item(i
);
1541 String value
= attr
.getNodeValue();
1542 if (Stylesheet
.XSL_NS
.equals(attr
.getNamespaceURI()) &&
1543 "use-attribute-sets".equals(attr
.getLocalName()))
1545 useAttributeSets
= value
;
1549 int start
= value
.indexOf('{');
1550 int end
= value
.indexOf('}');
1551 if (start
!= -1 || end
!= -1)
1559 // Create an element-producing template node instead
1560 // with appropriate attribute-producing child template nodes
1561 Node children
= node
.getFirstChild();
1562 TemplateNode child
= parse(children
);
1563 for (int i
= 0; i
< len
; i
++)
1565 Node attr
= attrs
.item(i
);
1566 String ans
= attr
.getNamespaceURI();
1567 String aname
= attr
.getNodeName();
1568 if (Stylesheet
.XSL_NS
.equals(ans
) &&
1569 "use-attribute-sets".equals(attr
.getLocalName()))
1571 String value
= attr
.getNodeValue();
1572 TemplateNode grandchild
=
1573 parseAttributeValueTemplate(value
, node
);
1575 parseAttributeValueTemplate(aname
, node
);
1576 TemplateNode ns
= (ans
== null) ?
null :
1577 parseAttributeValueTemplate(ans
, node
);
1578 TemplateNode newChild
= new AttributeNode(n
, ns
, attr
);
1579 newChild
.children
= grandchild
;
1580 newChild
.next
= child
;
1583 String ename
= node
.getNodeName();
1584 TemplateNode n
= parseAttributeValueTemplate(ename
, node
);
1585 //TemplateNode ns = (namespaceUri == null) ? null :
1586 // parseAttributeValueTemplate(namespaceUri, node);
1587 TemplateNode ns
= null;
1588 ElementNode ret
= new ElementNode(n
, ns
, useAttributeSets
,
1590 ret
.children
= child
;
1593 // Otherwise fall through
1597 catch (XPathExpressionException e
)
1599 DOMSourceLocator l
= new DOMSourceLocator(node
);
1600 throw new TransformerConfigurationException(e
.getMessage(), l
, e
);
1602 Node children
= node
.getFirstChild();
1603 LiteralNode ret
= new LiteralNode(node
);
1604 ret
.children
= parse(children
);
1608 final List
parseSortKeys(Node node
)
1609 throws TransformerConfigurationException
, XPathExpressionException
1611 List ret
= new LinkedList();
1612 while (node
!= null)
1614 String namespaceUri
= node
.getNamespaceURI();
1615 if (Stylesheet
.XSL_NS
.equals(namespaceUri
) &&
1616 Node
.ELEMENT_NODE
== node
.getNodeType() &&
1617 "sort".equals(node
.getLocalName()))
1619 NamedNodeMap attrs
= node
.getAttributes();
1620 String s
= getAttribute(attrs
, "select");
1623 Expr select
= (Expr
) xpath
.compile(s
);
1624 String l
= getAttribute(attrs
, "lang");
1625 TemplateNode lang
= (l
== null) ?
null :
1626 parseAttributeValueTemplate(l
, node
);
1627 String dt
= getAttribute(attrs
, "data-type");
1628 TemplateNode dataType
= (dt
== null) ?
null :
1629 parseAttributeValueTemplate(dt
, node
);
1630 String o
= getAttribute(attrs
, "order");
1631 TemplateNode order
= (o
== null) ?
null :
1632 parseAttributeValueTemplate(o
, node
);
1633 String co
= getAttribute(attrs
, "case-order");
1634 TemplateNode caseOrder
= (co
== null) ?
null :
1635 parseAttributeValueTemplate(co
, node
);
1636 ret
.add(new SortKey(select
, lang
, dataType
, order
, caseOrder
));
1638 node
= node
.getNextSibling();
1640 return ret
.isEmpty() ?
null : ret
;
1643 final List
parseWithParams(Node node
)
1644 throws TransformerConfigurationException
, XPathExpressionException
1646 List ret
= new LinkedList();
1647 while (node
!= null)
1649 String namespaceUri
= node
.getNamespaceURI();
1650 if (Stylesheet
.XSL_NS
.equals(namespaceUri
) &&
1651 Node
.ELEMENT_NODE
== node
.getNodeType() &&
1652 "with-param".equals(node
.getLocalName()))
1654 NamedNodeMap attrs
= node
.getAttributes();
1655 TemplateNode content
= parse(node
.getFirstChild());
1657 getQName(getRequiredAttribute(attrs
, "name", node
));
1658 String select
= getAttribute(attrs
, "select");
1661 if (content
!= null)
1663 String msg
= "parameter '" + name
+
1664 "' has both select and content";
1665 DOMSourceLocator l
= new DOMSourceLocator(node
);
1666 throw new TransformerConfigurationException(msg
, l
);
1668 Expr expr
= (Expr
) xpath
.compile(select
);
1669 ret
.add(new WithParam(name
, expr
));
1672 ret
.add(new WithParam(name
, content
));
1674 node
= node
.getNextSibling();
1676 return ret
.isEmpty() ?
null : ret
;
1680 * Created element nodes have a copy of the namespace nodes in the
1681 * stylesheet, except the XSLT namespace, extension namespaces, and
1682 * exclude-result-prefixes.
1684 final void addNamespaceNodes(Node source
, Node target
, Document doc
,
1685 Collection elementExcludeResultPrefixes
)
1687 NamedNodeMap attrs
= source
.getAttributes();
1690 int len
= attrs
.getLength();
1691 for (int i
= 0; i
< len
; i
++)
1693 Node attr
= attrs
.item(i
);
1694 String uri
= attr
.getNamespaceURI();
1695 if (uri
== XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
)
1697 String prefix
= attr
.getLocalName();
1698 if (XMLConstants
.XMLNS_ATTRIBUTE
.equals(prefix
))
1699 prefix
= "#default";
1700 String ns
= attr
.getNodeValue();
1701 // Should the namespace be excluded?
1702 if (XSL_NS
.equals(ns
) ||
1703 extensionElementPrefixes
.contains(prefix
) ||
1704 elementExcludeResultPrefixes
.contains(prefix
) ||
1705 excludeResultPrefixes
.contains(prefix
))
1707 // Is the namespace already defined on the target?
1708 if (prefix
== "#default")
1710 if (target
.lookupNamespaceURI(prefix
) != null)
1712 attr
= attr
.cloneNode(true);
1713 attr
= doc
.adoptNode(attr
);
1714 target
.getAttributes().setNamedItemNS(attr
);
1718 Node parent
= source
.getParentNode();
1720 addNamespaceNodes(parent
, target
, doc
, elementExcludeResultPrefixes
);
1723 static final String
getAttribute(NamedNodeMap attrs
, String name
)
1725 Node attr
= attrs
.getNamedItem(name
);
1728 String ret
= attr
.getNodeValue();
1729 if (ret
.length() == 0)
1734 static final String
getRequiredAttribute(NamedNodeMap attrs
, String name
,
1736 throws TransformerConfigurationException
1738 String value
= getAttribute(attrs
, name
);
1739 if (value
== null || value
.length() == 0)
1742 name
+ " attribute is required on " + source
.getNodeName();
1743 DOMSourceLocator l
= new DOMSourceLocator(source
);
1744 throw new TransformerConfigurationException(msg
, l
);
1749 // Handle user data changes when nodes are cloned etc
1751 public void handle(short op
, String key
, Object data
, Node src
, Node dst
)
1753 dst
.setUserData(key
, data
, this);