summaryrefslogtreecommitdiff
path: root/libjava/classpath/gnu/xml/transform
diff options
context:
space:
mode:
authorupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
committerupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
commit554fd8c5195424bdbcabf5de30fdc183aba391bd (patch)
tree976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/gnu/xml/transform
downloadcbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2
cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository.
Diffstat (limited to 'libjava/classpath/gnu/xml/transform')
-rw-r--r--libjava/classpath/gnu/xml/transform/AbstractNumberNode.java330
-rw-r--r--libjava/classpath/gnu/xml/transform/ApplyImportsNode.java82
-rw-r--r--libjava/classpath/gnu/xml/transform/ApplyTemplatesNode.java219
-rw-r--r--libjava/classpath/gnu/xml/transform/AttributeNode.java244
-rw-r--r--libjava/classpath/gnu/xml/transform/AttributeSet.java66
-rw-r--r--libjava/classpath/gnu/xml/transform/Bindings.java346
-rw-r--r--libjava/classpath/gnu/xml/transform/CallTemplateNode.java156
-rw-r--r--libjava/classpath/gnu/xml/transform/ChooseNode.java88
-rw-r--r--libjava/classpath/gnu/xml/transform/CommentNode.java103
-rw-r--r--libjava/classpath/gnu/xml/transform/CopyNode.java168
-rw-r--r--libjava/classpath/gnu/xml/transform/CopyOfNode.java174
-rw-r--r--libjava/classpath/gnu/xml/transform/CurrentFunction.java103
-rw-r--r--libjava/classpath/gnu/xml/transform/DOMSourceLocator.java84
-rw-r--r--libjava/classpath/gnu/xml/transform/DocumentFunction.java244
-rw-r--r--libjava/classpath/gnu/xml/transform/ElementAvailableFunction.java181
-rw-r--r--libjava/classpath/gnu/xml/transform/ElementNode.java261
-rw-r--r--libjava/classpath/gnu/xml/transform/ErrorListenerErrorHandler.java101
-rw-r--r--libjava/classpath/gnu/xml/transform/ForEachNode.java159
-rw-r--r--libjava/classpath/gnu/xml/transform/FormatNumberFunction.java145
-rw-r--r--libjava/classpath/gnu/xml/transform/FunctionAvailableFunction.java192
-rw-r--r--libjava/classpath/gnu/xml/transform/GenerateIdFunction.java139
-rw-r--r--libjava/classpath/gnu/xml/transform/IfNode.java112
-rw-r--r--libjava/classpath/gnu/xml/transform/Key.java70
-rw-r--r--libjava/classpath/gnu/xml/transform/KeyFunction.java227
-rw-r--r--libjava/classpath/gnu/xml/transform/LiteralNode.java202
-rw-r--r--libjava/classpath/gnu/xml/transform/MessageNode.java110
-rw-r--r--libjava/classpath/gnu/xml/transform/NamespaceProxy.java77
-rw-r--r--libjava/classpath/gnu/xml/transform/NodeNumberNode.java269
-rw-r--r--libjava/classpath/gnu/xml/transform/NumberNode.java88
-rw-r--r--libjava/classpath/gnu/xml/transform/OtherwiseNode.java83
-rw-r--r--libjava/classpath/gnu/xml/transform/ParameterNode.java172
-rw-r--r--libjava/classpath/gnu/xml/transform/ProcessingInstructionNode.java118
-rw-r--r--libjava/classpath/gnu/xml/transform/SAXSerializer.java305
-rw-r--r--libjava/classpath/gnu/xml/transform/SAXTemplatesHandler.java97
-rw-r--r--libjava/classpath/gnu/xml/transform/SAXTransformerHandler.java111
-rw-r--r--libjava/classpath/gnu/xml/transform/SortKey.java239
-rw-r--r--libjava/classpath/gnu/xml/transform/StreamSerializer.java854
-rw-r--r--libjava/classpath/gnu/xml/transform/StrippingInstruction.java73
-rw-r--r--libjava/classpath/gnu/xml/transform/Stylesheet.java1772
-rw-r--r--libjava/classpath/gnu/xml/transform/SystemPropertyFunction.java140
-rw-r--r--libjava/classpath/gnu/xml/transform/Template.java263
-rw-r--r--libjava/classpath/gnu/xml/transform/TemplateNode.java130
-rw-r--r--libjava/classpath/gnu/xml/transform/TemplatesImpl.java95
-rw-r--r--libjava/classpath/gnu/xml/transform/TextNode.java121
-rw-r--r--libjava/classpath/gnu/xml/transform/TransformerFactoryImpl.java438
-rw-r--r--libjava/classpath/gnu/xml/transform/TransformerImpl.java805
-rw-r--r--libjava/classpath/gnu/xml/transform/TransformerOutputProperties.java186
-rw-r--r--libjava/classpath/gnu/xml/transform/URIResolverEntityResolver.java83
-rw-r--r--libjava/classpath/gnu/xml/transform/UnparsedEntityUriFunction.java132
-rw-r--r--libjava/classpath/gnu/xml/transform/ValueOfNode.java141
-rw-r--r--libjava/classpath/gnu/xml/transform/WhenNode.java115
-rw-r--r--libjava/classpath/gnu/xml/transform/WithParam.java127
-rw-r--r--libjava/classpath/gnu/xml/transform/XSLComparator.java118
-rw-r--r--libjava/classpath/gnu/xml/transform/XSLURIResolver.java321
-rw-r--r--libjava/classpath/gnu/xml/transform/package.html77
55 files changed, 11856 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/xml/transform/AbstractNumberNode.java b/libjava/classpath/gnu/xml/transform/AbstractNumberNode.java
new file mode 100644
index 000000000..6e478bdc4
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/AbstractNumberNode.java
@@ -0,0 +1,330 @@
+/* AbstractNumberNode.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>number</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+abstract class AbstractNumberNode
+ extends TemplateNode
+{
+
+ static final int ALPHABETIC = 0;
+ static final int TRADITIONAL = 1;
+
+ final TemplateNode format;
+ final String lang;
+ final int letterValue;
+ final String groupingSeparator;
+ final int groupingSize;
+
+ AbstractNumberNode(TemplateNode format, String lang,
+ int letterValue, String groupingSeparator,
+ int groupingSize)
+ {
+ this.format = format;
+ this.lang = lang;
+ this.letterValue = letterValue;
+ this.groupingSeparator = groupingSeparator;
+ this.groupingSize = groupingSize;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ DocumentFragment fragment = doc.createDocumentFragment();
+ format.apply(stylesheet, mode, context, pos, len, fragment, null);
+ String f = Expr._string(context, Collections.singleton(fragment));
+ String value = format(f, compute(stylesheet, context, pos, len));
+ Text text = doc.createTextNode(value);
+ if (nextSibling != null)
+ {
+ parent.insertBefore(text, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(text);
+ }
+ // xsl:number doesn't process children
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ String format(String format, int[] number)
+ {
+ if (number.length == 0)
+ {
+ return "";
+ }
+ int start = 0, end = 0, len = format.length(); // region of format
+ // Tokenize
+ List tokens = new ArrayList((number.length * 2) + 1);
+ List types = new ArrayList(tokens.size());
+ while (end < len)
+ {
+ while (end < len && !isAlphanumeric(format.charAt(end)))
+ {
+ end++;
+ }
+ if (end > start)
+ {
+ tokens.add(format.substring(start, end));
+ types.add(Boolean.FALSE);
+ }
+ start = end;
+ while (end < len && isAlphanumeric(format.charAt(end)))
+ {
+ end++;
+ }
+ if (end > start)
+ {
+ tokens.add(format.substring(start, end));
+ types.add(Boolean.TRUE);
+ }
+ start = end;
+ }
+ // Process tokens
+ CPStringBuilder buf = new CPStringBuilder();
+ len = tokens.size();
+ int pos = 0;
+ for (int i = 0; i < len; i++)
+ {
+ String token = (i < 0) ? "." : (String) tokens.get(i);
+ boolean alpha = (i < 0) ? true :
+ ((Boolean) types.get(i)).booleanValue();
+ if (!alpha)
+ {
+ buf.append(token);
+ }
+ else
+ {
+ if (pos < number.length)
+ {
+ format(buf, number[pos++], token);
+ if (((i + 1 == len) || (i + 2 == len)) &&
+ (pos < number.length))
+ {
+ // More numbers than tokens, reuse last token
+ i -= 2;
+ }
+ }
+ if (pos == number.length && i < (len - 2))
+ {
+ // No more numbers. Skip to the end...
+ i = len - 2;
+ if (((Boolean) types.get(i + 1)).booleanValue())
+ {
+ // number formatting token, ignore
+ i++;
+ }
+ }
+ }
+ }
+ //System.err.println("format: '"+format+"' "+asList(number)+" = '"+buf.toString()+"'");
+ return buf.toString();
+ }
+
+ /*List asList(int[] number)
+ {
+ List l = new ArrayList();
+ for (int i = 0; i < number.length; i++)
+ l.add(new Integer(number[i]));
+ return l;
+ }*/
+
+ void format(CPStringBuilder buf, int number, String formatToken)
+ {
+ int len = formatToken.length();
+ char c = formatToken.charAt(len - 1);
+ if (Character.digit(c, 10) == 1)
+ {
+ // Check preceding characters
+ for (int i = len - 2; i >= 0; i--)
+ {
+ if (formatToken.charAt(i) != (c - 1))
+ {
+ format(buf, number, "1");
+ return;
+ }
+ }
+ // Decimal representation
+ String val = Integer.toString(number);
+ for (int d = len - val.length(); d > 0; d--)
+ {
+ buf.append('0');
+ }
+ buf.append(val);
+ }
+ else if ("A".equals(formatToken))
+ {
+ buf.append(alphabetic('@', number));
+ }
+ else if ("a".equals(formatToken))
+ {
+ buf.append(alphabetic('`', number));
+ }
+ else if ("i".equals(formatToken))
+ {
+ buf.append(roman(false, number));
+ }
+ else if ("I".equals(formatToken))
+ {
+ buf.append(roman(true, number));
+ }
+ else
+ {
+ // Unknown numbering sequence
+ format(buf, number, "1");
+ }
+ }
+
+ static final boolean isAlphanumeric(char c)
+ {
+ switch (Character.getType(c))
+ {
+ case Character.DECIMAL_DIGIT_NUMBER: // Nd
+ case Character.LETTER_NUMBER: // Nl
+ case Character.OTHER_NUMBER: // No
+ case Character.UPPERCASE_LETTER: // Lu
+ case Character.LOWERCASE_LETTER: // Ll
+ case Character.TITLECASE_LETTER: // Lt
+ case Character.MODIFIER_LETTER: // Lm
+ case Character.OTHER_LETTER: // Lo
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static final String alphabetic(char offset, int number)
+ {
+ CPStringBuilder buf = new CPStringBuilder();
+ while (number > 0)
+ {
+ int r = number % 26;
+ number = number / 26;
+ buf.insert(0, (char) (offset + r));
+ }
+ return buf.toString();
+ }
+
+ static final int[] roman_numbers = {1, 5, 10, 50, 100, 500, 1000};
+ static final char[] roman_chars = {'i', 'v', 'x', 'l', 'c', 'd', 'm'};
+
+ static final String roman(boolean upper, int number)
+ {
+ CPStringBuilder buf = new CPStringBuilder();
+ for (int pos = roman_numbers.length - 1; pos >= 0; pos -= 2)
+ {
+ int f = number / roman_numbers[pos];
+ if (f != 0)
+ {
+ number = number % (f * roman_numbers[pos]);
+ }
+ if (f > 4 && f < 9)
+ {
+ buf.append(roman_chars[pos + 1]);
+ f -= 5;
+ }
+ if (f == 4)
+ {
+ buf.append(roman_chars[pos]);
+ buf.append(roman_chars[pos + 1]);
+ }
+ else if (f == 9)
+ {
+ buf.append(roman_chars[pos]);
+ buf.append(roman_chars[pos + 2]);
+ }
+ else
+ {
+ for (; f > 0; f--)
+ {
+ buf.append(roman_chars[pos]);
+ }
+ }
+ }
+ return upper ? buf.toString().toUpperCase() : buf.toString();
+ }
+
+ abstract int[] compute(Stylesheet stylesheet, Node context, int pos, int len)
+ throws TransformerException;
+
+ public boolean references(QName var)
+ {
+ if (format.references(var))
+ {
+ return true;
+ }
+ return super.references(var);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("number");
+ buf.append('[');
+ buf.append("format=");
+ buf.append(format);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/ApplyImportsNode.java b/libjava/classpath/gnu/xml/transform/ApplyImportsNode.java
new file mode 100644
index 000000000..298c49da4
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/ApplyImportsNode.java
@@ -0,0 +1,82 @@
+/* ApplyImportsNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+
+/**
+ * A template node representing an XSLT <code>apply-imports</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ApplyImportsNode
+ extends TemplateNode
+{
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new ApplyImportsNode();
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ TemplateNode t = stylesheet.getTemplate(mode, context, true);
+ if (t != null)
+ t.apply(stylesheet, mode, context, pos, len,
+ parent, nextSibling);
+ if (next != null)
+ next.apply(stylesheet, mode, context, pos, len,
+ parent, nextSibling);
+ }
+
+ public String toString()
+ {
+ return "apply-imports";
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/ApplyTemplatesNode.java b/libjava/classpath/gnu/xml/transform/ApplyTemplatesNode.java
new file mode 100644
index 000000000..6aa36954a
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/ApplyTemplatesNode.java
@@ -0,0 +1,219 @@
+/* ApplyTemplatesNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>apply-templates</code>
+ * instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ApplyTemplatesNode
+ extends TemplateNode
+{
+
+ final Expr select;
+ final QName mode;
+ final List sortKeys;
+ final List withParams;
+ final boolean isDefault;
+
+ ApplyTemplatesNode(Expr select, QName mode,
+ List sortKeys, List withParams, boolean isDefault)
+ {
+ this.select = select;
+ this.mode = mode;
+ this.sortKeys = sortKeys;
+ this.withParams = withParams;
+ this.isDefault = isDefault;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ int len = sortKeys != null ? sortKeys.size() : 0;
+ List sortKeys2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ sortKeys2.add(((Key) sortKeys.get(i)).clone(stylesheet));
+ len = withParams != null ? withParams.size() : 0;
+ List withParams2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ withParams2.add(((WithParam) withParams.get(i)).clone(stylesheet));
+ TemplateNode ret = new ApplyTemplatesNode(select.clone(stylesheet),
+ mode, sortKeys2, withParams2,
+ isDefault);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Object ret = select.evaluate(context, pos, len);
+ if (ret != null && ret instanceof Collection)
+ {
+ if (withParams != null)
+ {
+ // compute the parameter values
+ LinkedList values = new LinkedList();
+ for (Iterator i = withParams.iterator(); i.hasNext(); )
+ {
+ WithParam p = (WithParam) i.next();
+ Object value = p.getValue(stylesheet, mode, context, pos, len);
+ Object[] pair = new Object[2];
+ pair[0] = p.name;
+ pair[1] = value;
+ values.add(pair);
+ }
+ // push the parameter context
+ stylesheet.bindings.push(Bindings.WITH_PARAM);
+ // set the parameters
+ for (Iterator i = values.iterator(); i.hasNext(); )
+ {
+ Object[] pair = (Object[]) i.next();
+ QName name = (QName) pair[0];
+ Object value = pair[1];
+ stylesheet.bindings.set(name, value, Bindings.WITH_PARAM);
+ }
+ }
+ Collection ns = (Collection) ret;
+ List nodes = new ArrayList(ns);
+ if (sortKeys != null)
+ {
+ for (Iterator i = sortKeys.iterator(); i.hasNext(); )
+ {
+ SortKey sortKey = (SortKey) i.next();
+ sortKey.init(stylesheet, mode, context, pos, len, parent,
+ nextSibling);
+ }
+ Collections.sort(nodes, new XSLComparator(sortKeys));
+ }
+ else
+ Collections.sort(nodes, documentOrderComparator);
+ int l = nodes.size();
+ QName effectiveMode = isDefault ? mode : this.mode;
+ for (int i = 0; i < l; i++)
+ {
+ Node node = (Node) nodes.get(i);
+ TemplateNode t = stylesheet.getTemplate(effectiveMode, node,
+ false);
+ if (t != null)
+ {
+ stylesheet.current = node;
+ t.apply(stylesheet, effectiveMode, node, i + 1, l,
+ parent, nextSibling);
+ }
+ }
+ if (withParams != null)
+ {
+ // pop the variable context
+ stylesheet.bindings.pop(Bindings.WITH_PARAM);
+ }
+ }
+ // apply-templates doesn't have processable children
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public boolean references(QName var)
+ {
+ if (select != null && select.references(var))
+ return true;
+ if (withParams != null)
+ {
+ for (Iterator i = withParams.iterator(); i.hasNext(); )
+ {
+ if (((WithParam) i.next()).references(var))
+ return true;
+ }
+ }
+ if (sortKeys != null)
+ {
+ for (Iterator i = sortKeys.iterator(); i.hasNext(); )
+ {
+ if (((SortKey) i.next()).references(var))
+ return true;
+ }
+ }
+ return super.references(var);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("apply-templates");
+ buf.append('[');
+ boolean o = false;
+ if (select != null)
+ {
+ buf.append("select=");
+ buf.append(select);
+ o = true;
+ }
+ if (mode != null)
+ {
+ if (o)
+ {
+ buf.append(',');
+ }
+ buf.append("mode=");
+ buf.append(mode);
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/AttributeNode.java b/libjava/classpath/gnu/xml/transform/AttributeNode.java
new file mode 100644
index 000000000..4c71f6a92
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/AttributeNode.java
@@ -0,0 +1,244 @@
+/* AttributeNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSL <code>attribute</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class AttributeNode
+ extends TemplateNode
+{
+
+ final TemplateNode name;
+ final TemplateNode namespace;
+ final Node source;
+
+ AttributeNode(TemplateNode name,
+ TemplateNode namespace, Node source)
+ {
+ this.name = name;
+ this.namespace = namespace;
+ this.source = source;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new AttributeNode(name.clone(stylesheet),
+ (namespace == null) ? null :
+ namespace.clone(stylesheet),
+ source);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ // Create a document fragment to hold the name
+ DocumentFragment fragment = doc.createDocumentFragment();
+ // Apply name to the fragment
+ name.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ String nameValue = Expr.stringValue(fragment);
+
+ String namespaceValue = null;
+ if (namespace != null)
+ {
+ // Create a document fragment to hold the namespace
+ fragment = doc.createDocumentFragment();
+ // Apply namespace to the fragment
+ namespace.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ namespaceValue = Expr.stringValue(fragment);
+ if (namespaceValue.length() == 0)
+ namespaceValue = null;
+ }
+
+ String prefix = getPrefix(nameValue);
+ if (namespaceValue == null)
+ {
+ if (prefix != null)
+ {
+ if (XMLConstants.XML_NS_PREFIX.equals(prefix))
+ namespaceValue = XMLConstants.XML_NS_URI;
+ else
+ {
+ // Resolve namespace for this prefix
+ namespaceValue = source.lookupNamespaceURI(prefix);
+ }
+ }
+ }
+ else
+ {
+ if (prefix != null)
+ {
+ String ns2 = source.lookupNamespaceURI(prefix);
+ if (ns2 != null && !ns2.equals(namespaceValue))
+ {
+ // prefix clashes, reset it
+ prefix = null;
+ int ci = nameValue.indexOf(':');
+ nameValue = nameValue.substring(ci + 1);
+ }
+ }
+ }
+ if (prefix == null)
+ {
+ // Resolve prefix for this namespace
+ prefix = source.lookupPrefix(namespaceValue);
+ if (prefix != null)
+ nameValue = prefix + ":" + nameValue;
+ else
+ {
+ if (namespaceValue != null)
+ {
+ // Must invent a prefix
+ prefix = inventPrefix(parent);
+ nameValue = prefix + ":" + nameValue;
+ }
+ }
+ }
+ NamedNodeMap attrs = parent.getAttributes();
+ boolean insert = true;
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceValue) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(nameValue) ||
+ nameValue.startsWith("xmlns:"))
+ {
+ // Namespace declaration, do not output
+ insert = false;
+ }
+ if (prefix != null && namespaceValue == null)
+ {
+ // Not a QName
+ insert = false;
+ }
+ if (parent.getNodeType() == Node.ELEMENT_NODE &&
+ parent.getFirstChild() != null)
+ {
+ // XSLT 7.1.3 Adding an attribute to an element after children have
+ // been added to it is an error
+ insert = false;
+ }
+ if (insert)
+ {
+ // Insert attribute
+ Attr attr = (namespaceValue != null) ?
+ doc.createAttributeNS(namespaceValue, nameValue) :
+ doc.createAttribute(nameValue);
+ if (attrs != null)
+ {
+ if (namespace != null)
+ attrs.setNamedItemNS(attr);
+ else
+ attrs.setNamedItem(attr);
+ }
+ if (children != null)
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ attr, null);
+ }
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ final String getPrefix(String name)
+ {
+ int ci = name.indexOf(':');
+ return (ci == -1) ? null : name.substring(0, ci);
+ }
+
+ final String inventPrefix(Node parent)
+ {
+ String base = "ns";
+ int count = 0;
+ String ret = base + Integer.toString(count);
+ while (parent.lookupNamespaceURI(ret) != null)
+ {
+ count++;
+ ret = base + Integer.toString(count);
+ }
+ return ret;
+ }
+
+ public boolean references(QName var)
+ {
+ if (name != null && name.references(var))
+ return true;
+ if (namespace != null && namespace.references(var))
+ return true;
+ return super.references(var);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("attribute");
+ buf.append('[');
+ buf.append("name=");
+ buf.append(name);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/AttributeSet.java b/libjava/classpath/gnu/xml/transform/AttributeSet.java
new file mode 100644
index 000000000..ced50f11c
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/AttributeSet.java
@@ -0,0 +1,66 @@
+/* AttributeSet.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+/**
+ * An attribute-set entry in a stylesheet.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class AttributeSet
+{
+
+ final TemplateNode children;
+ final String name;
+ final String uas;
+
+ AttributeSet(TemplateNode children, String name, String uas)
+ {
+ this.children = children;
+ this.name = name;
+ this.uas = uas;
+ }
+
+ AttributeSet clone(Stylesheet stylesheet)
+ {
+ return new AttributeSet((children == null) ? null :
+ children.clone(stylesheet),
+ name, uas);
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/Bindings.java b/libjava/classpath/gnu/xml/transform/Bindings.java
new file mode 100644
index 000000000..37d45880c
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/Bindings.java
@@ -0,0 +1,346 @@
+/* Bindings.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathVariableResolver;
+import org.w3c.dom.Node;
+
+/**
+ * The set of variable bindings in effect for a stylesheet.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class Bindings
+ implements XPathVariableResolver, Cloneable
+{
+
+ static final int VARIABLE = 0;
+ static final int PARAM = 1;
+ static final int WITH_PARAM = 2;
+
+ final Stylesheet stylesheet;
+
+ /**
+ * Global variables.
+ */
+ final LinkedList<Map<QName,Object>> variables;
+
+ /**
+ * Parameter value stack.
+ */
+ final LinkedList<Map<QName,Object>> parameters;
+
+ /**
+ * Argument (with-param) value stack.
+ */
+ final LinkedList<Map<QName,Object>> withParameters;
+
+ /**
+ * Only search globals.
+ */
+ boolean global;
+
+ Bindings(Stylesheet stylesheet)
+ {
+ this.stylesheet = stylesheet;
+ variables = new LinkedList<Map<QName,Object>>();
+ parameters = new LinkedList<Map<QName,Object>>();
+ withParameters = new LinkedList<Map<QName,Object>>();
+ for (int i = 0; i < 3; i++)
+ {
+ push(i);
+ }
+ }
+
+ public Object clone()
+ {
+ try
+ {
+ return (Bindings) super.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new Error(e.getMessage());
+ }
+ }
+
+ void push(int type)
+ {
+ switch (type)
+ {
+ case VARIABLE:
+ variables.addFirst(new HashMap<QName,Object>());
+ break;
+ case PARAM:
+ parameters.addFirst(new HashMap<QName,Object>());
+ break;
+ case WITH_PARAM:
+ withParameters.addFirst(new HashMap<QName,Object>());
+ break;
+ }
+ }
+
+ void pop(int type)
+ {
+ switch (type)
+ {
+ case VARIABLE:
+ variables.removeFirst();
+ break;
+ case PARAM:
+ parameters.removeFirst();
+ break;
+ case WITH_PARAM:
+ withParameters.removeFirst();
+ break;
+ }
+ }
+
+ public boolean containsKey(QName name, int type)
+ {
+ if (global)
+ {
+ Map<QName,Object> ctx1 = variables.getLast();
+ Map<QName,Object> ctx2 = parameters.getLast();
+ return (ctx1.containsKey(name) || ctx2.containsKey(name));
+ }
+ Iterator<Map<QName,Object>> i = null;
+ switch (type)
+ {
+ case VARIABLE:
+ i = variables.iterator();
+ break;
+ case PARAM:
+ i = parameters.iterator();
+ break;
+ case WITH_PARAM:
+ Map<QName,Object> ctx = withParameters.getFirst();
+ return ctx.containsKey(name);
+ }
+ if (i != null)
+ {
+ while (i.hasNext())
+ {
+ Map<QName,Object> ctx = i.next();
+ if (ctx.containsKey(name))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public Object get(QName name, Node context, int pos, int len)
+ {
+ if (global)
+ {
+ Map<QName,Object> ctx = variables.getLast();
+ Object ret = ctx.get(name);
+ if (ret == null)
+ {
+ ctx = parameters.getLast();
+ ret = ctx.get(name);
+ }
+ return ret;
+ }
+ //System.err.println("bindings.get: "+name);
+ //System.err.println("\t"+toString());
+ Object ret = null;
+ //if (parameters.size() > 1 && containsKey(name, PARAM))
+ // check that template defines parameter
+ {
+ Map<QName,Object> cwp = withParameters.getFirst();
+ ret = cwp.get(name);
+ //System.err.println("\twith-param: ret="+ret);
+ }
+ if (ret == null)
+ {
+ for (Iterator<Map<QName,Object>> i = variables.iterator();
+ i.hasNext() && ret == null; )
+ {
+ Map<QName,Object> vctx = i.next();
+ ret = vctx.get(name);
+ }
+ //System.err.println("\tvariable: ret="+ret);
+ }
+ if (ret == null)
+ {
+ for (Iterator<Map<QName,Object>> i = parameters.iterator();
+ i.hasNext() && ret == null; )
+ {
+ Map<QName,Object> pctx = i.next();
+ ret = pctx.get(name);
+ }
+ //System.err.println("\tparam: ret="+ret);
+ }
+ /*if (ret instanceof Expr && context != null)
+ {
+ Expr expr = (Expr) ret;
+ ret = expr.evaluate(context, 1, 1);
+ }*/
+ if (ret instanceof Node)
+ {
+ ret = Collections.singleton(ret);
+ }
+ if (ret == null)
+ {
+ ret = "";
+ }
+ //System.err.println("\tret="+ret);
+ return ret;
+ }
+
+ void set(QName name, Object value, int type)
+ {
+ switch (type)
+ {
+ case VARIABLE:
+ Map<QName,Object> vctx = variables.getFirst();
+ vctx.put(name, value);
+ break;
+ case PARAM:
+ Map<QName,Object> pctx = parameters.getFirst();
+ pctx.put(name, value);
+ break;
+ case WITH_PARAM:
+ Map<QName,Object> wctx = withParameters.getFirst();
+ wctx.put(name, value);
+ break;
+ }
+ //System.err.println("Set "+name+"="+value);
+ }
+
+ public Object resolveVariable(QName qName)
+ {
+ return get(qName, null, 1, 1);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder();
+ boolean next = false;
+ Collection<QName> seen = new HashSet<QName>();
+ Map<QName,Object> wctx = withParameters.getFirst();
+ buf.append('(');
+ for (Map.Entry<QName,Object> entry : wctx.entrySet())
+ {
+ if (next)
+ {
+ buf.append(',');
+ }
+ else
+ {
+ next = true;
+ }
+ QName key = entry.getKey();
+ if (!seen.contains(key))
+ {
+ buf.append(key);
+ buf.append('=');
+ buf.append(entry.getValue());
+ seen.add(key);
+ }
+ }
+ buf.append(')');
+ next = false;
+ seen.clear();
+ buf.append('{');
+ for (Map<QName,Object> ctx : variables)
+ {
+ for (Map.Entry<QName,Object> entry : ctx.entrySet())
+ {
+ if (next)
+ {
+ buf.append(',');
+ }
+ else
+ {
+ next = true;
+ }
+ QName key = entry.getKey();
+ if (!seen.contains(key))
+ {
+ buf.append(key);
+ buf.append('=');
+ buf.append(entry.getValue());
+ seen.add(key);
+ }
+ }
+ }
+ buf.append('}');
+ next = false;
+ seen.clear();
+ buf.append('[');
+ for (Map<QName,Object> ctx : parameters)
+ {
+ for (Map.Entry<QName,Object> entry : ctx.entrySet())
+ {
+ if (next)
+ {
+ buf.append(',');
+ }
+ else
+ {
+ next = true;
+ }
+ QName key = entry.getKey();
+ if (!seen.contains(key))
+ {
+ buf.append(key);
+ buf.append('=');
+ buf.append(entry.getValue());
+ seen.add(key);
+ }
+ }
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/CallTemplateNode.java b/libjava/classpath/gnu/xml/transform/CallTemplateNode.java
new file mode 100644
index 000000000..cc08d93e3
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/CallTemplateNode.java
@@ -0,0 +1,156 @@
+/* CallTemplateNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+
+/**
+ * A template node representing the XSL <code>call-template</code>
+ * instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CallTemplateNode
+ extends TemplateNode
+{
+
+ final QName name;
+ final List withParams;
+
+ CallTemplateNode(QName name, List withParams)
+ {
+ this.name = name;
+ this.withParams = withParams;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ int len = withParams.size();
+ List withParams2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ withParams2.add(((WithParam) withParams.get(i)).clone(stylesheet));
+ TemplateNode ret = new CallTemplateNode(name, withParams2);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ TemplateNode t = stylesheet.getTemplate(mode, name);
+ if (t != null)
+ {
+ if (!withParams.isEmpty())
+ {
+ // compute the parameter values
+ LinkedList values = new LinkedList();
+ for (Iterator i = withParams.iterator(); i.hasNext(); )
+ {
+ WithParam p = (WithParam) i.next();
+ if (t.hasParam(p.name)) // ignore parameters not specified
+ {
+ Object value = p.getValue(stylesheet, mode, context,
+ pos, len);
+ Object[] pair = new Object[2];
+ pair[0] = p.name;
+ pair[1] = value;
+ values.add(pair);
+ }
+ }
+ // push the parameter context
+ stylesheet.bindings.push(Bindings.WITH_PARAM);
+ // set the parameters
+ for (Iterator i = values.iterator(); i.hasNext(); )
+ {
+ Object[] pair = (Object[]) i.next();
+ QName name = (QName) pair[0];
+ Object value = pair[1];
+ stylesheet.bindings.set(name, value, Bindings.WITH_PARAM);
+ if (stylesheet.debug)
+ System.err.println("with-param: " + name + " = " + value);
+ }
+ }
+ t.apply(stylesheet, mode, context, pos, len,
+ parent, nextSibling);
+ if (!withParams.isEmpty())
+ {
+ // pop the variable context
+ stylesheet.bindings.pop(Bindings.WITH_PARAM);
+ }
+ }
+ // call-template doesn't have processable children
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public boolean references(QName var)
+ {
+ for (Iterator i = withParams.iterator(); i.hasNext(); )
+ {
+ if (((WithParam) i.next()).references(var))
+ return true;
+ }
+ return super.references(var);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("call-template");
+ buf.append('[');
+ buf.append("name=");
+ buf.append(name);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/ChooseNode.java b/libjava/classpath/gnu/xml/transform/ChooseNode.java
new file mode 100644
index 000000000..262cde144
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/ChooseNode.java
@@ -0,0 +1,88 @@
+/* ChooseNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+
+/**
+ * A template node representing an XSL <code>choose</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ChooseNode
+ extends TemplateNode
+{
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new ChooseNode();
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (children != null)
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("choose");
+ buf.append('[');
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/CommentNode.java b/libjava/classpath/gnu/xml/transform/CommentNode.java
new file mode 100644
index 000000000..1c14e5fa8
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/CommentNode.java
@@ -0,0 +1,103 @@
+/* CommentNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>comment</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CommentNode
+ extends TemplateNode
+{
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new CommentNode();
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ String value = "";
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ if (children != null)
+ {
+ // Create a document fragment to hold the text
+ DocumentFragment fragment = doc.createDocumentFragment();
+ // Apply children to the fragment
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ value = Expr.stringValue(fragment);
+ }
+ Comment comment = doc.createComment(value);
+ // Insert into result tree
+ if (nextSibling != null)
+ parent.insertBefore(comment, nextSibling);
+ else
+ parent.appendChild(comment);
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public String toString()
+ {
+ return "comment";
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/CopyNode.java b/libjava/classpath/gnu/xml/transform/CopyNode.java
new file mode 100644
index 000000000..9cc2fdecd
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/CopyNode.java
@@ -0,0 +1,168 @@
+/* CopyNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * A template node representing the XSL <code>copy</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CopyNode
+ extends TemplateNode
+{
+
+ final String uas;
+
+ CopyNode(String uas)
+ {
+ this.uas = uas;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new CopyNode(uas);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Node copy = parent;
+ switch (context.getNodeType())
+ {
+ case Node.TEXT_NODE:
+ case Node.ATTRIBUTE_NODE:
+ case Node.ELEMENT_NODE:
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ case Node.COMMENT_NODE:
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ copy = context.cloneNode(false);
+ copy = doc.adoptNode(copy);
+ if (copy.getNodeType() == Node.ATTRIBUTE_NODE)
+ {
+ if (parent.getFirstChild() != null)
+ {
+ // Ignore attempt to add attribute after children
+ }
+ else
+ {
+ NamedNodeMap attrs = parent.getAttributes();
+ if (attrs != null)
+ attrs.setNamedItemNS(copy);
+ }
+ }
+ else
+ {
+ if (nextSibling != null)
+ parent.insertBefore(copy, nextSibling);
+ else
+ parent.appendChild(copy);
+ }
+ }
+ if (uas != null)
+ {
+ StringTokenizer st = new StringTokenizer(uas, " ");
+ while (st.hasMoreTokens())
+ addAttributeSet(stylesheet, mode, context, pos, len,
+ copy, null, st.nextToken());
+ }
+ if (children != null)
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ copy, null);
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ void addAttributeSet(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling, String attributeSet)
+ throws TransformerException
+ {
+ for (Iterator i = stylesheet.attributeSets.iterator(); i.hasNext(); )
+ {
+ AttributeSet as = (AttributeSet) i.next();
+ if (!as.name.equals(attributeSet))
+ continue;
+ if (as.uas != null)
+ {
+ StringTokenizer st = new StringTokenizer(as.uas, " ");
+ while (st.hasMoreTokens())
+ addAttributeSet(stylesheet, mode, context, pos, len,
+ parent, nextSibling, st.nextToken());
+ }
+ if (as.children != null)
+ as.children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("copy");
+ if (uas != null)
+ {
+ buf.append('[');
+ buf.append("uas=");
+ buf.append(uas);
+ buf.append(']');
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/CopyOfNode.java b/libjava/classpath/gnu/xml/transform/CopyOfNode.java
new file mode 100644
index 000000000..2b06fed4d
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/CopyOfNode.java
@@ -0,0 +1,174 @@
+/* CopyOfNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSLT <code>copy-of</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CopyOfNode
+ extends TemplateNode
+{
+
+ final Expr select;
+
+ CopyOfNode(Expr select)
+ {
+ this.select = select;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new CopyOfNode(select.clone(stylesheet));
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Object ret = select.evaluate(context, pos, len);
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ if (ret instanceof Collection)
+ {
+ Collection ns = (Collection) ret;
+ List list = new ArrayList(ns);
+ Collections.sort(list, documentOrderComparator);
+ for (Iterator i = list.iterator(); i.hasNext(); )
+ {
+ Node src = (Node) i.next();
+ short nodeType = src.getNodeType();
+ if (nodeType == Node.DOCUMENT_NODE)
+ {
+ // Use document element
+ src = ((Document) src).getDocumentElement();
+ if (src == null)
+ continue;
+ nodeType = Node.ELEMENT_NODE;
+ }
+ else if (nodeType == Node.ATTRIBUTE_NODE)
+ {
+ if (parent.getFirstChild() != null)
+ {
+ // Ignore attempt to add attribute after children
+ continue;
+ }
+ }
+ if (parent.getNodeType() == Node.ATTRIBUTE_NODE &&
+ nodeType != Node.TEXT_NODE &&
+ nodeType != Node.ENTITY_REFERENCE_NODE)
+ {
+ // Ignore
+ continue;
+ }
+ Node node = src.cloneNode(true);
+ node = doc.adoptNode(node);
+ if (nodeType == Node.ATTRIBUTE_NODE)
+ {
+ NamedNodeMap attrs = parent.getAttributes();
+ if (attrs != null)
+ attrs.setNamedItemNS(node);
+ }
+ else
+ {
+ if (nextSibling != null)
+ parent.insertBefore(node, nextSibling);
+ else
+ parent.appendChild(node);
+ }
+ }
+ }
+ else
+ {
+ String value = Expr._string(context, ret);
+ if (value != null && value.length() > 0)
+ {
+ Text textNode = doc.createTextNode(value);
+ if (nextSibling != null)
+ parent.insertBefore(textNode, nextSibling);
+ else
+ parent.appendChild(textNode);
+ }
+ }
+ // copy-of doesn't process children
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public boolean references(QName var)
+ {
+ if (select != null && select.references(var))
+ return true;
+ return super.references(var);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("copy-of");
+ buf.append('[');
+ buf.append("select=");
+ buf.append(select);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/CurrentFunction.java b/libjava/classpath/gnu/xml/transform/CurrentFunction.java
new file mode 100644
index 000000000..96899679a
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/CurrentFunction.java
@@ -0,0 +1,103 @@
+/* CurrentFunction.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.Collections;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>current()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CurrentFunction
+ extends Expr
+ implements Function, XPathFunction
+{
+
+ final Stylesheet stylesheet;
+
+ CurrentFunction(Stylesheet stylesheet)
+ {
+ this.stylesheet = stylesheet;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // We can't do anything useful here.
+ // So much for the JAXP API...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ return Collections.singleton(stylesheet.current);
+ }
+
+ public Expr clone(Object context)
+ {
+ Stylesheet s = stylesheet;
+ if (context instanceof Stylesheet)
+ {
+ s = (Stylesheet) context;
+ }
+ return new CurrentFunction(s);
+ }
+
+ public boolean references(QName var)
+ {
+ return false;
+ }
+
+ public String toString()
+ {
+ return "current()";
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/DOMSourceLocator.java b/libjava/classpath/gnu/xml/transform/DOMSourceLocator.java
new file mode 100644
index 000000000..fbf31de55
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/DOMSourceLocator.java
@@ -0,0 +1,84 @@
+/* DOMSourceLocator.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import javax.xml.transform.dom.DOMLocator;
+import org.w3c.dom.Node;
+
+/**
+ * Simple DOMLocator implementation.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class DOMSourceLocator
+ implements DOMLocator
+{
+
+ final Node node;
+
+ DOMSourceLocator(Node node)
+ {
+ this.node = node;
+ }
+
+ public Node getOriginatingNode()
+ {
+ return node;
+ }
+
+ public String getPublicId()
+ {
+ return null;
+ }
+
+ public String getSystemId()
+ {
+ return null;
+ }
+
+ public int getLineNumber()
+ {
+ return -1;
+ }
+
+ public int getColumnNumber()
+ {
+ return -1;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/DocumentFunction.java b/libjava/classpath/gnu/xml/transform/DocumentFunction.java
new file mode 100644
index 000000000..d0aee25b3
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/DocumentFunction.java
@@ -0,0 +1,244 @@
+/* DocumentFunction.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Constant;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+import gnu.xml.xpath.IdFunction;
+
+/**
+ * The XSLT <code>document()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class DocumentFunction
+ extends Expr
+ implements Function, XPathFunction
+{
+
+ final Stylesheet stylesheet;
+ final Node base;
+ List args;
+ List values;
+
+ DocumentFunction(Stylesheet stylesheet, Node base)
+ {
+ this.stylesheet = stylesheet;
+ this.base = base;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ values = args;
+ return evaluate(null, 1, 1);
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ int arity = args.size();
+ if (values == null)
+ {
+ values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ }
+ Object ret;
+ switch (arity)
+ {
+ case 1:
+ Object arg = values.get(0);
+ if (arg instanceof Collection)
+ {
+ Collection ns = (Collection) arg;
+ Collection acc = new TreeSet();
+ for (Iterator i = ns.iterator(); i.hasNext(); )
+ {
+ Node node = (Node) i.next();
+ String s = Expr.stringValue(node);
+ acc.addAll(document(s, node.getBaseURI()));
+ }
+ ret = acc;
+ }
+ else
+ {
+ String s = Expr._string(context, arg);
+ ret = document(s, base.getBaseURI());
+ }
+ break;
+ case 2:
+ Object arg1 = values.get(0);
+ Object arg2 = values.get(1);
+ if (!(arg2 instanceof Collection))
+ throw new RuntimeException("second argument is not a node-set");
+ Collection arg2ns = (Collection) arg2;
+ String base2 = arg2ns.isEmpty() ? null :
+ ((Node) arg2ns.iterator().next()).getBaseURI();
+ if (arg1 instanceof Collection)
+ {
+ Collection arg1ns = (Collection) arg1;
+ Collection acc = new TreeSet();
+ for (Iterator i = arg1ns.iterator(); i.hasNext(); )
+ {
+ Node node = (Node) i.next();
+ String s = Expr.stringValue(node);
+ acc.addAll(document(s, base2));
+ }
+ ret = acc;
+ }
+ else
+ {
+ String s = Expr._string(context, arg1);
+ ret = document(s, base2);
+ }
+ break;
+ default:
+ throw new RuntimeException("invalid arity");
+ }
+ values = null;
+ return ret;
+ }
+
+ /**
+ * The XSL <code>document</code> function.
+ * @see XSLT 12.1
+ * @param uri the URI from which to retrieve nodes
+ * @param base the base URI for relative URIs
+ */
+ Collection document(String uri, String base)
+ {
+ if ("".equals(uri) || uri == null)
+ uri = this.base.getBaseURI();
+
+ // Get fragment
+ Expr fragment = null;
+ int hi = uri.indexOf('#');
+ if (hi != -1)
+ {
+ String f = uri.substring(hi + 1);
+ uri = uri.substring(0, hi);
+ // TODO handle xpointer() here
+ // this only handles IDs
+ fragment = new IdFunction(new Constant(f));
+ }
+
+ // Get document source
+ try
+ {
+ DOMSource source;
+ XSLURIResolver resolver = stylesheet.factory.resolver;
+ synchronized (resolver)
+ {
+ if (stylesheet.transformer != null)
+ {
+ resolver.setUserResolver(stylesheet.transformer.uriResolver);
+ resolver.setUserListener(stylesheet.transformer.errorListener);
+ }
+ source = resolver.resolveDOM(null, base, uri);
+ }
+ Node node = source.getNode();
+ // Strip whitespace
+ TransformerImpl.strip(stylesheet, node);
+ if (fragment == null)
+ return Collections.singleton(node);
+ else
+ {
+ Object ret = fragment.evaluate(node, 1, 1);
+ if (!(ret instanceof Collection))
+ {
+ // XXX Report error?
+ return Collections.EMPTY_SET;
+ }
+ return (Collection) ret;
+ }
+ }
+ catch (TransformerException e)
+ {
+ String msg = "can't open " + uri;
+ if (base != null)
+ msg += " with base " + base;
+ throw new RuntimeException(msg);
+ }
+ }
+
+ public Expr clone(Object context)
+ {
+ Stylesheet s = stylesheet;
+ if (context instanceof Stylesheet)
+ s = (Stylesheet) context;
+ DocumentFunction f = new DocumentFunction(s, base);
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ args2.add(((Expr) args.get(i)).clone(context));
+ f.setArguments(args2);
+ return f;
+ }
+
+ public boolean references(QName var)
+ {
+ for (Iterator i = args.iterator(); i.hasNext(); )
+ {
+ if (((Expr) i.next()).references(var))
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/ElementAvailableFunction.java b/libjava/classpath/gnu/xml/transform/ElementAvailableFunction.java
new file mode 100644
index 000000000..6a9d256be
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/ElementAvailableFunction.java
@@ -0,0 +1,181 @@
+/* ElementAvailableFunction.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+import javax.xml.namespace.QName;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>element-available</code> function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class ElementAvailableFunction
+ extends Expr
+ implements Function, XPathFunction
+{
+
+ static final Collection elements;
+ static
+ {
+ TreeSet acc = new TreeSet();
+ acc.add("stylesheet");
+ acc.add("template");
+ acc.add("param");
+ acc.add("variable");
+ acc.add("include");
+ acc.add("import");
+ acc.add("output");
+ acc.add("preserve-space");
+ acc.add("strip-space");
+ acc.add("key");
+ acc.add("decimal-format");
+ acc.add("namespace-alias");
+ acc.add("attribute-set");
+ acc.add("apply-templates");
+ acc.add("call-template");
+ acc.add("value-of");
+ acc.add("for-each");
+ acc.add("if");
+ acc.add("choose");
+ acc.add("when");
+ acc.add("otherwise");
+ acc.add("element");
+ acc.add("attribute");
+ acc.add("text");
+ acc.add("copy");
+ acc.add("processing-instruction");
+ acc.add("comment");
+ acc.add("number");
+ acc.add("copy-of");
+ acc.add("message");
+ acc.add("sort");
+ acc.add("with-param");
+ acc.add("fallback");
+ acc.add("apply-imports");
+ elements = Collections.unmodifiableSet(acc);
+ }
+
+ final NamespaceContext nsctx;
+ List args;
+
+ ElementAvailableFunction(NamespaceContext nsctx)
+ {
+ this.nsctx = nsctx;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Expr arg = (Expr) args.get(0);
+ Object val = arg.evaluate(context, pos, len);
+ String name = _string(context, val);
+ String prefix, localName, uri;
+ int ci = name.indexOf(':');
+ if (ci == -1)
+ {
+ prefix = null;
+ localName = name;
+ }
+ else
+ {
+ prefix = name.substring(0, ci);
+ localName = name.substring(ci + 1);
+ }
+ uri = nsctx.getNamespaceURI(prefix);
+ if (Stylesheet.XSL_NS.equals(uri))
+ {
+ return elements.contains(localName) ?
+ Boolean.TRUE : Boolean.FALSE;
+ }
+ // TODO extension elements
+ return Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ NamespaceContext n = nsctx;
+ if (context instanceof NamespaceContext)
+ n = (NamespaceContext) context;
+ ElementAvailableFunction f = new ElementAvailableFunction(n);
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ args2.add(((Expr) args.get(i)).clone(context));
+ f.setArguments(args2);
+ return f;
+ }
+
+ public boolean references(QName var)
+ {
+ for (Iterator i = args.iterator(); i.hasNext(); )
+ {
+ if (((Expr) i.next()).references(var))
+ return true;
+ }
+ return false;
+ }
+
+ public String toString()
+ {
+ return "element-available(" + args.get(0) + ")";
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/ElementNode.java b/libjava/classpath/gnu/xml/transform/ElementNode.java
new file mode 100644
index 000000000..78a2007b7
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/ElementNode.java
@@ -0,0 +1,261 @@
+/* ElementNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSL <code>element</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ElementNode
+ extends TemplateNode
+{
+
+ final TemplateNode name;
+ final TemplateNode namespace;
+ final String uas;
+ final Node source;
+ final Collection elementExcludeResultPrefixes;
+
+ ElementNode(TemplateNode name,
+ TemplateNode namespace, String uas, Node source)
+ {
+ this.name = name;
+ this.namespace = namespace;
+ this.uas = uas;
+ this.source = source;
+ NamedNodeMap attrs = source.getAttributes();
+ Node attr = attrs.getNamedItemNS(Stylesheet.XSL_NS,
+ "exclude-result-prefixes");
+ if (attr != null)
+ {
+ elementExcludeResultPrefixes = new HashSet();
+ StringTokenizer st = new StringTokenizer(attr.getNodeValue());
+ while (st.hasMoreTokens())
+ elementExcludeResultPrefixes.add(st.nextToken());
+ }
+ else
+ elementExcludeResultPrefixes = Collections.EMPTY_SET;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new ElementNode(name.clone(stylesheet),
+ (namespace == null) ? null :
+ namespace.clone(stylesheet),
+ uas, source);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ // Create a document fragment to hold the name
+ DocumentFragment fragment = doc.createDocumentFragment();
+ // Apply name to the fragment
+ name.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ String nameValue = Expr.stringValue(fragment);
+
+ String namespaceValue = null;
+ if (namespace != null)
+ {
+ // Create a document fragment to hold the namespace
+ fragment = doc.createDocumentFragment();
+ // Apply namespace to the fragment
+ namespace.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ namespaceValue = Expr.stringValue(fragment);
+ if (namespaceValue.length() == 0)
+ namespaceValue = null;
+ }
+ else
+ {
+ String prefix = getPrefix(nameValue);
+ if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix))
+ {
+ int ci = nameValue.indexOf(':');
+ nameValue = nameValue.substring(ci + 1);
+ }
+ else
+ {
+ // Namespace aliasing
+ if (prefix == null)
+ prefix = "#default";
+ String resultPrefix =
+ (String) stylesheet.namespaceAliases.get(prefix);
+ if (resultPrefix != null)
+ {
+ if ("#default".equals(resultPrefix))
+ resultPrefix = null;
+ namespaceValue = source.lookupNamespaceURI(resultPrefix);
+ }
+ if (prefix == "#default")
+ prefix = null;
+ // Look up ordinary namespace for this prefix
+ if (namespaceValue == null)
+ {
+ if (XMLConstants.XML_NS_PREFIX.equals(prefix))
+ namespaceValue = XMLConstants.XML_NS_URI;
+ else
+ {
+ // Resolve namespace for this prefix
+ namespaceValue = source.lookupNamespaceURI(prefix);
+ }
+ }
+ }
+ }
+
+ // Create element
+ Element element = (namespaceValue != null) ?
+ doc.createElementNS(namespaceValue, nameValue) :
+ doc.createElement(nameValue);
+ if (nextSibling != null)
+ parent.insertBefore(element, nextSibling);
+ else
+ parent.appendChild(element);
+ stylesheet.addNamespaceNodes(source, element, doc,
+ elementExcludeResultPrefixes);
+ if (uas != null)
+ {
+ StringTokenizer st = new StringTokenizer(uas, " ");
+ while (st.hasMoreTokens())
+ addAttributeSet(stylesheet, mode, context, pos, len,
+ element, null, st.nextToken());
+ }
+ if (children != null)
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ element, null);
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ final String getPrefix(String name)
+ {
+ int ci = name.indexOf(':');
+ return (ci == -1) ? null : name.substring(0, ci);
+ }
+
+ void addAttributeSet(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling, String attributeSet)
+ throws TransformerException
+ {
+ stylesheet.bindings.global = true;
+ for (Iterator i = stylesheet.attributeSets.iterator(); i.hasNext(); )
+ {
+ AttributeSet as = (AttributeSet) i.next();
+ if (!as.name.equals(attributeSet))
+ continue;
+ if (as.uas != null)
+ {
+ StringTokenizer st = new StringTokenizer(as.uas, " ");
+ while (st.hasMoreTokens())
+ addAttributeSet(stylesheet, mode, context, pos, len,
+ parent, nextSibling, st.nextToken());
+ }
+ if (as.children != null)
+ as.children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ stylesheet.bindings.global = false;
+ }
+
+ public boolean references(QName var)
+ {
+ if (name != null && name.references(var))
+ return true;
+ if (namespace != null && namespace.references(var))
+ return true;
+ return super.references(var);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("element");
+ buf.append('[');
+ buf.append("name=");
+ if (namespace != null)
+ {
+ buf.append(",namespace=");
+ buf.append(namespace);
+ }
+ buf.append(name);
+ if (uas != null)
+ {
+ buf.append(",uas=");
+ buf.append(uas);
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/ErrorListenerErrorHandler.java b/libjava/classpath/gnu/xml/transform/ErrorListenerErrorHandler.java
new file mode 100644
index 000000000..8a28795c4
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/ErrorListenerErrorHandler.java
@@ -0,0 +1,101 @@
+/* ErrorListenerErrorHandler.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * An ErrorHandler that wraps an ErrorListener.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class ErrorListenerErrorHandler
+ implements ErrorHandler
+{
+
+ final ErrorListener listener;
+
+ ErrorListenerErrorHandler(ErrorListener listener)
+ {
+ this.listener = listener;
+ }
+
+ public void warning(SAXParseException e)
+ throws SAXException
+ {
+ try
+ {
+ listener.warning(new TransformerException(e));
+ }
+ catch (TransformerException e2)
+ {
+ throw new SAXException(e2);
+ }
+ }
+
+ public void error(SAXParseException e)
+ throws SAXException
+ {
+ try
+ {
+ listener.error(new TransformerException(e));
+ }
+ catch (TransformerException e2)
+ {
+ throw new SAXException(e2);
+ }
+ }
+
+ public void fatalError(SAXParseException e)
+ throws SAXException
+ {
+ try
+ {
+ listener.fatalError(new TransformerException(e));
+ }
+ catch (TransformerException e2)
+ {
+ throw new SAXException(e2);
+ }
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/ForEachNode.java b/libjava/classpath/gnu/xml/transform/ForEachNode.java
new file mode 100644
index 000000000..1613f768b
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/ForEachNode.java
@@ -0,0 +1,159 @@
+/* ForEachNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSLT <code>for-each</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ForEachNode
+ extends TemplateNode
+{
+
+ final Expr select;
+ final List<SortKey> sortKeys;
+
+ ForEachNode(Expr select, List<SortKey> sortKeys)
+ {
+ this.select = select;
+ this.sortKeys = sortKeys;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ int len = sortKeys.size();
+ List<SortKey> sortKeys2 = new ArrayList<SortKey>(len);
+ for (int i = 0; i < len; i++)
+ sortKeys2.add(sortKeys.get(i).clone(stylesheet));
+ TemplateNode ret = new ForEachNode(select.clone(stylesheet),
+ sortKeys2);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ @Override
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (children != null)
+ {
+ // Set current template to null
+ Template saved = stylesheet.currentTemplate;
+ stylesheet.currentTemplate = null;
+ Object ret = select.evaluate(context, pos, len);
+ //System.err.println(toString() + ": " + context+" -> "+ret);
+ if (ret instanceof Collection)
+ {
+ /* Suppression is safe, as we know context produces Collection<Node> */
+ @SuppressWarnings("unchecked")
+ Collection<Node> ns = (Collection<Node>) ret;
+ List<Node> list = new ArrayList<Node>(ns);
+ if (!sortKeys.isEmpty())
+ {
+ for (SortKey sortKey : sortKeys)
+ {
+ sortKey.init(stylesheet, mode, context, pos, len, parent,
+ nextSibling);
+ }
+ Collections.sort(list, new XSLComparator(sortKeys));
+ }
+ else
+ Collections.sort(list, documentOrderComparator);
+ // Perform children for each node
+ int l = list.size();
+ int p = 1;
+ for (Node node : list)
+ {
+ stylesheet.current = node;
+ children.apply(stylesheet, mode,
+ node, p++, l,
+ parent, nextSibling);
+ }
+ }
+ // Restore current template
+ stylesheet.currentTemplate = saved;
+ }
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ @Override
+ public boolean references(QName var)
+ {
+ if (select != null && select.references(var))
+ return true;
+ for (Iterator<SortKey> i = sortKeys.iterator(); i.hasNext(); )
+ {
+ if (i.next().references(var))
+ return true;
+ }
+ return super.references(var);
+ }
+
+ @Override
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("for-each");
+ buf.append('[');
+ buf.append("select=");
+ buf.append(select);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/FormatNumberFunction.java b/libjava/classpath/gnu/xml/transform/FormatNumberFunction.java
new file mode 100644
index 000000000..82872be77
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/FormatNumberFunction.java
@@ -0,0 +1,145 @@
+/* FormatNumberFunction.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>format-number()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class FormatNumberFunction
+ extends Expr
+ implements XPathFunction, Function
+{
+
+ final Stylesheet stylesheet;
+ List args;
+
+ FormatNumberFunction(Stylesheet stylesheet)
+ {
+ this.stylesheet = stylesheet;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ int arity = args.size();
+ List values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ double number = _number(context, values.get(0));
+ String pattern = _string(context, values.get(1));
+ // Currency symbol &#x00a4; is not supposed to be present
+ if (pattern.indexOf('\u00a4') != -1)
+ {
+ // Replace with $ (Xalan does this)
+ pattern = pattern.replace('\u00a4', '$');
+ }
+ String dfName = null;
+ if (arity > 2)
+ {
+ dfName = _string(context, values.get(2));
+ // otherwise the default decimal-format will be used
+ }
+ DecimalFormat df = (DecimalFormat) stylesheet.decimalFormats.get(dfName);
+ if (df == null)
+ {
+ throw new IllegalArgumentException("No such decimal-format: " +
+ dfName);
+ }
+ df.applyLocalizedPattern(pattern);
+ return df.format(number);
+ }
+
+ public Expr clone(Object context)
+ {
+ Stylesheet s = stylesheet;
+ if (context instanceof Stylesheet)
+ {
+ s = (Stylesheet) context;
+ }
+ FormatNumberFunction f = new FormatNumberFunction(s);
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+ public boolean references(QName var)
+ {
+ for (Iterator i = args.iterator(); i.hasNext(); )
+ {
+ if (((Expr) i.next()).references(var))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/FunctionAvailableFunction.java b/libjava/classpath/gnu/xml/transform/FunctionAvailableFunction.java
new file mode 100644
index 000000000..1ac50327e
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/FunctionAvailableFunction.java
@@ -0,0 +1,192 @@
+/* FunctionAvailableFunction.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>function-available</code> function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class FunctionAvailableFunction
+ extends Expr
+ implements Function, XPathFunction
+{
+
+ static final Collection xsltFunctions;
+ static final Collection xpathFunctions;
+ static
+ {
+ TreeSet acc = new TreeSet();
+ acc.add("document");
+ acc.add("key");
+ acc.add("format-number");
+ acc.add("current");
+ acc.add("unparsed-entity-uri");
+ acc.add("generate-id");
+ acc.add("system-property");
+ acc.add("element-available");
+ acc.add("function-available");
+ xsltFunctions = Collections.unmodifiableSet(acc);
+ acc = new TreeSet();
+ acc.add("boolean");
+ acc.add("ceiling");
+ acc.add("concat");
+ acc.add("contains");
+ acc.add("count");
+ acc.add("false");
+ acc.add("floor");
+ acc.add("id");
+ acc.add("lang");
+ acc.add("last");
+ acc.add("local-name");
+ acc.add("name");
+ acc.add("namespace-uri");
+ acc.add("normalize-space");
+ acc.add("not");
+ acc.add("number");
+ acc.add("position");
+ acc.add("round");
+ acc.add("starts-with");
+ acc.add("string");
+ acc.add("string-length");
+ acc.add("substring-after");
+ acc.add("substring-before");
+ acc.add("substring");
+ acc.add("sum");
+ acc.add("translate");
+ acc.add("true");
+ xpathFunctions = Collections.unmodifiableSet(acc);
+ }
+
+ final NamespaceContext nsctx;
+ List args;
+
+ FunctionAvailableFunction(NamespaceContext nsctx)
+ {
+ this.nsctx = nsctx;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Expr arg = (Expr) args.get(0);
+ Object val = arg.evaluate(context, pos, len);
+ String name = _string(context, val);
+ String prefix, localName, uri;
+ int ci = name.indexOf(':');
+ if (ci == -1)
+ {
+ prefix = null;
+ localName = name;
+ }
+ else
+ {
+ prefix = name.substring(0, ci);
+ localName = name.substring(ci + 1);
+ }
+ uri = nsctx.getNamespaceURI(prefix);
+ if (uri == null)
+ {
+ return (xpathFunctions.contains(localName) ||
+ xsltFunctions.contains(localName)) ?
+ Boolean.TRUE : Boolean.FALSE;
+ }
+ else if (Stylesheet.XSL_NS.equals(uri))
+ {
+ return xsltFunctions.contains(localName) ?
+ Boolean.TRUE : Boolean.FALSE;
+ }
+ // TODO extension functions
+ return Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ NamespaceContext n = nsctx;
+ if (context instanceof NamespaceContext)
+ n = (NamespaceContext) context;
+ FunctionAvailableFunction f = new FunctionAvailableFunction(n);
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ args2.add(((Expr) args.get(i)).clone(context));
+ f.setArguments(args2);
+ return f;
+ }
+
+ public boolean references(QName var)
+ {
+ for (Iterator i = args.iterator(); i.hasNext(); )
+ {
+ if (((Expr) i.next()).references(var))
+ return true;
+ }
+ return false;
+ }
+
+ public String toString()
+ {
+ return "function-available(" + args.get(0) + ")";
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/GenerateIdFunction.java b/libjava/classpath/gnu/xml/transform/GenerateIdFunction.java
new file mode 100644
index 000000000..efc583cc8
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/GenerateIdFunction.java
@@ -0,0 +1,139 @@
+/* GenerateIdFunction.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>generate-id()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class GenerateIdFunction
+ extends Expr
+ implements XPathFunction, Function
+{
+
+ List args;
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ int arity = args.size();
+ List values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ Node node;
+ Collection ns = (arity == 0) ? Collections.EMPTY_SET :
+ (Collection) values.get(0);
+ if (ns.isEmpty())
+ {
+ node = context;
+ }
+ else
+ {
+ List list = new ArrayList(ns);
+ Collections.sort(list, documentOrderComparator);
+ node = (Node) list.get(0);
+ }
+
+ String name = node.getNodeName();
+ int index = 0, depth = 0;
+ for (Node ctx = node.getPreviousSibling(); ctx != null;
+ ctx = ctx.getPreviousSibling())
+ {
+ index++;
+ }
+ for (Node ctx = node.getParentNode(); ctx != null;
+ ctx = ctx.getParentNode())
+ {
+ depth++;
+ }
+ return name + "-" + index + "-" + depth;
+ }
+
+ public Expr clone(Object context)
+ {
+ GenerateIdFunction f = new GenerateIdFunction();
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+ public boolean references(QName var)
+ {
+ for (Iterator i = args.iterator(); i.hasNext(); )
+ {
+ if (((Expr) i.next()).references(var))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/IfNode.java b/libjava/classpath/gnu/xml/transform/IfNode.java
new file mode 100644
index 000000000..f31fed555
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/IfNode.java
@@ -0,0 +1,112 @@
+/* IfNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSL <code>if</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class IfNode
+ extends TemplateNode
+{
+
+ final Expr test;
+
+ IfNode(Expr test)
+ {
+ this.test = test;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new IfNode(test.clone(stylesheet));
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Object ret = test.evaluate(context, pos, len);
+ boolean success = (ret instanceof Boolean) ?
+ ((Boolean) ret).booleanValue() :
+ Expr._boolean(context, ret);
+ if (success)
+ {
+ if (children != null)
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public boolean references(QName var)
+ {
+ if (test != null && test.references(var))
+ return true;
+ return super.references(var);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("if");
+ buf.append('[');
+ buf.append("test=");
+ buf.append(test);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/Key.java b/libjava/classpath/gnu/xml/transform/Key.java
new file mode 100644
index 000000000..18d83394f
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/Key.java
@@ -0,0 +1,70 @@
+/* Key.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import javax.xml.namespace.QName;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Pattern;
+
+/**
+ * An XSL key.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class Key
+{
+
+ final QName name;
+ final Pattern match;
+ final Expr use;
+
+ Key(QName name, Pattern match, Expr use)
+ {
+ this.name = name;
+ this.match = match;
+ this.use = use;
+ }
+
+ Key clone(Stylesheet stylesheet)
+ {
+ return new Key(name,
+ (Pattern) match.clone(stylesheet),
+ use.clone(stylesheet));
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/KeyFunction.java b/libjava/classpath/gnu/xml/transform/KeyFunction.java
new file mode 100644
index 000000000..b962626c9
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/KeyFunction.java
@@ -0,0 +1,227 @@
+/* KeyFunction.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+import gnu.xml.xpath.Pattern;
+
+/**
+ * The XSLT <code>key()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class KeyFunction
+ extends Pattern
+ implements XPathFunction, Function
+{
+
+ final Stylesheet stylesheet;
+ List args;
+
+ KeyFunction(Stylesheet stylesheet)
+ {
+ this.stylesheet = stylesheet;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public boolean matches(Node context)
+ {
+ Object ret = evaluate(context, 1, 1);
+ return !((Collection) ret).isEmpty();
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ // Evaluate arguments
+ int arity = args.size();
+ List values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ // Get key name
+ QName keyName = QName.valueOf(_string(context, values.get(0)));
+ // Expand qualified name
+ String uri = keyName.getNamespaceURI();
+ String prefix = keyName.getPrefix();
+ if ((uri == null || uri.length() == 0) &&
+ (prefix != null && prefix.length() > 0))
+ {
+ uri = stylesheet.getNamespaceURI(prefix);
+ if (uri != null && uri.length() > 0)
+ {
+ String localName = keyName.getLocalPart();
+ keyName = new QName(uri, localName, prefix);
+ }
+ }
+ // Compute matching key set
+ Collection keySet = new LinkedList();
+ for (Iterator i = stylesheet.keys.iterator(); i.hasNext(); )
+ {
+ Key key = (Key) i.next();
+ if (key.name.equals(keyName))
+ {
+ keySet.add(key);
+ }
+ }
+ // Get target
+ Object target = values.get(1);
+ Collection acc = new LinkedHashSet();
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ if (target instanceof Collection)
+ {
+ for (Iterator i = ((Collection) target).iterator(); i.hasNext(); )
+ {
+ String val = Expr.stringValue((Node) i.next());
+ addKeyNodes(doc, keySet, val, acc);
+ }
+ }
+ else
+ {
+ String val = Expr._string(context, target);
+ addKeyNodes(doc, keySet, val, acc);
+ }
+ List ret = new ArrayList(acc);
+ Collections.sort(ret, documentOrderComparator);
+ return ret;
+ }
+
+ final void addKeyNodes(Node node, Collection keySet,
+ String value, Collection acc)
+ {
+ addKeyNodeIfMatch(node, keySet, value, acc);
+ // Apply children
+ for (Node ctx = node.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ addKeyNodes(ctx, keySet, value, acc);
+ }
+ }
+
+ final void addKeyNodeIfMatch(Node node, Collection keySet,
+ String value, Collection acc)
+ {
+ for (Iterator i = keySet.iterator(); i.hasNext(); )
+ {
+ Key key = (Key) i.next();
+ if (key.match.matches(node))
+ {
+ Object eval = key.use.evaluate(node, 1, 1);
+ if (eval instanceof Collection)
+ {
+ for (Iterator j = ((Collection) eval).iterator();
+ j.hasNext(); )
+ {
+ String keyValue = Expr.stringValue((Node) j.next());
+ if (value.equals(keyValue))
+ {
+ acc.add(node);
+ return;
+ }
+ }
+ }
+ else
+ {
+ String keyValue = Expr._string(node, eval);
+ if (value.equals(keyValue))
+ {
+ acc.add(node);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ public Expr clone(Object context)
+ {
+ Stylesheet s = stylesheet;
+ if (context instanceof Stylesheet)
+ {
+ s = (Stylesheet) context;
+ }
+ KeyFunction f = new KeyFunction(s);
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+ public boolean references(QName var)
+ {
+ for (Iterator i = args.iterator(); i.hasNext(); )
+ {
+ if (((Expr) i.next()).references(var))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/LiteralNode.java b/libjava/classpath/gnu/xml/transform/LiteralNode.java
new file mode 100644
index 000000000..3405b3364
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/LiteralNode.java
@@ -0,0 +1,202 @@
+/* LiteralNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.StringTokenizer;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * A template node that copies a DOM node in the template to the result
+ * tree.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class LiteralNode
+ extends TemplateNode
+{
+
+ /**
+ * The source node in the XSL template.
+ */
+ final Node source;
+
+ final Collection elementExcludeResultPrefixes;
+
+ LiteralNode(Node source)
+ {
+ this.source = source;
+ if (source.getNodeType() == Node.ELEMENT_NODE)
+ {
+ NamedNodeMap attrs = source.getAttributes();
+ Node attr = attrs.getNamedItemNS(Stylesheet.XSL_NS,
+ "exclude-result-prefixes");
+ if (attr != null)
+ {
+ elementExcludeResultPrefixes = new HashSet();
+ StringTokenizer st = new StringTokenizer(attr.getNodeValue());
+ while (st.hasMoreTokens())
+ elementExcludeResultPrefixes.add(st.nextToken());
+ }
+ else
+ elementExcludeResultPrefixes = Collections.EMPTY_SET;
+ }
+ else
+ elementExcludeResultPrefixes = null;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new LiteralNode(source);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Node result = null;
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ short nodeType = source.getNodeType();
+ if (nodeType == Node.ATTRIBUTE_NODE &&
+ parent.getFirstChild() != null)
+ {
+ // Ignore attributes added after child elements
+ }
+ else
+ {
+ // Namespace aliasing
+ if (nodeType == Node.ELEMENT_NODE)
+ {
+ String prefix = source.getPrefix();
+ if (prefix == null)
+ prefix = "#default";
+ String resultPrefix =
+ (String) stylesheet.namespaceAliases.get(prefix);
+ if (resultPrefix != null)
+ {
+ if ("#default".equals(resultPrefix))
+ resultPrefix = null;
+ String uri = source.lookupNamespaceURI(resultPrefix);
+ String name = source.getNodeName();
+ // Create a new element node in the result document
+ result = doc.createElementNS(uri, name);
+ // copy attributes
+ NamedNodeMap srcAttrs = source.getAttributes();
+ NamedNodeMap dstAttrs = result.getAttributes();
+ int l = srcAttrs.getLength();
+ for (int i = 0; i < l; i++)
+ {
+ Node attr = srcAttrs.item(i);
+ if (!Stylesheet.XSL_NS.equals(attr.getNamespaceURI()))
+ {
+ attr = attr.cloneNode(true);
+ attr = doc.adoptNode(attr);
+ dstAttrs.setNamedItemNS(attr);
+ }
+ }
+ }
+ }
+ if (result == null)
+ {
+ // Create result node
+ result = source.cloneNode(false);
+ // Remove any XSL attributes
+ NamedNodeMap attrs = result.getAttributes();
+ if (attrs != null)
+ {
+ int l = attrs.getLength();
+ for (int i = 0; i < l; i++)
+ {
+ Node attr = attrs.item(i);
+ if (Stylesheet.XSL_NS.equals(attr.getNamespaceURI()))
+ {
+ attrs.removeNamedItem(attr.getNodeName());
+ i--;
+ l--;
+ }
+ }
+ }
+ Node result2 = doc.adoptNode(result);
+ if (result2 == null)
+ {
+ String msg = "Error adopting node to result tree: " +
+ result + " (" + result.getClass().getName() + ")";
+ DOMSourceLocator l = new DOMSourceLocator(context);
+ throw new TransformerException(msg, l);
+ }
+ result = result2;
+ }
+ if (nextSibling != null)
+ parent.insertBefore(result, nextSibling);
+ else
+ parent.appendChild(result);
+ if (nodeType == Node.ELEMENT_NODE)
+ stylesheet.addNamespaceNodes(source, result, doc,
+ elementExcludeResultPrefixes);
+ // children
+ if (children != null)
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ result, null);
+ }
+ // next sibling
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public String toString()
+ {
+ return source.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/MessageNode.java b/libjava/classpath/gnu/xml/transform/MessageNode.java
new file mode 100644
index 000000000..3e4899aeb
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/MessageNode.java
@@ -0,0 +1,110 @@
+/* MessageNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.logging.Logger;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * An XSL <code>message</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class MessageNode
+ extends TemplateNode
+{
+
+ static final Logger logger = Logger.getLogger("gnu.xml.transform");
+
+ final boolean terminate;
+
+ MessageNode(boolean terminate)
+ {
+ this.terminate = terminate;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new MessageNode(terminate);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (children != null)
+ {
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ DocumentFragment fragment = doc.createDocumentFragment();
+ children.apply(stylesheet, mode, context, pos, len, fragment, null);
+ String message = Expr.stringValue(fragment);
+ logger.info(message);
+ if (terminate)
+ stylesheet.terminated = true;
+ }
+ if (next != null && !terminate)
+ next.apply(stylesheet, mode, context, pos, len, parent, nextSibling);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("message");
+ if (terminate)
+ {
+ buf.append('[');
+ buf.append("terminate");
+ buf.append(']');
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/NamespaceProxy.java b/libjava/classpath/gnu/xml/transform/NamespaceProxy.java
new file mode 100644
index 000000000..695fbeb52
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/NamespaceProxy.java
@@ -0,0 +1,77 @@
+/* NamespaceProxy.java --
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.Collections;
+import java.util.Iterator;
+import javax.xml.namespace.NamespaceContext;
+import org.w3c.dom.Node;
+
+/**
+ * A namespace context using a DOM node to resolve the namespace.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class NamespaceProxy
+ implements NamespaceContext
+{
+
+ private final Node node;
+
+ NamespaceProxy(Node node)
+ {
+ this.node = node;
+ }
+
+ public String getNamespaceURI(String prefix)
+ {
+ return (node == null) ? null : node.lookupNamespaceURI(prefix);
+ }
+
+ public String getPrefix(String namespaceURI)
+ {
+ return (node == null) ? null : node.lookupPrefix(namespaceURI);
+ }
+
+ public Iterator getPrefixes(String namespaceURI)
+ {
+ // TODO
+ return Collections.singleton(getPrefix(namespaceURI)).iterator();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/NodeNumberNode.java b/libjava/classpath/gnu/xml/transform/NodeNumberNode.java
new file mode 100644
index 000000000..6663db371
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/NodeNumberNode.java
@@ -0,0 +1,269 @@
+/* NodeNumberNode.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Pattern;
+import gnu.xml.xpath.Selector;
+import gnu.xml.xpath.UnionExpr;
+
+/**
+ * A template node representing the XSL <code>number</code> instruction
+ * with no <code>value</code> expression, i.e. the value is computed from
+ * the document position of the context node.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class NodeNumberNode
+ extends AbstractNumberNode
+{
+
+ static final int SINGLE = 0;
+ static final int MULTIPLE = 1;
+ static final int ANY = 2;
+
+ final int level;
+ final Pattern count;
+ final Pattern from;
+
+ NodeNumberNode(int level, Pattern count, Pattern from,
+ TemplateNode format, String lang,
+ int letterValue, String groupingSeparator, int groupingSize)
+ {
+ super(format, lang, letterValue, groupingSeparator, groupingSize);
+ this.level = level;
+ this.count = count;
+ this.from = from;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new NodeNumberNode(level,
+ (count == null) ? null :
+ (Pattern) count.clone(stylesheet),
+ (from == null) ? from :
+ (Pattern) from.clone(stylesheet),
+ format, lang, letterValue,
+ groupingSeparator, groupingSize);
+ if (children != null)
+ {
+ ret.children = children.clone(stylesheet);
+ }
+ if (next != null)
+ {
+ ret.next = next.clone(stylesheet);
+ }
+ return ret;
+ }
+
+ int[] compute(Stylesheet stylesheet, Node context, int pos, int len)
+ throws TransformerException
+ {
+ /*if (from != null)
+ {
+ Object ret = from.evaluate(context, pos, len);
+ if (ret instanceof Collection)
+ {
+ Collection ns = (Collection) ret;
+ if (ns.size() > 0)
+ {
+ List list = new ArrayList(ns);
+ Collections.sort(list, documentOrderComparator);
+ context = (Node) list.get(0);
+ }
+ else
+ {
+ return new int[0];
+ }
+ }
+ else
+ {
+ return new int[0];
+ }
+ }*/
+ Node current = context;
+ switch (level)
+ {
+ case SINGLE:
+ if (from == null)
+ {
+ while (context != null && !countMatches(current, context))
+ {
+ context = context.getParentNode();
+ }
+ }
+ else
+ {
+ while (context != null && !countMatches(current, context) &&
+ !fromMatches(context))
+ {
+ context = context.getParentNode();
+ }
+ }
+ return (context == null) ? new int[0] :
+ new int[] { (context == current) ? pos : getIndex(current, context) };
+ case MULTIPLE:
+ List ancestors = new ArrayList();
+ while (context != null)
+ {
+ if (countMatches(current, context))
+ {
+ if (from == null || fromMatches(context))
+ {
+ ancestors.add(context);
+ }
+ }
+ context = context.getParentNode();
+ }
+ Collections.sort(ancestors, documentOrderComparator);
+ int[] ret = new int[ancestors.size()];
+ for (int i = 0; i < ret.length; i++)
+ {
+ ret[i] = getIndex(current, (Node) ancestors.get(i));
+ }
+ return ret;
+ case ANY:
+ Expr preceding = new Selector(Selector.PRECEDING,
+ Collections.EMPTY_LIST);
+ Expr ancestorOrSelf = new Selector(Selector.ANCESTOR_OR_SELF,
+ Collections.EMPTY_LIST);
+ Expr any = new UnionExpr(preceding, ancestorOrSelf);
+ Object eval = any.evaluate(context, pos, len);
+ if (eval instanceof Collection)
+ {
+ Collection ns = (Collection) eval;
+ List candidates = new ArrayList();
+ for (Iterator i = ns.iterator(); i.hasNext(); )
+ {
+ Node candidate = (Node) i.next();
+ if (countMatches(current, candidate))
+ {
+ candidates.add(candidate);
+ if (from != null && from.matches(candidate))
+ {
+ break;
+ }
+ }
+ }
+ return new int[] { candidates.size() };
+ }
+ return new int[0];
+ default:
+ throw new TransformerException("invalid level");
+ }
+ }
+
+ boolean countMatches(Node current, Node node)
+ {
+ if (count == null)
+ {
+ int cnt = current.getNodeType();
+ int nnt = node.getNodeType();
+ if (cnt != nnt)
+ {
+ return false;
+ }
+ if (nnt == Node.ELEMENT_NODE || nnt == Node.ATTRIBUTE_NODE)
+ {
+ String curi = current.getNamespaceURI();
+ String nuri = node.getNamespaceURI();
+ if ((curi == null && nuri != null) ||
+ (curi != null && !curi.equals(nuri)))
+ {
+ return false;
+ }
+ String cn = current.getLocalName();
+ if (cn == null)
+ {
+ cn = current.getNodeName();
+ }
+ String nn = node.getLocalName();
+ if (nn == null)
+ {
+ nn = node.getNodeName();
+ }
+ if (!cn.equals(nn))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return count.matches(node);
+ }
+ }
+
+ boolean fromMatches(Node node)
+ {
+ for (Node ctx = node.getParentNode(); ctx != null;
+ ctx = ctx.getParentNode())
+ {
+ if (from.matches(ctx))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int getIndex(Node current, Node node)
+ {
+ int index = 0;
+ do
+ {
+ do
+ {
+ node = node.getPreviousSibling();
+ }
+ while (node != null && !countMatches(current, node));
+ index++;
+ }
+ while (node != null);
+ return index;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/NumberNode.java b/libjava/classpath/gnu/xml/transform/NumberNode.java
new file mode 100644
index 000000000..cec594b99
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/NumberNode.java
@@ -0,0 +1,88 @@
+/* NumberNode.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>number</code> instruction
+ * with a <code>value</code> expression.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class NumberNode
+ extends AbstractNumberNode
+{
+
+ final Expr value;
+
+ NumberNode(Expr value, TemplateNode format, String lang,
+ int letterValue, String groupingSeparator, int groupingSize)
+ {
+ super(format, lang, letterValue, groupingSeparator, groupingSize);
+ this.value = value;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new NumberNode(value.clone(stylesheet),
+ format, lang, letterValue,
+ groupingSeparator, groupingSize);
+ if (children != null)
+ {
+ ret.children = children.clone(stylesheet);
+ }
+ if (next != null)
+ {
+ ret.next = next.clone(stylesheet);
+ }
+ return ret;
+ }
+
+ int[] compute(Stylesheet stylesheet, Node context, int pos, int len)
+ throws TransformerException
+ {
+ Object ret = value.evaluate(context, pos, len);
+ Double d = (ret instanceof Double) ? ((Double) ret) :
+ new Double(Expr._number(context, ret));
+ return new int[] { d.intValue() };
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/OtherwiseNode.java b/libjava/classpath/gnu/xml/transform/OtherwiseNode.java
new file mode 100644
index 000000000..09057840d
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/OtherwiseNode.java
@@ -0,0 +1,83 @@
+/* OtherwiseNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+
+/**
+ * A template node representing an XSL <code>otherwise</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class OtherwiseNode
+ extends TemplateNode
+{
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new OtherwiseNode();
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (children != null)
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public String toString()
+ {
+ return "otherwise";
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/ParameterNode.java b/libjava/classpath/gnu/xml/transform/ParameterNode.java
new file mode 100644
index 000000000..4f3dc7569
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/ParameterNode.java
@@ -0,0 +1,172 @@
+/* ParameterNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.Collections;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node that sets a variable or parameter during template
+ * processing.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ParameterNode
+ extends TemplateNode
+ implements Comparable<ParameterNode>
+{
+
+ final QName name;
+ final Expr select;
+ final int type;
+
+ ParameterNode(QName name, Expr select, int type)
+ {
+ this.name = name;
+ this.select = select;
+ this.type = type;
+ }
+
+ @Override
+ ParameterNode clone(Stylesheet stylesheet)
+ {
+ ParameterNode ret = new ParameterNode(name,
+ select == null ? null : select.clone(stylesheet),
+ type);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ // push the variable context
+ stylesheet.bindings.push(type);
+ // set the variable
+ Object value = getValue(stylesheet, mode, context, pos, len);
+ if (value != null)
+ {
+ stylesheet.bindings.set(name, value, type);
+ if (stylesheet.debug)
+ System.err.println(this + ": set to " + value);
+ }
+ // variable and param don't process children as such
+ // all subsequent instructions are processed with that variable context
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ // pop the variable context
+ stylesheet.bindings.pop(type);
+ }
+
+ Object getValue(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len)
+ throws TransformerException
+ {
+ if (select != null)
+ return select.evaluate(context, pos, len);
+ else if (children != null)
+ {
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ DocumentFragment fragment = doc.createDocumentFragment();
+ children.apply(stylesheet, mode, context, pos, len, fragment, null);
+ return Collections.singleton(fragment);
+ }
+ else
+ return null;
+ }
+
+ public boolean references(QName var)
+ {
+ if (select != null && select.references(var))
+ return true;
+ return super.references(var);
+ }
+
+ public int compareTo(ParameterNode pn)
+ {
+ boolean r1 = references(pn.name);
+ boolean r2 = pn.references(name);
+ if (r1 && r2)
+ throw new IllegalArgumentException("circular definitions");
+ if (r1)
+ return 1;
+ if (r2)
+ return -1;
+ return 0;
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder();
+ switch (type)
+ {
+ case Bindings.VARIABLE:
+ buf.append("variable");
+ break;
+ case Bindings.PARAM:
+ buf.append("param");
+ break;
+ case Bindings.WITH_PARAM:
+ buf.append("with-param");
+ break;
+ }
+ buf.append('[');
+ buf.append("name=");
+ buf.append(name);
+ buf.append(",select=");
+ buf.append(select);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/ProcessingInstructionNode.java b/libjava/classpath/gnu/xml/transform/ProcessingInstructionNode.java
new file mode 100644
index 000000000..1e723a8d4
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/ProcessingInstructionNode.java
@@ -0,0 +1,118 @@
+/* ProcessingInstructionNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>processing-instruction</code>
+ * instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ProcessingInstructionNode
+ extends TemplateNode
+{
+
+ final String name;
+
+ ProcessingInstructionNode(String name)
+ {
+ this.name = name;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new ProcessingInstructionNode(name);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ String data = null;
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ if (children != null)
+ {
+ // Create a document fragment to hold the text
+ DocumentFragment fragment = doc.createDocumentFragment();
+ // Apply children to the fragment
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ data = Expr.stringValue(fragment);
+ }
+ ProcessingInstruction pi = doc.createProcessingInstruction(name, data);
+ // Insert into result tree
+ if (nextSibling != null)
+ parent.insertBefore(pi, nextSibling);
+ else
+ parent.appendChild(pi);
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("processing-instruction");
+ buf.append('[');
+ buf.append("name=");
+ buf.append(name);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/SAXSerializer.java b/libjava/classpath/gnu/xml/transform/SAXSerializer.java
new file mode 100644
index 000000000..2bd1f97ab
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/SAXSerializer.java
@@ -0,0 +1,305 @@
+/* SAXSerializer.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import org.w3c.dom.Attr;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * Serializes a DOM node to a sequence of SAX events.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class SAXSerializer
+ implements Attributes
+{
+
+ transient NamedNodeMap attrs;
+ transient LinkedList namespaces = new LinkedList();
+
+ boolean isDefined(String prefix, String uri)
+ {
+ for (Iterator i = namespaces.iterator(); i.hasNext(); )
+ {
+ HashMap ctx = (HashMap) i.next();
+ if (uri.equals(ctx.get(prefix)))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void define(String prefix, String uri)
+ {
+ for (Iterator i = namespaces.iterator(); i.hasNext(); )
+ {
+ HashMap ctx = (HashMap) i.next();
+ if (ctx.containsKey(prefix))
+ {
+ HashMap newCtx = new HashMap();
+ newCtx.put(prefix, uri);
+ namespaces.addFirst(newCtx);
+ return;
+ }
+ }
+ HashMap ctx;
+ if (namespaces.isEmpty())
+ {
+ ctx = new HashMap();
+ namespaces.add(ctx);
+ }
+ else
+ {
+ ctx = (HashMap) namespaces.getFirst();
+ }
+ ctx.put(prefix, uri);
+ }
+
+ void undefine(String prefix, String uri)
+ {
+ for (Iterator i = namespaces.iterator(); i.hasNext(); )
+ {
+ HashMap ctx = (HashMap) i.next();
+ if (uri.equals(ctx.get(prefix)))
+ {
+ ctx.remove(prefix);
+ if (ctx.isEmpty())
+ {
+ namespaces.remove(ctx);
+ }
+ return;
+ }
+ }
+ }
+
+ public int getLength()
+ {
+ return attrs.getLength();
+ }
+
+ public String getURI(int index)
+ {
+ return attrs.item(index).getNamespaceURI();
+ }
+
+ public String getLocalName(int index)
+ {
+ return attrs.item(index).getLocalName();
+ }
+
+ public String getQName(int index)
+ {
+ return attrs.item(index).getNodeName();
+ }
+
+ public String getType(int index)
+ {
+ Attr attr = (Attr) attrs.item(index);
+ return attr.isId() ? "ID" : "CDATA";
+ }
+
+ public String getValue(int index)
+ {
+ return attrs.item(index).getNodeValue();
+ }
+
+ public int getIndex(String uri, String localName)
+ {
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String a_uri = attr.getNamespaceURI();
+ String a_localName = attr.getLocalName();
+ if (((a_uri == null && uri == null) ||
+ (a_uri != null && a_uri.equals(uri))) &&
+ a_localName.equals(localName))
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int getIndex(String qName)
+ {
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String a_name = attr.getNodeName();
+ if (a_name.equals(qName))
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public String getType(String uri, String localName)
+ {
+ Attr attr = (Attr) attrs.getNamedItemNS(uri, localName);
+ return attr.isId() ? "ID" : "CDATA";
+ }
+
+ public String getType(String qName)
+ {
+ Attr attr = (Attr) attrs.getNamedItem(qName);
+ return attr.isId() ? "ID" : "CDATA";
+ }
+
+ public String getValue(String uri, String localName)
+ {
+ return attrs.getNamedItemNS(uri, localName).getNodeValue();
+ }
+
+ public String getValue(String qName)
+ {
+ return attrs.getNamedItem(qName).getNodeValue();
+ }
+
+ void serialize(Node node, ContentHandler ch, LexicalHandler lh)
+ throws SAXException
+ {
+ attrs = node.getAttributes();
+ Node children;
+ Node next = node.getNextSibling();
+ switch (node.getNodeType())
+ {
+ case Node.ELEMENT_NODE:
+ String uri = node.getNamespaceURI();
+ String prefix = node.getPrefix();
+ boolean defined = isDefined(prefix, uri);
+ if (!defined)
+ {
+ define(prefix, uri);
+ ch.startPrefixMapping(prefix, uri);
+ }
+ String localName = node.getLocalName();
+ String qName = node.getNodeName();
+ ch.startElement(uri, localName, qName, this);
+ children = node.getFirstChild();
+ if (children != null)
+ {
+ serialize(children, ch, lh);
+ }
+ ch.endElement(uri, localName, qName);
+ if (!defined)
+ {
+ ch.endPrefixMapping(prefix);
+ undefine(prefix, uri);
+ }
+ break;
+ case Node.TEXT_NODE:
+ char[] chars = node.getNodeValue().toCharArray();
+ ch.characters(chars, 0, chars.length);
+ break;
+ case Node.CDATA_SECTION_NODE:
+ char[] cdata = node.getNodeValue().toCharArray();
+ if (lh != null)
+ {
+ lh.startCDATA();
+ ch.characters(cdata, 0, cdata.length);
+ lh.endCDATA();
+ }
+ else
+ {
+ ch.characters(cdata, 0, cdata.length);
+ }
+ break;
+ case Node.COMMENT_NODE:
+ if (lh != null)
+ {
+ char[] comment = node.getNodeValue().toCharArray();
+ lh.comment(comment, 0, comment.length);
+ }
+ break;
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ ch.startDocument();
+ children = node.getFirstChild();
+ if (children != null)
+ {
+ serialize(children, ch, lh);
+ }
+ ch.endDocument();
+ break;
+ case Node.DOCUMENT_TYPE_NODE:
+ if (lh != null)
+ {
+ DocumentType doctype = (DocumentType) node;
+ String publicId = doctype.getPublicId();
+ String systemId = doctype.getSystemId();
+ lh.startDTD(node.getNodeName(), publicId, systemId);
+ NamedNodeMap entities = doctype.getEntities();
+ int len = entities.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node entity = entities.item(i);
+ String entityName = entity.getNodeName();
+ lh.startEntity(entityName);
+ lh.endEntity(entityName);
+ }
+ lh.endDTD();
+ }
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ ch.processingInstruction(node.getNodeName(), node.getNodeValue());
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ ch.skippedEntity(node.getNodeName());
+ break;
+ }
+ attrs = null;
+ if (next != null)
+ {
+ serialize(next, ch, lh);
+ }
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/SAXTemplatesHandler.java b/libjava/classpath/gnu/xml/transform/SAXTemplatesHandler.java
new file mode 100644
index 000000000..24464c067
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/SAXTemplatesHandler.java
@@ -0,0 +1,97 @@
+/* SAXTemplatesHandler.java --
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import javax.xml.transform.Templates;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.TemplatesHandler;
+import org.w3c.dom.Document;
+import gnu.xml.dom.ls.SAXEventSink;
+
+/**
+ * A content handler that acts as a sink for SAX parse events,
+ * constructing an XSL stylesheet.
+ * Internally, this class simply creates a DOM tree from the events,
+ * and then parses the DOM into a Templates object.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+class SAXTemplatesHandler
+ extends SAXEventSink
+ implements TemplatesHandler
+{
+
+ final TransformerFactoryImpl factory;
+ String systemId;
+
+ SAXTemplatesHandler(TransformerFactoryImpl factory)
+ {
+ this.factory = factory;
+ }
+
+ public String getSystemId()
+ {
+ return systemId;
+ }
+
+ public void setSystemId(String systemId)
+ {
+ this.systemId = systemId;
+ }
+
+ public Templates getTemplates()
+ {
+ Document doc = getDocument();
+ if (doc == null)
+ throw new IllegalStateException("Parsing of stylesheet incomplete");
+ DOMSource ds = new DOMSource(doc, systemId);
+ try
+ {
+ return factory.newTemplates(ds);
+ }
+ catch (TransformerConfigurationException e)
+ {
+ String msg = "Unable to construct templates from this event stream";
+ IllegalStateException e2 = new IllegalStateException(msg);
+ e2.initCause(e);
+ throw e2;
+ }
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/SAXTransformerHandler.java b/libjava/classpath/gnu/xml/transform/SAXTransformerHandler.java
new file mode 100644
index 000000000..890e15100
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/SAXTransformerHandler.java
@@ -0,0 +1,111 @@
+/* SAXTransformerHandler.java --
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.TransformerHandler;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+import gnu.xml.dom.ls.SAXEventSink;
+
+/**
+ * A SAX event sink that processes an XML source represented as a stream of
+ * SAX events into a result tree.
+ * This works by simply buffering all the events into a DOM tree and then
+ * using this DOM tree as the source of the transformation.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+class SAXTransformerHandler
+ extends SAXEventSink
+ implements TransformerHandler
+{
+
+ final TransformerFactoryImpl factory;
+ final Transformer transformer;
+ String systemId;
+ Result result;
+
+ SAXTransformerHandler(TransformerFactoryImpl factory, Transformer transformer)
+ {
+ this.factory = factory;
+ this.transformer = transformer;
+ }
+
+ public String getSystemId()
+ {
+ return systemId;
+ }
+
+ public void setSystemId(String systemId)
+ {
+ this.systemId = systemId;
+ }
+
+ public Transformer getTransformer()
+ {
+ return transformer;
+ }
+
+ public void setResult(Result result)
+ {
+ this.result = result;
+ }
+
+ public void endDocument()
+ throws SAXException
+ {
+ super.endDocument();
+ try
+ {
+ Document doc = getDocument();
+ DOMSource ds = new DOMSource(doc, systemId);
+ transformer.transform(ds, result);
+ }
+ catch (TransformerException e)
+ {
+ SAXException e2 = new SAXException(e.getMessage());
+ e2.initCause(e);
+ throw e2;
+ }
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/SortKey.java b/libjava/classpath/gnu/xml/transform/SortKey.java
new file mode 100644
index 000000000..dd57df986
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/SortKey.java
@@ -0,0 +1,239 @@
+/* SortKey.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * <p>
+ * An XSL sort key, as specified by section 10 of the XSL
+ * Transformations specification. This takes the form:
+ * </p>
+ * <pre>
+ * &lt;xsl:sort
+ * select = string-expression
+ * lang = { nmtoken }
+ * data-type = { "text" | "number" | qname-but-not-ncname }
+ * order = { "ascending" | "descending" }
+ * case-order = { "upper-first" | "lower-first" } /&rt;
+ * </pre>
+ * <p>
+ * Note that all but the selection expression are optional,
+ * and so may be {@code null}.
+ * </p>
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class SortKey
+{
+
+ static final int DEFAULT = 0;
+ static final int UPPER_FIRST = 1;
+ static final int LOWER_FIRST = 2;
+
+ final Expr select;
+ final TemplateNode langTemplate;
+ final TemplateNode dataTypeTemplate;
+ final TemplateNode orderTemplate;
+ final TemplateNode caseOrderTemplate;
+
+ transient String lang;
+ transient String dataType;
+ transient boolean descending;
+ transient int caseOrder;
+
+ /**
+ * Constructs a new {@link SortKey} to represent an &lt;xsl:sort&rt;
+ * tag.
+ *
+ * @param select the XPath expression which selects the nodes to be sorted.
+ * @param lang the language of the sort keys or {@code null} if unspecified.
+ * @param dataType the data type of the strings. May be "string", "number",
+ * a QName or {@code null} if unspecified.
+ * @param order the ordering of the nodes, which may be "ascending", "descending"
+ * or {@code null} if unspecified.
+ * @param caseOrder the treatment of case when the data type is a string. This
+ * may be "upper-first", "lower-first" or {@code null} if
+ * unspecified.
+ */
+ SortKey(Expr select, TemplateNode lang, TemplateNode dataType,
+ TemplateNode order, TemplateNode caseOrder)
+ {
+ this.select = select;
+ this.langTemplate = lang;
+ this.dataTypeTemplate = dataType;
+ this.orderTemplate = order;
+ this.caseOrderTemplate = caseOrder;
+ }
+
+ String key(Node node)
+ {
+ Object ret = select.evaluate(node, 1, 1);
+ if (ret instanceof String)
+ {
+ return (String) ret;
+ }
+ else
+ {
+ return Expr._string(node, ret);
+ }
+ }
+
+ /**
+ * Prepare for a sort.
+ * This sets all transient variables from their AVTs.
+ */
+ void init(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ if (langTemplate == null)
+ {
+ lang = null;
+ }
+ else
+ {
+ DocumentFragment fragment = doc.createDocumentFragment();
+ langTemplate.apply(stylesheet, mode, context, pos, len,
+ fragment, null);
+ lang = Expr.stringValue(fragment);
+ }
+ if (dataTypeTemplate == null)
+ {
+ dataType = "text";
+ }
+ else
+ {
+ DocumentFragment fragment = doc.createDocumentFragment();
+ dataTypeTemplate.apply(stylesheet, mode, context, pos, len,
+ fragment, null);
+ dataType = Expr.stringValue(fragment);
+ }
+ if (orderTemplate == null)
+ {
+ descending = false;
+ }
+ else
+ {
+ DocumentFragment fragment = doc.createDocumentFragment();
+ orderTemplate.apply(stylesheet, mode, context, pos, len,
+ fragment, null);
+ String order = Expr.stringValue(fragment);
+ descending = "descending".equals(order);
+ }
+ if (caseOrderTemplate == null)
+ {
+ caseOrder = DEFAULT;
+ }
+ else
+ {
+ DocumentFragment fragment = doc.createDocumentFragment();
+ caseOrderTemplate.apply(stylesheet, mode, context, pos, len,
+ fragment, null);
+ String co = Expr.stringValue(fragment);
+ caseOrder = "upper-first".equals(co) ? UPPER_FIRST :
+ "lower-first".equals(co) ? LOWER_FIRST :
+ DEFAULT;
+ }
+ }
+
+ boolean references(QName var)
+ {
+ if (select != null && select.references(var))
+ {
+ return true;
+ }
+ if (langTemplate != null && langTemplate.references(var))
+ {
+ return true;
+ }
+ if (dataTypeTemplate != null && dataTypeTemplate.references(var))
+ {
+ return true;
+ }
+ if (orderTemplate != null && orderTemplate.references(var))
+ {
+ return true;
+ }
+ if (caseOrderTemplate != null && caseOrderTemplate.references(var))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Provides a clone of this {@link SortKey}, using the given
+ * stylesheet as a context.
+ *
+ * @param stylesheet the stylesheet which provides context for the cloning.
+ * @return a clone of this instance.
+ */
+ SortKey clone(Stylesheet stylesheet)
+ {
+ return new SortKey(select.clone(stylesheet),
+ langTemplate == null ? null : cloneAttributeValueTemplate(langTemplate, stylesheet),
+ dataTypeTemplate == null ? null : cloneAttributeValueTemplate(dataTypeTemplate, stylesheet),
+ orderTemplate == null ? null : cloneAttributeValueTemplate(orderTemplate, stylesheet),
+ caseOrderTemplate == null ? null : cloneAttributeValueTemplate(caseOrderTemplate, stylesheet));
+ }
+
+ /**
+ * Clones an attribute value template as created by
+ * {@link Stylesheet#parseAttributeValueTemplate(String, Node)}.
+ * The node may either by a literal node or an xsl:value-of expression.
+ *
+ * @param node the node to clone.
+ * @param stylesheet the stylesheet which provides context for the cloning.
+ * @return the cloned node.
+ */
+ private TemplateNode cloneAttributeValueTemplate(TemplateNode node, Stylesheet stylesheet)
+ {
+ if (node instanceof ValueOfNode)
+ return ((ValueOfNode) node).clone(stylesheet);
+ return ((LiteralNode) node).clone(stylesheet);
+ }
+}
diff --git a/libjava/classpath/gnu/xml/transform/StreamSerializer.java b/libjava/classpath/gnu/xml/transform/StreamSerializer.java
new file mode 100644
index 000000000..a5705f891
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/StreamSerializer.java
@@ -0,0 +1,854 @@
+/* StreamSerializer.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import javax.xml.XMLConstants;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * Serializes a DOM node to an output stream.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class StreamSerializer
+{
+
+ static final int SPACE = 0x20;
+ static final int BANG = 0x21; // !
+ static final int APOS = 0x27; // '
+ static final int SLASH = 0x2f; // /
+ static final int BRA = 0x3c; // <
+ static final int KET = 0x3e; // >
+ static final int EQ = 0x3d; // =
+
+ /**
+ * HTML 4.01 boolean attributes
+ */
+ static final Map HTML_BOOLEAN_ATTRIBUTES = new HashMap();
+ static
+ {
+ HashSet set;
+
+ set = new HashSet();
+ set.add("nohref");
+ HTML_BOOLEAN_ATTRIBUTES.put("area", set);
+
+ set = new HashSet();
+ set.add("ismap");
+ HTML_BOOLEAN_ATTRIBUTES.put("img", set);
+
+ set = new HashSet();
+ set.add("declare");
+ HTML_BOOLEAN_ATTRIBUTES.put("object", set);
+
+ set = new HashSet();
+ set.add("noshade");
+ HTML_BOOLEAN_ATTRIBUTES.put("hr", set);
+
+ set = new HashSet();
+ set.add("compact");
+ HTML_BOOLEAN_ATTRIBUTES.put("dl", set);
+ HTML_BOOLEAN_ATTRIBUTES.put("ol", set);
+ HTML_BOOLEAN_ATTRIBUTES.put("ul", set);
+ HTML_BOOLEAN_ATTRIBUTES.put("dir", set);
+ HTML_BOOLEAN_ATTRIBUTES.put("menu", set);
+
+ set = new HashSet();
+ set.add("checked");
+ set.add("disabled");
+ set.add("readonly");
+ set.add("ismap");
+ HTML_BOOLEAN_ATTRIBUTES.put("input", set);
+
+ set = new HashSet();
+ set.add("multiple");
+ set.add("disabled");
+ HTML_BOOLEAN_ATTRIBUTES.put("select", set);
+
+ set = new HashSet();
+ set.add("disabled");
+ HTML_BOOLEAN_ATTRIBUTES.put("optgroup", set);
+
+ set = new HashSet();
+ set.add("selected");
+ set.add("disabled");
+ HTML_BOOLEAN_ATTRIBUTES.put("option", set);
+
+ set = new HashSet();
+ set.add("disabled");
+ set.add("readonly");
+ HTML_BOOLEAN_ATTRIBUTES.put("textarea", set);
+
+ set = new HashSet();
+ set.add("disabled");
+ HTML_BOOLEAN_ATTRIBUTES.put("button", set);
+
+ set = new HashSet();
+ set.add("nowrap");
+ HTML_BOOLEAN_ATTRIBUTES.put("th", set);
+ HTML_BOOLEAN_ATTRIBUTES.put("td", set);
+
+ set = new HashSet();
+ set.add("noresize");
+ HTML_BOOLEAN_ATTRIBUTES.put("frame", set);
+
+ set = new HashSet();
+ set.add("defer");
+ HTML_BOOLEAN_ATTRIBUTES.put("script", set);
+ }
+
+ // HTML namespace URIs
+ static final HashSet HTML_URIS = new HashSet();
+ static {
+ HTML_URIS.add("http://www.w3.org/1999/xhtml");
+ }
+
+ protected final String encoding;
+ final Charset charset;
+ final CharsetEncoder encoder;
+ final int mode;
+ final LinkedList namespaces;
+ protected String eol;
+ Collection cdataSectionElements = Collections.EMPTY_SET;
+
+ protected boolean discardDefaultContent;
+ protected boolean xmlDeclaration = true;
+
+ // has a META element with the encoding been added?
+ private boolean htmlEncoded;
+
+ public StreamSerializer()
+ {
+ this(Stylesheet.OUTPUT_XML, null, null);
+ }
+
+ public StreamSerializer(String encoding)
+ {
+ this(Stylesheet.OUTPUT_XML, encoding, null);
+ }
+
+ public StreamSerializer(int mode, String encoding, String eol)
+ {
+ this.mode = mode;
+ if (encoding == null)
+ encoding = (mode == Stylesheet.OUTPUT_HTML) ? "ISO-8859-1" : "UTF-8";
+ this.encoding = encoding.intern();
+ charset = Charset.forName(this.encoding);
+ encoder = charset.newEncoder();
+ this.eol = (eol != null) ? eol : System.getProperty("line.separator");
+ namespaces = new LinkedList();
+ }
+
+ void setCdataSectionElements(Collection c)
+ {
+ cdataSectionElements = c;
+ }
+
+ public void serialize(final Node node, final OutputStream out)
+ throws IOException
+ {
+ serialize(node, out, false);
+ }
+
+ void serialize(Node node, final OutputStream out,
+ boolean convertToCdata)
+ throws IOException
+ {
+ while (node != null)
+ {
+ Node next = node.getNextSibling();
+ doSerialize(node, out, convertToCdata);
+ node = next;
+ }
+ }
+
+ private void doSerialize(final Node node, final OutputStream out,
+ boolean convertToCdata)
+ throws IOException
+ {
+ if (out == null)
+ throw new NullPointerException("no output stream");
+ htmlEncoded = false;
+ String value, prefix;
+ Node children;
+ String uri = node.getNamespaceURI();
+ short nt = node.getNodeType();
+ if (convertToCdata && nt == Node.TEXT_NODE)
+ nt = Node.CDATA_SECTION_NODE;
+ switch (nt)
+ {
+ case Node.ATTRIBUTE_NODE:
+ prefix = node.getPrefix();
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) ||
+ (prefix != null && prefix.startsWith("xmlns:")))
+ {
+ String nsuri = node.getNodeValue();
+ if (isDefined(nsuri, prefix))
+ break;
+ String name = node.getLocalName();
+ if (name == null)
+ {
+ // Namespace-unaware
+ name = node.getNodeName();
+ int ci = name.indexOf(':');
+ if (ci != -1)
+ name = name.substring(ci + 1);
+ }
+ define(nsuri, name);
+ }
+ else if (uri != null && !isDefined(uri, prefix))
+ {
+ prefix = define(uri, prefix);
+ String nsname = (prefix == null) ? "xmlns" : "xmlns:" + prefix;
+ out.write(SPACE);
+ out.write(encodeText(nsname));
+ out.write(EQ);
+ String nsvalue = "\"" + encode(uri, true, true) + "\"";
+ out.write(nsvalue.getBytes(encoding));
+ }
+ out.write(SPACE);
+ String a_nodeName = node.getNodeName();
+ out.write(encodeText(a_nodeName));
+ String a_nodeValue = node.getNodeValue();
+ if (mode == Stylesheet.OUTPUT_HTML &&
+ a_nodeName.equals(a_nodeValue) &&
+ isHTMLBoolean((Attr) node, a_nodeName))
+ break;
+ out.write(EQ);
+ value = "\"" + encode(a_nodeValue, true, true) + "\"";
+ out.write(encodeText(value));
+ break;
+ case Node.ELEMENT_NODE:
+ pushNamespaceContext();
+ value = node.getNodeName();
+ out.write(BRA);
+ out.write(encodeText(value));
+ prefix = node.getPrefix();
+ if (uri != null && !isDefined(uri, prefix))
+ {
+ prefix = define(uri, prefix);
+ String nsname = (prefix == null) ? "xmlns" : "xmlns:" + prefix;
+ out.write(SPACE);
+ out.write(encodeText(nsname));
+ out.write(EQ);
+ String nsvalue = "\"" + encode(uri, true, true) + "\"";
+ out.write(encodeText(nsvalue));
+ }
+ NamedNodeMap attrs = node.getAttributes();
+ if (attrs != null)
+ {
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Attr attr = (Attr) attrs.item(i);
+ if (discardDefaultContent && !attr.getSpecified())
+ {
+ // NOOP
+ }
+ else
+ serialize(attr, out, false);
+ }
+ }
+ convertToCdata = cdataSectionElements.contains(value);
+ children = node.getFirstChild();
+ if (children == null)
+ {
+ out.write(SLASH);
+ out.write(KET);
+ }
+ else
+ {
+ out.write(KET);
+ serialize(children, out, convertToCdata);
+ out.write(BRA);
+ out.write(SLASH);
+ out.write(encodeText(value));
+ out.write(KET);
+ }
+ popNamespaceContext();
+ break;
+ case Node.TEXT_NODE:
+ value = node.getNodeValue();
+ if (!"yes".equals(node.getUserData("disable-output-escaping")) &&
+ mode != Stylesheet.OUTPUT_TEXT)
+ value = encode(value, false, false);
+ out.write(encodeText(value));
+ break;
+ case Node.CDATA_SECTION_NODE:
+ value = node.getNodeValue();
+ // Where any instanceof of ]]> occur, split into multiple CDATA
+ // sections
+ int bbk = value.indexOf("]]>");
+ while (bbk != -1)
+ {
+ String head = value.substring(0, bbk + 2);
+ out.write(encodeText("<![CDATA[" + head + "]]>"));
+ value = value.substring(bbk + 2);
+ bbk = value.indexOf("]]>");
+ }
+ // Write final tail value
+ out.write(encodeText("<![CDATA[" + value + "]]>"));
+ break;
+ case Node.COMMENT_NODE:
+ value = "<!--" + node.getNodeValue() + "-->";
+ out.write(encodeText(value));
+ Node cp = node.getParentNode();
+ if (cp != null && cp.getNodeType() == Node.DOCUMENT_NODE)
+ out.write(encodeText(eol));
+ break;
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ if (mode == Stylesheet.OUTPUT_XML)
+ {
+ if ("UTF-16".equalsIgnoreCase(encoding))
+ {
+ out.write(0xfe);
+ out.write(0xff);
+ }
+ if (!"yes".equals(node.getUserData("omit-xml-declaration")) &&
+ xmlDeclaration)
+ {
+ Document doc = (node instanceof Document) ?
+ (Document) node : null;
+ String version = (doc != null) ? doc.getXmlVersion() : null;
+ if (version == null)
+ version = (String) node.getUserData("version");
+ if (version == null)
+ version = "1.0";
+ out.write(BRA);
+ out.write(0x3f);
+ out.write("xml version=\"".getBytes("US-ASCII"));
+ out.write(version.getBytes("US-ASCII"));
+ out.write(0x22);
+ if (!("UTF-8".equalsIgnoreCase(encoding)))
+ {
+ out.write(" encoding=\"".getBytes("US-ASCII"));
+ out.write(encoding.getBytes("US-ASCII"));
+ out.write(0x22);
+ }
+ if ((doc != null && doc.getXmlStandalone()) ||
+ "yes".equals(node.getUserData("standalone")))
+ out.write(" standalone=\"yes\"".getBytes("US-ASCII"));
+ out.write(0x3f);
+ out.write(KET);
+ out.write(encodeText(eol));
+ }
+ // TODO warn if not outputting the declaration would be a
+ // problem
+ }
+ else if (mode == Stylesheet.OUTPUT_HTML)
+ {
+ // Ensure that encoding is accessible if head element is present
+ String mediaType = (String) node.getUserData("media-type");
+ if (mediaType == null)
+ mediaType = "text/html";
+ String contentType = mediaType + "; charset=" +
+ ((encoding.indexOf(' ') != -1) ?
+ "\"" + encoding + "\"" :
+ encoding);
+ Document doc = (node instanceof Document) ? (Document) node :
+ node.getOwnerDocument();
+ Node html = null;
+ for (Node ctx = node.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ if (ctx.getNodeType() == Node.ELEMENT_NODE &&
+ isHTMLElement(ctx, "html"))
+ {
+ html = ctx;
+ break;
+ }
+ }
+ if (html != null)
+ {
+ Node head = null;
+ for (Node ctx = html.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ if (isHTMLElement(ctx, "head"))
+ {
+ head = ctx;
+ break;
+ }
+ }
+ if (head != null)
+ {
+ Node meta = null;
+ Node metaContent = null;
+ for (Node ctx = head.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ if (isHTMLElement(ctx, "meta"))
+ {
+ NamedNodeMap metaAttrs = ctx.getAttributes();
+ int len = metaAttrs.getLength();
+ String httpEquiv = null;
+ Node content = null;
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = metaAttrs.item(i);
+ String attrName = attr.getNodeName();
+ if ("http-equiv".equalsIgnoreCase(attrName))
+ httpEquiv = attr.getNodeValue();
+ else if ("content".equalsIgnoreCase(attrName))
+ content = attr;
+ }
+ if ("Content-Type".equalsIgnoreCase(httpEquiv))
+ {
+ meta = ctx;
+ metaContent = content;
+ break;
+ }
+ }
+ }
+ if (meta == null)
+ {
+ meta = doc.createElement("meta");
+ // Insert first
+ Node first = head.getFirstChild();
+ if (first == null)
+ head.appendChild(meta);
+ else
+ head.insertBefore(meta, first);
+ Node metaHttpEquiv = doc.createAttribute("http-equiv");
+ meta.getAttributes().setNamedItem(metaHttpEquiv);
+ metaHttpEquiv.setNodeValue("Content-Type");
+ }
+ if (metaContent == null)
+ {
+ metaContent = doc.createAttribute("content");
+ meta.getAttributes().setNamedItem(metaContent);
+ }
+ metaContent.setNodeValue(contentType);
+ htmlEncoded = true;
+ }
+ }
+ }
+ children = node.getFirstChild();
+ if (children != null)
+ serialize(children, out, convertToCdata);
+ break;
+ case Node.DOCUMENT_TYPE_NODE:
+ DocumentType doctype = (DocumentType) node;
+ out.write(BRA);
+ out.write(BANG);
+ out.write(encodeText("DOCTYPE "));
+ value = doctype.getNodeName();
+ out.write(encodeText(value));
+ String publicId = doctype.getPublicId();
+ if (publicId != null)
+ {
+ out.write(encodeText(" PUBLIC "));
+ out.write(APOS);
+ out.write(encodeText(publicId));
+ out.write(APOS);
+ }
+ String systemId = doctype.getSystemId();
+ if (systemId != null)
+ {
+ out.write(encodeText(" SYSTEM "));
+ out.write(APOS);
+ out.write(encodeText(systemId));
+ out.write(APOS);
+ }
+ String internalSubset = doctype.getInternalSubset();
+ if (internalSubset != null)
+ {
+ out.write(encodeText(internalSubset));
+ }
+ out.write(KET);
+ out.write(eol.getBytes(encoding));
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ value = "&" + node.getNodeValue() + ";";
+ out.write(encodeText(value));
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ value = "<?" + node.getNodeName() + " " + node.getNodeValue() + "?>";
+ out.write(encodeText(value));
+ Node pp = node.getParentNode();
+ if (pp != null && pp.getNodeType() == Node.DOCUMENT_NODE)
+ {
+ out.write(encodeText(eol));
+ }
+ break;
+ default:
+ System.err.println("Unhandled node type: "+nt);
+ }
+ }
+
+ boolean isHTMLElement(Node node, String name)
+ {
+ if (node.getNodeType() != Node.ELEMENT_NODE)
+ return false;
+ String localName = node.getLocalName();
+ if (localName == null)
+ localName = node.getNodeName();
+ if (!name.equalsIgnoreCase(localName))
+ return false;
+ String uri = node.getNamespaceURI();
+ return (uri == null || HTML_URIS.contains(uri));
+ }
+
+ boolean isDefined(String uri, String prefix)
+ {
+ if (XMLConstants.XML_NS_URI.equals(uri))
+ return "xml".equals(prefix);
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri))
+ return "xmlns".equals(prefix);
+ if (prefix == null)
+ prefix = "";
+ for (Iterator i = namespaces.iterator(); i.hasNext(); )
+ {
+ Map ctx = (Map) i.next();
+ String val = (String) ctx.get(uri);
+ if (val != null && val.equals(prefix))
+ return true;
+ }
+ return false;
+ }
+
+ void pushNamespaceContext()
+ {
+ namespaces.addFirst(new HashMap());
+ }
+
+ String define(String uri, String prefix)
+ {
+ if (namespaces.isEmpty())
+ return prefix;
+ HashMap ctx = (HashMap) namespaces.getFirst();
+ while (ctx.containsValue(prefix))
+ {
+ // Fabricate new prefix
+ prefix = prefix + "_";
+ }
+ ctx.put(uri, prefix);
+ return prefix;
+ }
+
+ void popNamespaceContext()
+ {
+ namespaces.removeFirst();
+ }
+
+ final byte[] encodeText(String text)
+ throws IOException
+ {
+ encoder.reset();
+ boolean htmlNeedingEncoding =
+ (mode == Stylesheet.OUTPUT_HTML && !htmlEncoded);
+ if (!encoder.canEncode(text) || htmlNeedingEncoding)
+ {
+ // Check each character
+ CPStringBuilder buf = new CPStringBuilder();
+ int len = text.length();
+ for (int i = 0; i < len; i++)
+ {
+ char c = text.charAt(i);
+ if (!encoder.canEncode(c))
+ {
+ // Replace with character entity reference
+ String hex = Integer.toHexString((int) c);
+ buf.append("&#x");
+ buf.append(hex);
+ buf.append(';');
+ }
+ else if (htmlNeedingEncoding)
+ {
+ String entityName = getHTMLCharacterEntity(c);
+ if (entityName != null)
+ {
+ buf.append('&');
+ buf.append(entityName);
+ buf.append(';');
+ }
+ else
+ buf.append(c);
+ }
+ else
+ buf.append(c);
+ }
+ text = buf.toString();
+ }
+ ByteBuffer encoded = encoder.encode(CharBuffer.wrap(text));
+ int len = encoded.limit() - encoded.position();
+ if (encoded.hasArray())
+ {
+ byte[] ret = encoded.array();
+ if (ret.length > len)
+ {
+ // Why?
+ byte[] ret2 = new byte[len];
+ System.arraycopy(ret, 0, ret2, 0, len);
+ ret = ret2;
+ }
+ return ret;
+ }
+ encoded.flip();
+ byte[] ret = new byte[len];
+ encoded.get(ret, 0, len);
+ return ret;
+ }
+
+ String encode(String text, boolean encodeCtl, boolean inAttr)
+ {
+ int len = text.length();
+ CPStringBuilder buf = null;
+ for (int i = 0; i < len; i++)
+ {
+ char c = text.charAt(i);
+ if (c == '<')
+ {
+ if (buf == null)
+ buf = new CPStringBuilder(text.substring(0, i));
+ buf.append("&lt;");
+ }
+ else if (c == '>')
+ {
+ if (buf == null)
+ buf = new CPStringBuilder(text.substring(0, i));
+ buf.append("&gt;");
+ }
+ else if (c == '&')
+ {
+ if (mode == Stylesheet.OUTPUT_HTML && (i + 1) < len &&
+ text.charAt(i + 1) == '{')
+ {
+ if (buf != null)
+ buf.append(c);
+ }
+ else
+ {
+ if (buf == null)
+ buf = new CPStringBuilder(text.substring(0, i));
+ buf.append("&amp;");
+ }
+ }
+ else if (c == '\'' && inAttr)
+ {
+ if (buf == null)
+ buf = new CPStringBuilder(text.substring(0, i));
+ if (mode == Stylesheet.OUTPUT_HTML)
+ // HTML does not define &apos;, use character entity ref
+ buf.append("&#x27;");
+ else
+ buf.append("&apos;");
+ }
+ else if (c == '"' && inAttr)
+ {
+ if (buf == null)
+ buf = new CPStringBuilder(text.substring(0, i));
+ buf.append("&quot;");
+ }
+ else if (encodeCtl)
+ {
+ if (c < 0x20)
+ {
+ if (buf == null)
+ buf = new CPStringBuilder(text.substring(0, i));
+ buf.append('&');
+ buf.append('#');
+ buf.append((int) c);
+ buf.append(';');
+ }
+ else if (buf != null)
+ buf.append(c);
+ }
+ else if (buf != null)
+ buf.append(c);
+ }
+ return (buf == null) ? text : buf.toString();
+ }
+
+ String toString(Node node)
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try
+ {
+ serialize(node, out);
+ return new String(out.toByteArray(), encoding);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ boolean isHTMLBoolean(Attr attr, String attrName)
+ {
+ attrName = attrName.toLowerCase();
+ Node element = attr.getOwnerElement();
+ String elementName = element.getLocalName();
+ if (elementName == null)
+ {
+ elementName = element.getNodeName();
+ }
+ elementName = elementName.toLowerCase();
+ Collection attributes =
+ (Collection) HTML_BOOLEAN_ATTRIBUTES.get(elementName);
+ return (attributes != null && attributes.contains(attrName));
+ }
+
+ static String getHTMLCharacterEntity(char c)
+ {
+ // Hardcode these here to avoid loading the HTML DTD
+ switch (c)
+ {
+ case 160: return "nbsp";
+ case 161: return "iexcl";
+ case 162: return "cent";
+ case 163: return "pound";
+ case 164: return "curren";
+ case 165: return "yen";
+ case 166: return "brvbar";
+ case 167: return "sect";
+ case 168: return "uml";
+ case 169: return "copy";
+ case 170: return "ordf";
+ case 171: return "laquo";
+ case 172: return "not";
+ case 173: return "shy";
+ case 174: return "reg";
+ case 175: return "macr";
+ case 176: return "deg";
+ case 177: return "plusmn";
+ case 178: return "sup2";
+ case 179: return "sup3";
+ case 180: return "acute";
+ case 181: return "micro";
+ case 182: return "para";
+ case 183: return "middot";
+ case 184: return "cedil";
+ case 185: return "sup1";
+ case 186: return "ordm";
+ case 187: return "raquo";
+ case 188: return "frac14";
+ case 189: return "frac12";
+ case 190: return "frac34";
+ case 191: return "iquest";
+ case 192: return "Agrave";
+ case 193: return "Aacute";
+ case 194: return "Acirc";
+ case 195: return "Atilde";
+ case 196: return "Auml";
+ case 197: return "Aring";
+ case 198: return "AElig";
+ case 199: return "Ccedil";
+ case 200: return "Egrave";
+ case 201: return "Eacute";
+ case 202: return "Ecirc";
+ case 203: return "Euml";
+ case 204: return "Igrave";
+ case 205: return "Iacute";
+ case 206: return "Icirc";
+ case 207: return "Iuml";
+ case 208: return "ETH";
+ case 209: return "Ntilde";
+ case 210: return "Ograve";
+ case 211: return "Oacute";
+ case 212: return "Ocirc";
+ case 213: return "Otilde";
+ case 214: return "Ouml";
+ case 215: return "times";
+ case 216: return "Oslash";
+ case 217: return "Ugrave";
+ case 218: return "Uacute";
+ case 219: return "Ucirc";
+ case 220: return "Uuml";
+ case 221: return "Yacute";
+ case 222: return "THORN";
+ case 223: return "szlig";
+ case 224: return "agrave";
+ case 225: return "aacute";
+ case 226: return "acirc";
+ case 227: return "atilde";
+ case 228: return "auml";
+ case 229: return "aring";
+ case 230: return "aelig";
+ case 231: return "ccedil";
+ case 232: return "egrave";
+ case 233: return "eacute";
+ case 234: return "ecirc";
+ case 235: return "euml";
+ case 236: return "igrave";
+ case 237: return "iacute";
+ case 238: return "icirc";
+ case 239: return "iuml";
+ case 240: return "eth";
+ case 241: return "ntilde";
+ case 242: return "ograve";
+ case 243: return "oacute";
+ case 244: return "ocirc";
+ case 245: return "otilde";
+ case 246: return "ouml";
+ case 247: return "divide";
+ case 248: return "oslash";
+ case 249: return "ugrave";
+ case 250: return "uacute";
+ case 251: return "ucirc";
+ case 252: return "uuml";
+ case 253: return "yacute";
+ case 254: return "thorn";
+ case 255: return "yuml";
+ default: return null;
+ }
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/StrippingInstruction.java b/libjava/classpath/gnu/xml/transform/StrippingInstruction.java
new file mode 100644
index 000000000..27e9fc8ca
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/StrippingInstruction.java
@@ -0,0 +1,73 @@
+/* StrippingInstruction.java --
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.xml.xpath.NameTest;
+
+/**
+ * An entry in a strip-space or preserve-space list.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class StrippingInstruction
+{
+
+ final NameTest element;
+ final int precedence;
+
+ StrippingInstruction(NameTest element, int precedence)
+ {
+ this.element = element;
+ this.precedence = precedence;
+ }
+
+ /**
+ * Returns the <i>default priority</i> of the element name test.
+ * @see http://www.w3.org/TR/xslt#dt-default-priority
+ */
+ float getPriority()
+ {
+ if (element.matchesAny())
+ return -0.5f;
+ else if (element.matchesAnyLocalName())
+ return -0.25f;
+ else
+ return 0.0f;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/Stylesheet.java b/libjava/classpath/gnu/xml/transform/Stylesheet.java
new file mode 100644
index 000000000..786b258f5
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/Stylesheet.java
@@ -0,0 +1,1772 @@
+/* Stylesheet.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathExpressionException;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import org.w3c.dom.UserDataHandler;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.NameTest;
+import gnu.xml.xpath.NodeTypeTest;
+import gnu.xml.xpath.Pattern;
+import gnu.xml.xpath.Selector;
+import gnu.xml.xpath.Root;
+import gnu.xml.xpath.Test;
+import gnu.xml.xpath.XPathImpl;
+
+/**
+ * An XSL stylesheet.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class Stylesheet
+ implements NamespaceContext, XPathFunctionResolver, UserDataHandler, Cloneable
+{
+
+ static final String XSL_NS = "http://www.w3.org/1999/XSL/Transform";
+ private static final NameTest STYLESHEET_PRESERVE_TEXT =
+ new NameTest(new QName(XSL_NS, "text"), false, false);
+
+ static final int OUTPUT_XML = 0;
+ static final int OUTPUT_HTML = 1;
+ static final int OUTPUT_TEXT = 2;
+
+ final TransformerFactoryImpl factory;
+ TransformerImpl transformer;
+ Stylesheet parent;
+ final XPathImpl xpath;
+ final String systemId;
+ final int precedence;
+
+ final boolean debug;
+
+ /**
+ * Version of XSLT.
+ */
+ String version;
+
+ Collection<String> extensionElementPrefixes;
+ Collection<String> excludeResultPrefixes;
+
+ /**
+ * Set of element names for which we should strip whitespace.
+ */
+ Set<StrippingInstruction> stripSpace;
+
+ /**
+ * Set of element names for which we should preserve whitespace.
+ */
+ Set<StrippingInstruction> preserveSpace;
+
+ /**
+ * Output options.
+ */
+ Node output;
+ int outputMethod;
+ String outputVersion;
+ String outputEncoding;
+ boolean outputOmitXmlDeclaration;
+ boolean outputStandalone;
+ String outputPublicId;
+ String outputSystemId;
+ Collection<String> outputCdataSectionElements;
+ boolean outputIndent;
+ String outputMediaType;
+
+ /**
+ * Keys.
+ */
+ Collection<Key> keys;
+
+ /**
+ * Decimal formats.
+ */
+ Map<String,DecimalFormat> decimalFormats;
+
+ /**
+ * Namespace aliases.
+ */
+ Map<String,String> namespaceAliases;
+
+ /**
+ * Attribute-sets.
+ */
+ List<AttributeSet> attributeSets;
+
+ /**
+ * Variables.
+ */
+ List<ParameterNode> variables;
+
+ /**
+ * Variable and parameter bindings.
+ */
+ Bindings bindings;
+
+ /**
+ * Templates.
+ */
+ LinkedList<Template> templates;
+
+ TemplateNode builtInNodeTemplate;
+ TemplateNode builtInTextTemplate;
+
+ /**
+ * Holds the current node while parsing.
+ * Necessary to associate the document function with its declaring node,
+ * to resolve namespaces, and to maintain the current node for the
+ * current() function.
+ */
+ Node current;
+
+ /**
+ * Set by a terminating message.
+ */
+ transient boolean terminated;
+
+ /**
+ * Current template in force.
+ */
+ transient Template currentTemplate;
+
+ Stylesheet(TransformerFactoryImpl factory,
+ Stylesheet parent,
+ Document doc,
+ String systemId,
+ int precedence)
+ throws TransformerConfigurationException
+ {
+ this.factory = factory;
+ this.systemId = systemId;
+ this.precedence = precedence;
+ this.parent = parent;
+ extensionElementPrefixes = new HashSet<String>();
+ excludeResultPrefixes = new HashSet<String>();
+ stripSpace = new LinkedHashSet<StrippingInstruction>();
+ preserveSpace = new LinkedHashSet<StrippingInstruction>();
+ outputCdataSectionElements = new LinkedHashSet<String>();
+ xpath = (XPathImpl) factory.xpathFactory.newXPath();
+ xpath.setNamespaceContext(this);
+ if (parent == null)
+ {
+ bindings = new Bindings(this);
+ attributeSets = new LinkedList<AttributeSet>();
+ variables = new LinkedList<ParameterNode>();
+ namespaceAliases = new LinkedHashMap<String,String>();
+ templates = new LinkedList<Template>();
+ keys = new LinkedList<Key>();
+ decimalFormats = new LinkedHashMap<String,DecimalFormat>();
+ initDefaultDecimalFormat();
+ xpath.setXPathFunctionResolver(this);
+ }
+ else
+ {
+ /* Test for import circularity */
+ for (Stylesheet ctx = this; ctx.parent != null; ctx = ctx.parent)
+ {
+ if (systemId != null && systemId.equals(ctx.parent.systemId))
+ {
+ String msg = "circularity importing " + systemId;
+ throw new TransformerConfigurationException(msg);
+ }
+ }
+ /* OK */
+ Stylesheet root = getRootStylesheet();
+ bindings = root.bindings;
+ attributeSets = root.attributeSets;
+ variables = root.variables;
+ namespaceAliases = root.namespaceAliases;
+ templates = root.templates;
+ keys = root.keys;
+ decimalFormats = root.decimalFormats;
+ xpath.setXPathFunctionResolver(root);
+ }
+ xpath.setXPathVariableResolver(bindings);
+
+ Test anyNode = new NodeTypeTest((short) 0);
+ List<Test> tests = Collections.singletonList(anyNode);
+ builtInNodeTemplate =
+ new ApplyTemplatesNode(new Selector(Selector.CHILD, tests),
+ null, null, null, true);
+ builtInTextTemplate =
+ new ValueOfNode(new Selector(Selector.SELF, tests),
+ false);
+
+ parse(doc.getDocumentElement(), true);
+ current = doc; // Alow namespace resolution during processing
+
+ debug = ("yes".equals(System.getProperty("xsl.debug")));
+
+ if (debug)
+ {
+ System.err.println("Stylesheet: " + doc.getDocumentURI());
+ for (Template t : templates)
+ {
+ t.list(System.err);
+ System.err.println("--------------------");
+ }
+ }
+ }
+
+ Stylesheet getRootStylesheet()
+ {
+ Stylesheet stylesheet = this;
+ while (stylesheet.parent != null)
+ stylesheet = stylesheet.parent;
+ return stylesheet;
+ }
+
+ void initDefaultDecimalFormat()
+ {
+ DecimalFormat defaultDecimalFormat = new DecimalFormat();
+ DecimalFormatSymbols symbols = new DecimalFormatSymbols();
+ symbols.setDecimalSeparator('.');
+ symbols.setGroupingSeparator(',');
+ symbols.setPercent('%');
+ symbols.setPerMill('\u2030');
+ symbols.setZeroDigit('0');
+ symbols.setDigit('#');
+ symbols.setPatternSeparator(';');
+ symbols.setInfinity("Infinity");
+ symbols.setNaN("NaN");
+ symbols.setMinusSign('-');
+ defaultDecimalFormat.setDecimalFormatSymbols(symbols);
+ decimalFormats.put(null, defaultDecimalFormat);
+ }
+
+ // -- Cloneable --
+
+ public Object clone()
+ {
+ try
+ {
+ Stylesheet clone = (Stylesheet) super.clone();
+ clone.bindings = (Bindings) bindings.clone();
+
+ LinkedList<Template> templates2 = new LinkedList<Template>();
+ for (Template t : templates)
+ {
+ templates2.add(t.clone(clone));
+ }
+ clone.templates = templates2;
+
+ LinkedList<AttributeSet> attributeSets2 = new LinkedList<AttributeSet>();
+ for (AttributeSet as : attributeSets)
+ {
+ attributeSets2.add(as.clone(clone));
+ }
+ clone.attributeSets = attributeSets2;
+
+ LinkedList<ParameterNode> variables2 = new LinkedList<ParameterNode>();
+ for (ParameterNode var : variables)
+ {
+ variables2.add(var.clone(clone));
+ }
+ clone.variables = variables2;
+
+ LinkedList<Key> keys2 = new LinkedList<Key>();
+ for (Key k : keys)
+ {
+ keys2.add(k.clone(clone));
+ }
+ clone.keys = keys2;
+
+ return clone;
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new Error(e.getMessage());
+ }
+ }
+
+ // -- Variable evaluation --
+
+ void initTopLevelVariables(Node context)
+ throws TransformerException
+ {
+ current = context;
+ // Sort the variables into order
+ // See XSLT 11.4: "If the template or expression specifying the value of
+ // a global variable x references a global variable y, then the value
+ // for y must be computed before the value of x."
+ List<ParameterNode> topLevel = new ArrayList<ParameterNode>(variables);
+ Collections.sort(topLevel);
+ for (ParameterNode var : topLevel)
+ {
+ bindings.set(var.name,
+ var.getValue(this, null, context, 1, 1),
+ var.type);
+ }
+ current = null;
+ }
+
+ // -- NamespaceContext --
+
+ public String getNamespaceURI(String prefix)
+ {
+ return (current == null) ? null : current.lookupNamespaceURI(prefix);
+ }
+
+ public String getPrefix(String namespaceURI)
+ {
+ return (current == null) ? null : current.lookupPrefix(namespaceURI);
+ }
+
+ public Iterator<String> getPrefixes(String namespaceURI)
+ {
+ // TODO
+ return Collections.singleton(getPrefix(namespaceURI)).iterator();
+ }
+
+ final QName getQName(String name)
+ {
+ String localName = name, uri = null, prefix = null;
+ int ci = name.indexOf(':');
+ if (ci != -1)
+ {
+ prefix = name.substring(0, ci);
+ localName = name.substring(ci + 1);
+ uri = getNamespaceURI(prefix);
+ }
+ return new QName(uri, localName, prefix);
+ }
+
+ // -- Template selection --
+
+ TemplateNode getTemplate(QName mode, Node context, boolean applyImports)
+ throws TransformerException
+ {
+ if (debug)
+ System.err.println("getTemplate: mode="+mode+" context="+context);
+ Template selected = null;
+ for (Template t : templates)
+ {
+ boolean isMatch = t.matches(mode, context);
+ if (applyImports)
+ {
+ if (currentTemplate == null)
+ {
+ String msg = "current template may not be null " +
+ "during apply-imports";
+ throw new TransformerException(msg);
+ }
+ if (!currentTemplate.imports(t))
+ isMatch = false;
+ }
+ //System.err.println("\t"+context+" "+t+"="+isMatch);
+ if (isMatch)
+ {
+ // Conflict resolution
+ // @see http://www.w3.org/TR/xslt#conflict
+ if (selected == null)
+ selected = t;
+ else
+ {
+ if (t.precedence < selected.precedence ||
+ t.priority < selected.priority)
+ continue;
+ selected = t;
+ }
+ }
+ }
+ if (selected == null)
+ {
+ // Apply built-in template
+ // Current template is unchanged
+ if (debug)
+ System.err.println("\tbuiltInTemplate context="+context);
+ switch (context.getNodeType())
+ {
+ case Node.ELEMENT_NODE:
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ case Node.COMMENT_NODE:
+ return builtInNodeTemplate;
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ case Node.ATTRIBUTE_NODE:
+ return builtInTextTemplate;
+ default:
+ return null;
+ }
+ }
+ // Set current template
+ currentTemplate = selected;
+ if (debug)
+ System.err.println("\ttemplate="+currentTemplate+" context="+context);
+ return currentTemplate.node;
+ }
+
+ TemplateNode getTemplate(QName mode, QName name)
+ throws TransformerException
+ {
+ Template selected = null;
+ for (Template t : templates)
+ {
+ boolean isMatch = t.matches(name);
+ if (isMatch)
+ {
+ // Conflict resolution
+ // @see http://www.w3.org/TR/xslt#conflict
+ if (selected == null)
+ selected = t;
+ else
+ {
+ if (t.precedence < selected.precedence ||
+ t.priority < selected.priority)
+ continue;
+ selected = t;
+ }
+ }
+ }
+ if (selected == null)
+ return null;
+ return selected.node;
+ }
+
+ /**
+ * template
+ */
+ final Template parseTemplate(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ String n = getAttribute(attrs, "name");
+ QName name = (n == null) ? null : getQName(n);
+ String m = getAttribute(attrs, "match");
+ Pattern match = null;
+ if (m != null)
+ {
+ try
+ {
+ match = (Pattern) xpath.compile(m);
+ }
+ catch (ClassCastException e)
+ {
+ String msg = "illegal pattern: " + m;
+ throw new TransformerConfigurationException(msg);
+ }
+ }
+ String p = getAttribute(attrs, "priority");
+ String mm = getAttribute(attrs, "mode");
+ QName mode = (mm == null) ? null : getQName(mm);
+ Node children = node.getFirstChild();
+ return new Template(this, name, match, parse(children),
+ precedence, p, mode);
+ }
+
+ /**
+ * output
+ */
+ final void parseOutput(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException
+ {
+ output = node;
+ String method = getAttribute(attrs, "method");
+ if ("xml".equals(method) || method == null)
+ outputMethod = OUTPUT_XML;
+ else if ("html".equals(method))
+ outputMethod = OUTPUT_HTML;
+ else if ("text".equals(method))
+ outputMethod = OUTPUT_TEXT;
+ else
+ {
+ String msg = "unsupported output method: " + method;
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ outputPublicId = getAttribute(attrs, "doctype-public");
+ outputSystemId = getAttribute(attrs, "doctype-system");
+ outputEncoding = getAttribute(attrs, "encoding");
+ String indent = getAttribute(attrs, "indent");
+ if (indent != null)
+ outputIndent = "yes".equals(indent);
+ outputVersion = getAttribute(attrs, "version");
+ String omitXmlDecl = getAttribute(attrs, "omit-xml-declaration");
+ if (omitXmlDecl != null)
+ outputOmitXmlDeclaration = "yes".equals(omitXmlDecl);
+ String standalone = getAttribute(attrs, "standalone");
+ if (standalone != null)
+ outputStandalone = "yes".equals(standalone);
+ outputMediaType = getAttribute(attrs, "media-type");
+ String cdataSectionElements =
+ getAttribute(attrs, "cdata-section-elements");
+ if (cdataSectionElements != null)
+ {
+ StringTokenizer st = new StringTokenizer(cdataSectionElements, " ");
+ while (st.hasMoreTokens())
+ outputCdataSectionElements.add(st.nextToken());
+ }
+ }
+
+ /**
+ * key
+ */
+ final void parseKey(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ String n = getRequiredAttribute(attrs, "name", node);
+ String m = getRequiredAttribute(attrs, "match", node);
+ String u = getRequiredAttribute(attrs, "use", node);
+ QName name = getQName(n);
+ Expr use = (Expr) xpath.compile(u);
+ try
+ {
+ Pattern match = (Pattern) xpath.compile(m);
+ Key key = new Key(name, match, use);
+ keys.add(key);
+ }
+ catch (ClassCastException e)
+ {
+ throw new TransformerConfigurationException("invalid pattern: " + m);
+ }
+ }
+
+ /**
+ * decimal-format
+ */
+ final void parseDecimalFormat(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException
+ {
+ String dfName = getAttribute(attrs, "name");
+ DecimalFormat df = new DecimalFormat();
+ DecimalFormatSymbols symbols = new DecimalFormatSymbols();
+ symbols.setDecimalSeparator(parseDFChar(attrs, "decimal-separator", '.'));
+ symbols.setGroupingSeparator(parseDFChar(attrs, "grouping-separator", ','));
+ symbols.setInfinity(parseDFString(attrs, "infinity", "Infinity"));
+ symbols.setMinusSign(parseDFChar(attrs, "minus-sign", '-'));
+ symbols.setNaN(parseDFString(attrs, "NaN", "NaN"));
+ symbols.setPercent(parseDFChar(attrs, "percent", '%'));
+ symbols.setPerMill(parseDFChar(attrs, "per-mille", '\u2030'));
+ symbols.setZeroDigit(parseDFChar(attrs, "zero-digit", '0'));
+ symbols.setDigit(parseDFChar(attrs, "digit", '#'));
+ symbols.setPatternSeparator(parseDFChar(attrs, "pattern-separator", ';'));
+ df.setDecimalFormatSymbols(symbols);
+ decimalFormats.put(dfName, df);
+ }
+
+ private final char parseDFChar(NamedNodeMap attrs, String name, char def)
+ throws TransformerConfigurationException
+ {
+ Node attr = attrs.getNamedItem(name);
+ try
+ {
+ return (attr == null) ? def : attr.getNodeValue().charAt(0);
+ }
+ catch (StringIndexOutOfBoundsException e)
+ {
+ throw new TransformerConfigurationException("empty attribute '" +
+ name +
+ "' in decimal-format", e);
+ }
+ }
+
+ private final String parseDFString(NamedNodeMap attrs, String name,
+ String def)
+ {
+ Node attr = attrs.getNamedItem(name);
+ return (attr == null) ? def : attr.getNodeValue();
+ }
+
+ /**
+ * namespace-alias
+ */
+ final void parseNamespaceAlias(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException
+ {
+ String sp = getRequiredAttribute(attrs, "stylesheet-prefix", node);
+ String rp = getRequiredAttribute(attrs, "result-prefix", node);
+ namespaceAliases.put(sp, rp);
+ }
+
+ /**
+ * attribute-set
+ */
+ final void parseAttributeSet(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ TemplateNode children = parse(node.getFirstChild());
+ String name = getRequiredAttribute(attrs, "name", node);
+ String uas = getAttribute(attrs, "use-attribute-sets");
+ attributeSets.add(new AttributeSet(children, name, uas));
+ }
+
+ /**
+ * Parse top-level elements.
+ */
+ void parse(Node node, boolean root)
+ throws TransformerConfigurationException
+ {
+ while (node != null)
+ {
+ current = node;
+ doParse(node, root);
+ node = node.getNextSibling();
+ }
+ }
+
+ void doParse(Node node, boolean root)
+ throws TransformerConfigurationException
+ {
+ try
+ {
+ String namespaceUri = node.getNamespaceURI();
+ if (XSL_NS.equals(namespaceUri) &&
+ node.getNodeType() == Node.ELEMENT_NODE)
+ {
+ String name = node.getLocalName();
+ NamedNodeMap attrs = node.getAttributes();
+ if ("stylesheet".equals(name))
+ {
+ version = getAttribute(attrs, "version");
+ String eep = getAttribute(attrs, "extension-element-prefixes");
+ if (eep != null)
+ {
+ StringTokenizer st = new StringTokenizer(eep);
+ while (st.hasMoreTokens())
+ {
+ extensionElementPrefixes.add(st.nextToken());
+ }
+ }
+ String erp = getAttribute(attrs, "exclude-result-prefixes");
+ if (erp != null)
+ {
+ StringTokenizer st = new StringTokenizer(erp);
+ while (st.hasMoreTokens())
+ {
+ excludeResultPrefixes.add(st.nextToken());
+ }
+ }
+ parse(node.getFirstChild(), false);
+ }
+ else if ("template".equals(name))
+ templates.add(parseTemplate(node, attrs));
+ else if ("param".equals(name) ||
+ "variable".equals(name))
+ {
+ int type = "variable".equals(name) ?
+ Bindings.VARIABLE : Bindings.PARAM;
+ TemplateNode content = parse(node.getFirstChild());
+ QName paramName =
+ getQName(getRequiredAttribute(attrs, "name", node));
+ String select = getAttribute(attrs, "select");
+ ParameterNode param;
+ if (select != null && select.length() > 0)
+ {
+ if (content != null)
+ {
+ String msg = "parameter '" + paramName +
+ "' has both select and content";
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ Expr expr = (Expr) xpath.compile(select);
+ param = new ParameterNode(paramName, expr, type);
+ }
+ else
+ {
+ param = new ParameterNode(paramName, null, type);
+ param.children = content;
+ }
+ variables.add(param);
+ }
+ else if ("include".equals(name) || "import".equals(name))
+ {
+ int delta = "import".equals(name) ? -1 : 0;
+ String href = getRequiredAttribute(attrs, "href", node);
+ Source source;
+ synchronized (factory.resolver)
+ {
+ if (transformer != null)
+ {
+ factory.resolver
+ .setUserResolver(transformer.getURIResolver());
+ factory.resolver
+ .setUserListener(transformer.getErrorListener());
+ }
+ source = factory.resolver.resolve(systemId, href);
+ }
+ factory.newStylesheet(source, precedence + delta, this);
+ }
+ else if ("output".equals(name))
+ parseOutput(node, attrs);
+ else if ("preserve-space".equals(name))
+ {
+ String elements =
+ getRequiredAttribute(attrs, "elements", node);
+ StringTokenizer st = new StringTokenizer(elements,
+ " \t\n\r");
+ while (st.hasMoreTokens())
+ {
+ NameTest element = parseNameTest(st.nextToken());
+ preserveSpace.add(new StrippingInstruction(element,
+ precedence));
+ }
+ }
+ else if ("strip-space".equals(name))
+ {
+ String elements =
+ getRequiredAttribute(attrs, "elements", node);
+ StringTokenizer st = new StringTokenizer(elements,
+ " \t\n\r");
+ while (st.hasMoreTokens())
+ {
+ NameTest element = parseNameTest(st.nextToken());
+ stripSpace.add(new StrippingInstruction(element,
+ precedence));
+ }
+ }
+ else if ("key".equals(name))
+ parseKey(node, attrs);
+ else if ("decimal-format".equals(name))
+ parseDecimalFormat(node, attrs);
+ else if ("namespace-alias".equals(name))
+ parseNamespaceAlias(node, attrs);
+ else if ("attribute-set".equals(name))
+ parseAttributeSet(node, attrs);
+ }
+ else if (root)
+ {
+ // Literal document element
+ Attr versionNode =
+ ((Element)node).getAttributeNodeNS(XSL_NS, "version");
+ if (versionNode == null)
+ {
+ String msg = "no xsl:version attribute on literal result node";
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ version = versionNode.getValue();
+ Node rootClone = node.cloneNode(true);
+ NamedNodeMap attrs = rootClone.getAttributes();
+ attrs.removeNamedItemNS(XSL_NS, "version");
+ templates.add(new Template(this, null, new Root(),
+ parse(rootClone),
+ precedence,
+ null,
+ null));
+ }
+ else
+ {
+ // Skip unknown elements, text, comments, etc
+ }
+ }
+ catch (TransformerException e)
+ {
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(e.getMessage(), l, e);
+ }
+ catch (DOMException e)
+ {
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(e.getMessage(), l, e);
+ }
+ catch (XPathExpressionException e)
+ {
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(e.getMessage(), l, e);
+ }
+ }
+
+ final NameTest parseNameTest(String token)
+ {
+ if ("*".equals(token))
+ return new NameTest(null, true, true);
+ else if (token.endsWith(":*"))
+ {
+ QName qName = getQName(token);
+ return new NameTest(qName, true, false);
+ }
+ else
+ {
+ QName qName = getQName(token);
+ return new NameTest(qName, false, false);
+ }
+ }
+
+ final TemplateNode parseAttributeValueTemplate(String value, Node source)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ current = source;
+ // Tokenize
+ int len = value.length();
+ int off = 0;
+ List<String> tokens = new ArrayList<String>(); // text tokens
+ List<Boolean> types = new ArrayList<Boolean>(); // literal or expression
+ int depth = 0;
+ for (int i = 0; i < len; i++)
+ {
+ char c = value.charAt(i);
+ if (c == '{')
+ {
+ if (i < (len - 1) && value.charAt(i + 1) == '{')
+ {
+ tokens.add(value.substring(off, i + 1));
+ types.add(Boolean.FALSE);
+ i++;
+ off = i + 1;
+ continue;
+ }
+ if (depth == 0)
+ {
+ if (i - off > 0)
+ {
+ tokens.add(value.substring(off, i));
+ types.add(Boolean.FALSE);
+ }
+ off = i + 1;
+ }
+ depth++;
+ }
+ else if (c == '}')
+ {
+ if (i < (len - 1) && value.charAt(i + 1) == '}')
+ {
+ tokens.add(value.substring(off, i + 1));
+ types.add(Boolean.FALSE);
+ i++;
+ off = i + 1;
+ continue;
+ }
+ if (depth == 1)
+ {
+ if (i - off > 0)
+ {
+ tokens.add(value.substring(off, i));
+ types.add(Boolean.TRUE);
+ }
+ else
+ {
+ String msg = "attribute value template " +
+ "must contain expression: " + value;
+ DOMSourceLocator l = new DOMSourceLocator(source);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ off = i + 1;
+ }
+ depth--;
+ }
+ }
+ if (depth > 0)
+ {
+ String msg = "invalid attribute value template: " + value;
+ throw new TransformerConfigurationException(msg);
+ }
+ if (len - off > 0)
+ {
+ // Trailing text
+ tokens.add(value.substring(off));
+ types.add(Boolean.FALSE);
+ }
+
+ // Construct template node tree
+ TemplateNode ret = null;
+ Document doc = source.getOwnerDocument();
+ len = tokens.size();
+ for (int i = len - 1; i >= 0; i--)
+ {
+ String token = tokens.get(i);
+ Boolean type = types.get(i);
+ if (type == Boolean.TRUE)
+ {
+ // Expression text
+ Expr select = (Expr) xpath.compile(token);
+ TemplateNode ret2 = new ValueOfNode(select, false);
+ ret2.next = ret;
+ ret = ret2;
+ }
+ else
+ {
+ // Verbatim text
+ TemplateNode ret2 = new LiteralNode(doc.createTextNode(token));
+ ret2.next = ret;
+ ret = ret2;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Whitespace stripping.
+ * @param text the text node
+ * @param source true if a source node, false if a stylesheet text node
+ * @see http://www.w3.org/TR/xslt#strip
+ */
+ boolean isPreserved(Text text, boolean source)
+ throws TransformerConfigurationException
+ {
+ // Check characters in text
+ String value = text.getData();
+ if (value != null)
+ {
+ int len = value.length();
+ for (int i = 0; i < len; i++)
+ {
+ char c = value.charAt(i);
+ if (c != 0x20 && c != 0x09 && c != 0x0a && c != 0x0d)
+ return true;
+ }
+ }
+ // Check parent node
+ Node ctx = text.getParentNode();
+ if (source)
+ {
+ // Source document text node
+ boolean preserve = true;
+ float psPriority = 0.0f, ssPriority = 0.0f;
+ if (!stripSpace.isEmpty())
+ {
+ // Conflict resolution
+ StrippingInstruction ssi = null, psi = null;
+ for (StrippingInstruction si : stripSpace)
+ {
+ if (si.element.matches(ctx, 1, 1))
+ {
+ if (ssi != null)
+ {
+ if (si.precedence < ssi.precedence)
+ continue;
+ float p = si.getPriority();
+ if (p < ssPriority)
+ continue;
+ }
+ ssi = si;
+ }
+ }
+ for (StrippingInstruction si : preserveSpace)
+ {
+ if (si.element.matches(ctx, 1, 1))
+ {
+ if (psi != null)
+ {
+ if (si.precedence < psi.precedence)
+ continue;
+ float p = si.getPriority();
+ if (p < psPriority)
+ continue;
+ }
+ psi = si;
+ }
+ }
+ if (ssi != null)
+ {
+ if (psi != null)
+ {
+ if (psi.precedence < ssi.precedence)
+ preserve = false;
+ else if (psPriority < ssPriority)
+ preserve = false;
+ }
+ else
+ preserve = false;
+ }
+ }
+ if (preserve)
+ return true;
+ }
+ else
+ {
+ // Stylesheet text node
+ if (STYLESHEET_PRESERVE_TEXT.matches(ctx, 1, 1))
+ return true;
+ }
+ // Check whether any ancestor specified xml:space
+ while (ctx != null)
+ {
+ if (ctx.getNodeType() == Node.ELEMENT_NODE)
+ {
+ Element element = (Element) ctx;
+ String xmlSpace = element.getAttribute("xml:space");
+ if ("default".equals(xmlSpace))
+ break;
+ else if ("preserve".equals(xmlSpace))
+ return true;
+ else if (xmlSpace.length() > 0)
+ {
+ String msg = "Illegal value for xml:space: " + xmlSpace;
+ throw new TransformerConfigurationException(msg);
+ }
+ }
+ ctx = ctx.getParentNode();
+ }
+ return false;
+ }
+
+ public XPathFunction resolveFunction(QName name, int arity)
+ {
+ String uri = name.getNamespaceURI();
+ if (XSL_NS.equals(uri) || uri == null || uri.length() == 0)
+ {
+ String localName = name.getLocalPart();
+ if ("document".equals(localName) && (arity == 1 || arity == 2))
+ {
+ if (current == null)
+ throw new RuntimeException("current is null");
+ return new DocumentFunction(getRootStylesheet(), current);
+ }
+ else if ("key".equals(localName) && (arity == 2))
+ return new KeyFunction(getRootStylesheet());
+ else if ("format-number".equals(localName) &&
+ (arity == 2 || arity == 3))
+ return new FormatNumberFunction(getRootStylesheet());
+ else if ("current".equals(localName) && (arity == 0))
+ return new CurrentFunction(getRootStylesheet());
+ else if ("unparsed-entity-uri".equals(localName) && (arity == 1))
+ return new UnparsedEntityUriFunction();
+ else if ("generate-id".equals(localName) &&
+ (arity == 1 || arity == 0))
+ return new GenerateIdFunction();
+ else if ("system-property".equals(localName) && (arity == 1))
+ return new SystemPropertyFunction();
+ else if ("element-available".equals(localName) && (arity == 1))
+ return new ElementAvailableFunction(new NamespaceProxy(current));
+ else if ("function-available".equals(localName) && (arity == 1))
+ return new FunctionAvailableFunction(new NamespaceProxy(current));
+ }
+ return null;
+ }
+
+ // -- Parsing --
+
+ /**
+ * apply-templates
+ */
+ final TemplateNode parseApplyTemplates(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String m = getAttribute(attrs, "mode");
+ QName mode = (m == null) ? null : getQName(m);
+ String s = getAttribute(attrs, "select");
+ if (s == null)
+ s = "child::node()";
+ Node children = node.getFirstChild();
+ List<SortKey> sortKeys = parseSortKeys(children);
+ List<WithParam> withParams = parseWithParams(children);
+ Expr select = (Expr) xpath.compile(s);
+ return new ApplyTemplatesNode(select, mode,
+ sortKeys, withParams, false);
+ }
+
+ /**
+ * call-template
+ */
+ final TemplateNode parseCallTemplate(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String n = getRequiredAttribute(attrs, "name", node);
+ QName name = getQName(n);
+ Node children = node.getFirstChild();
+ List<WithParam> withParams = parseWithParams(children);
+ return new CallTemplateNode(name, withParams);
+ }
+
+ /**
+ * value-of
+ */
+ final TemplateNode parseValueOf(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String s = getRequiredAttribute(attrs, "select", node);
+ String doe = getAttribute(attrs, "disable-output-escaping");
+ boolean d = "yes".equals(doe);
+ Expr select = (Expr) xpath.compile(s);
+ return new ValueOfNode(select, d);
+ }
+
+ /**
+ * for-each
+ */
+ final TemplateNode parseForEach(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String s = getRequiredAttribute(attrs, "select", node);
+ Node children = node.getFirstChild();
+ List<SortKey> sortKeys = parseSortKeys(children);
+ Expr select = (Expr) xpath.compile(s);
+ ForEachNode ret = new ForEachNode(select, sortKeys);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * if
+ */
+ final TemplateNode parseIf(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String t = getRequiredAttribute(attrs, "test", node);
+ Expr test = (Expr) xpath.compile(t);
+ Node children = node.getFirstChild();
+ IfNode ret = new IfNode(test);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * when
+ */
+ final TemplateNode parseWhen(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String t = getRequiredAttribute(attrs, "test", node);
+ Expr test = (Expr) xpath.compile(t);
+ Node children = node.getFirstChild();
+ WhenNode ret = new WhenNode(test);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * element
+ */
+ final TemplateNode parseElement(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String name = getRequiredAttribute(attrs, "name", node);
+ String namespace = getAttribute(attrs, "namespace");
+ String uas = getAttribute(attrs, "use-attribute-sets");
+ TemplateNode n = parseAttributeValueTemplate(name, node);
+ TemplateNode ns = (namespace == null) ? null :
+ parseAttributeValueTemplate(namespace, node);
+ Node children = node.getFirstChild();
+ ElementNode ret = new ElementNode(n, ns, uas, node);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * attribute
+ */
+ final TemplateNode parseAttribute(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String name = getRequiredAttribute(attrs, "name", node);
+ String namespace = getAttribute(attrs, "namespace");
+ TemplateNode n = parseAttributeValueTemplate(name, node);
+ TemplateNode ns = (namespace == null) ? null :
+ parseAttributeValueTemplate(namespace, node);
+ Node children = node.getFirstChild();
+ AttributeNode ret = new AttributeNode(n, ns, node);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * text
+ */
+ final TemplateNode parseText(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String doe = getAttribute(attrs, "disable-output-escaping");
+ boolean d = "yes".equals(doe);
+ Node children = node.getFirstChild();
+ TextNode ret = new TextNode(d);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * copy
+ */
+ final TemplateNode parseCopy(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String uas = getAttribute(attrs, "use-attribute-sets");
+ Node children = node.getFirstChild();
+ CopyNode ret = new CopyNode(uas);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * processing-instruction
+ */
+ final TemplateNode parseProcessingInstruction(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String name = getRequiredAttribute(attrs, "name", node);
+ Node children = node.getFirstChild();
+ ProcessingInstructionNode ret = new ProcessingInstructionNode(name);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * number
+ */
+ final TemplateNode parseNumber(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String v = getAttribute(attrs, "value");
+ String ff = getAttribute(attrs, "format");
+ if (ff == null)
+ {
+ ff = "1";
+ }
+ TemplateNode format = parseAttributeValueTemplate(ff, node);
+ String lang = getAttribute(attrs, "lang");
+ String lv = getAttribute(attrs, "letter-value");
+ int letterValue = "traditional".equals(lv) ?
+ AbstractNumberNode.TRADITIONAL :
+ AbstractNumberNode.ALPHABETIC;
+ String gs = getAttribute(attrs, "grouping-separator");
+ String gz = getAttribute(attrs, "grouping-size");
+ int gz2 = (gz != null && gz.length() > 0) ?
+ Integer.parseInt(gz) : 1;
+ Node children = node.getFirstChild();
+ TemplateNode ret;
+ if (v != null && v.length() > 0)
+ {
+ Expr value = (Expr) xpath.compile(v);
+ ret = new NumberNode(value, format, lang,
+ letterValue, gs, gz2);
+ }
+ else
+ {
+ String l = getAttribute(attrs, "level");
+ int level =
+ "multiple".equals(l) ? NodeNumberNode.MULTIPLE :
+ "any".equals(l) ? NodeNumberNode.ANY :
+ NodeNumberNode.SINGLE;
+ String c = getAttribute(attrs, "count");
+ String f = getAttribute(attrs, "from");
+ Pattern count = null;
+ Pattern from = null;
+ if (c != null)
+ {
+ try
+ {
+ count = (Pattern) xpath.compile(c);
+ }
+ catch (ClassCastException e)
+ {
+ String msg = "invalid pattern: " + c;
+ throw new TransformerConfigurationException(msg);
+ }
+ }
+ if (f != null)
+ {
+ try
+ {
+ from = (Pattern) xpath.compile(f);
+ }
+ catch (ClassCastException e)
+ {
+ String msg = "invalid pattern: " + f;
+ throw new TransformerConfigurationException(msg);
+ }
+ }
+ ret = new NodeNumberNode(level, count, from,
+ format, lang,
+ letterValue, gs, gz2);
+ }
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * copy-of
+ */
+ final TemplateNode parseCopyOf(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String s = getRequiredAttribute(attrs, "select", node);
+ Expr select = (Expr) xpath.compile(s);
+ Node children = node.getFirstChild();
+ CopyOfNode ret = new CopyOfNode(select);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * message
+ */
+ final TemplateNode parseMessage(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String t = getAttribute(attrs, "terminate");
+ boolean terminate = "yes".equals(t);
+ Node children = node.getFirstChild();
+ MessageNode ret = new MessageNode(terminate);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ /**
+ * Parse template-level elements.
+ */
+ final TemplateNode parse(Node node)
+ throws TransformerConfigurationException
+ {
+ TemplateNode first = null;
+ TemplateNode previous = null;
+ while (node != null)
+ {
+ Node next = node.getNextSibling();
+ TemplateNode tnode = doParse(node);
+ if (tnode != null)
+ {
+ if (first == null)
+ first = tnode;
+ if (previous != null)
+ previous.next = tnode;
+ previous = tnode;
+ }
+ node = next;
+ }
+ return first;
+ }
+
+ private final TemplateNode doParse(Node node)
+ throws TransformerConfigurationException
+ {
+ // Hack to associate the document function with its declaring node
+ current = node;
+ try
+ {
+ String namespaceUri = node.getNamespaceURI();
+ if (Stylesheet.XSL_NS.equals(namespaceUri) &&
+ Node.ELEMENT_NODE == node.getNodeType())
+ {
+ String name = node.getLocalName();
+ if ("apply-templates".equals(name))
+ return parseApplyTemplates(node);
+ else if ("call-template".equals(name))
+ return parseCallTemplate(node);
+ else if ("value-of".equals(name))
+ return parseValueOf(node);
+ else if ("for-each".equals(name))
+ return parseForEach(node);
+ else if ("if".equals(name))
+ return parseIf(node);
+ else if ("choose".equals(name))
+ {
+ Node children = node.getFirstChild();
+ ChooseNode ret = new ChooseNode();
+ ret.children = parse(children);
+ return ret;
+ }
+ else if ("when".equals(name))
+ return parseWhen(node);
+ else if ("otherwise".equals(name))
+ {
+ Node children = node.getFirstChild();
+ OtherwiseNode ret = new OtherwiseNode();
+ ret.children = parse(children);
+ return ret;
+ }
+ else if ("element".equals(name))
+ return parseElement(node);
+ else if ("attribute".equals(name))
+ return parseAttribute(node);
+ else if ("text".equals(name))
+ return parseText(node);
+ else if ("copy".equals(name))
+ return parseCopy(node);
+ else if ("processing-instruction".equals(name))
+ return parseProcessingInstruction(node);
+ else if ("comment".equals(name))
+ {
+ Node children = node.getFirstChild();
+ CommentNode ret = new CommentNode();
+ ret.children = parse(children);
+ return ret;
+ }
+ else if ("number".equals(name))
+ return parseNumber(node);
+ else if ("param".equals(name) ||
+ "variable".equals(name))
+ {
+ int type = "variable".equals(name) ?
+ Bindings.VARIABLE : Bindings.PARAM;
+ NamedNodeMap attrs = node.getAttributes();
+ Node children = node.getFirstChild();
+ TemplateNode content = parse(children);
+ QName paramName =
+ getQName(getRequiredAttribute(attrs, "name", node));
+ String select = getAttribute(attrs, "select");
+ ParameterNode ret;
+ if (select != null)
+ {
+ if (content != null)
+ {
+ String msg = "parameter '" + paramName +
+ "' has both select and content";
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ Expr expr = (Expr) xpath.compile(select);
+ ret = new ParameterNode(paramName, expr, type);
+ }
+ else
+ {
+ ret = new ParameterNode(paramName, null, type);
+ ret.children = content;
+ }
+ return ret;
+ }
+ else if ("copy-of".equals(name))
+ return parseCopyOf(node);
+ else if ("message".equals(name))
+ return parseMessage(node);
+ else if ("apply-imports".equals(name))
+ {
+ Node children = node.getFirstChild();
+ ApplyImportsNode ret = new ApplyImportsNode();
+ ret.children = parse(children);
+ return ret;
+ }
+ else
+ {
+ // xsl:fallback
+ // Pass over any other XSLT nodes
+ return null;
+ }
+ }
+ String prefix = node.getPrefix();
+ if (extensionElementPrefixes.contains(prefix))
+ {
+ // Check for xsl:fallback
+ for (Node ctx = node.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ String ctxUri = ctx.getNamespaceURI();
+ if (XSL_NS.equals(ctxUri) &&
+ "fallback".equals(ctx.getLocalName()))
+ {
+ ctx = ctx.getFirstChild();
+ return (ctx == null) ? null : parse(ctx);
+ }
+ }
+ // Otherwise pass over extension element
+ return null;
+ }
+ switch (node.getNodeType())
+ {
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ // Determine whether to strip whitespace
+ Text text = (Text) node;
+ if (!isPreserved(text, false))
+ {
+ // Strip
+ text.getParentNode().removeChild(text);
+ return null;
+ }
+ break;
+ case Node.COMMENT_NODE:
+ // Ignore comments
+ return null;
+ case Node.ELEMENT_NODE:
+ // Check for attribute value templates and use-attribute-sets
+ NamedNodeMap attrs = node.getAttributes();
+ boolean convert = false;
+ String useAttributeSets = null;
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String value = attr.getNodeValue();
+ if (Stylesheet.XSL_NS.equals(attr.getNamespaceURI()) &&
+ "use-attribute-sets".equals(attr.getLocalName()))
+ {
+ useAttributeSets = value;
+ convert = true;
+ break;
+ }
+ int start = value.indexOf('{');
+ int end = value.indexOf('}');
+ if (start != -1 || end != -1)
+ {
+ convert = true;
+ break;
+ }
+ }
+ if (convert)
+ {
+ // Create an element-producing template node instead
+ // with appropriate attribute-producing child template nodes
+ Node children = node.getFirstChild();
+ TemplateNode child = parse(children);
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String ans = attr.getNamespaceURI();
+ String aname = attr.getNodeName();
+ if (Stylesheet.XSL_NS.equals(ans) &&
+ "use-attribute-sets".equals(attr.getLocalName()))
+ continue;
+ String value = attr.getNodeValue();
+ TemplateNode grandchild =
+ parseAttributeValueTemplate(value, node);
+ TemplateNode n =
+ parseAttributeValueTemplate(aname, node);
+ TemplateNode ns = (ans == null) ? null :
+ parseAttributeValueTemplate(ans, node);
+ TemplateNode newChild = new AttributeNode(n, ns, attr);
+ newChild.children = grandchild;
+ newChild.next = child;
+ child = newChild;
+ }
+ String ename = node.getNodeName();
+ TemplateNode n = parseAttributeValueTemplate(ename, node);
+ //TemplateNode ns = (namespaceUri == null) ? null :
+ // parseAttributeValueTemplate(namespaceUri, node);
+ TemplateNode ns = null;
+ ElementNode ret = new ElementNode(n, ns, useAttributeSets,
+ node);
+ ret.children = child;
+ return ret;
+ }
+ // Otherwise fall through
+ break;
+ }
+ }
+ catch (XPathExpressionException e)
+ {
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(e.getMessage(), l, e);
+ }
+ Node children = node.getFirstChild();
+ LiteralNode ret = new LiteralNode(node);
+ ret.children = parse(children);
+ return ret;
+ }
+
+ final List<SortKey> parseSortKeys(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ List<SortKey> ret = new LinkedList<SortKey>();
+ while (node != null)
+ {
+ String namespaceUri = node.getNamespaceURI();
+ if (Stylesheet.XSL_NS.equals(namespaceUri) &&
+ Node.ELEMENT_NODE == node.getNodeType() &&
+ "sort".equals(node.getLocalName()))
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String s = getAttribute(attrs, "select");
+ if (s == null)
+ s = ".";
+ Expr select = (Expr) xpath.compile(s);
+ String l = getAttribute(attrs, "lang");
+ TemplateNode lang = (l == null) ? null :
+ parseAttributeValueTemplate(l, node);
+ String dt = getAttribute(attrs, "data-type");
+ TemplateNode dataType = (dt == null) ? null :
+ parseAttributeValueTemplate(dt, node);
+ String o = getAttribute(attrs, "order");
+ TemplateNode order = (o == null) ? null :
+ parseAttributeValueTemplate(o, node);
+ String co = getAttribute(attrs, "case-order");
+ TemplateNode caseOrder = (co == null) ? null :
+ parseAttributeValueTemplate(co, node);
+ ret.add(new SortKey(select, lang, dataType, order, caseOrder));
+ }
+ node = node.getNextSibling();
+ }
+ return ret;
+ }
+
+ final List<WithParam> parseWithParams(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ List<WithParam> ret = new LinkedList<WithParam>();
+ while (node != null)
+ {
+ String namespaceUri = node.getNamespaceURI();
+ if (Stylesheet.XSL_NS.equals(namespaceUri) &&
+ Node.ELEMENT_NODE == node.getNodeType() &&
+ "with-param".equals(node.getLocalName()))
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ TemplateNode content = parse(node.getFirstChild());
+ QName name =
+ getQName(getRequiredAttribute(attrs, "name", node));
+ String select = getAttribute(attrs, "select");
+ if (select != null)
+ {
+ if (content != null)
+ {
+ String msg = "parameter '" + name +
+ "' has both select and content";
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ Expr expr = (Expr) xpath.compile(select);
+ ret.add(new WithParam(name, expr));
+ }
+ else
+ ret.add(new WithParam(name, content));
+ }
+ node = node.getNextSibling();
+ }
+ return ret;
+ }
+
+ /**
+ * Created element nodes have a copy of the namespace nodes in the
+ * stylesheet, except the XSLT namespace, extension namespaces, and
+ * exclude-result-prefixes.
+ */
+ final void addNamespaceNodes(Node source, Node target, Document doc,
+ Collection<String> elementExcludeResultPrefixes)
+ {
+ NamedNodeMap attrs = source.getAttributes();
+ if (attrs != null)
+ {
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String uri = attr.getNamespaceURI();
+ if (uri == XMLConstants.XMLNS_ATTRIBUTE_NS_URI)
+ {
+ String prefix = attr.getLocalName();
+ if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix))
+ prefix = "#default";
+ String ns = attr.getNodeValue();
+ // Should the namespace be excluded?
+ if (XSL_NS.equals(ns) ||
+ extensionElementPrefixes.contains(prefix) ||
+ elementExcludeResultPrefixes.contains(prefix) ||
+ excludeResultPrefixes.contains(prefix))
+ continue;
+ // Is the namespace already defined on the target?
+ if (prefix == "#default")
+ prefix = null;
+ if (target.lookupNamespaceURI(prefix) != null)
+ continue;
+ attr = attr.cloneNode(true);
+ attr = doc.adoptNode(attr);
+ target.getAttributes().setNamedItemNS(attr);
+ }
+ }
+ }
+ Node parent = source.getParentNode();
+ if (parent != null)
+ addNamespaceNodes(parent, target, doc, elementExcludeResultPrefixes);
+ }
+
+ static final String getAttribute(NamedNodeMap attrs, String name)
+ {
+ Node attr = attrs.getNamedItem(name);
+ if (attr == null)
+ return null;
+ String ret = attr.getNodeValue();
+ if (ret.length() == 0)
+ return null;
+ return ret;
+ }
+
+ static final String getRequiredAttribute(NamedNodeMap attrs, String name,
+ Node source)
+ throws TransformerConfigurationException
+ {
+ String value = getAttribute(attrs, name);
+ if (value == null || value.length() == 0)
+ {
+ String msg =
+ name + " attribute is required on " + source.getNodeName();
+ DOMSourceLocator l = new DOMSourceLocator(source);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ return value;
+ }
+
+ // Handle user data changes when nodes are cloned etc
+
+ public void handle(short op, String key, Object data, Node src, Node dst)
+ {
+ dst.setUserData(key, data, this);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder b = new CPStringBuilder(getClass().getName());
+ b.append("[templates=");
+ b.append(templates);
+ b.append("]");
+ return b.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/SystemPropertyFunction.java b/libjava/classpath/gnu/xml/transform/SystemPropertyFunction.java
new file mode 100644
index 000000000..f314227e6
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/SystemPropertyFunction.java
@@ -0,0 +1,140 @@
+/* SystemPropertyFunction.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>system-property()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class SystemPropertyFunction
+ extends Expr
+ implements XPathFunction, Function
+{
+
+ List args;
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ String name = (String) args.get(0);
+ return systemProperty(QName.valueOf(name));
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ int arity = args.size();
+ List values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ String name = _string(context, values.get(0));
+ return systemProperty(QName.valueOf(name));
+ }
+
+ Object systemProperty(QName name)
+ {
+ String localName = name.getLocalPart();
+ String prefix = name.getPrefix();
+ String uri = name.getNamespaceURI();
+ if (Stylesheet.XSL_NS.equals(uri) ||
+ "xsl".equals(prefix))
+ {
+ if ("version".equals(localName))
+ {
+ return new Double(1.0d);
+ }
+ else if ("vendor".equals(localName))
+ {
+ return "The Free Software Foundation";
+ }
+ else if ("vendor-url".equals(localName))
+ {
+ return "http://www.gnu.org/";
+ }
+ else
+ {
+ return "";
+ }
+ }
+ return System.getProperty(localName);
+ }
+
+ public Expr clone(Object context)
+ {
+ SystemPropertyFunction f = new SystemPropertyFunction();
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+ public boolean references(QName var)
+ {
+ for (Iterator i = args.iterator(); i.hasNext(); )
+ {
+ if (((Expr) i.next()).references(var))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/Template.java b/libjava/classpath/gnu/xml/transform/Template.java
new file mode 100644
index 000000000..09d1a997c
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/Template.java
@@ -0,0 +1,263 @@
+/* Template.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.PrintStream;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.NameTest;
+import gnu.xml.xpath.NodeTypeTest;
+import gnu.xml.xpath.Pattern;
+import gnu.xml.xpath.Selector;
+import gnu.xml.xpath.Test;
+
+/**
+ * A template in an XSL stylesheet.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class Template
+ implements Comparable
+{
+
+ static final double DEFAULT_PRIORITY = 0.5d;
+
+ final Stylesheet stylesheet;
+ final QName name;
+ final Pattern match;
+ final TemplateNode node;
+ final double priority;
+ final int precedence;
+ final QName mode;
+ final boolean isAnyNode; // is the match simply "node()"?
+
+ Template(Stylesheet stylesheet,
+ QName name, Pattern match, TemplateNode node,
+ int precedence, String priorityAttr, QName mode)
+ {
+ this.stylesheet = stylesheet;
+ this.name = name;
+ this.match = match;
+ this.node = node;
+ this.precedence = precedence;
+ this.mode = mode;
+
+ double p = DEFAULT_PRIORITY;
+ boolean a = false;
+ if (priorityAttr != null)
+ p = Double.parseDouble(priorityAttr);
+ else
+ {
+ // adjust priority if necessary
+ // see XSLT section 5.5
+ if (match instanceof Selector)
+ {
+ Selector selector = (Selector) match;
+ Test[] tests = selector.getTests();
+ if (tests.length > 0)
+ {
+ Test test = tests[0];
+ if (test instanceof NameTest)
+ {
+ NameTest nameTest = (NameTest) test;
+ if (nameTest.matchesAny())
+ p = -0.25d;
+ else if (nameTest.matchesAnyLocalName())
+ p = -0.20d;
+ else
+ p = 0.0d;
+ }
+ else
+ {
+ NodeTypeTest nodeTypeTest = (NodeTypeTest) test;
+ if (nodeTypeTest.getNodeType() ==
+ Node.PROCESSING_INSTRUCTION_NODE &&
+ nodeTypeTest.getData() != null)
+ p = 0.0d;
+ else
+ p = -0.5d;
+ a = (nodeTypeTest.getNodeType() == 0);
+ }
+ // Add a small difference for predicates
+ if (tests.length > 1)
+ p += ((double) tests.length - 1) * 0.001;
+ }
+ }
+ }
+ this.priority = p;
+ this.isAnyNode = a;
+ }
+
+ private Template(Stylesheet stylesheet,
+ QName name, Pattern match, TemplateNode node,
+ int precedence, double priority, QName mode, boolean isAnyNode)
+ {
+ this.stylesheet = stylesheet;
+ this.name = name;
+ this.match = match;
+ this.node = node;
+ this.precedence = precedence;
+ this.priority = priority;
+ this.mode = mode;
+ this.isAnyNode = isAnyNode;
+ }
+
+ Template clone(Stylesheet stylesheet)
+ {
+ // FIXME by cloning we lose the imports() functionality, so
+ // apply-imports will be broken.
+ return new Template(stylesheet,
+ name,
+ (match == null) ? null :
+ (Pattern) match.clone(stylesheet),
+ (node == null) ? null : node.clone(stylesheet),
+ precedence,
+ priority,
+ mode,
+ isAnyNode);
+ }
+
+ public int compareTo(Object other)
+ {
+ if (other instanceof Template)
+ {
+ Template t = (Template) other;
+ int d = t.precedence - precedence;
+ if (d != 0)
+ return d;
+ double d2 = t.priority - priority;
+ if (d2 != 0.0d)
+ return (int) Math.round(d2 * 1000.0d);
+ }
+ return 0;
+ }
+
+ Test getNodeTest(Expr expr)
+ {
+ return null;
+ }
+
+ boolean matches(QName mode, Node node)
+ {
+ if ((mode == null && this.mode != null) ||
+ (mode != null && !mode.equals(this.mode)))
+ return false;
+ if (match == null)
+ return false;
+ if (isAnyNode && node.getNodeType() == Node.DOCUMENT_NODE)
+ return false; // don't match document node
+ return match.matches(node);
+ }
+
+ boolean matches(QName name)
+ {
+ return name.equals(this.name);
+ }
+
+ boolean imports(Template other)
+ {
+ for (Stylesheet ctx = other.stylesheet.parent;
+ ctx != null;
+ ctx = ctx.parent)
+ {
+ if (ctx == stylesheet)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param stylesheet the stylesheet
+ * @param parent the parent of result nodes
+ * @param context the context node in the source document
+ * @param pos the context position
+ * @param len the context size
+ * @param nextSibling if non-null, add result nodes before this node
+ */
+ void apply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (stylesheet.debug)
+ System.err.println("...applying " + toString() + " to " + context);
+ if (node != null)
+ node.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder(getClass().getName());
+ buf.append('[');
+ if (name != null)
+ {
+ buf.append("name=");
+ buf.append(name);
+ }
+ else if (match != null)
+ {
+ buf.append("match=");
+ buf.append(match);
+ }
+ if (mode != null)
+ {
+ buf.append(",mode=");
+ buf.append(mode);
+ }
+ buf.append(",node=");
+ buf.append(node);
+ buf.append(']');
+ return buf.toString();
+
+ //return (name != null) ? name.toString() : match.toString();
+ }
+
+ void list(PrintStream out)
+ {
+ out.println(toString());
+ if (node != null)
+ node.list(1, out, true);
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/TemplateNode.java b/libjava/classpath/gnu/xml/transform/TemplateNode.java
new file mode 100644
index 000000000..d4c223e01
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/TemplateNode.java
@@ -0,0 +1,130 @@
+/* TemplateNode.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.io.PrintStream;
+import java.util.Comparator;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.DocumentOrderComparator;
+
+/**
+ * Wrapper for a source node in a template.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+abstract class TemplateNode
+{
+
+ static final Comparator<Node> documentOrderComparator =
+ new DocumentOrderComparator();
+
+ TemplateNode children;
+ TemplateNode next;
+
+ final void apply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (stylesheet.terminated)
+ return;
+ if (Thread.currentThread().isInterrupted())
+ {
+ // Try to head off any infinite loops at the pass
+ return;
+ }
+ if (stylesheet.debug)
+ {
+ System.err.println("Applying " + toString());
+ System.err.println("\twith context=" + context + ", pos=" + pos +
+ ", len=" + len);
+ }
+ doApply(stylesheet, mode, context, pos, len, parent, nextSibling);
+ }
+
+ abstract void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException;
+
+ abstract TemplateNode clone(Stylesheet stylesheet);
+
+ public boolean references(QName var)
+ {
+ if (children != null && children.references(var))
+ return true;
+ if (next != null && next.references(var))
+ return true;
+ return false;
+ }
+
+ /**
+ * Debugging
+ */
+ void list(int depth, PrintStream out, boolean listNext)
+ {
+ for (int i = 0; i < depth; i++)
+ out.print(" ");
+ out.println(toString());
+ if (children != null)
+ children.list(depth + 1, out, true);
+ if (listNext && next != null)
+ next.list(depth, out, listNext);
+ }
+
+ /**
+ * Indicates whether the template for which this template node is the
+ * first node specifies the given parameter.
+ */
+ boolean hasParam(QName name)
+ {
+ for (TemplateNode ctx = this; ctx != null; ctx = ctx.next)
+ {
+ if (ctx instanceof ParameterNode)
+ {
+ ParameterNode param = (ParameterNode) ctx;
+ if (param.type == Bindings.PARAM && param.name.equals(name))
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/TemplatesImpl.java b/libjava/classpath/gnu/xml/transform/TemplatesImpl.java
new file mode 100644
index 000000000..fc741bc81
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/TemplatesImpl.java
@@ -0,0 +1,95 @@
+/* TemplatesImpl.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.Properties;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+
+/**
+ * GNU precompiled stylesheet implementation.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class TemplatesImpl
+ implements Templates
+{
+
+ final TransformerFactoryImpl factory;
+ final Stylesheet stylesheet;
+ final Properties outputProperties;
+
+ TemplatesImpl(TransformerFactoryImpl factory, Stylesheet stylesheet)
+ {
+ this.factory = factory;
+ this.stylesheet = stylesheet;
+ outputProperties = new TransformerOutputProperties(stylesheet);
+ }
+
+ public Transformer newTransformer()
+ throws TransformerConfigurationException
+ {
+ Stylesheet stylesheet = (Stylesheet) this.stylesheet.clone();
+ TransformerImpl transformer =
+ new TransformerImpl(factory, stylesheet, outputProperties);
+ stylesheet.transformer = transformer;
+ return transformer;
+ }
+
+ public Properties getOutputProperties()
+ {
+ return (Properties) outputProperties.clone();
+ }
+
+ public String toString()
+ {
+ CPStringBuilder b = new CPStringBuilder(getClass().getName());
+ b.append("[factory=");
+ b.append(factory);
+ b.append(",stylesheet=");
+ b.append(stylesheet);
+ b.append(",outputProperties=");
+ b.append(outputProperties);
+ b.append("]");
+ return b.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/TextNode.java b/libjava/classpath/gnu/xml/transform/TextNode.java
new file mode 100644
index 000000000..8263ef5bc
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/TextNode.java
@@ -0,0 +1,121 @@
+/* TextNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>text</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class TextNode
+ extends TemplateNode
+{
+
+ final boolean disableOutputEscaping;
+
+ TextNode(boolean disableOutputEscaping)
+ {
+ this.disableOutputEscaping = disableOutputEscaping;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new TextNode(disableOutputEscaping);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ String value = "";
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ if (children != null)
+ {
+ // Create a document fragment to hold the text
+ DocumentFragment fragment = doc.createDocumentFragment();
+ // Apply children to the fragment
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ value = Expr.stringValue(fragment);
+ }
+ Text text = doc.createTextNode(value);
+ if (disableOutputEscaping)
+ text.setUserData("disable-output-escaping", "yes", stylesheet);
+ // Insert into result tree
+ if (nextSibling != null)
+ parent.insertBefore(text, nextSibling);
+ else
+ parent.appendChild(text);
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("text");
+ if (disableOutputEscaping)
+ {
+ buf.append('[');
+ buf.append("disable-output-escaping");
+ buf.append(']');
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/TransformerFactoryImpl.java b/libjava/classpath/gnu/xml/transform/TransformerFactoryImpl.java
new file mode 100644
index 000000000..972e8a282
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/TransformerFactoryImpl.java
@@ -0,0 +1,438 @@
+/* TransformerFactoryImpl.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Properties;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TemplatesHandler;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.xpath.XPathFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.XMLFilter;
+import gnu.xml.dom.DomDocument;
+
+/**
+ * GNU transformer factory implementation.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class TransformerFactoryImpl
+ extends SAXTransformerFactory
+{
+
+ final XPathFactory xpathFactory;
+ final XSLURIResolver resolver;
+ ErrorListener userListener;
+ URIResolver userResolver;
+
+ public TransformerFactoryImpl()
+ {
+ xpathFactory = new gnu.xml.xpath.XPathFactoryImpl();
+ resolver = new XSLURIResolver();
+ }
+
+ public Transformer newTransformer(Source source)
+ throws TransformerConfigurationException
+ {
+ Stylesheet stylesheet = newStylesheet(source, 0, null);
+ Properties outputProperties =
+ new TransformerOutputProperties(stylesheet);
+ TransformerImpl transformer =
+ new TransformerImpl(this, stylesheet, outputProperties);
+ stylesheet.transformer = transformer;
+ return transformer;
+ }
+
+ public Transformer newTransformer()
+ throws TransformerConfigurationException
+ {
+ return new TransformerImpl(this, null, new Properties());
+ }
+
+ public Templates newTemplates(Source source)
+ throws TransformerConfigurationException
+ {
+ Stylesheet stylesheet = newStylesheet(source, 0, null);
+ return new TemplatesImpl(this, stylesheet);
+ }
+
+ Stylesheet newStylesheet(Source source, int precedence, Stylesheet parent)
+ throws TransformerConfigurationException
+ {
+ Document doc = null;
+ String systemId = null;
+ if (source != null)
+ {
+ try
+ {
+ DOMSource ds;
+ synchronized (resolver)
+ {
+ resolver.setUserResolver(userResolver);
+ resolver.setUserListener(userListener);
+ ds = resolver.resolveDOM(source, null, null);
+ }
+ Node node = ds.getNode();
+ if (node == null)
+ {
+ throw new TransformerConfigurationException("no source document");
+ }
+ doc = (node instanceof Document) ? (Document) node :
+ node.getOwnerDocument();
+ systemId = ds.getSystemId();
+ }
+ catch (TransformerException e)
+ {
+ throw new TransformerConfigurationException(e);
+ }
+ }
+ return new Stylesheet(this, parent, doc, systemId, precedence);
+ }
+
+ public Source getAssociatedStylesheet(Source source,
+ String media,
+ String title,
+ String charset)
+ throws TransformerConfigurationException
+ {
+ try
+ {
+ DOMSource ds;
+ synchronized (resolver)
+ {
+ resolver.setUserResolver(userResolver);
+ resolver.setUserListener(userListener);
+ ds = resolver.resolveDOM(source, null, null);
+ }
+ Node node = ds.getNode();
+ if (node == null)
+ {
+ throw new TransformerConfigurationException("no source document");
+ }
+ Document doc = (node instanceof Document) ? (Document) node :
+ node.getOwnerDocument();
+ LinkedList matches = new LinkedList();
+ for (node = doc.getFirstChild();
+ node != null;
+ node = node.getNextSibling())
+ {
+ if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE &&
+ "xml-stylesheet".equals(node.getNodeName()))
+ {
+ Map params = parseParameters(node.getNodeValue());
+ if (media != null && !media.equals(params.get("media")))
+ {
+ continue;
+ }
+ if (title != null && !title.equals(params.get("title")))
+ {
+ continue;
+ }
+ if (charset != null && !charset.equals(params.get("charset")))
+ {
+ continue;
+ }
+ String href = (String) params.get("href");
+ URL url = resolver.resolveURL(null, node.getBaseURI(), href);
+ matches.add(url);
+ }
+ }
+ switch (matches.size())
+ {
+ case 0:
+ return null;
+ case 1:
+ return new StreamSource(((URL) matches.getFirst()).toString());
+ default:
+ // Create a source representing a stylesheet with a list of
+ // imports
+ DomDocument ssDoc = new DomDocument();
+ ssDoc.setBuilding(true);
+ // Create document element
+ Node root =
+ ssDoc.createElementNS(Stylesheet.XSL_NS, "stylesheet");
+ Node version =
+ ssDoc.createAttributeNS(null, "version");
+ version.setNodeValue("1.0");
+ root.getAttributes().setNamedItemNS(version);
+ ssDoc.appendChild(root);
+ // Create xsl:import for each URL
+ for (Iterator i = matches.iterator(); i.hasNext(); )
+ {
+ URL url = (URL) i.next();
+ Node imp =
+ ssDoc.createElementNS(Stylesheet.XSL_NS, "import");
+ Node href =
+ ssDoc.createAttributeNS(null, "href");
+ href.setNodeValue(url.toString());
+ imp.getAttributes().setNamedItemNS(href);
+ root.appendChild(imp);
+ }
+ ssDoc.setBuilding(false);
+ return new DOMSource(ssDoc);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new TransformerConfigurationException(e);
+ }
+ catch (TransformerException e)
+ {
+ throw new TransformerConfigurationException(e);
+ }
+ }
+
+ Map parseParameters(String data)
+ {
+ Map ret = new LinkedHashMap();
+ int len = data.length();
+ String key = null;
+ int start = 0;
+ char quoteChar = '\u0000';
+ for (int i = 0; i < len; i++)
+ {
+ char c = data.charAt(i);
+ if (quoteChar == '\u0000' && c == ' ')
+ {
+ if (key == null && start < i)
+ {
+ key = data.substring(start, i);
+ }
+ else
+ {
+ String val = unquote(data.substring(start, i).trim());
+ ret.put(key, val);
+ key = null;
+ }
+ start = i + 1;
+ }
+ else if (c == '"')
+ {
+ quoteChar = (quoteChar == c) ? '\u0000' : c;
+ }
+ else if (c == '\'')
+ {
+ quoteChar = (quoteChar == c) ? '\u0000' : c;
+ }
+ }
+ if (start < len && key != null)
+ {
+ String val = unquote(data.substring(start, len).trim());
+ ret.put(key, val);
+ }
+ return ret;
+ }
+
+ String unquote(String text)
+ {
+ int end = text.length() - 1;
+ if (text.charAt(0) == '\'' && text.charAt(end) == '\'')
+ {
+ return text.substring(1, end);
+ }
+ if (text.charAt(0) == '"' && text.charAt(end) == '"')
+ {
+ return text.substring(1, end);
+ }
+ return text;
+ }
+
+ public void setURIResolver(URIResolver resolver)
+ {
+ userResolver = resolver;
+ }
+
+ public URIResolver getURIResolver()
+ {
+ return userResolver;
+ }
+
+ public void setFeature(String name, boolean value)
+ throws TransformerConfigurationException
+ {
+ throw new TransformerConfigurationException("not supported");
+ }
+
+ public boolean getFeature(String name)
+ {
+ if (SAXSource.FEATURE.equals(name) ||
+ SAXResult.FEATURE.equals(name) ||
+ StreamSource.FEATURE.equals(name) ||
+ StreamResult.FEATURE.equals(name) ||
+ DOMSource.FEATURE.equals(name) ||
+ DOMResult.FEATURE.equals(name) ||
+ SAXTransformerFactory.FEATURE.equals(name))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public void setAttribute(String name, Object value)
+ throws IllegalArgumentException
+ {
+ throw new IllegalArgumentException("not supported");
+ }
+
+ public Object getAttribute(String name)
+ throws IllegalArgumentException
+ {
+ throw new IllegalArgumentException("not supported");
+ }
+
+ public void setErrorListener(ErrorListener listener)
+ throws IllegalArgumentException
+ {
+ userListener = listener;
+ }
+
+ public ErrorListener getErrorListener()
+ {
+ return userListener;
+ }
+
+ // -- SAXTransformerFactory --
+
+ public TemplatesHandler newTemplatesHandler()
+ throws TransformerConfigurationException
+ {
+ return new SAXTemplatesHandler(this);
+ }
+
+ public TransformerHandler newTransformerHandler()
+ throws TransformerConfigurationException
+ {
+ Transformer transformer = newTransformer();
+ return new SAXTransformerHandler(this, transformer);
+ }
+
+ public TransformerHandler newTransformerHandler(Source source)
+ throws TransformerConfigurationException
+ {
+ Transformer transformer = newTransformer(source);
+ return new SAXTransformerHandler(this, transformer);
+ }
+
+ public TransformerHandler newTransformerHandler(Templates templates)
+ throws TransformerConfigurationException
+ {
+ Transformer transformer = templates.newTransformer();
+ return new SAXTransformerHandler(this, transformer);
+ }
+
+ public XMLFilter newXMLFilter(Source source)
+ throws TransformerConfigurationException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public XMLFilter newXMLFilter(Templates templates)
+ throws TransformerConfigurationException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ // -- SAXTransformerFactory end --
+
+ /**
+ * Syntax: TransformerFactoryImpl [<stylesheet> [<input> [<output>]]]
+ */
+ public static void main(String[] args)
+ throws Exception
+ {
+ InputStream stylesheet = null, in = null;
+ OutputStream out = null;
+ try
+ {
+ if (args.length > 0)
+ {
+ stylesheet = new FileInputStream(args[0]);
+ if (args.length > 1)
+ {
+ in = new FileInputStream(args[1]);
+ if (args.length > 2)
+ out = new FileOutputStream(args[2]);
+ }
+ }
+ if (in == null)
+ in = System.in;
+ if (out == null)
+ out = System.out;
+ TransformerFactory f = new TransformerFactoryImpl();
+ Transformer t = (stylesheet != null) ?
+ f.newTransformer(new StreamSource(stylesheet)) :
+ f.newTransformer();
+ t.transform(new StreamSource(in), new StreamResult(out));
+ }
+ finally
+ {
+ if (stylesheet != null)
+ stylesheet.close();
+ if (in != null && in instanceof FileInputStream)
+ in.close();
+ if (out != null && out instanceof FileOutputStream)
+ out.close();
+ }
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/TransformerImpl.java b/libjava/classpath/gnu/xml/transform/TransformerImpl.java
new file mode 100644
index 000000000..eb6a5dea1
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/TransformerImpl.java
@@ -0,0 +1,805 @@
+/* TransformerImpl.java --
+ Copyright (C) 2004,2005,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.UnknownServiceException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import javax.xml.namespace.QName;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamResult;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+import gnu.xml.dom.DomDoctype;
+import gnu.xml.dom.DomDocument;
+import gnu.xml.dom.ls.WriterOutputStream;
+
+/**
+ * The transformation process for a given stylesheet.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class TransformerImpl
+ extends Transformer
+{
+
+ final TransformerFactoryImpl factory;
+ final Stylesheet stylesheet;
+ URIResolver uriResolver;
+ ErrorListener errorListener;
+ Properties outputProperties;
+
+ TransformerImpl(TransformerFactoryImpl factory,
+ Stylesheet stylesheet,
+ Properties outputProperties)
+ throws TransformerConfigurationException
+ {
+ this.factory = factory;
+ uriResolver = factory.userResolver;
+ errorListener = factory.userListener;
+ this.stylesheet = stylesheet;
+ this.outputProperties = outputProperties;
+ if (stylesheet != null)
+ {
+ // Set up parameter context for this transformer
+ stylesheet.bindings.push(Bindings.PARAM);
+ }
+ }
+
+ public void transform(Source xmlSource, Result outputTarget)
+ throws TransformerException
+ {
+ // Get the source tree
+ DOMSource source;
+ synchronized (factory.resolver)
+ {
+ factory.resolver.setUserResolver(uriResolver);
+ factory.resolver.setUserListener(errorListener);
+ source = factory.resolver.resolveDOM(xmlSource, null, null);
+ }
+ Node context = source.getNode();
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ if (doc instanceof DomDocument)
+ {
+ // Suppress mutation events
+ ((DomDocument) doc).setBuilding(true);
+ }
+ // Get the result tree
+ Node parent = null, nextSibling = null;
+ if (outputTarget instanceof DOMResult)
+ {
+ DOMResult dr = (DOMResult) outputTarget;
+ parent = dr.getNode();
+ nextSibling = dr.getNextSibling();
+
+ Document rdoc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ if (rdoc instanceof DomDocument)
+ {
+ // Suppress mutation events and allow multiple root elements
+ DomDocument drdoc = (DomDocument) rdoc;
+ drdoc.setBuilding(true);
+ drdoc.setCheckWellformedness(false);
+ }
+ }
+ boolean created = false;
+ // Transformation
+ if (stylesheet != null)
+ {
+ if (parent == null)
+ {
+ // Create a new document to hold the result
+ DomDocument resultDoc = new DomDocument();
+ resultDoc.setBuilding(true);
+ resultDoc.setCheckWellformedness(false);
+ parent = resultDoc;
+ created = true;
+ }
+ // Make a copy of the source node, and strip it
+ context = context.cloneNode(true);
+ strip(stylesheet, context);
+ // XSLT transformation
+ try
+ {
+ // Set output properties in the underlying stylesheet
+ ((TransformerOutputProperties) outputProperties).apply();
+ stylesheet.initTopLevelVariables(context);
+ TemplateNode t = stylesheet.getTemplate(null, context, false);
+ if (t != null)
+ {
+ stylesheet.current = context;
+ t.apply(stylesheet, null, context, 1, 1, parent, nextSibling);
+ }
+ }
+ catch (TransformerException e)
+ {
+ // Done transforming, reset document
+ if (doc instanceof DomDocument)
+ ((DomDocument) doc).setBuilding(false);
+ throw e;
+ }
+ }
+ else
+ {
+ // Identity transform
+ Node clone = context.cloneNode(true);
+ if (context.getNodeType() != Node.DOCUMENT_NODE)
+ {
+ Document resultDoc;
+ if (parent == null)
+ {
+ // Create a new document to hold the result
+ DomDocument rd = new DomDocument();
+ rd.setBuilding(true);
+ rd.setCheckWellformedness(false);
+ parent = resultDoc = rd;
+ created = true;
+ }
+ else
+ {
+ resultDoc = (parent instanceof Document) ?
+ (Document) parent :
+ parent.getOwnerDocument();
+ }
+ Document sourceDoc = context.getOwnerDocument();
+ if (sourceDoc != resultDoc)
+ clone = resultDoc.adoptNode(clone);
+ if (nextSibling != null)
+ parent.insertBefore(clone, nextSibling);
+ else
+ parent.appendChild(clone);
+ }
+ else
+ {
+ // Cannot append document to another tree
+ parent = clone;
+ created = true;
+ }
+ }
+ String method = outputProperties.getProperty(OutputKeys.METHOD);
+ int outputMethod = "html".equals(method) ? Stylesheet.OUTPUT_HTML :
+ "text".equals(method) ? Stylesheet.OUTPUT_TEXT :
+ Stylesheet.OUTPUT_XML;
+ String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
+ String publicId = outputProperties.getProperty(OutputKeys.DOCTYPE_PUBLIC);
+ String systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
+ String version = outputProperties.getProperty(OutputKeys.VERSION);
+ boolean omitXmlDeclaration =
+ "yes".equals(outputProperties.getProperty(OutputKeys.OMIT_XML_DECLARATION));
+ boolean standalone =
+ "yes".equals(outputProperties.getProperty(OutputKeys.STANDALONE));
+ String mediaType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
+ String cdataSectionElements =
+ outputProperties.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
+ boolean indent =
+ "yes".equals(outputProperties.getProperty(OutputKeys.INDENT));
+ if (created && parent instanceof DomDocument)
+ {
+ // Discover document element
+ DomDocument resultDoc = (DomDocument) parent;
+ Node root = resultDoc.getDocumentElement();
+ // Add doctype if specified
+ if (publicId != null || systemId != null)
+ {
+ if (root != null)
+ {
+ // We must know the name of the root element to
+ // create the document type
+ DocumentType doctype = new DomDoctype(resultDoc,
+ root.getNodeName(),
+ publicId,
+ systemId);
+ resultDoc.insertBefore(doctype, root);
+ }
+ }
+ resultDoc.setBuilding(false);
+ resultDoc.setCheckWellformedness(true);
+ }
+ else if (publicId != null || systemId != null)
+ {
+ switch (parent.getNodeType())
+ {
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ Document resultDoc = (parent instanceof Document) ?
+ (Document) parent :
+ parent.getOwnerDocument();
+ DOMImplementation impl = resultDoc.getImplementation();
+ Node root = resultDoc.getDocumentElement();
+ if (root != null)
+ {
+ DocumentType doctype =
+ impl.createDocumentType(root.getNodeName(),
+ publicId,
+ systemId);
+ resultDoc.insertBefore(doctype, root);
+ }
+ }
+ }
+ if (version != null)
+ parent.setUserData("version", version, stylesheet);
+ if (omitXmlDeclaration)
+ parent.setUserData("omit-xml-declaration", "yes", stylesheet);
+ if (standalone)
+ parent.setUserData("standalone", "yes", stylesheet);
+ if (mediaType != null)
+ parent.setUserData("media-type", mediaType, stylesheet);
+ if (cdataSectionElements != null)
+ {
+ List list = new LinkedList();
+ StringTokenizer st = new StringTokenizer(cdataSectionElements);
+ while (st.hasMoreTokens())
+ {
+ String name = st.nextToken();
+ String localName = name;
+ String uri = null;
+ String prefix = null;
+ int ci = name.indexOf(':');
+ if (ci != -1)
+ {
+ // Use namespaces defined on xsl:output node to resolve
+ // namespaces for QName
+ prefix = name.substring(0, ci);
+ localName = name.substring(ci + 1);
+ uri = stylesheet.output.lookupNamespaceURI(prefix);
+ }
+ list.add(new QName(uri, localName, prefix));
+ }
+ if (!list.isEmpty())
+ {
+ Document resultDoc = (parent instanceof Document) ?
+ (Document) parent :
+ parent.getOwnerDocument();
+ convertCdataSectionElements(resultDoc, parent, list);
+ }
+ }
+ if (indent)
+ {
+ if (created && parent instanceof DomDocument)
+ {
+ DomDocument domDoc = (DomDocument) parent;
+ domDoc.setBuilding(true);
+ domDoc.setCheckWellformedness(false);
+ }
+ parent.normalize();
+ if (stylesheet != null)
+ strip(stylesheet, parent);
+ Document resultDoc = (parent instanceof Document) ?
+ (Document) parent :
+ parent.getOwnerDocument();
+ reindent(resultDoc, parent, 0);
+ if (created && parent instanceof DomDocument)
+ {
+ DomDocument domDoc = (DomDocument) parent;
+ domDoc.setBuilding(false);
+ domDoc.setCheckWellformedness(true);
+ }
+ }
+ // Render result to the target device
+ if (outputTarget instanceof DOMResult)
+ {
+ if (created)
+ {
+ DOMResult dr = (DOMResult) outputTarget;
+ dr.setNode(parent);
+ dr.setNextSibling(null);
+ }
+ }
+ else if (outputTarget instanceof StreamResult)
+ {
+ StreamResult sr = (StreamResult) outputTarget;
+ IOException ex = null;
+ try
+ {
+ writeStreamResult(parent, sr, outputMethod, encoding);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ try
+ {
+ writeStreamResult(parent, sr, outputMethod, "UTF-8");
+ }
+ catch (IOException e2)
+ {
+ ex = e2;
+ }
+ }
+ catch (IOException e)
+ {
+ ex = e;
+ }
+ if (ex != null)
+ {
+ if (errorListener != null)
+ errorListener.error(new TransformerException(ex));
+ else
+ ex.printStackTrace(System.err);
+ }
+ }
+ else if (outputTarget instanceof SAXResult)
+ {
+ SAXResult sr = (SAXResult) outputTarget;
+ try
+ {
+ ContentHandler ch = sr.getHandler();
+ LexicalHandler lh = sr.getLexicalHandler();
+ if (lh == null && ch instanceof LexicalHandler)
+ lh = (LexicalHandler) ch;
+ SAXSerializer serializer = new SAXSerializer();
+ serializer.serialize(parent, ch, lh);
+ }
+ catch (SAXException e)
+ {
+ if (errorListener != null)
+ errorListener.error(new TransformerException(e));
+ else
+ e.printStackTrace(System.err);
+ }
+ }
+ }
+
+ /**
+ * Strip whitespace from the source tree.
+ */
+ static boolean strip(Stylesheet stylesheet, Node node)
+ throws TransformerConfigurationException
+ {
+ short nt = node.getNodeType();
+ if (nt == Node.ENTITY_REFERENCE_NODE)
+ {
+ // Replace entity reference with its content
+ Node parent = node.getParentNode();
+ Node nextSibling = node.getNextSibling();
+ Node child = node.getFirstChild();
+ while (child != null)
+ {
+ Node next = child.getNextSibling();
+ node.removeChild(child);
+ if (nextSibling != null)
+ parent.insertBefore(child, nextSibling);
+ else
+ parent.appendChild(child);
+ child = next;
+ }
+ return true;
+ }
+ if (nt == Node.TEXT_NODE || nt == Node.CDATA_SECTION_NODE)
+ {
+ // Denormalize text into whitespace and non-whitespace nodes
+ String text = node.getNodeValue();
+ String[] tokens = tokenizeWhitespace(text);
+ if (tokens.length > 1)
+ {
+ node.setNodeValue(tokens[0]);
+ Node parent = node.getParentNode();
+ Node nextSibling = node.getNextSibling();
+ Document doc = node.getOwnerDocument();
+ for (int i = 1; i < tokens.length; i++)
+ {
+ Node newChild = (nt == Node.CDATA_SECTION_NODE) ?
+ doc.createCDATASection(tokens[i]) :
+ doc.createTextNode(tokens[i]);
+ if (nextSibling != null)
+ parent.insertBefore(newChild, nextSibling);
+ else
+ parent.appendChild(newChild);
+ }
+ }
+ return !stylesheet.isPreserved((Text) node, true);
+ }
+ else
+ {
+ Node child = node.getFirstChild();
+ while (child != null)
+ {
+ boolean remove = strip(stylesheet, child);
+ Node next = child.getNextSibling();
+ if (remove)
+ node.removeChild(child);
+ child = next;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tokenize the specified text into contiguous whitespace-only and
+ * non-whitespace chunks.
+ */
+ private static String[] tokenizeWhitespace(String text)
+ {
+ int len = text.length();
+ int start = 0, end = len - 1;
+ // Find index of text start
+ for (int i = 0; i < len; i++)
+ {
+ char c = text.charAt(i);
+ boolean whitespace = (c == ' ' || c == '\n' || c == '\t' || c == '\r');
+ if (whitespace)
+ start++;
+ else
+ break;
+ }
+ if (start == end) // all whitespace
+ return new String[] { text };
+ // Find index of text end
+ for (int i = end; i > start; i--)
+ {
+ char c = text.charAt(i);
+ boolean whitespace = (c == ' ' || c == '\n' || c == '\t' || c == '\r');
+ if (whitespace)
+ end--;
+ else
+ break;
+ }
+ if (start == 0 && end == len - 1) // all non-whitespace
+ return new String[] { text };
+ // whitespace, then text, then whitespace
+ String[] ret = (start > 0 && end < len - 1) ?
+ new String[3] : new String[2];
+ int i = 0;
+ if (start > 0)
+ ret[i++] = text.substring(0, start);
+ ret[i++] = text.substring(start, end + 1);
+ if (end < len - 1)
+ ret[i++] = text.substring(end + 1);
+ return ret;
+ }
+
+ /**
+ * Obtain a suitable output stream for writing the result to,
+ * and use the StreamSerializer to write the result tree to the stream.
+ */
+ void writeStreamResult(Node node, StreamResult sr, int outputMethod,
+ String encoding)
+ throws IOException
+ {
+ OutputStream out = null;
+ boolean created = false;
+ try
+ {
+ out = sr.getOutputStream();
+ if (out == null)
+ {
+ Writer writer = sr.getWriter();
+ if (writer != null)
+ out = new WriterOutputStream(writer);
+ }
+ if (out == null)
+ {
+ String systemId = sr.getSystemId();
+ try
+ {
+ URL url = new URL(systemId);
+ URLConnection connection = url.openConnection();
+ // We need to call setDoInput(false), because our
+ // implementation of the file protocol allows writing
+ // (unlike Sun), but it will fail with a FileNotFoundException
+ // if we also open the connection for input and the output
+ // file doesn't yet exist.
+ connection.setDoInput(false);
+ connection.setDoOutput(true);
+ out = connection.getOutputStream();
+ }
+ catch (MalformedURLException e)
+ {
+ out = new FileOutputStream(systemId);
+ }
+ catch (UnknownServiceException e)
+ {
+ URL url = new URL(systemId);
+ out = new FileOutputStream(url.getPath());
+ }
+ created = true;
+ }
+ out = new BufferedOutputStream(out);
+ StreamSerializer serializer =
+ new StreamSerializer(outputMethod, encoding, null);
+ if (stylesheet != null)
+ {
+ Collection celem = stylesheet.outputCdataSectionElements;
+ serializer.setCdataSectionElements(celem);
+ }
+ serializer.serialize(node, out);
+ out.flush();
+ }
+ finally
+ {
+ try
+ {
+ if (out != null && created)
+ out.close();
+ }
+ catch (IOException e)
+ {
+ if (errorListener != null)
+ {
+ try
+ {
+ errorListener.error(new TransformerException(e));
+ }
+ catch (TransformerException e2)
+ {
+ e2.printStackTrace(System.err);
+ }
+ }
+ else
+ e.printStackTrace(System.err);
+ }
+ }
+ }
+
+ void copyChildren(Document dstDoc, Node src, Node dst)
+ {
+ Node srcChild = src.getFirstChild();
+ while (srcChild != null)
+ {
+ Node dstChild = dstDoc.adoptNode(srcChild);
+ dst.appendChild(dstChild);
+ srcChild = srcChild.getNextSibling();
+ }
+ }
+
+ public void setParameter(String name, Object value)
+ {
+ if (stylesheet != null)
+ stylesheet.bindings.set(new QName(null, name), value, Bindings.PARAM);
+ }
+
+ public Object getParameter(String name)
+ {
+ if (stylesheet != null)
+ return stylesheet.bindings.get(new QName(null, name), null, 1, 1);
+ return null;
+ }
+
+ public void clearParameters()
+ {
+ if (stylesheet != null)
+ {
+ stylesheet.bindings.pop(Bindings.PARAM);
+ stylesheet.bindings.push(Bindings.PARAM);
+ }
+ }
+
+ public void setURIResolver(URIResolver resolver)
+ {
+ uriResolver = resolver;
+ }
+
+ public URIResolver getURIResolver()
+ {
+ return uriResolver;
+ }
+
+ public void setOutputProperties(Properties oformat)
+ throws IllegalArgumentException
+ {
+ if (oformat == null)
+ outputProperties.clear();
+ else
+ outputProperties.putAll(oformat);
+ }
+
+ public Properties getOutputProperties()
+ {
+ return (Properties) outputProperties.clone();
+ }
+
+ public void setOutputProperty(String name, String value)
+ throws IllegalArgumentException
+ {
+ outputProperties.put(name, value);
+ }
+
+ public String getOutputProperty(String name)
+ throws IllegalArgumentException
+ {
+ return outputProperties.getProperty(name);
+ }
+
+ public void setErrorListener(ErrorListener listener)
+ {
+ errorListener = listener;
+ }
+
+ public ErrorListener getErrorListener()
+ {
+ return errorListener;
+ }
+
+ static final String INDENT_WHITESPACE = " ";
+
+ /*
+ * Apply indent formatting to the given tree.
+ */
+ void reindent(Document doc, Node node, int offset)
+ {
+ if (node.hasChildNodes())
+ {
+ boolean markupContent = false;
+ boolean textContent = false;
+ List children = new LinkedList();
+ Node ctx = node.getFirstChild();
+ while (ctx != null)
+ {
+ switch (ctx.getNodeType())
+ {
+ case Node.ELEMENT_NODE:
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ case Node.DOCUMENT_TYPE_NODE:
+ markupContent = true;
+ break;
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ case Node.ENTITY_REFERENCE_NODE:
+ case Node.COMMENT_NODE:
+ textContent = true;
+ break;
+ }
+ children.add(ctx);
+ ctx = ctx.getNextSibling();
+ }
+ if (markupContent)
+ {
+ if (textContent)
+ {
+ // XXX handle mixed content differently?
+ }
+ int nodeType = node.getNodeType();
+ if (nodeType == Node.DOCUMENT_NODE)
+ {
+ for (Iterator i = children.iterator(); i.hasNext(); )
+ {
+ ctx = (Node) i.next();
+ reindent(doc, ctx, offset);
+ }
+ }
+ else
+ {
+ CPStringBuilder buf = new CPStringBuilder();
+ buf.append('\n');
+ for (int i = 0; i < offset + 1; i++)
+ buf.append(INDENT_WHITESPACE);
+ String ws = buf.toString();
+ for (Iterator i = children.iterator(); i.hasNext(); )
+ {
+ ctx = (Node) i.next();
+ node.insertBefore(doc.createTextNode(ws), ctx);
+ reindent(doc, ctx, offset + 1);
+ }
+ buf = new CPStringBuilder();
+ buf.append('\n');
+ for (int i = 0; i < offset; i++)
+ buf.append(INDENT_WHITESPACE);
+ ws = buf.toString();
+ node.appendChild(doc.createTextNode(ws));
+ }
+ }
+ }
+ }
+
+ /**
+ * Converts the text node children of any cdata-section-elements in the
+ * tree to CDATA section nodes.
+ */
+ void convertCdataSectionElements(Document doc, Node node, List list)
+ {
+ if (node.getNodeType() == Node.ELEMENT_NODE)
+ {
+ boolean match = false;
+ for (Iterator i = list.iterator(); i.hasNext(); )
+ {
+ QName qname = (QName) i.next();
+ if (match(qname, node))
+ {
+ match = true;
+ break;
+ }
+ }
+ if (match)
+ {
+ Node ctx = node.getFirstChild();
+ while (ctx != null)
+ {
+ if (ctx.getNodeType() == Node.TEXT_NODE)
+ {
+ Node cdata = doc.createCDATASection(ctx.getNodeValue());
+ node.replaceChild(cdata, ctx);
+ ctx = cdata;
+ }
+ ctx = ctx.getNextSibling();
+ }
+ }
+ }
+ Node ctx = node.getFirstChild();
+ while (ctx != null)
+ {
+ if (ctx.hasChildNodes())
+ convertCdataSectionElements(doc, ctx, list);
+ ctx = ctx.getNextSibling();
+ }
+ }
+
+ boolean match(QName qname, Node node)
+ {
+ String ln1 = qname.getLocalPart();
+ String ln2 = node.getLocalName();
+ if (ln2 == null)
+ return ln1.equals(node.getNodeName());
+ else
+ {
+ String uri1 = qname.getNamespaceURI();
+ String uri2 = node.getNamespaceURI();
+ return (uri1.equals(uri2) && ln1.equals(ln2));
+ }
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/TransformerOutputProperties.java b/libjava/classpath/gnu/xml/transform/TransformerOutputProperties.java
new file mode 100644
index 000000000..ddaa260c4
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/TransformerOutputProperties.java
@@ -0,0 +1,186 @@
+/* TransformerOutputProperties.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import javax.xml.transform.OutputKeys;
+
+/**
+ * Helper class to manage JAXP user setting of output properties.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class TransformerOutputProperties
+ extends Properties
+{
+
+ final Properties defaultProperties;
+ final Stylesheet stylesheet;
+ boolean dirty;
+
+ TransformerOutputProperties(Stylesheet stylesheet)
+ {
+ this.stylesheet = stylesheet;
+ defaultProperties = new Properties();
+ switch (stylesheet.outputMethod)
+ {
+ case Stylesheet.OUTPUT_XML:
+ defaultProperties.put(OutputKeys.METHOD, "xml");
+ break;
+ case Stylesheet.OUTPUT_HTML:
+ defaultProperties.put(OutputKeys.METHOD, "html");
+ break;
+ case Stylesheet.OUTPUT_TEXT:
+ defaultProperties.put(OutputKeys.METHOD, "text");
+ break;
+ }
+ if (stylesheet.outputVersion != null)
+ {
+ defaultProperties.put(OutputKeys.VERSION, stylesheet.outputVersion);
+ }
+ if (stylesheet.outputEncoding != null)
+ {
+ defaultProperties.put(OutputKeys.ENCODING, stylesheet.outputEncoding);
+ }
+ defaultProperties.put(OutputKeys.OMIT_XML_DECLARATION,
+ stylesheet.outputOmitXmlDeclaration ? "yes" : "no");
+ defaultProperties.put(OutputKeys.STANDALONE,
+ stylesheet.outputStandalone ? "yes" : "no");
+ if (stylesheet.outputPublicId != null)
+ {
+ defaultProperties.put(OutputKeys.DOCTYPE_PUBLIC,
+ stylesheet.outputPublicId);
+ }
+ if (stylesheet.outputSystemId != null)
+ {
+ defaultProperties.put(OutputKeys.DOCTYPE_SYSTEM,
+ stylesheet.outputSystemId);
+ }
+ CPStringBuilder buf = new CPStringBuilder();
+ for (Iterator i = stylesheet.outputCdataSectionElements.iterator();
+ i.hasNext(); )
+ {
+ if (buf.length() > 0)
+ {
+ buf.append(' ');
+ }
+ buf.append((String) i.next());
+ }
+ defaultProperties.put(OutputKeys.CDATA_SECTION_ELEMENTS, buf.toString());
+ defaultProperties.put(OutputKeys.INDENT,
+ stylesheet.outputIndent ? "yes" : "no");
+ if (stylesheet.outputMediaType != null)
+ {
+ defaultProperties.put(OutputKeys.MEDIA_TYPE,
+ stylesheet.outputMediaType);
+ }
+ }
+
+ public String getProperty(String key)
+ {
+ String val = super.getProperty(key);
+ if (val == null)
+ {
+ val = defaultProperties.getProperty(key);
+ }
+ return val;
+ }
+
+ public Object put(Object key, Object value)
+ {
+ Object ret = super.put(key, value);
+ dirty = true;
+ return ret;
+ }
+
+ public void clear()
+ {
+ super.clear();
+ dirty = true;
+ }
+
+ /**
+ * Applies the current set of properties to the underlying stylesheet.
+ */
+ void apply()
+ {
+ if (!dirty)
+ {
+ return;
+ }
+ String method = getProperty(OutputKeys.METHOD);
+ if ("xml".equals(method))
+ {
+ stylesheet.outputMethod = Stylesheet.OUTPUT_XML;
+ }
+ else if ("html".equals(method))
+ {
+ stylesheet.outputMethod = Stylesheet.OUTPUT_HTML;
+ }
+ else if ("text".equals(method))
+ {
+ stylesheet.outputMethod = Stylesheet.OUTPUT_TEXT;
+ }
+ stylesheet.outputVersion = getProperty(OutputKeys.VERSION);
+ stylesheet.outputEncoding = getProperty(OutputKeys.ENCODING);
+ stylesheet.outputOmitXmlDeclaration =
+ "yes".equals(getProperty(OutputKeys.OMIT_XML_DECLARATION));
+ stylesheet.outputStandalone =
+ "yes".equals(getProperty(OutputKeys.STANDALONE));
+ stylesheet.outputPublicId = getProperty(OutputKeys.DOCTYPE_PUBLIC);
+ stylesheet.outputSystemId = getProperty(OutputKeys.DOCTYPE_SYSTEM);
+ StringTokenizer st =
+ new StringTokenizer(getProperty(OutputKeys.CDATA_SECTION_ELEMENTS));
+ Collection acc = new LinkedHashSet();
+ while (st.hasMoreTokens())
+ {
+ acc.add(st.nextToken());
+ }
+ stylesheet.outputCdataSectionElements = acc;
+ stylesheet.outputIndent = "yes".equals(getProperty(OutputKeys.INDENT));
+ stylesheet.outputMediaType = getProperty(OutputKeys.MEDIA_TYPE);
+ dirty = false;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/URIResolverEntityResolver.java b/libjava/classpath/gnu/xml/transform/URIResolverEntityResolver.java
new file mode 100644
index 000000000..49193a473
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/URIResolverEntityResolver.java
@@ -0,0 +1,83 @@
+/* URIResolverEntityResolver.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.io.IOException;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXSource;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * EntityResolver that wraps a URIResolver.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class URIResolverEntityResolver
+ implements EntityResolver
+{
+
+ final URIResolver resolver;
+
+ URIResolverEntityResolver(URIResolver resolver)
+ {
+ this.resolver = resolver;
+ }
+
+ public InputSource resolveEntity(String publicId, String systemId)
+ throws SAXException, IOException
+ {
+ try
+ {
+ Source source = resolver.resolve(null, systemId);
+ if (source == null)
+ {
+ return null;
+ }
+ return SAXSource.sourceToInputSource(source);
+ }
+ catch (TransformerException e)
+ {
+ throw new SAXException(e);
+ }
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/UnparsedEntityUriFunction.java b/libjava/classpath/gnu/xml/transform/UnparsedEntityUriFunction.java
new file mode 100644
index 000000000..5eae78cb2
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/UnparsedEntityUriFunction.java
@@ -0,0 +1,132 @@
+/* UnparsedEntityUriFunction.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Notation;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>unparsed-entity-uri()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class UnparsedEntityUriFunction
+ extends Expr
+ implements XPathFunction, Function
+{
+
+ List<Expr> args;
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.emptySet();
+ }
+
+ public void setArguments(List<Expr> args)
+ {
+ this.args = args;
+ }
+
+ @Override
+ public Object evaluate(Node context, int pos, int len)
+ {
+ int arity = args.size();
+ List<Object> values = new ArrayList<Object>(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ String name = _string(context, values.get(0));
+ DocumentType doctype = context.getOwnerDocument().getDoctype();
+ if (doctype != null)
+ {
+ NamedNodeMap notations = doctype.getNotations();
+ Notation notation = (Notation) notations.getNamedItem(name);
+ if (notation != null)
+ {
+ String systemId = notation.getSystemId();
+ // XXX absolutize?
+ if (systemId != null)
+ {
+ return systemId;
+ }
+ }
+ }
+ return "";
+ }
+
+ public Expr clone(Object context)
+ {
+ UnparsedEntityUriFunction f = new UnparsedEntityUriFunction();
+ int len = args.size();
+ List<Expr> args2 = new ArrayList<Expr>(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(args.get(i).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+ public boolean references(QName var)
+ {
+ for (Iterator<Expr> i = args.iterator(); i.hasNext(); )
+ {
+ if (i.next().references(var))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/ValueOfNode.java b/libjava/classpath/gnu/xml/transform/ValueOfNode.java
new file mode 100644
index 000000000..0d9a656e7
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/ValueOfNode.java
@@ -0,0 +1,141 @@
+/* ValueOfNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.Collection;
+import java.util.Iterator;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSLT <code>value-of</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ValueOfNode
+ extends TemplateNode
+{
+
+ final Expr select;
+ final boolean disableOutputEscaping;
+
+ ValueOfNode(Expr select, boolean disableOutputEscaping)
+ {
+ this.select = select;
+ this.disableOutputEscaping = disableOutputEscaping;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new ValueOfNode(select.clone(stylesheet),
+ disableOutputEscaping);
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ @Override
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Object ret = select.evaluate(context, pos, len);
+ String value;
+ if (ret instanceof Collection)
+ {
+ CPStringBuilder buf = new CPStringBuilder();
+ for (Node node : ((Collection<Node>) ret))
+ {
+ buf.append(Expr.stringValue(node));
+ }
+ value = buf.toString();
+ }
+ else if (ret == null)
+ value = "";
+ else
+ value = Expr._string(context, ret);
+ if (stylesheet.debug)
+ System.err.println("value-of: "+context+" "+ select + " -> "+ value);
+ if (value != null && value.length() > 0)
+ {
+ Document doc = (parent instanceof Document) ?
+ (Document) parent : parent.getOwnerDocument();
+ Text textNode = doc.createTextNode(value);
+ if (disableOutputEscaping)
+ textNode.setUserData("disable-output-escaping", "yes", stylesheet);
+ if (nextSibling != null)
+ parent.insertBefore(textNode, nextSibling);
+ else
+ parent.appendChild(textNode);
+ }
+ // value-of doesn't process children
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+
+ public boolean references(QName var)
+ {
+ if (select != null && select.references(var))
+ return true;
+ return super.references(var);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("value-of");
+ buf.append('[');
+ buf.append("select=");
+ buf.append(select);
+ if (disableOutputEscaping)
+ buf.append(",disableOutputEscaping");
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/WhenNode.java b/libjava/classpath/gnu/xml/transform/WhenNode.java
new file mode 100644
index 000000000..abfe796fd
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/WhenNode.java
@@ -0,0 +1,115 @@
+/* WhenNode.java --
+ Copyright (C) 2004,2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import gnu.java.lang.CPStringBuilder;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSL <code>when</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class WhenNode
+ extends TemplateNode
+{
+
+ final Expr test;
+
+ WhenNode(Expr test)
+ {
+ this.test = test;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ TemplateNode ret = new WhenNode(test.clone(stylesheet));
+ if (children != null)
+ ret.children = children.clone(stylesheet);
+ if (next != null)
+ ret.next = next.clone(stylesheet);
+ return ret;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Object ret = test.evaluate(context, pos, len);
+ boolean success = (ret instanceof Boolean) ?
+ ((Boolean) ret).booleanValue() :
+ Expr._boolean(context, ret);
+ if (success)
+ {
+ if (children != null)
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ else
+ {
+ if (next != null)
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public boolean references(QName var)
+ {
+ if (test != null && test.references(var))
+ return true;
+ return super.references(var);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("when");
+ buf.append('[');
+ buf.append("test=");
+ buf.append(test);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/WithParam.java b/libjava/classpath/gnu/xml/transform/WithParam.java
new file mode 100644
index 000000000..2e726fb82
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/WithParam.java
@@ -0,0 +1,127 @@
+/* WithParam.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.Collections;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A specification for setting a variable or parameter during template
+ * processing with <code>apply-templates</code> or
+ * <code>call-template</code>.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class WithParam
+{
+
+ final QName name;
+ final Expr select;
+ final TemplateNode content;
+
+ WithParam(QName name, Expr select)
+ {
+ this.name = name;
+ this.select = select;
+ content = null;
+ }
+
+ WithParam(QName name, TemplateNode content)
+ {
+ this.name = name;
+ this.content = content;
+ select = null;
+ }
+
+ Object getValue(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len)
+ throws TransformerException
+ {
+ if (select != null)
+ {
+ return select.evaluate(context, pos, len);
+ }
+ else if (content == null)
+ {
+ return "";
+ }
+ else
+ {
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ DocumentFragment fragment = doc.createDocumentFragment();
+ content.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ return Collections.singleton(fragment);
+ }
+ }
+
+ WithParam clone(Stylesheet stylesheet)
+ {
+ if (content == null)
+ {
+ return new WithParam(name,
+ select.clone(stylesheet));
+ }
+ else
+ {
+ return new WithParam(name,
+ content.clone(stylesheet));
+ }
+ }
+
+ boolean references(QName var)
+ {
+ if (select != null && select.references(var))
+ {
+ return true;
+ }
+ if (content != null && content.references(var))
+ {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/XSLComparator.java b/libjava/classpath/gnu/xml/transform/XSLComparator.java
new file mode 100644
index 000000000..1fd0b20e6
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/XSLComparator.java
@@ -0,0 +1,118 @@
+/* XSLComparator.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.text.Collator;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * Comparator for sorting lists of nodes according to a list of sort keys.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class XSLComparator
+ implements Comparator<Node>
+{
+
+ final List<SortKey> sortKeys;
+
+ XSLComparator(List<SortKey> sortKeys)
+ {
+ this.sortKeys = sortKeys;
+ }
+
+ public int compare(Node n1, Node n2)
+ {
+ for (SortKey sortKey : sortKeys)
+ {
+ String k1 = sortKey.key(n1);
+ String k2 = sortKey.key(n2);
+ if ("text".equals(sortKey.dataType))
+ {
+ Locale locale = (sortKey.lang == null) ? Locale.getDefault() :
+ new Locale(sortKey.lang);
+ Collator collator = Collator.getInstance(locale);
+ int d = collator.compare(k1, k2);
+ if (d != 0)
+ {
+ switch (sortKey.caseOrder)
+ {
+ case SortKey.UPPER_FIRST:
+ // TODO
+ break;
+ case SortKey.LOWER_FIRST:
+ // TODO
+ break;
+ }
+ if (sortKey.descending)
+ {
+ d = -d;
+ }
+ return d;
+ }
+ }
+ else if ("number".equals(sortKey.dataType))
+ {
+ double kn1 = Expr._number(n1, k1);
+ double kn2 = Expr._number(n2, k2);
+ int d;
+ if (Double.isNaN(kn1) || Double.isInfinite(kn2))
+ {
+ d = -1;
+ }
+ else if (Double.isNaN(kn2) || Double.isInfinite(kn1))
+ {
+ d = 1;
+ }
+ else
+ {
+ // conversion to int may give 0 for small numbers
+ d = (kn1 > kn2) ? 1 : (kn1 < kn2) ? -1 : 0;
+ }
+ return (sortKey.descending) ? -d : d;
+ }
+ }
+ return 0;
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/XSLURIResolver.java b/libjava/classpath/gnu/xml/transform/XSLURIResolver.java
new file mode 100644
index 000000000..cf596c1e6
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/XSLURIResolver.java
@@ -0,0 +1,321 @@
+/* XSLURIResolver.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.transform;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import gnu.xml.dom.DomDocument;
+import gnu.xml.dom.ls.SAXEventSink;
+import gnu.xml.dom.ls.ReaderInputStream;
+
+/**
+ * URI resolver for XSLT.
+ * This resolver parses external entities into DOMSources. It
+ * maintains a cache of URIs to DOMSources to avoid expensive re-parsing.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class XSLURIResolver
+ implements URIResolver
+{
+
+ final Map<String,Long> lastModifiedCache = new HashMap<String,Long>();
+ final Map<String,Node> nodeCache = new HashMap<String,Node>();
+ DocumentBuilder builder;
+ URIResolver userResolver;
+ ErrorListener userListener;
+
+ void setUserResolver(URIResolver userResolver)
+ {
+ this.userResolver = userResolver;
+ }
+
+ void setUserListener(ErrorListener userListener)
+ {
+ this.userListener = userListener;
+ }
+
+ /**
+ * Clear the cache.
+ */
+ void flush()
+ {
+ lastModifiedCache.clear();
+ nodeCache.clear();
+ }
+
+ public Source resolve(String href, String base)
+ throws TransformerException
+ {
+ Source source = null;
+ if (userResolver != null)
+ {
+ source = userResolver.resolve(base, href);
+ }
+ return resolveDOM(source, href, base);
+ }
+
+ DOMSource resolveDOM(Source source, String base, String href)
+ throws TransformerException
+ {
+ if (source != null && source instanceof DOMSource)
+ {
+ return (DOMSource) source;
+ }
+ String systemId = (source == null) ? null : source.getSystemId();
+ long lastModified = 0L, lastLastModified = 0L;
+
+ try
+ {
+ Node node = null;
+ InputStream in = null;
+ if (source != null && source instanceof StreamSource)
+ {
+ StreamSource ss = (StreamSource) source;
+ in = ss.getInputStream();
+ if (in == null)
+ {
+ Reader reader = ss.getReader();
+ if (reader != null)
+ {
+ in = new ReaderInputStream(reader);
+ }
+ }
+ }
+ else if (source != null && source instanceof SAXSource)
+ {
+ SAXSource ss = (SAXSource) source;
+ InputSource input = ss.getInputSource();
+ if (input != null)
+ {
+ if (systemId == null)
+ systemId = input.getSystemId();
+ XMLReader reader = ss.getXMLReader();
+ if (reader != null)
+ return parse(input, reader);
+ }
+ }
+ if (in == null)
+ {
+ URL url = resolveURL(systemId, base, href);
+ if (url != null)
+ {
+ systemId = url.toString();
+ node = nodeCache.get(systemId);
+ // Is the resource up to date?
+ URLConnection conn = url.openConnection();
+ Long llm = lastModifiedCache.get(systemId);
+ if (llm != null)
+ {
+ lastLastModified = llm.longValue();
+ conn.setIfModifiedSince(lastLastModified);
+ }
+ conn.connect();
+ lastModified = conn.getLastModified();
+ if (node != null &&
+ lastModified > 0L &&
+ lastModified <= lastLastModified)
+ {
+ // Resource unchanged
+ return new DOMSource(node, systemId);
+ }
+ else
+ {
+ // Resource new or modified
+ in = conn.getInputStream();
+ nodeCache.put(systemId, node);
+ lastModifiedCache.put(systemId, new Long(lastModified));
+ }
+ }
+ else
+ {
+ throw new TransformerException("can't resolve URL: " +
+ systemId);
+ }
+ }
+ InputSource input = new InputSource(in);
+ input.setSystemId(systemId);
+ DocumentBuilder builder = getDocumentBuilder();
+ node = builder.parse(input);
+ return new DOMSource(node, systemId);
+ }
+ catch (IOException e)
+ {
+ throw new TransformerException(e);
+ }
+ catch (SAXException e)
+ {
+ throw new TransformerException(e);
+ }
+ }
+
+ URL resolveURL(String systemId, String base, String href)
+ throws IOException
+ {
+ URL url = null;
+ try
+ {
+ if (systemId != null)
+ {
+ try
+ {
+ url = new URL(systemId);
+ }
+ catch (MalformedURLException e)
+ {
+ // Try building from base + href
+ }
+ }
+ if (url == null)
+ {
+ if (base != null)
+ {
+ URL baseURL = new URL(base);
+ url = new URL(baseURL, href);
+ }
+ else if (href != null)
+ {
+ url = new URL(href);
+ }
+ else
+ {
+ // See below
+ throw new MalformedURLException(systemId);
+ }
+ }
+ return url;
+ }
+ catch (MalformedURLException e)
+ {
+ // Fall back to local filesystem
+ File file = null;
+ if (href == null)
+ {
+ href = systemId;
+ }
+ if (base != null)
+ {
+ int lsi = base.lastIndexOf(File.separatorChar);
+ if (lsi != -1 && lsi < base.length() - 1)
+ {
+ base = base.substring(0, lsi);
+ }
+ File baseFile = new File(base);
+ file = new File(baseFile, href);
+ }
+ else if (href != null)
+ {
+ file = new File(href);
+ }
+ return (file == null) ? null : file.toURL();
+ }
+ }
+
+ DocumentBuilder getDocumentBuilder()
+ throws TransformerException
+ {
+ try
+ {
+ if (builder == null)
+ {
+ DocumentBuilderFactory factory =
+ DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setExpandEntityReferences(true);
+ builder = factory.newDocumentBuilder();
+ }
+ if (userResolver != null)
+ {
+ builder.setEntityResolver(new URIResolverEntityResolver(userResolver));
+ }
+ if (userListener != null)
+ {
+ builder.setErrorHandler(new ErrorListenerErrorHandler(userListener));
+ }
+ return builder;
+ }
+ catch (Exception e)
+ {
+ throw new TransformerException(e);
+ }
+ }
+
+ DOMSource parse(InputSource source, XMLReader reader)
+ throws SAXException, IOException
+ {
+ SAXEventSink eventSink = new SAXEventSink();
+ eventSink.setReader(reader);
+ eventSink.setNamespaceAware(true);
+ reader.setContentHandler(eventSink);
+ reader.setDTDHandler(eventSink);
+ reader.setProperty("http://xml.org/sax/properties/lexical-handler",
+ eventSink);
+ reader.setProperty("http://xml.org/sax/properties/declaration-handler",
+ eventSink);
+ // XXX entityResolver
+ // XXX errorHandler
+ reader.parse(source);
+ Document doc = eventSink.getDocument();
+ String systemId = source.getSystemId();
+ if (systemId != null && doc instanceof DomDocument)
+ ((DomDocument) doc).setDocumentURI(systemId);
+ return new DOMSource(doc, systemId);
+ }
+
+}
diff --git a/libjava/classpath/gnu/xml/transform/package.html b/libjava/classpath/gnu/xml/transform/package.html
new file mode 100644
index 000000000..d4355966c
--- /dev/null
+++ b/libjava/classpath/gnu/xml/transform/package.html
@@ -0,0 +1,77 @@
+<html>
+<body>
+
+<h1>GNU JAXP XSL transformer</h1>
+
+<div>
+This package contains a Java XSL transformer compliant with the JAXP
+specification. It depends on the GNU DOM and XPath implementations, and
+will generate GNU DOM nodes unless a specific target from another
+implementation was given. It understands DOM, SAX, and stream sources
+and result sinks and supports these JAXP features.
+</div>
+
+<div>
+To use this transformer, set the system property
+<code>javax.xml.transform.TransformerFactory</code> to the value
+<code>gnu.xml.transform.TransformerFactoryImpl</code>. You can then
+instantiate <a href='TransformerFactory.html'>TransformerFactory</a>
+and transformers in the ordinary manner. Reuse of stylesheets is
+supported using the JAXP <a href='Templates.html'>Templates</a>
+mechanism.
+</div>
+
+<h3>Architecture</h3>
+
+<div>
+When given a stylesheet source, this implementation compiles it internally
+into a Stylesheet object, which is a container for templates and state.
+Each stylesheet instruction is represented by a subclass of TemplateNode,
+which is arranged in a directed graph: each TemplateNode has a reference
+to its first child and the next node.
+</div>
+
+<div>
+The transformation process consists of identifying the Template that matches
+the root of the source context, and calling <code>apply</code> on its
+corresponding TemplateNode. This in turn processes its children and next
+TemplateNode, depending on the semantics of each node type.
+</div>
+
+<div>
+Template nodes may reference XPath expressions or patterns. These are fully
+compiled to objects of type <a href='../xpath/Expr.html'>Expr</a> at the
+time the stylesheet is compiled.
+</div>
+
+<h3>Conformance</h3>
+
+<div>
+This implementation is feature complete, but the XSLT specification is
+large and there are still many bugs that need to be ironed out. It has
+been tested against the OASIS XSLT TC test suite, comprising unit tests
+from the Xalan project and Microsoft. Conformance to these unit tests
+is approximately 70% at the current time, although normal usage of the
+transformer should involve relatively few surprises (the test suite is
+designed to test very complex and obscure functionality).
+</div>
+
+<h3>Known bugs</h3>
+
+<ul>
+<li>When reusing stylesheets using the JAXP Templates mechanism, XSL
+<code>apply-imports</code> instructions will not work.</li>
+<li>XPath filter expressions do not always work as expected (this is a
+problem with the GNU XPath implementation rather than the transformer).
+This can result in problems with the <code>position()</code> function,
+as well as <code>select</code> expressions and numbering.</li>
+</ul>
+
+<div>
+Obviously we'd like to improve conformance and fix these bugs. If you're
+interested in working on any of these issues please
+<a href='mailto:classpathx-xml@gnu.org'>contact us</a>.
+</div>
+
+</body>
+</html>