/* * reserved comment block * DO NOT REMOVE OR ALTER! */ /* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id: BasisLibrary.java,v 1.6 2006/06/20 21:51:58 spericas Exp $ */ package com.sun.org.apache.xalan.internal.xsltc.runtime; import com.sun.org.apache.xalan.internal.utils.SecuritySupport; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.FieldPosition; import java.text.MessageFormat; import java.text.NumberFormat; import java.util.Locale; import java.util.ResourceBundle; import javax.xml.transform.dom.DOMSource; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.Translet; import com.sun.org.apache.xalan.internal.xsltc.dom.AbsoluteIterator; import com.sun.org.apache.xml.internal.dtm.Axis; import com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter; import com.sun.org.apache.xalan.internal.xsltc.dom.MultiDOM; import com.sun.org.apache.xalan.internal.xsltc.dom.SingletonIterator; import com.sun.org.apache.xalan.internal.xsltc.dom.StepIterator; import com.sun.org.apache.xalan.internal.xsltc.dom.ArrayNodeListIterator; import com.sun.org.apache.xml.internal.dtm.DTM; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.dtm.DTMManager; import com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBase; import com.sun.org.apache.xml.internal.dtm.ref.DTMNodeProxy; import org.w3c.dom.DOMException; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import com.sun.org.apache.xml.internal.serializer.NamespaceMappings; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import com.sun.org.apache.xml.internal.utils.XML11Char; /** * Standard XSLT functions. All standard functions expect the current node * and the DOM as their last two arguments. */ public final class BasisLibrary { private final static String EMPTYSTRING = ""; /** * Re-use a single instance of StringBuffer (per thread) in the basis library. * StringBuilder is better, however, DecimalFormat only accept StringBuffer */ private static final ThreadLocal threadLocalStringBuilder = new ThreadLocal () { @Override protected StringBuilder initialValue() { return new StringBuilder(); } }; /** * ThreadLocal for StringBuffer used */ private static final ThreadLocal threadLocalStringBuffer = new ThreadLocal () { @Override protected StringBuffer initialValue() { return new StringBuffer(); } }; /** * Standard function count(node-set) */ public static int countF(DTMAxisIterator iterator) { return(iterator.getLast()); } /** * Standard function position() * @deprecated This method exists only for backwards compatibility with old * translets. New code should not reference it. */ public static int positionF(DTMAxisIterator iterator) { return iterator.isReverse() ? iterator.getLast() - iterator.getPosition() + 1 : iterator.getPosition(); } /** * XSLT Standard function sum(node-set). * stringToDouble is inlined */ public static double sumF(DTMAxisIterator iterator, DOM dom) { try { double result = 0.0; int node; while ((node = iterator.next()) != DTMAxisIterator.END) { result += Double.parseDouble(dom.getStringValueX(node)); } return result; } catch (NumberFormatException e) { return Double.NaN; } } /** * XSLT Standard function string() */ public static String stringF(int node, DOM dom) { return dom.getStringValueX(node); } /** * XSLT Standard function string(value) */ public static String stringF(Object obj, DOM dom) { if (obj instanceof DTMAxisIterator) { return dom.getStringValueX(((DTMAxisIterator)obj).reset().next()); } else if (obj instanceof Node) { return dom.getStringValueX(((Node)obj).node); } else if (obj instanceof DOM) { return ((DOM)obj).getStringValue(); } else { return obj.toString(); } } /** * XSLT Standard function string(value) */ public static String stringF(Object obj, int node, DOM dom) { if (obj instanceof DTMAxisIterator) { return dom.getStringValueX(((DTMAxisIterator)obj).reset().next()); } else if (obj instanceof Node) { return dom.getStringValueX(((Node)obj).node); } else if (obj instanceof DOM) { // When the first argument is a DOM we want the whole // DOM and not just a single node - that would not make sense. //return ((DOM)obj).getStringValueX(node); return ((DOM)obj).getStringValue(); } else if (obj instanceof Double) { Double d = (Double)obj; final String result = d.toString(); final int length = result.length(); if ((result.charAt(length-2)=='.') && (result.charAt(length-1) == '0')) return result.substring(0, length-2); else return result; } else { return obj != null ? obj.toString() : ""; } } /** * XSLT Standard function number() */ public static double numberF(int node, DOM dom) { return stringToReal(dom.getStringValueX(node)); } /** * XSLT Standard function number(value) */ public static double numberF(Object obj, DOM dom) { if (obj instanceof Double) { return ((Double) obj).doubleValue(); } else if (obj instanceof Integer) { return ((Integer) obj).doubleValue(); } else if (obj instanceof Boolean) { return ((Boolean) obj).booleanValue() ? 1.0 : 0.0; } else if (obj instanceof String) { return stringToReal((String) obj); } else if (obj instanceof DTMAxisIterator) { DTMAxisIterator iter = (DTMAxisIterator) obj; return stringToReal(dom.getStringValueX(iter.reset().next())); } else if (obj instanceof Node) { return stringToReal(dom.getStringValueX(((Node) obj).node)); } else if (obj instanceof DOM) { return stringToReal(((DOM) obj).getStringValue()); } else { final String className = obj.getClass().getName(); runTimeError(INVALID_ARGUMENT_ERR, className, "number()"); return 0.0; } } /** * XSLT Standard function round() */ public static double roundF(double d) { return (d<-0.5 || d>0.0)?Math.floor(d+0.5):((d==0.0)? d:(Double.isNaN(d)?Double.NaN:-0.0)); } /** * XSLT Standard function boolean() */ public static boolean booleanF(Object obj) { if (obj instanceof Double) { final double temp = ((Double) obj).doubleValue(); return temp != 0.0 && !Double.isNaN(temp); } else if (obj instanceof Integer) { return ((Integer) obj).doubleValue() != 0; } else if (obj instanceof Boolean) { return ((Boolean) obj).booleanValue(); } else if (obj instanceof String) { return !((String) obj).equals(EMPTYSTRING); } else if (obj instanceof DTMAxisIterator) { DTMAxisIterator iter = (DTMAxisIterator) obj; return iter.reset().next() != DTMAxisIterator.END; } else if (obj instanceof Node) { return true; } else if (obj instanceof DOM) { String temp = ((DOM) obj).getStringValue(); return !temp.equals(EMPTYSTRING); } else { final String className = obj.getClass().getName(); runTimeError(INVALID_ARGUMENT_ERR, className, "boolean()"); } return false; } /** * XSLT Standard function substring(). Must take a double because of * conversions resulting into NaNs and rounding. */ public static String substringF(String value, double start) { if (Double.isNaN(start)) return(EMPTYSTRING); final int strlen = value.length(); int istart = (int)Math.round(start) - 1; if (istart > strlen) return(EMPTYSTRING); if (istart < 1) istart = 0; try { return value.substring(istart); } catch (IndexOutOfBoundsException e) { runTimeError(RUN_TIME_INTERNAL_ERR, "substring()"); return null; } } /** * XSLT Standard function substring(). Must take a double because of * conversions resulting into NaNs and rounding. */ public static String substringF(String value, double start, double length) { if (Double.isInfinite(start) || Double.isNaN(start) || Double.isNaN(length)) return(EMPTYSTRING); int istart = (int)Math.round(start) - 1; final int isum; if (Double.isInfinite(length)) isum = Integer.MAX_VALUE; else isum = istart + (int)Math.round(length); final int strlen = value.length(); if (isum < 0 || istart > strlen) return(EMPTYSTRING); if (istart < 0) istart = 0; try { if (isum > strlen) return value.substring(istart); else return value.substring(istart, isum); } catch (IndexOutOfBoundsException e) { runTimeError(RUN_TIME_INTERNAL_ERR, "substring()"); return null; } } /** * XSLT Standard function substring-after(). */ public static String substring_afterF(String value, String substring) { final int index = value.indexOf(substring); if (index >= 0) return value.substring(index + substring.length()); else return EMPTYSTRING; } /** * XSLT Standard function substring-before(). */ public static String substring_beforeF(String value, String substring) { final int index = value.indexOf(substring); if (index >= 0) return value.substring(0, index); else return EMPTYSTRING; } /** * XSLT Standard function translate(). */ public static String translateF(String value, String from, String to) { final int tol = to.length(); final int froml = from.length(); final int valuel = value.length(); final StringBuilder result = threadLocalStringBuilder.get(); result.setLength(0); for (int j, i = 0; i < valuel; i++) { final char ch = value.charAt(i); for (j = 0; j < froml; j++) { if (ch == from.charAt(j)) { if (j < tol) result.append(to.charAt(j)); break; } } if (j == froml) result.append(ch); } return result.toString(); } /** * XSLT Standard function normalize-space(). */ public static String normalize_spaceF(int node, DOM dom) { return normalize_spaceF(dom.getStringValueX(node)); } /** * XSLT Standard function normalize-space(string). */ public static String normalize_spaceF(String value) { int i = 0, n = value.length(); StringBuilder result = threadLocalStringBuilder.get(); result.setLength(0); while (i < n && isWhiteSpace(value.charAt(i))) i++; while (true) { while (i < n && !isWhiteSpace(value.charAt(i))) { result.append(value.charAt(i++)); } if (i == n) break; while (i < n && isWhiteSpace(value.charAt(i))) { i++; } if (i < n) result.append(' '); } return result.toString(); } /** * XSLT Standard function generate-id(). */ public static String generate_idF(int node) { if (node > 0) // Only generate ID if node exists return "N" + node; else // Otherwise return an empty string return EMPTYSTRING; } /** * utility function for calls to local-name(). */ public static String getLocalName(String value) { int idx = value.lastIndexOf(':'); if (idx >= 0) value = value.substring(idx + 1); idx = value.lastIndexOf('@'); if (idx >= 0) value = value.substring(idx + 1); return(value); } /** * External functions that cannot be resolved are replaced with a call * to this method. This method will generate a runtime errors. A good * stylesheet checks whether the function exists using conditional * constructs, and never really tries to call it if it doesn't exist. * But simple stylesheets may result in a call to this method. * The compiler should generate a warning if it encounters a call to * an unresolved external function. */ public static void unresolved_externalF(String name) { runTimeError(EXTERNAL_FUNC_ERR, name); } /** * Utility function to throw a runtime error on the use of an extension * function when the secure processing feature is set to true. */ public static void unallowed_extension_functionF(String name) { runTimeError(UNALLOWED_EXTENSION_FUNCTION_ERR, name); } /** * Utility function to throw a runtime error on the use of an extension * element when the secure processing feature is set to true. */ public static void unallowed_extension_elementF(String name) { runTimeError(UNALLOWED_EXTENSION_ELEMENT_ERR, name); } /** * Utility function to throw a runtime error for an unsupported element. * * This is only used in forward-compatibility mode, when the control flow * cannot be determined. In 1.0 mode, the error message is emitted at * compile time. */ public static void unsupported_ElementF(String qname, boolean isExtension) { if (isExtension) runTimeError(UNSUPPORTED_EXT_ERR, qname); else runTimeError(UNSUPPORTED_XSL_ERR, qname); } /** * XSLT Standard function namespace-uri(node-set). */ public static String namespace_uriF(DTMAxisIterator iter, DOM dom) { return namespace_uriF(iter.next(), dom); } /** * XSLT Standard function system-property(name) */ public static String system_propertyF(String name) { if (name.equals("xsl:version")) return("1.0"); if (name.equals("xsl:vendor")) return("Apache Software Foundation (Xalan XSLTC)"); if (name.equals("xsl:vendor-url")) return("http://xml.apache.org/xalan-j"); runTimeError(INVALID_ARGUMENT_ERR, name, "system-property()"); return(EMPTYSTRING); } /** * XSLT Standard function namespace-uri(). */ public static String namespace_uriF(int node, DOM dom) { final String value = dom.getNodeName(node); final int colon = value.lastIndexOf(':'); if (colon >= 0) return value.substring(0, colon); else return EMPTYSTRING; } /** * Implements the object-type() extension function. * * @see EXSLT */ public static String objectTypeF(Object obj) { if (obj instanceof String) return "string"; else if (obj instanceof Boolean) return "boolean"; else if (obj instanceof Number) return "number"; else if (obj instanceof DOM) return "RTF"; else if (obj instanceof DTMAxisIterator) return "node-set"; else return "unknown"; } /** * Implements the nodeset() extension function. */ public static DTMAxisIterator nodesetF(Object obj) { if (obj instanceof DOM) { //final DOMAdapter adapter = (DOMAdapter) obj; final DOM dom = (DOM)obj; return new SingletonIterator(dom.getDocument(), true); } else if (obj instanceof DTMAxisIterator) { return (DTMAxisIterator) obj; } else { final String className = obj.getClass().getName(); runTimeError(DATA_CONVERSION_ERR, "node-set", className); return null; } } //-- Begin utility functions private static boolean isWhiteSpace(char ch) { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; } private static boolean compareStrings(String lstring, String rstring, int op, DOM dom) { switch (op) { case Operators.EQ: return lstring.equals(rstring); case Operators.NE: return !lstring.equals(rstring); case Operators.GT: return numberF(lstring, dom) > numberF(rstring, dom); case Operators.LT: return numberF(lstring, dom) < numberF(rstring, dom); case Operators.GE: return numberF(lstring, dom) >= numberF(rstring, dom); case Operators.LE: return numberF(lstring, dom) <= numberF(rstring, dom); default: runTimeError(RUN_TIME_INTERNAL_ERR, "compare()"); return false; } } /** * Utility function: node-set/node-set compare. */ public static boolean compare(DTMAxisIterator left, DTMAxisIterator right, int op, DOM dom) { int lnode; left.reset(); while ((lnode = left.next()) != DTMAxisIterator.END) { final String lvalue = dom.getStringValueX(lnode); int rnode; right.reset(); while ((rnode = right.next()) != DTMAxisIterator.END) { // String value must be the same if both nodes are the same if (lnode == rnode) { if (op == Operators.EQ) { return true; } else if (op == Operators.NE) { continue; } } if (compareStrings(lvalue, dom.getStringValueX(rnode), op, dom)) { return true; } } } return false; } public static boolean compare(int node, DTMAxisIterator iterator, int op, DOM dom) { //iterator.reset(); int rnode; String value; switch(op) { case Operators.EQ: rnode = iterator.next(); if (rnode != DTMAxisIterator.END) { value = dom.getStringValueX(node); do { if (node == rnode || value.equals(dom.getStringValueX(rnode))) { return true; } } while ((rnode = iterator.next()) != DTMAxisIterator.END); } break; case Operators.NE: rnode = iterator.next(); if (rnode != DTMAxisIterator.END) { value = dom.getStringValueX(node); do { if (node != rnode && !value.equals(dom.getStringValueX(rnode))) { return true; } } while ((rnode = iterator.next()) != DTMAxisIterator.END); } break; case Operators.LT: // Assume we're comparing document order here while ((rnode = iterator.next()) != DTMAxisIterator.END) { if (rnode > node) return true; } break; case Operators.GT: // Assume we're comparing document order here while ((rnode = iterator.next()) != DTMAxisIterator.END) { if (rnode < node) return true; } break; } return(false); } /** * Utility function: node-set/number compare. */ public static boolean compare(DTMAxisIterator left, final double rnumber, final int op, DOM dom) { int node; //left.reset(); switch (op) { case Operators.EQ: while ((node = left.next()) != DTMAxisIterator.END) { if (numberF(dom.getStringValueX(node), dom) == rnumber) return true; } break; case Operators.NE: while ((node = left.next()) != DTMAxisIterator.END) { if (numberF(dom.getStringValueX(node), dom) != rnumber) return true; } break; case Operators.GT: while ((node = left.next()) != DTMAxisIterator.END) { if (numberF(dom.getStringValueX(node), dom) > rnumber) return true; } break; case Operators.LT: while ((node = left.next()) != DTMAxisIterator.END) { if (numberF(dom.getStringValueX(node), dom) < rnumber) return true; } break; case Operators.GE: while ((node = left.next()) != DTMAxisIterator.END) { if (numberF(dom.getStringValueX(node), dom) >= rnumber) return true; } break; case Operators.LE: while ((node = left.next()) != DTMAxisIterator.END) { if (numberF(dom.getStringValueX(node), dom) <= rnumber) return true; } break; default: runTimeError(RUN_TIME_INTERNAL_ERR, "compare()"); } return false; } /** * Utility function: node-set/string comparison. */ public static boolean compare(DTMAxisIterator left, final String rstring, int op, DOM dom) { int node; //left.reset(); while ((node = left.next()) != DTMAxisIterator.END) { if (compareStrings(dom.getStringValueX(node), rstring, op, dom)) { return true; } } return false; } public static boolean compare(Object left, Object right, int op, DOM dom) { boolean result = false; boolean hasSimpleArgs = hasSimpleType(left) && hasSimpleType(right); if (op != Operators.EQ && op != Operators.NE) { // If node-boolean comparison -> convert node to boolean if (left instanceof Node || right instanceof Node) { if (left instanceof Boolean) { right = new Boolean(booleanF(right)); hasSimpleArgs = true; } if (right instanceof Boolean) { left = new Boolean(booleanF(left)); hasSimpleArgs = true; } } if (hasSimpleArgs) { switch (op) { case Operators.GT: return numberF(left, dom) > numberF(right, dom); case Operators.LT: return numberF(left, dom) < numberF(right, dom); case Operators.GE: return numberF(left, dom) >= numberF(right, dom); case Operators.LE: return numberF(left, dom) <= numberF(right, dom); default: runTimeError(RUN_TIME_INTERNAL_ERR, "compare()"); } } // falls through } if (hasSimpleArgs) { if (left instanceof Boolean || right instanceof Boolean) { result = booleanF(left) == booleanF(right); } else if (left instanceof Double || right instanceof Double || left instanceof Integer || right instanceof Integer) { result = numberF(left, dom) == numberF(right, dom); } else { // compare them as strings result = stringF(left, dom).equals(stringF(right, dom)); } if (op == Operators.NE) { result = !result; } } else { if (left instanceof Node) { left = new SingletonIterator(((Node)left).node); } if (right instanceof Node) { right = new SingletonIterator(((Node)right).node); } if (hasSimpleType(left) || left instanceof DOM && right instanceof DTMAxisIterator) { // swap operands and operator final Object temp = right; right = left; left = temp; op = Operators.swapOp(op); } if (left instanceof DOM) { if (right instanceof Boolean) { result = ((Boolean)right).booleanValue(); return result == (op == Operators.EQ); } final String sleft = ((DOM)left).getStringValue(); if (right instanceof Number) { result = ((Number)right).doubleValue() == stringToReal(sleft); } else if (right instanceof String) { result = sleft.equals((String)right); } else if (right instanceof DOM) { result = sleft.equals(((DOM)right).getStringValue()); } if (op == Operators.NE) { result = !result; } return result; } // Next, node-set/t for t in {real, string, node-set, result-tree} DTMAxisIterator iter = ((DTMAxisIterator)left).reset(); if (right instanceof DTMAxisIterator) { result = compare(iter, (DTMAxisIterator)right, op, dom); } else if (right instanceof String) { result = compare(iter, (String)right, op, dom); } else if (right instanceof Number) { final double temp = ((Number)right).doubleValue(); result = compare(iter, temp, op, dom); } else if (right instanceof Boolean) { boolean temp = ((Boolean)right).booleanValue(); result = (iter.reset().next() != DTMAxisIterator.END) == temp; } else if (right instanceof DOM) { result = compare(iter, ((DOM)right).getStringValue(), op, dom); } else if (right == null) { return(false); } else { final String className = right.getClass().getName(); runTimeError(INVALID_ARGUMENT_ERR, className, "compare()"); } } return result; } /** * Utility function: used to test context node's language */ public static boolean testLanguage(String testLang, DOM dom, int node) { // language for context node (if any) String nodeLang = dom.getLanguage(node); if (nodeLang == null) return(false); else nodeLang = nodeLang.toLowerCase(); // compare context node's language agains test language testLang = testLang.toLowerCase(); if (testLang.length() == 2) { return(nodeLang.startsWith(testLang)); } else { return(nodeLang.equals(testLang)); } } private static boolean hasSimpleType(Object obj) { return obj instanceof Boolean || obj instanceof Double || obj instanceof Integer || obj instanceof String || obj instanceof Node || obj instanceof DOM; } /** * Utility function: used in StringType to convert a string to a real. */ public static double stringToReal(String s) { try { return Double.valueOf(s).doubleValue(); } catch (NumberFormatException e) { return Double.NaN; } } /** * Utility function: used in StringType to convert a string to an int. */ public static int stringToInt(String s) { try { return Integer.parseInt(s); } catch (NumberFormatException e) { return(-1); // ??? } } private static final int DOUBLE_FRACTION_DIGITS = 340; private static final double lowerBounds = 0.001; private static final double upperBounds = 10000000; private static DecimalFormat defaultFormatter, xpathFormatter; private static String defaultPattern = ""; static { NumberFormat f = NumberFormat.getInstance(Locale.getDefault()); defaultFormatter = (f instanceof DecimalFormat) ? (DecimalFormat) f : new DecimalFormat(); // Set max fraction digits so that truncation does not occur. Setting // the max to Integer.MAX_VALUE may cause problems with some JDK's. defaultFormatter.setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS); defaultFormatter.setMinimumFractionDigits(0); defaultFormatter.setMinimumIntegerDigits(1); defaultFormatter.setGroupingUsed(false); // This formatter is used to convert numbers according to the XPath // 1.0 syntax which ignores locales (http://www.w3.org/TR/xpath#NT-Number) xpathFormatter = new DecimalFormat("", new DecimalFormatSymbols(Locale.US)); xpathFormatter.setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS); xpathFormatter.setMinimumFractionDigits(0); xpathFormatter.setMinimumIntegerDigits(1); xpathFormatter.setGroupingUsed(false); } /** * Utility function: used in RealType to convert a real to a string. * Removes the decimal if null. Uses a specialized formatter object * for very large and very small numbers that ignores locales, thus * using always using "." as a decimal separator. */ public static String realToString(double d) { final double m = Math.abs(d); if ((m >= lowerBounds) && (m < upperBounds)) { final String result = Double.toString(d); final int length = result.length(); // Remove leading zeros. if ((result.charAt(length-2) == '.') && (result.charAt(length-1) == '0')) return result.substring(0, length-2); else return result; } else { if (!Double.isFinite(d)) return(Double.toString(d)); //Convert -0.0 to +0.0 other values remains the same d = d + 0.0; // Use the XPath formatter to ignore locales StringBuffer result = threadLocalStringBuffer.get(); result.setLength(0); xpathFormatter.format(d, result, _fieldPosition); return result.toString(); } } /** * Utility function: used in RealType to convert a real to an integer */ public static int realToInt(double d) { return (int)d; } /** * Utility function: used to format/adjust a double to a string. The * DecimalFormat object comes from the 'formatSymbols' hashtable in * AbstractTranslet. */ private static FieldPosition _fieldPosition = new FieldPosition(0); public static String formatNumber(double number, String pattern, DecimalFormat formatter) { // bugzilla fix 12813 if (formatter == null) { formatter = defaultFormatter; } try { StringBuffer result = threadLocalStringBuffer.get(); result.setLength(0); if (pattern != defaultPattern) { formatter.applyLocalizedPattern(pattern); } formatter.format(number, result, _fieldPosition); return result.toString(); } catch (IllegalArgumentException e) { runTimeError(FORMAT_NUMBER_ERR, Double.toString(number), pattern); return(EMPTYSTRING); } } /** * Utility function: used to convert references to node-sets. If the * obj is an instanceof Node then create a singleton iterator. */ public static DTMAxisIterator referenceToNodeSet(Object obj) { // Convert var/param -> node if (obj instanceof Node) { return(new SingletonIterator(((Node)obj).node)); } // Convert var/param -> node-set else if (obj instanceof DTMAxisIterator) { return(((DTMAxisIterator)obj).cloneIterator().reset()); } else { final String className = obj.getClass().getName(); runTimeError(DATA_CONVERSION_ERR, className, "node-set"); return null; } } /** * Utility function: used to convert reference to org.w3c.dom.NodeList. */ public static NodeList referenceToNodeList(Object obj, DOM dom) { if (obj instanceof Node || obj instanceof DTMAxisIterator) { DTMAxisIterator iter = referenceToNodeSet(obj); return dom.makeNodeList(iter); } else if (obj instanceof DOM) { dom = (DOM)obj; return dom.makeNodeList(DTMDefaultBase.ROOTNODE); } else { final String className = obj.getClass().getName(); runTimeError(DATA_CONVERSION_ERR, className, "org.w3c.dom.NodeList"); return null; } } /** * Utility function: used to convert reference to org.w3c.dom.Node. */ public static org.w3c.dom.Node referenceToNode(Object obj, DOM dom) { if (obj instanceof Node || obj instanceof DTMAxisIterator) { DTMAxisIterator iter = referenceToNodeSet(obj); return dom.makeNode(iter); } else if (obj instanceof DOM) { dom = (DOM)obj; DTMAxisIterator iter = dom.getChildren(DTMDefaultBase.ROOTNODE); return dom.makeNode(iter); } else { final String className = obj.getClass().getName(); runTimeError(DATA_CONVERSION_ERR, className, "org.w3c.dom.Node"); return null; } } /** * Utility function: used to convert reference to long. */ public static long referenceToLong(Object obj) { if (obj instanceof Number) { return ((Number) obj).longValue(); // handles Integer and Double } else { final String className = obj.getClass().getName(); runTimeError(DATA_CONVERSION_ERR, className, Long.TYPE); return 0; } } /** * Utility function: used to convert reference to double. */ public static double referenceToDouble(Object obj) { if (obj instanceof Number) { return ((Number) obj).doubleValue(); // handles Integer and Double } else { final String className = obj.getClass().getName(); runTimeError(DATA_CONVERSION_ERR, className, Double.TYPE); return 0; } } /** * Utility function: used to convert reference to boolean. */ public static boolean referenceToBoolean(Object obj) { if (obj instanceof Boolean) { return ((Boolean) obj).booleanValue(); } else { final String className = obj.getClass().getName(); runTimeError(DATA_CONVERSION_ERR, className, Boolean.TYPE); return false; } } /** * Utility function: used to convert reference to String. */ public static String referenceToString(Object obj, DOM dom) { if (obj instanceof String) { return (String) obj; } else if (obj instanceof DTMAxisIterator) { return dom.getStringValueX(((DTMAxisIterator)obj).reset().next()); } else if (obj instanceof Node) { return dom.getStringValueX(((Node)obj).node); } else if (obj instanceof DOM) { return ((DOM) obj).getStringValue(); } else { final String className = obj.getClass().getName(); runTimeError(DATA_CONVERSION_ERR, className, String.class); return null; } } /** * Utility function used to convert a w3c Node into an internal DOM iterator. */ public static DTMAxisIterator node2Iterator(org.w3c.dom.Node node, Translet translet, DOM dom) { final org.w3c.dom.Node inNode = node; // Create a dummy NodeList which only contains the given node to make // use of the nodeList2Iterator() interface. org.w3c.dom.NodeList nodelist = new org.w3c.dom.NodeList() { public int getLength() { return 1; } public org.w3c.dom.Node item(int index) { if (index == 0) return inNode; else return null; } }; return nodeList2Iterator(nodelist, translet, dom); } /** * In a perfect world, this would be the implementation for * nodeList2Iterator. In reality, though, this causes a * ClassCastException in getDTMHandleFromNode because SAXImpl is * not an instance of DOM2DTM. So we use the more lengthy * implementation below until this issue has been addressed. * * @see org.apache.xml.dtm.ref.DTMManagerDefault#getDTMHandleFromNode */ private static DTMAxisIterator nodeList2IteratorUsingHandleFromNode( org.w3c.dom.NodeList nodeList, Translet translet, DOM dom) { final int n = nodeList.getLength(); final int[] dtmHandles = new int[n]; DTMManager dtmManager = null; if (dom instanceof MultiDOM) dtmManager = ((MultiDOM) dom).getDTMManager(); for (int i = 0; i < n; ++i) { org.w3c.dom.Node node = nodeList.item(i); int handle; if (dtmManager != null) { handle = dtmManager.getDTMHandleFromNode(node); } else if (node instanceof DTMNodeProxy && ((DTMNodeProxy) node).getDTM() == dom) { handle = ((DTMNodeProxy) node).getDTMNodeNumber(); } else { runTimeError(RUN_TIME_INTERNAL_ERR, "need MultiDOM"); return null; } dtmHandles[i] = handle; System.out.println("Node " + i + " has handle 0x" + Integer.toString(handle, 16)); } return new ArrayNodeListIterator(dtmHandles); } /** * Utility function used to convert a w3c NodeList into a internal * DOM iterator. */ public static DTMAxisIterator nodeList2Iterator( org.w3c.dom.NodeList nodeList, Translet translet, DOM dom) { // First pass: build w3c DOM for all nodes not proxied from our DOM. // // Notice: this looses some (esp. parent) context for these nodes, // so some way to wrap the original nodes inside a DTMAxisIterator // might be preferable in the long run. int n = 0; // allow for change in list length, just in case. Document doc = null; DTMManager dtmManager = null; int[] proxyNodes = new int[nodeList.getLength()]; if (dom instanceof MultiDOM) dtmManager = ((MultiDOM) dom).getDTMManager(); for (int i = 0; i < nodeList.getLength(); ++i) { org.w3c.dom.Node node = nodeList.item(i); if (node instanceof DTMNodeProxy) { DTMNodeProxy proxy = (DTMNodeProxy)node; DTM nodeDTM = proxy.getDTM(); int handle = proxy.getDTMNodeNumber(); boolean isOurDOM = (nodeDTM == dom); if (!isOurDOM && dtmManager != null) { try { isOurDOM = (nodeDTM == dtmManager.getDTM(handle)); } catch (ArrayIndexOutOfBoundsException e) { // invalid node handle, so definitely not our doc } } if (isOurDOM) { proxyNodes[i] = handle; ++n; continue; } } proxyNodes[i] = DTM.NULL; int nodeType = node.getNodeType(); if (doc == null) { if (dom instanceof MultiDOM == false) { runTimeError(RUN_TIME_INTERNAL_ERR, "need MultiDOM"); return null; } try { AbstractTranslet at = (AbstractTranslet) translet; doc = at.newDocument("", "__top__"); } catch (javax.xml.parsers.ParserConfigurationException e) { runTimeError(RUN_TIME_INTERNAL_ERR, e.getMessage()); return null; } } // Use one dummy element as container for each node of the // list. That way, it is easier to detect resp. avoid // funny things which change the number of nodes, // e.g. auto-concatenation of text nodes. Element mid; switch (nodeType) { case org.w3c.dom.Node.ELEMENT_NODE: case org.w3c.dom.Node.TEXT_NODE: case org.w3c.dom.Node.CDATA_SECTION_NODE: case org.w3c.dom.Node.COMMENT_NODE: case org.w3c.dom.Node.ENTITY_REFERENCE_NODE: case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE: mid = doc.createElementNS(null, "__dummy__"); mid.appendChild(doc.importNode(node, true)); doc.getDocumentElement().appendChild(mid); ++n; break; case org.w3c.dom.Node.ATTRIBUTE_NODE: // The mid element also serves as a container for // attributes, avoiding problems with conflicting // attributes or node order. mid = doc.createElementNS(null, "__dummy__"); mid.setAttributeNodeNS((Attr)doc.importNode(node, true)); doc.getDocumentElement().appendChild(mid); ++n; break; default: // Better play it safe for all types we aren't sure we know // how to deal with. runTimeError(RUN_TIME_INTERNAL_ERR, "Don't know how to convert node type " + nodeType); } } // w3cDOM -> DTM -> DOMImpl DTMAxisIterator iter = null, childIter = null, attrIter = null; if (doc != null) { final MultiDOM multiDOM = (MultiDOM) dom; DOM idom = (DOM)dtmManager.getDTM(new DOMSource(doc), false, null, true, false); // Create DOMAdapter and register with MultiDOM DOMAdapter domAdapter = new DOMAdapter(idom, translet.getNamesArray(), translet.getUrisArray(), translet.getTypesArray(), translet.getNamespaceArray()); multiDOM.addDOMAdapter(domAdapter); DTMAxisIterator iter1 = idom.getAxisIterator(Axis.CHILD); DTMAxisIterator iter2 = idom.getAxisIterator(Axis.CHILD); iter = new AbsoluteIterator( new StepIterator(iter1, iter2)); iter.setStartNode(DTMDefaultBase.ROOTNODE); childIter = idom.getAxisIterator(Axis.CHILD); attrIter = idom.getAxisIterator(Axis.ATTRIBUTE); } // Second pass: find DTM handles for every node in the list. int[] dtmHandles = new int[n]; n = 0; for (int i = 0; i < nodeList.getLength(); ++i) { if (proxyNodes[i] != DTM.NULL) { dtmHandles[n++] = proxyNodes[i]; continue; } org.w3c.dom.Node node = nodeList.item(i); DTMAxisIterator iter3 = null; int nodeType = node.getNodeType(); switch (nodeType) { case org.w3c.dom.Node.ELEMENT_NODE: case org.w3c.dom.Node.TEXT_NODE: case org.w3c.dom.Node.CDATA_SECTION_NODE: case org.w3c.dom.Node.COMMENT_NODE: case org.w3c.dom.Node.ENTITY_REFERENCE_NODE: case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE: iter3 = childIter; break; case org.w3c.dom.Node.ATTRIBUTE_NODE: iter3 = attrIter; break; default: // Should not happen, as first run should have got all these throw new InternalRuntimeError("Mismatched cases"); } if (iter3 != null) { iter3.setStartNode(iter.next()); dtmHandles[n] = iter3.next(); // For now, play it self and perform extra checks: if (dtmHandles[n] == DTMAxisIterator.END) throw new InternalRuntimeError("Expected element missing at " + i); if (iter3.next() != DTMAxisIterator.END) throw new InternalRuntimeError("Too many elements at " + i); ++n; } } if (n != dtmHandles.length) throw new InternalRuntimeError("Nodes lost in second pass"); return new ArrayNodeListIterator(dtmHandles); } /** * Utility function used to convert references to DOMs. */ public static DOM referenceToResultTree(Object obj) { try { return ((DOM) obj); } catch (IllegalArgumentException e) { final String className = obj.getClass().getName(); runTimeError(DATA_CONVERSION_ERR, "reference", className); return null; } } /** * Utility function: used with nth position filters to convert a sequence * of nodes to just one single node (the one at position n). */ public static DTMAxisIterator getSingleNode(DTMAxisIterator iterator) { int node = iterator.next(); return(new SingletonIterator(node)); } /** * Utility function: used in xsl:copy. */ private static char[] _characterArray = new char[32]; public static void copy(Object obj, SerializationHandler handler, int node, DOM dom) { try { if (obj instanceof DTMAxisIterator) { DTMAxisIterator iter = (DTMAxisIterator) obj; dom.copy(iter.reset(), handler); } else if (obj instanceof Node) { dom.copy(((Node) obj).node, handler); } else if (obj instanceof DOM) { //((DOM)obj).copy(((com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBase)((DOMAdapter)obj).getDOMImpl()).getDocument(), handler); DOM newDom = (DOM)obj; newDom.copy(newDom.getDocument(), handler); } else { String string = obj.toString(); // or call stringF() final int length = string.length(); if (length > _characterArray.length) _characterArray = new char[length]; string.getChars(0, length, _characterArray, 0); handler.characters(_characterArray, 0, length); } } catch (SAXException e) { runTimeError(RUN_TIME_COPY_ERR); } } /** * Utility function to check if xsl:attribute has a valid qname * This method should only be invoked if the name attribute is an AVT */ public static void checkAttribQName(String name) { final int firstOccur = name.indexOf(":"); final int lastOccur = name.lastIndexOf(":"); final String localName = name.substring(lastOccur + 1); if (firstOccur > 0) { final String newPrefix = name.substring(0, firstOccur); if (firstOccur != lastOccur) { final String oriPrefix = name.substring(firstOccur+1, lastOccur); if (!XML11Char.isXML11ValidNCName(oriPrefix)) { // even though the orignal prefix is ignored, it should still get checked for valid NCName runTimeError(INVALID_QNAME_ERR,oriPrefix+":"+localName); } } // prefix must be a valid NCName if (!XML11Char.isXML11ValidNCName(newPrefix)) { runTimeError(INVALID_QNAME_ERR,newPrefix+":"+localName); } } // local name must be a valid NCName and must not be XMLNS if ((!XML11Char.isXML11ValidNCName(localName))||(localName.equals(Constants.XMLNS_PREFIX))) { runTimeError(INVALID_QNAME_ERR,localName); } } /** * Utility function to check if a name is a valid ncname * This method should only be invoked if the attribute value is an AVT */ public static void checkNCName(String name) { if (!XML11Char.isXML11ValidNCName(name)) { runTimeError(INVALID_NCNAME_ERR,name); } } /** * Utility function to check if a name is a valid qname * This method should only be invoked if the attribute value is an AVT */ public static void checkQName(String name) { if (!XML11Char.isXML11ValidQName(name)) { runTimeError(INVALID_QNAME_ERR,name); } } /** * Utility function for the implementation of xsl:element. */ public static String startXslElement(String qname, String namespace, SerializationHandler handler, DOM dom, int node) { try { // Get prefix from qname String prefix; final int index = qname.indexOf(':'); if (index > 0) { prefix = qname.substring(0, index); // Handle case when prefix is not known at compile time if (namespace == null || namespace.length() == 0) { try { // not sure if this line of code ever works namespace = dom.lookupNamespace(node, prefix); } catch(RuntimeException e) { handler.flushPending(); // need to flush or else can't get namespacemappings NamespaceMappings nm = handler.getNamespaceMappings(); namespace = nm.lookupNamespace(prefix); if (namespace == null) { runTimeError(NAMESPACE_PREFIX_ERR,prefix); } } } handler.startElement(namespace, qname.substring(index+1), qname); handler.namespaceAfterStartElement(prefix, namespace); } else { // Need to generate a prefix? if (namespace != null && namespace.length() > 0) { prefix = generatePrefix(); qname = prefix + ':' + qname; handler.startElement(namespace, qname, qname); handler.namespaceAfterStartElement(prefix, namespace); } else { handler.startElement(null, null, qname); } } } catch (SAXException e) { throw new RuntimeException(e.getMessage()); } return qname; } /** * This function is used in the execution of xsl:element */ public static String getPrefix(String qname) { final int index = qname.indexOf(':'); return (index > 0) ? qname.substring(0, index) : null; } /** * This function is used in the execution of xsl:element */ private static int prefixIndex = 0; public static String generatePrefix() { synchronized (BasisLibrary.class) { return ("ns" + prefixIndex++); } } public static final String RUN_TIME_INTERNAL_ERR = "RUN_TIME_INTERNAL_ERR"; public static final String RUN_TIME_COPY_ERR = "RUN_TIME_COPY_ERR"; public static final String DATA_CONVERSION_ERR = "DATA_CONVERSION_ERR"; public static final String EXTERNAL_FUNC_ERR = "EXTERNAL_FUNC_ERR"; public static final String EQUALITY_EXPR_ERR = "EQUALITY_EXPR_ERR"; public static final String INVALID_ARGUMENT_ERR = "INVALID_ARGUMENT_ERR"; public static final String FORMAT_NUMBER_ERR = "FORMAT_NUMBER_ERR"; public static final String ITERATOR_CLONE_ERR = "ITERATOR_CLONE_ERR"; public static final String AXIS_SUPPORT_ERR = "AXIS_SUPPORT_ERR"; public static final String TYPED_AXIS_SUPPORT_ERR = "TYPED_AXIS_SUPPORT_ERR"; public static final String STRAY_ATTRIBUTE_ERR = "STRAY_ATTRIBUTE_ERR"; public static final String STRAY_NAMESPACE_ERR = "STRAY_NAMESPACE_ERR"; public static final String NAMESPACE_PREFIX_ERR = "NAMESPACE_PREFIX_ERR"; public static final String DOM_ADAPTER_INIT_ERR = "DOM_ADAPTER_INIT_ERR"; public static final String PARSER_DTD_SUPPORT_ERR = "PARSER_DTD_SUPPORT_ERR"; public static final String NAMESPACES_SUPPORT_ERR = "NAMESPACES_SUPPORT_ERR"; public static final String CANT_RESOLVE_RELATIVE_URI_ERR = "CANT_RESOLVE_RELATIVE_URI_ERR"; public static final String UNSUPPORTED_XSL_ERR = "UNSUPPORTED_XSL_ERR"; public static final String UNSUPPORTED_EXT_ERR = "UNSUPPORTED_EXT_ERR"; public static final String UNKNOWN_TRANSLET_VERSION_ERR = "UNKNOWN_TRANSLET_VERSION_ERR"; public static final String INVALID_QNAME_ERR = "INVALID_QNAME_ERR"; public static final String INVALID_NCNAME_ERR = "INVALID_NCNAME_ERR"; public static final String UNALLOWED_EXTENSION_FUNCTION_ERR = "UNALLOWED_EXTENSION_FUNCTION_ERR"; public static final String UNALLOWED_EXTENSION_ELEMENT_ERR = "UNALLOWED_EXTENSION_ELEMENT_ERR"; // All error messages are localized and are stored in resource bundles. private static ResourceBundle m_bundle; public final static String ERROR_MESSAGES_KEY = "error-messages"; static { String resource = "com.sun.org.apache.xalan.internal.xsltc.runtime.ErrorMessages"; m_bundle = SecuritySupport.getResourceBundle(resource); } /** * Print a run-time error message. */ public static void runTimeError(String code) { throw new RuntimeException(m_bundle.getString(code)); } public static void runTimeError(String code, Object[] args) { final String message = MessageFormat.format(m_bundle.getString(code), args); throw new RuntimeException(message); } public static void runTimeError(String code, Object arg0) { runTimeError(code, new Object[]{ arg0 } ); } public static void runTimeError(String code, Object arg0, Object arg1) { runTimeError(code, new Object[]{ arg0, arg1 } ); } public static void consoleOutput(String msg) { System.out.println(msg); } /** * Replace a certain character in a string with a new substring. */ public static String replace(String base, char ch, String str) { return (base.indexOf(ch) < 0) ? base : replace(base, String.valueOf(ch), new String[] { str }); } public static String replace(String base, String delim, String[] str) { final int len = base.length(); final StringBuilder result = threadLocalStringBuilder.get(); result.setLength(0); for (int i = 0; i < len; i++) { final char ch = base.charAt(i); final int k = delim.indexOf(ch); if (k >= 0) { result.append(str[k]); } else { result.append(ch); } } return result.toString(); } /** * Utility method to allow setting parameters of the form * {namespaceuri}localName * which get mapped to an instance variable in the class * Hence a parameter of the form "{http://foo.bar}xyz" * will be replaced with the corresponding values * by the BasisLibrary's utility method mapQNametoJavaName * and thus get mapped to legal java variable names */ public static String mapQNameToJavaName (String base ) { return replace(base, ".-:/{}?#%*", new String[] { "$dot$", "$dash$" ,"$colon$", "$slash$", "","$colon$","$ques$","$hash$","$per$", "$aster$"}); } //-- End utility functions }