libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / gnu / xml / transform / Stylesheet.java
blob30bd953495be797ba2366ac95058b6a4ab806d0f
1 /* Stylesheet.java --
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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;
53 import java.util.Map;
54 import java.util.Set;
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;
82 /**
83 * An XSL stylesheet.
85 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
87 class Stylesheet
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;
101 Stylesheet parent;
102 final XPathImpl xpath;
103 final String systemId;
104 final int precedence;
106 final boolean debug;
109 * Version of XSLT.
111 String version;
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;
127 * Output options.
129 Node output;
130 int outputMethod;
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;
142 * Keys.
144 Collection<Key> keys;
147 * Decimal formats.
149 Map<String,DecimalFormat> decimalFormats;
152 * Namespace aliases.
154 Map<String,String> namespaceAliases;
157 * Attribute-sets.
159 List<AttributeSet> attributeSets;
162 * Variables.
164 List<ParameterNode> variables;
167 * Variable and parameter bindings.
169 Bindings bindings;
172 * Templates.
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.
185 Node current;
188 * Set by a terminating message.
190 transient boolean terminated;
193 * Current template in force.
195 transient Template currentTemplate;
197 Stylesheet(TransformerFactoryImpl factory,
198 Stylesheet parent,
199 Document doc,
200 String systemId,
201 int precedence)
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);
215 if (parent == null)
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);
227 else
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);
238 /* OK */
239 Stylesheet root = getRootStylesheet();
240 bindings = root.bindings;
241 attributeSets = root.attributeSets;
242 variables = root.variables;
243 namespaceAliases = root.namespaceAliases;
244 templates = root.templates;
245 keys = root.keys;
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),
258 false);
260 parse(doc.getDocumentElement(), true);
261 current = doc; // Alow namespace resolution during processing
263 debug = ("yes".equals(System.getProperty("xsl.debug")));
265 if (debug)
267 System.err.println("Stylesheet: " + doc.getDocumentURI());
268 for (Template t : templates)
270 t.list(System.err);
271 System.err.println("--------------------");
276 Stylesheet getRootStylesheet()
278 Stylesheet stylesheet = this;
279 while (stylesheet.parent != null)
280 stylesheet = stylesheet.parent;
281 return stylesheet;
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);
302 // -- Cloneable --
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>();
333 for (Key k : keys)
335 keys2.add(k.clone(clone));
337 clone.keys = keys2;
339 return clone;
341 catch (CloneNotSupportedException e)
343 throw new Error(e.getMessage());
347 // -- Variable evaluation --
349 void initTopLevelVariables(Node context)
350 throws TransformerException
352 current = context;
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),
363 var.type);
365 current = null;
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)
382 // TODO
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(':');
390 if (ci != -1)
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
404 if (debug)
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);
410 if (applyImports)
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))
419 isMatch = false;
421 //System.err.println("\t"+context+" "+t+"="+isMatch);
422 if (isMatch)
424 // Conflict resolution
425 // @see http://www.w3.org/TR/xslt#conflict
426 if (selected == null)
427 selected = t;
428 else
430 if (t.precedence < selected.precedence ||
431 t.priority < selected.priority)
432 continue;
433 selected = t;
437 if (selected == null)
439 // Apply built-in template
440 // Current template is unchanged
441 if (debug)
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;
451 case Node.TEXT_NODE:
452 case Node.CDATA_SECTION_NODE:
453 case Node.ATTRIBUTE_NODE:
454 return builtInTextTemplate;
455 default:
456 return null;
459 // Set current template
460 currentTemplate = selected;
461 if (debug)
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);
473 if (isMatch)
475 // Conflict resolution
476 // @see http://www.w3.org/TR/xslt#conflict
477 if (selected == null)
478 selected = t;
479 else
481 if (t.precedence < selected.precedence ||
482 t.priority < selected.priority)
483 continue;
484 selected = t;
488 if (selected == null)
489 return null;
490 return selected.node;
494 * template
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;
503 if (m != 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);
524 * output
526 final void parseOutput(Node node, NamedNodeMap attrs)
527 throws TransformerConfigurationException
529 output = node;
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;
537 else
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");
547 if (indent != null)
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());
568 * key
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);
582 keys.add(key);
584 catch (ClassCastException e)
586 throw new TransformerConfigurationException("invalid pattern: " + m);
591 * decimal-format
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 '" +
624 name +
625 "' in decimal-format", e);
629 private final String parseDFString(NamedNodeMap attrs, String name,
630 String def)
632 Node attr = attrs.getNamedItem(name);
633 return (attr == null) ? def : attr.getNodeValue();
637 * namespace-alias
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);
648 * attribute-set
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
665 while (node != null)
667 current = node;
668 doParse(node, root);
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");
688 if (eep != null)
690 StringTokenizer st = new StringTokenizer(eep);
691 while (st.hasMoreTokens())
693 extensionElementPrefixes.add(st.nextToken());
696 String erp = getAttribute(attrs, "exclude-result-prefixes");
697 if (erp != null)
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());
715 QName paramName =
716 getQName(getRequiredAttribute(attrs, "name", node));
717 String select = getAttribute(attrs, "select");
718 ParameterNode param;
719 if (select != null && select.length() > 0)
721 if (content != null)
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);
731 else
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);
742 Source source;
743 synchronized (factory.resolver)
745 if (transformer != null)
747 factory.resolver
748 .setUserResolver(transformer.getURIResolver());
749 factory.resolver
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))
760 String elements =
761 getRequiredAttribute(attrs, "elements", node);
762 StringTokenizer st = new StringTokenizer(elements,
763 " \t\n\r");
764 while (st.hasMoreTokens())
766 NameTest element = parseNameTest(st.nextToken());
767 preserveSpace.add(new StrippingInstruction(element,
768 precedence));
771 else if ("strip-space".equals(name))
773 String elements =
774 getRequiredAttribute(attrs, "elements", node);
775 StringTokenizer st = new StringTokenizer(elements,
776 " \t\n\r");
777 while (st.hasMoreTokens())
779 NameTest element = parseNameTest(st.nextToken());
780 stripSpace.add(new StrippingInstruction(element,
781 precedence));
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);
793 else if (root)
795 // Literal document element
796 Attr versionNode =
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(),
809 parse(rootClone),
810 precedence,
811 null,
812 null));
814 else
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);
845 else
847 QName qName = getQName(token);
848 return new NameTest(qName, false, false);
852 final TemplateNode parseAttributeValueTemplate(String value, Node source)
853 throws TransformerConfigurationException, XPathExpressionException
855 current = source;
856 // Tokenize
857 int len = value.length();
858 int off = 0;
859 List<String> tokens = new ArrayList<String>(); // text tokens
860 List<Boolean> types = new ArrayList<Boolean>(); // literal or expression
861 int depth = 0;
862 for (int i = 0; i < len; i++)
864 char c = value.charAt(i);
865 if (c == '{')
867 if (i < (len - 1) && value.charAt(i + 1) == '{')
869 tokens.add(value.substring(off, i + 1));
870 types.add(Boolean.FALSE);
871 i++;
872 off = i + 1;
873 continue;
875 if (depth == 0)
877 if (i - off > 0)
879 tokens.add(value.substring(off, i));
880 types.add(Boolean.FALSE);
882 off = i + 1;
884 depth++;
886 else if (c == '}')
888 if (i < (len - 1) && value.charAt(i + 1) == '}')
890 tokens.add(value.substring(off, i + 1));
891 types.add(Boolean.FALSE);
892 i++;
893 off = i + 1;
894 continue;
896 if (depth == 1)
898 if (i - off > 0)
900 tokens.add(value.substring(off, i));
901 types.add(Boolean.TRUE);
903 else
905 String msg = "attribute value template " +
906 "must contain expression: " + value;
907 DOMSourceLocator l = new DOMSourceLocator(source);
908 throw new TransformerConfigurationException(msg, l);
910 off = i + 1;
912 depth--;
915 if (depth > 0)
917 String msg = "invalid attribute value template: " + value;
918 throw new TransformerConfigurationException(msg);
920 if (len - off > 0)
922 // Trailing text
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();
930 len = tokens.size();
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)
937 // Expression text
938 Expr select = (Expr) xpath.compile(token);
939 TemplateNode ret2 = new ValueOfNode(select, false);
940 ret2.next = ret;
941 ret = ret2;
943 else
945 // Verbatim text
946 TemplateNode ret2 = new LiteralNode(doc.createTextNode(token));
947 ret2.next = ret;
948 ret = ret2;
951 return ret;
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();
965 if (value != null)
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)
972 return true;
975 // Check parent node
976 Node ctx = text.getParentNode();
977 if (source)
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))
990 if (ssi != null)
992 if (si.precedence < ssi.precedence)
993 continue;
994 float p = si.getPriority();
995 if (p < ssPriority)
996 continue;
998 ssi = si;
1001 for (StrippingInstruction si : preserveSpace)
1003 if (si.element.matches(ctx, 1, 1))
1005 if (psi != null)
1007 if (si.precedence < psi.precedence)
1008 continue;
1009 float p = si.getPriority();
1010 if (p < psPriority)
1011 continue;
1013 psi = si;
1016 if (ssi != null)
1018 if (psi != null)
1020 if (psi.precedence < ssi.precedence)
1021 preserve = false;
1022 else if (psPriority < ssPriority)
1023 preserve = false;
1025 else
1026 preserve = false;
1029 if (preserve)
1030 return true;
1032 else
1034 // Stylesheet text node
1035 if (STYLESHEET_PRESERVE_TEXT.matches(ctx, 1, 1))
1036 return true;
1038 // Check whether any ancestor specified xml:space
1039 while (ctx != null)
1041 if (ctx.getNodeType() == Node.ELEMENT_NODE)
1043 Element element = (Element) ctx;
1044 String xmlSpace = element.getAttribute("xml:space");
1045 if ("default".equals(xmlSpace))
1046 break;
1047 else if ("preserve".equals(xmlSpace))
1048 return true;
1049 else if (xmlSpace.length() > 0)
1051 String msg = "Illegal value for xml:space: " + xmlSpace;
1052 throw new TransformerConfigurationException(msg);
1055 ctx = ctx.getParentNode();
1057 return false;
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));
1091 return null;
1094 // -- Parsing --
1097 * apply-templates
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");
1106 if (s == null)
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);
1117 * call-template
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);
1131 * value-of
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);
1145 * for-each
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);
1157 return ret;
1161 * if
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);
1172 return ret;
1176 * when
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);
1187 return ret;
1191 * element
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);
1206 return ret;
1210 * attribute
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);
1224 return ret;
1228 * text
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);
1239 return ret;
1243 * copy
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);
1253 return ret;
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);
1267 return ret;
1271 * number
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");
1279 if (ff == null)
1281 ff = "1";
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();
1294 TemplateNode ret;
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);
1301 else
1303 String l = getAttribute(attrs, "level");
1304 int 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;
1312 if (c != null)
1316 count = (Pattern) xpath.compile(c);
1318 catch (ClassCastException e)
1320 String msg = "invalid pattern: " + c;
1321 throw new TransformerConfigurationException(msg);
1324 if (f != null)
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,
1337 format, lang,
1338 letterValue, gs, gz2);
1340 ret.children = parse(children);
1341 return ret;
1345 * copy-of
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);
1356 return ret;
1360 * message
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);
1371 return ret;
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);
1386 if (tnode != null)
1388 if (first == null)
1389 first = tnode;
1390 if (previous != null)
1391 previous.next = tnode;
1392 previous = tnode;
1394 node = next;
1396 return first;
1399 private final TemplateNode doParse(Node node)
1400 throws TransformerConfigurationException
1402 // Hack to associate the document function with its declaring node
1403 current = 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);
1426 return ret;
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);
1435 return ret;
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);
1452 return ret;
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);
1464 QName paramName =
1465 getQName(getRequiredAttribute(attrs, "name", node));
1466 String select = getAttribute(attrs, "select");
1467 ParameterNode ret;
1468 if (select != null)
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);
1480 else
1482 ret = new ParameterNode(paramName, null, type);
1483 ret.children = content;
1485 return ret;
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);
1496 return ret;
1498 else
1500 // xsl:fallback
1501 // Pass over any other XSLT nodes
1502 return null;
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
1521 return null;
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))
1531 // Strip
1532 text.getParentNode().removeChild(text);
1533 return null;
1535 break;
1536 case Node.COMMENT_NODE:
1537 // Ignore comments
1538 return null;
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;
1553 convert = true;
1554 break;
1556 int start = value.indexOf('{');
1557 int end = value.indexOf('}');
1558 if (start != -1 || end != -1)
1560 convert = true;
1561 break;
1564 if (convert)
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()))
1577 continue;
1578 String value = attr.getNodeValue();
1579 TemplateNode grandchild =
1580 parseAttributeValueTemplate(value, node);
1581 TemplateNode n =
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;
1588 child = newChild;
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,
1596 node);
1597 ret.children = child;
1598 return ret;
1600 // Otherwise fall through
1601 break;
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);
1612 return ret;
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");
1628 if (s == null)
1629 s = ".";
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();
1647 return ret;
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());
1663 QName name =
1664 getQName(getRequiredAttribute(attrs, "name", node));
1665 String select = getAttribute(attrs, "select");
1666 if (select != null)
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));
1678 else
1679 ret.add(new WithParam(name, content));
1681 node = node.getNextSibling();
1683 return ret;
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();
1695 if (attrs != null)
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))
1713 continue;
1714 // Is the namespace already defined on the target?
1715 if (prefix == "#default")
1716 prefix = null;
1717 if (target.lookupNamespaceURI(prefix) != null)
1718 continue;
1719 attr = attr.cloneNode(true);
1720 attr = doc.adoptNode(attr);
1721 target.getAttributes().setNamedItemNS(attr);
1725 Node parent = source.getParentNode();
1726 if (parent != null)
1727 addNamespaceNodes(parent, target, doc, elementExcludeResultPrefixes);
1730 static final String getAttribute(NamedNodeMap attrs, String name)
1732 Node attr = attrs.getNamedItem(name);
1733 if (attr == null)
1734 return null;
1735 String ret = attr.getNodeValue();
1736 if (ret.length() == 0)
1737 return null;
1738 return ret;
1741 static final String getRequiredAttribute(NamedNodeMap attrs, String name,
1742 Node source)
1743 throws TransformerConfigurationException
1745 String value = getAttribute(attrs, name);
1746 if (value == null || value.length() == 0)
1748 String msg =
1749 name + " attribute is required on " + source.getNodeName();
1750 DOMSourceLocator l = new DOMSourceLocator(source);
1751 throw new TransformerConfigurationException(msg, l);
1753 return value;
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);
1768 b.append("]");
1769 return b.toString();