diff options
Diffstat (limited to 'libjava/classpath/gnu/xml/transform')
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 ¤ 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> + * <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 <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("<"); + } + else if (c == '>') + { + if (buf == null) + buf = new CPStringBuilder(text.substring(0, i)); + buf.append(">"); + } + 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("&"); + } + } + else if (c == '\'' && inAttr) + { + if (buf == null) + buf = new CPStringBuilder(text.substring(0, i)); + if (mode == Stylesheet.OUTPUT_HTML) + // HTML does not define ', use character entity ref + buf.append("'"); + else + buf.append("'"); + } + else if (c == '"' && inAttr) + { + if (buf == null) + buf = new CPStringBuilder(text.substring(0, i)); + buf.append("""); + } + 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> |