/* * @(#)$Id$ * * The Apache Software License, Version 1.1 * * * Copyright (c) 2001 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Xalan" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation and was * originally based on software copyright (c) 2001, Sun * Microsystems., http://www.sun.com. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * @author Jacek Ambroziak * @author Santiago Pericas-Geertsen * @author Morten Jorgensen * */ package org.apache.xalan.xsltc.runtime; //import java.util.Hashtable; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.FieldPosition; import org.xml.sax.AttributeList; import org.apache.xalan.xsltc.*; import org.apache.xalan.xsltc.DOM; import org.apache.xalan.xsltc.NodeIterator; import org.apache.xalan.xsltc.dom.SingletonIterator; /** * Standard XSLT functions. All standard functions expect the current node * and the DOM as their last two arguments. */ public final class BasisLibrary implements Operators { /** * XSLT Standard function count(node-set) */ public static int countF(NodeIterator iterator) { int counter = 0; while (iterator.next() != NodeIterator.END) { ++counter; } return counter; } /** * XSLT Standard function sum(node-set). * stringToDouble is inlined */ public static double sumF(NodeIterator iterator, DOM dom) { try { double result = 0.0; int node; while ((node = iterator.next()) != NodeIterator.END) { result += Double.parseDouble(dom.getNodeValue(node)); } return result; } catch (NumberFormatException e) { return Double.NaN; } } /** * XSLT Standard function string() */ public static String stringF(int node, DOM dom) { return dom.getNodeValue(node); } /** * XSLT Standard function string(value) */ public static String stringF(Object obj, DOM dom) { if (obj instanceof NodeIterator) { return dom.getNodeValue(((NodeIterator)obj).reset().next()); } else if (obj instanceof Node) { return dom.getNodeValue(((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 NodeIterator) { return dom.getNodeValue(((NodeIterator)obj).reset().next()); } else if (obj instanceof Node) { return dom.getNodeValue(((Node)obj).node); } else if (obj instanceof DOM) { // When the first argument is a DOM we want the whole fecking // DOM and not just a single node - that would not make sense. //return ((DOM)obj).getNodeValue(node); return ((DOM)obj).getStringValue(); } else { return obj.toString(); } } /** * XSLT Standard function number() */ public static double numberF(int node, DOM dom) { return stringToReal(dom.getNodeValue(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 NodeIterator) { NodeIterator iter = (NodeIterator) obj; return stringToReal(dom.getNodeValue(iter.reset().next())); } else if (obj instanceof Node) { return stringToReal(dom.getNodeValue(((Node) obj).node)); } else if (obj instanceof DOM) { return stringToReal(((DOM) obj).getStringValue()); } else { runTimeError("Invalid argument type in call to number()."); return 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(""); } else if (obj instanceof NodeIterator) { NodeIterator iter = (NodeIterator) obj; return iter.reset().next() != NodeIterator.END; } else if (obj instanceof Node) { return true; } else if (obj instanceof DOM) { String temp = ((DOM) obj).getStringValue(); return !temp.equals(""); } else { runTimeError("Invalid argument type in call to number()."); } 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) { try { final int valuel = value.length(); final int istart = (int)Math.round(start); if (Double.isNaN(start)) return(""); return value.substring(istart < 1 ? 0 : istart > valuel ? valuel : istart - 1); } catch (IndexOutOfBoundsException e) { runTimeInternalError(); 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) { try { final int valuel = value.length(); final int istart = (int)Math.round(start); final int ilength = (int)Math.round(length); final int isum = istart + ilength; if (Double.isNaN(start) || Double.isNaN(length)) return(""); return value.substring(istart < 1 || istart > valuel ? 0 : istart - 1, ilength < 0 ? 0 : isum - 1 > valuel ? valuel : isum < 1 ? 0 : isum - 1); } catch (IndexOutOfBoundsException e) { runTimeInternalError(); return null; } } /** * XSLT Standard function substring-after(). */ public static String substring_afterF(String value, String substring) { final int index = value.indexOf(substring); return index >= 0 ? value.substring(index + substring.length()) : ""; } /** * XSLT Standard function substring-before(). */ public static String substring_beforeF(String value, String substring) { final int index = value.indexOf(substring); return index >= 0 ? value.substring(0, index) : ""; } /** * 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 StringBuffer result = new StringBuffer(); 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.getNodeValue(node)); } /** * XSLT Standard function normalize-space(string). */ public static String normalize_spaceF(String value) { int i = 0, n = value.length(); StringBuffer result = new StringBuffer(); 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) { return "N" + node; } /** * 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); } /** * XSLT Standard function namespace-uri(node-set). */ public static String namespace_uriF(NodeIterator 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("Sun Microsystems' xsltc"); if (name.equals("xsl:vendor-url")) return("http://www.sun.com/"); runTimeError("Invalid argument type '"+name+ "' in call to system-property()."); return(""); } /** * 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(':'); return colon >= 0 ? value.substring(0, colon) : ""; } //-- 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 EQ: return lstring.equals(rstring); case NE: return !lstring.equals(rstring); case GT: return numberF(lstring, dom) > numberF(rstring, dom); case LT: return numberF(lstring, dom) < numberF(rstring, dom); case GE: return numberF(lstring, dom) >= numberF(rstring, dom); case LE: return numberF(lstring, dom) <= numberF(rstring, dom); default: runTimeInternalError(); return false; } } /** * Utility function: node-set/node-set compare. */ public static boolean compare(NodeIterator left, NodeIterator right, int op, int node, DOM dom) { int lnode; left.reset(); while ((lnode = left.next()) != NodeIterator.END) { final String lvalue = dom.getNodeValue(lnode); int rnode; right.reset(); while ((rnode = right.next()) != NodeIterator.END) { if (compareStrings(lvalue, dom.getNodeValue(rnode), op, dom)) { return true; } } } return false; } /** * Utility function: node/node-set compare. */ public static boolean compare(int node, NodeIterator nodeSet, int op, DOM dom) { final String lvalue = dom.getNodeValue(node); int rnode; nodeSet.reset(); while ((rnode = nodeSet.next()) != NodeIterator.END) { if (compareStrings(lvalue, dom.getNodeValue(rnode), op, dom)) { return true; } } return false; } public static boolean compare(int node, NodeIterator iterator, int op, int dummy, DOM dom) { iterator.reset(); int rnode; String value; switch(op) { case EQ: /* TODO: * This needs figuring out: What sort of comparison is done here? * Are we comparing exact node id's, node types, or node values? * Values is the obvious for attributes, but what about elements? */ value = dom.getNodeValue(node); while ((rnode = iterator.next()) != NodeIterator.END) if (value.equals(dom.getNodeValue(rnode))) return true; // if (rnode == node) return true; It just ain't that easy!!! break; case NE: value = dom.getNodeValue(node); while ((rnode = iterator.next()) != NodeIterator.END) if (!value.equals(dom.getNodeValue(rnode))) return true; // if (rnode != node) return true; break; case LT: // Assume we're comparing document order here while ((rnode = iterator.next()) != NodeIterator.END) if (rnode > node) return true; break; case GT: // Assume we're comparing document order here while ((rnode = iterator.next()) != NodeIterator.END) if (rnode < node) return true; break; } return(false); } public static boolean compare(NodeIterator left, final double rnumber, final int op, final int node, DOM dom) { return(compare(left,rnumber,op,dom)); } /** * Utility function: node-set/number compare. */ public static boolean compare(NodeIterator left, final double rnumber, final int op, DOM dom) { int node; left.reset(); switch (op) { case EQ: while ((node = left.next()) != NodeIterator.END) { if (numberF(dom.getNodeValue(node), dom) == rnumber) return true; } break; case NE: while ((node = left.next()) != NodeIterator.END) { if (numberF(dom.getNodeValue(node), dom) != rnumber) return true; } break; case GT: while ((node = left.next()) != NodeIterator.END) { if (numberF(dom.getNodeValue(node), dom) > rnumber) return true; } break; case LT: while ((node = left.next()) != NodeIterator.END) { if (numberF(dom.getNodeValue(node), dom) < rnumber) return true; } break; case GE: while ((node = left.next()) != NodeIterator.END) { if (numberF(dom.getNodeValue(node), dom) >= rnumber) return true; } break; case LE: while ((node = left.next()) != NodeIterator.END) { if (numberF(dom.getNodeValue(node), dom) <= rnumber) return true; } break; default: runTimeInternalError(); } return false; } /** * Utility function: node-set/string comparison. */ public static boolean compare(NodeIterator left, final String rstring, int op, DOM dom) { int node; left.reset(); while ((node = left.next()) != NodeIterator.END) { if (compareStrings(dom.getNodeValue(node), rstring, op, dom)) { return true; } } return false; } public static boolean compare(NodeIterator left, final String rstring, int op, int node, DOM dom) { if (compareStrings(dom.getNodeValue(node), rstring, op, dom)) { return true; } else { return false; } } public static boolean compare(Object left, Object right, int op, int node, DOM dom) { boolean result = false; boolean hasSimpleArgs = hasSimpleType(left) && hasSimpleType(right); if (op != EQ && op != 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 GT: return numberF(left, dom) > numberF(right, dom); case LT: return numberF(left, dom) < numberF(right, dom); case GE: return numberF(left, dom) >= numberF(right, dom); case LE: return numberF(left, dom) <= numberF(right, dom); default: runTimeInternalError(); } } // 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) == 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 NodeIterator) { // swap operands final Object temp = right; right = left; left = temp; } 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} NodeIterator iter = ((NodeIterator)left).reset(); if (right instanceof NodeIterator) { result = compare(iter, (NodeIterator)right, op, node, dom); } else if (right instanceof String) { //result = compare(iter, (String)right, op, node, dom); 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() != NodeIterator.END) == temp; } else if (right instanceof DOM) { result = compare(iter, ((DOM)right).getStringValue(), op, node, dom); } else if (right == null) { return(false); } else { runTimeError("Unknown argument type in call to equal."); } } 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; } /** * 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) { return Integer.valueOf(s).intValue(); } /** * Utility function: used in RealType to convert a real to a string. * Removes the decimal if null. */ public static String realToString(double d) { final String result = Double.toString(d); final int length = result.length(); if (result.charAt(length-2) == '.' && result.charAt(length-1) == '0') { return result.substring(0, length-2); } return result; } /** * Utility function: used in RealType to convert a real to an integer */ public static int realToInt(double d) { final Double result = new Double(d); return result.intValue(); } /** * Utility function: used to format/adjust a double to a string. The * DecimalFormat object comes from the 'format_symbols' hashtable in * AbstractTranslet. */ private static FieldPosition _fieldPosition = new FieldPosition(0); public static String formatNumber(double number, String pattern, DecimalFormat formatter) { try { StringBuffer result = new StringBuffer(); formatter.applyLocalizedPattern(pattern); formatter.format(number,result,_fieldPosition); return(result.toString()); } catch (IllegalArgumentException e) { runTimeError("Attempting to format number '"+ number + "' using pattern '" + pattern + "'."); return(""); } } /** * Utility function: used to convert references to node-sets. If the * obj is an instanceof Node then create a singleton iterator. */ public static NodeIterator referenceToNodeSet(Object obj) { try { return (obj instanceof Node == false) ? (NodeIterator) obj : (NodeIterator) new SingletonIterator(((Node) obj).node); } catch (ClassCastException e) { runTimeTypeError("reference", "node-set"); 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 NodeIterator getSingleNode(NodeIterator 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, TransletOutputHandler handler, int node, DOM dom) { try { if (obj instanceof NodeIterator) { NodeIterator iter = (NodeIterator) obj; //dom.copy(iter.reset(), handler); //!! System.err.println("not implemented copy iter"); } else if (obj instanceof Node) { dom.copy(((Node) obj).node, handler); } else if (obj instanceof DOM) { ((DOM)obj).copy(1, 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 (TransletException e) { runTimeError("TransletException raised in copy()."); } } /** * Print a run-time type error message. */ public static void runTimeTypeError(String from, String to) { runTimeError("Invalid conversion from '" + from + "' to '" + to + "'."); } /** * Print a run-time internal error message. */ public static void runTimeInternalError() { runTimeError("Internal error."); } /** * Print a run-time error message. */ public static void runTimeError(String message) { throw new RuntimeException(message); } public static void consoleOutput(String msg) { System.out.println(msg); } //-- End utility functions }