Merge from mainline
[official-gcc.git] / libjava / classpath / gnu / xml / transform / Stylesheet.java
blob51accaa3b9ab5a587026cf14d62eb7e9484ceb93
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 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;
51 import java.util.Map;
52 import java.util.Set;
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;
81 /**
82 * An XSL stylesheet.
84 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
86 class Stylesheet
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;
100 Stylesheet parent;
101 final XPathImpl xpath;
102 final String systemId;
103 final int precedence;
105 final boolean debug;
108 * Version of XSLT.
110 String version;
112 Collection extensionElementPrefixes;
113 Collection excludeResultPrefixes;
116 * Set of element names for which we should strip whitespace.
118 Set stripSpace;
121 * Set of element names for which we should preserve whitespace.
123 Set preserveSpace;
126 * Output options.
128 Node output;
129 int outputMethod;
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;
141 * Keys.
143 Collection keys;
146 * Decimal formats.
148 Map decimalFormats;
151 * Namespace aliases.
153 Map namespaceAliases;
156 * Attribute-sets.
158 List attributeSets;
161 * Variables.
163 List variables;
166 * Variable and parameter bindings.
168 Bindings bindings;
171 * Templates.
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.
184 Node current;
187 * Set by a terminating message.
189 transient boolean terminated;
192 * Current template in force.
194 transient Template currentTemplate;
196 Stylesheet(TransformerFactoryImpl factory,
197 Stylesheet parent,
198 Document doc,
199 String systemId,
200 int precedence)
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);
214 if (parent == null)
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);
226 else
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);
237 /* OK */
238 Stylesheet root = getRootStylesheet();
239 bindings = root.bindings;
240 attributeSets = root.attributeSets;
241 variables = root.variables;
242 namespaceAliases = root.namespaceAliases;
243 templates = root.templates;
244 keys = root.keys;
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),
257 false);
259 parse(doc.getDocumentElement(), true);
260 current = doc; // Alow namespace resolution during processing
262 debug = ("yes".equals(System.getProperty("xsl.debug")));
264 if (debug)
266 System.err.println("Stylesheet: " + doc.getDocumentURI());
267 for (Iterator i = templates.iterator(); i.hasNext(); )
269 Template t = (Template) i.next();
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 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));
341 clone.keys = keys2;
343 return clone;
345 catch (CloneNotSupportedException e)
347 throw new Error(e.getMessage());
351 // -- Variable evaluation --
353 void initTopLevelVariables(Node context)
354 throws TransformerException
356 current = context;
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),
368 var.type);
370 current = null;
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)
387 // TODO
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(':');
395 if (ci != -1)
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
409 if (debug)
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);
416 if (applyImports)
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))
425 isMatch = false;
427 //System.err.println("\t"+context+" "+t+"="+isMatch);
428 if (isMatch)
429 candidates.add(t);
431 //System.err.println("\tcandidates="+candidates);
432 if (candidates.isEmpty())
434 // Apply built-in template
435 // Current template is unchanged
436 if (debug)
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;
446 case Node.TEXT_NODE:
447 case Node.CDATA_SECTION_NODE:
448 case Node.ATTRIBUTE_NODE:
449 return builtInTextTemplate;
450 default:
451 return null;
454 else
456 Template t = (Template) candidates.iterator().next();
457 // Set current template
458 currentTemplate = t;
459 if (debug)
460 System.err.println("\ttemplate="+t+" context="+context);
461 return t.node;
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);
473 if (isMatch)
474 candidates.add(t);
476 if (candidates.isEmpty())
477 return null;
478 Template t = (Template) candidates.iterator().next();
479 return t.node;
483 * template
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;
492 if (m != 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);
515 * output
517 final void parseOutput(Node node, NamedNodeMap attrs)
518 throws TransformerConfigurationException
520 output = node;
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;
528 else
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");
538 if (indent != null)
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());
559 * key
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);
573 keys.add(key);
575 catch (ClassCastException e)
577 throw new TransformerConfigurationException("invalid pattern: " + m);
582 * decimal-format
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 '" +
615 name +
616 "' in decimal-format", e);
620 private final String parseDFString(NamedNodeMap attrs, String name,
621 String def)
623 Node attr = attrs.getNamedItem(name);
624 return (attr == null) ? def : attr.getNodeValue();
628 * namespace-alias
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);
639 * attribute-set
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
656 while (node != null)
658 current = node;
659 doParse(node, root);
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");
679 if (eep != null)
681 StringTokenizer st = new StringTokenizer(eep);
682 while (st.hasMoreTokens())
684 extensionElementPrefixes.add(st.nextToken());
687 String erp = getAttribute(attrs, "exclude-result-prefixes");
688 if (erp != null)
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());
706 QName paramName =
707 getQName(getRequiredAttribute(attrs, "name", node));
708 String select = getAttribute(attrs, "select");
709 ParameterNode param;
710 if (select != null && select.length() > 0)
712 if (content != null)
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);
722 else
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);
733 Source source;
734 synchronized (factory.resolver)
736 if (transformer != null)
738 factory.resolver
739 .setUserResolver(transformer.getURIResolver());
740 factory.resolver
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))
751 String elements =
752 getRequiredAttribute(attrs, "elements", node);
753 StringTokenizer st = new StringTokenizer(elements,
754 " \t\n\r");
755 while (st.hasMoreTokens())
757 NameTest element = parseNameTest(st.nextToken());
758 preserveSpace.add(new StrippingInstruction(element,
759 precedence));
762 else if ("strip-space".equals(name))
764 String elements =
765 getRequiredAttribute(attrs, "elements", node);
766 StringTokenizer st = new StringTokenizer(elements,
767 " \t\n\r");
768 while (st.hasMoreTokens())
770 NameTest element = parseNameTest(st.nextToken());
771 stripSpace.add(new StrippingInstruction(element,
772 precedence));
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);
784 else if (root)
786 // Literal document element
787 Attr versionNode =
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(),
800 parse(rootClone),
801 precedence,
802 Template.DEFAULT_PRIORITY,
803 null));
805 else
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);
836 else
838 QName qName = getQName(token);
839 return new NameTest(qName, false, false);
843 final TemplateNode parseAttributeValueTemplate(String value, Node source)
844 throws TransformerConfigurationException, XPathExpressionException
846 current = source;
847 // Tokenize
848 int len = value.length();
849 int off = 0;
850 List tokens = new ArrayList(); // text tokens
851 List types = new ArrayList(); // literal or expression
852 int depth = 0;
853 for (int i = 0; i < len; i++)
855 char c = value.charAt(i);
856 if (c == '{')
858 if (i < (len - 1) && value.charAt(i + 1) == '{')
860 tokens.add(value.substring(off, i + 1));
861 types.add(Boolean.FALSE);
862 i++;
863 off = i + 1;
864 continue;
866 if (depth == 0)
868 if (i - off > 0)
870 tokens.add(value.substring(off, i));
871 types.add(Boolean.FALSE);
873 off = i + 1;
875 depth++;
877 else if (c == '}')
879 if (i < (len - 1) && value.charAt(i + 1) == '}')
881 tokens.add(value.substring(off, i + 1));
882 types.add(Boolean.FALSE);
883 i++;
884 off = i + 1;
885 continue;
887 if (depth == 1)
889 if (i - off > 0)
891 tokens.add(value.substring(off, i));
892 types.add(Boolean.TRUE);
894 else
896 String msg = "attribute value template " +
897 "must contain expression: " + value;
898 DOMSourceLocator l = new DOMSourceLocator(source);
899 throw new TransformerConfigurationException(msg, l);
901 off = i + 1;
903 depth--;
906 if (depth > 0)
908 String msg = "invalid attribute value template: " + value;
909 throw new TransformerConfigurationException(msg);
911 if (len - off > 0)
913 // Trailing text
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();
921 len = tokens.size();
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)
928 // Expression text
929 Expr select = (Expr) xpath.compile(token);
930 TemplateNode ret2 = new ValueOfNode(select, false);
931 ret2.next = ret;
932 ret = ret2;
934 else
936 // Verbatim text
937 TemplateNode ret2 = new LiteralNode(doc.createTextNode(token));
938 ret2.next = ret;
939 ret = ret2;
942 return ret;
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();
956 if (value != null)
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)
963 return true;
966 // Check parent node
967 Node ctx = text.getParentNode();
968 if (source)
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))
982 if (ssi != null)
984 if (si.precedence < ssi.precedence)
985 continue;
986 float p = si.getPriority();
987 if (p < ssPriority)
988 continue;
990 ssi = si;
993 for (Iterator i = preserveSpace.iterator(); i.hasNext(); )
995 StrippingInstruction si = (StrippingInstruction) i.next();
996 if (si.element.matches(ctx, 1, 1))
998 if (psi != null)
1000 if (si.precedence < psi.precedence)
1001 continue;
1002 float p = si.getPriority();
1003 if (p < psPriority)
1004 continue;
1006 psi = si;
1009 if (ssi != null)
1011 if (psi != null)
1013 if (psi.precedence < ssi.precedence)
1014 preserve = false;
1015 else if (psPriority < ssPriority)
1016 preserve = false;
1018 else
1019 preserve = false;
1022 if (preserve)
1023 return true;
1025 else
1027 // Stylesheet text node
1028 if (STYLESHEET_PRESERVE_TEXT.matches(ctx, 1, 1))
1029 return true;
1031 // Check whether any ancestor specified xml:space
1032 while (ctx != null)
1034 if (ctx.getNodeType() == Node.ELEMENT_NODE)
1036 Element element = (Element) ctx;
1037 String xmlSpace = element.getAttribute("xml:space");
1038 if ("default".equals(xmlSpace))
1039 break;
1040 else if ("preserve".equals(xmlSpace))
1041 return true;
1042 else if (xmlSpace.length() > 0)
1044 String msg = "Illegal value for xml:space: " + xmlSpace;
1045 throw new TransformerConfigurationException(msg);
1048 ctx = ctx.getParentNode();
1050 return false;
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));
1084 return null;
1087 // -- Parsing --
1090 * apply-templates
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");
1099 if (s == null)
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);
1110 * call-template
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);
1124 * value-of
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);
1138 * for-each
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);
1150 return ret;
1154 * if
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);
1165 return ret;
1169 * when
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);
1180 return ret;
1184 * element
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);
1199 return ret;
1203 * attribute
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);
1217 return ret;
1221 * text
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);
1232 return ret;
1236 * copy
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);
1246 return ret;
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);
1260 return ret;
1264 * number
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");
1272 if (ff == null)
1274 ff = "1";
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();
1287 TemplateNode ret;
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);
1294 else
1296 String l = getAttribute(attrs, "level");
1297 int 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;
1305 if (c != null)
1309 count = (Pattern) xpath.compile(c);
1311 catch (ClassCastException e)
1313 String msg = "invalid pattern: " + c;
1314 throw new TransformerConfigurationException(msg);
1317 if (f != null)
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,
1330 format, lang,
1331 letterValue, gs, gz2);
1333 ret.children = parse(children);
1334 return ret;
1338 * copy-of
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);
1349 return ret;
1353 * message
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);
1364 return ret;
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);
1379 if (tnode != null)
1381 if (first == null)
1382 first = tnode;
1383 if (previous != null)
1384 previous.next = tnode;
1385 previous = tnode;
1387 node = next;
1389 return first;
1392 private final TemplateNode doParse(Node node)
1393 throws TransformerConfigurationException
1395 // Hack to associate the document function with its declaring node
1396 current = 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);
1419 return ret;
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);
1428 return ret;
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);
1445 return ret;
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);
1457 QName paramName =
1458 getQName(getRequiredAttribute(attrs, "name", node));
1459 String select = getAttribute(attrs, "select");
1460 ParameterNode ret;
1461 if (select != null)
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);
1473 else
1475 ret = new ParameterNode(paramName, null, type);
1476 ret.children = content;
1478 return ret;
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);
1489 return ret;
1491 else
1493 // xsl:fallback
1494 // Pass over any other XSLT nodes
1495 return null;
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
1514 return null;
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))
1524 // Strip
1525 text.getParentNode().removeChild(text);
1526 return null;
1528 break;
1529 case Node.COMMENT_NODE:
1530 // Ignore comments
1531 return null;
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;
1546 convert = true;
1547 break;
1549 int start = value.indexOf('{');
1550 int end = value.indexOf('}');
1551 if (start != -1 || end != -1)
1553 convert = true;
1554 break;
1557 if (convert)
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()))
1570 continue;
1571 String value = attr.getNodeValue();
1572 TemplateNode grandchild =
1573 parseAttributeValueTemplate(value, node);
1574 TemplateNode n =
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;
1581 child = newChild;
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,
1589 node);
1590 ret.children = child;
1591 return ret;
1593 // Otherwise fall through
1594 break;
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);
1605 return ret;
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");
1621 if (s == null)
1622 s = ".";
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());
1656 QName name =
1657 getQName(getRequiredAttribute(attrs, "name", node));
1658 String select = getAttribute(attrs, "select");
1659 if (select != null)
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));
1671 else
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();
1688 if (attrs != null)
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))
1706 continue;
1707 // Is the namespace already defined on the target?
1708 if (prefix == "#default")
1709 prefix = null;
1710 if (target.lookupNamespaceURI(prefix) != null)
1711 continue;
1712 attr = attr.cloneNode(true);
1713 attr = doc.adoptNode(attr);
1714 target.getAttributes().setNamedItemNS(attr);
1718 Node parent = source.getParentNode();
1719 if (parent != null)
1720 addNamespaceNodes(parent, target, doc, elementExcludeResultPrefixes);
1723 static final String getAttribute(NamedNodeMap attrs, String name)
1725 Node attr = attrs.getNamedItem(name);
1726 if (attr == null)
1727 return null;
1728 String ret = attr.getNodeValue();
1729 if (ret.length() == 0)
1730 return null;
1731 return ret;
1734 static final String getRequiredAttribute(NamedNodeMap attrs, String name,
1735 Node source)
1736 throws TransformerConfigurationException
1738 String value = getAttribute(attrs, name);
1739 if (value == null || value.length() == 0)
1741 String msg =
1742 name + " attribute is required on " + source.getNodeName();
1743 DOMSourceLocator l = new DOMSourceLocator(source);
1744 throw new TransformerConfigurationException(msg, l);
1746 return value;
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);