286N/A/*
286N/A * reserved comment block
286N/A * DO NOT REMOVE OR ALTER!
286N/A */
286N/A/*
286N/A * Copyright 2001-2004 The Apache Software Foundation.
286N/A *
286N/A * Licensed under the Apache License, Version 2.0 (the "License");
286N/A * you may not use this file except in compliance with the License.
286N/A * You may obtain a copy of the License at
286N/A *
286N/A * http://www.apache.org/licenses/LICENSE-2.0
286N/A *
286N/A * Unless required by applicable law or agreed to in writing, software
286N/A * distributed under the License is distributed on an "AS IS" BASIS,
286N/A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
286N/A * See the License for the specific language governing permissions and
286N/A * limitations under the License.
286N/A */
286N/A/*
286N/A * $Id: FunctionCall.java,v 1.2.4.1 2005/09/12 10:31:32 pvedula Exp $
286N/A */
286N/A
286N/Apackage com.sun.org.apache.xalan.internal.xsltc.compiler;
286N/A
286N/Aimport java.lang.reflect.Constructor;
286N/Aimport java.lang.reflect.Method;
286N/Aimport java.lang.reflect.Modifier;
286N/Aimport java.util.Enumeration;
286N/Aimport java.util.Hashtable;
286N/Aimport java.util.Vector;
286N/A
286N/Aimport com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
286N/Aimport com.sun.org.apache.bcel.internal.generic.IFEQ;
286N/Aimport com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
286N/Aimport com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
286N/Aimport com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
286N/Aimport com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
286N/Aimport com.sun.org.apache.bcel.internal.generic.InstructionConstants;
286N/Aimport com.sun.org.apache.bcel.internal.generic.InstructionList;
286N/Aimport com.sun.org.apache.bcel.internal.generic.InvokeInstruction;
286N/Aimport com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
286N/Aimport com.sun.org.apache.bcel.internal.generic.NEW;
286N/Aimport com.sun.org.apache.bcel.internal.generic.PUSH;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.MultiHashtable;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.ObjectType;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
286N/Aimport com.sun.org.apache.xalan.internal.utils.ObjectFactory;
570N/Aimport com.sun.org.apache.xalan.internal.utils.Objects;
286N/A
286N/A/**
286N/A * @author Jacek Ambroziak
286N/A * @author Santiago Pericas-Geertsen
286N/A * @author Morten Jorgensen
286N/A * @author Erwin Bolwidt <ejb@klomp.org>
286N/A * @author Todd Miller
286N/A */
286N/Aclass FunctionCall extends Expression {
286N/A
286N/A // Name of this function call
286N/A private QName _fname;
286N/A // Arguments to this function call (might not be any)
286N/A private final Vector _arguments;
286N/A // Empty argument list, used for certain functions
286N/A private final static Vector EMPTY_ARG_LIST = new Vector(0);
286N/A
286N/A // Valid namespaces for Java function-call extension
286N/A protected final static String EXT_XSLTC =
286N/A TRANSLET_URI;
286N/A
286N/A protected final static String JAVA_EXT_XSLTC =
286N/A EXT_XSLTC + "/java";
286N/A
286N/A protected final static String EXT_XALAN =
286N/A "http://xml.apache.org/xalan";
286N/A
286N/A protected final static String JAVA_EXT_XALAN =
286N/A "http://xml.apache.org/xalan/java";
286N/A
286N/A protected final static String JAVA_EXT_XALAN_OLD =
286N/A "http://xml.apache.org/xslt/java";
286N/A
286N/A protected final static String EXSLT_COMMON =
286N/A "http://exslt.org/common";
286N/A
286N/A protected final static String EXSLT_MATH =
286N/A "http://exslt.org/math";
286N/A
286N/A protected final static String EXSLT_SETS =
286N/A "http://exslt.org/sets";
286N/A
286N/A protected final static String EXSLT_DATETIME =
286N/A "http://exslt.org/dates-and-times";
286N/A
286N/A protected final static String EXSLT_STRINGS =
286N/A "http://exslt.org/strings";
286N/A
286N/A // Namespace format constants
286N/A protected final static int NAMESPACE_FORMAT_JAVA = 0;
286N/A protected final static int NAMESPACE_FORMAT_CLASS = 1;
286N/A protected final static int NAMESPACE_FORMAT_PACKAGE = 2;
286N/A protected final static int NAMESPACE_FORMAT_CLASS_OR_PACKAGE = 3;
286N/A
286N/A // Namespace format
286N/A private int _namespace_format = NAMESPACE_FORMAT_JAVA;
286N/A
286N/A /**
286N/A * Stores reference to object for non-static Java calls
286N/A */
286N/A Expression _thisArgument = null;
286N/A
286N/A // External Java function's class/method/signature
286N/A private String _className;
286N/A private Class _clazz;
286N/A private Method _chosenMethod;
286N/A private Constructor _chosenConstructor;
286N/A private MethodType _chosenMethodType;
286N/A
286N/A // Encapsulates all unsupported external function calls
286N/A private boolean unresolvedExternal;
286N/A
286N/A // If FunctionCall is a external java constructor
286N/A private boolean _isExtConstructor = false;
286N/A
286N/A // If the java method is static
286N/A private boolean _isStatic = false;
286N/A
286N/A // Legal conversions between internal and Java types.
286N/A private static final MultiHashtable _internal2Java = new MultiHashtable();
286N/A
286N/A // Legal conversions between Java and internal types.
286N/A private static final Hashtable _java2Internal = new Hashtable();
286N/A
286N/A // The mappings between EXSLT extension namespaces and implementation classes
286N/A private static final Hashtable _extensionNamespaceTable = new Hashtable();
286N/A
286N/A // Extension functions that are implemented in BasisLibrary
286N/A private static final Hashtable _extensionFunctionTable = new Hashtable();
286N/A /**
286N/A * inner class to used in internal2Java mappings, contains
286N/A * the Java type and the distance between the internal type and
286N/A * the Java type.
286N/A */
286N/A static class JavaType {
286N/A public Class type;
286N/A public int distance;
286N/A
286N/A public JavaType(Class type, int distance){
286N/A this.type = type;
286N/A this.distance = distance;
286N/A }
570N/A
570N/A @Override
570N/A public int hashCode() {
570N/A return Objects.hashCode(this.type);
570N/A }
570N/A
570N/A @Override
286N/A public boolean equals(Object query){
570N/A return query != null && query.equals(type);
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * Defines 2 conversion tables:
286N/A * 1. From internal types to Java types and
286N/A * 2. From Java types to internal types.
286N/A * These two tables are used when calling external (Java) functions.
286N/A */
286N/A static {
286N/A try {
286N/A final Class nodeClass = Class.forName("org.w3c.dom.Node");
286N/A final Class nodeListClass = Class.forName("org.w3c.dom.NodeList");
286N/A
286N/A // -- Internal to Java --------------------------------------------
286N/A
286N/A // Type.Boolean -> { boolean(0), Boolean(1), Object(2) }
286N/A _internal2Java.put(Type.Boolean, new JavaType(Boolean.TYPE, 0));
286N/A _internal2Java.put(Type.Boolean, new JavaType(Boolean.class, 1));
286N/A _internal2Java.put(Type.Boolean, new JavaType(Object.class, 2));
286N/A
286N/A // Type.Real -> { double(0), Double(1), float(2), long(3), int(4),
286N/A // short(5), byte(6), char(7), Object(8) }
286N/A _internal2Java.put(Type.Real, new JavaType(Double.TYPE, 0));
286N/A _internal2Java.put(Type.Real, new JavaType(Double.class, 1));
286N/A _internal2Java.put(Type.Real, new JavaType(Float.TYPE, 2));
286N/A _internal2Java.put(Type.Real, new JavaType(Long.TYPE, 3));
286N/A _internal2Java.put(Type.Real, new JavaType(Integer.TYPE, 4));
286N/A _internal2Java.put(Type.Real, new JavaType(Short.TYPE, 5));
286N/A _internal2Java.put(Type.Real, new JavaType(Byte.TYPE, 6));
286N/A _internal2Java.put(Type.Real, new JavaType(Character.TYPE, 7));
286N/A _internal2Java.put(Type.Real, new JavaType(Object.class, 8));
286N/A
286N/A // Type.Int must be the same as Type.Real
286N/A _internal2Java.put(Type.Int, new JavaType(Double.TYPE, 0));
286N/A _internal2Java.put(Type.Int, new JavaType(Double.class, 1));
286N/A _internal2Java.put(Type.Int, new JavaType(Float.TYPE, 2));
286N/A _internal2Java.put(Type.Int, new JavaType(Long.TYPE, 3));
286N/A _internal2Java.put(Type.Int, new JavaType(Integer.TYPE, 4));
286N/A _internal2Java.put(Type.Int, new JavaType(Short.TYPE, 5));
286N/A _internal2Java.put(Type.Int, new JavaType(Byte.TYPE, 6));
286N/A _internal2Java.put(Type.Int, new JavaType(Character.TYPE, 7));
286N/A _internal2Java.put(Type.Int, new JavaType(Object.class, 8));
286N/A
286N/A // Type.String -> { String(0), Object(1) }
286N/A _internal2Java.put(Type.String, new JavaType(String.class, 0));
286N/A _internal2Java.put(Type.String, new JavaType(Object.class, 1));
286N/A
286N/A // Type.NodeSet -> { NodeList(0), Node(1), Object(2), String(3) }
286N/A _internal2Java.put(Type.NodeSet, new JavaType(nodeListClass, 0));
286N/A _internal2Java.put(Type.NodeSet, new JavaType(nodeClass, 1));
286N/A _internal2Java.put(Type.NodeSet, new JavaType(Object.class, 2));
286N/A _internal2Java.put(Type.NodeSet, new JavaType(String.class, 3));
286N/A
286N/A // Type.Node -> { Node(0), NodeList(1), Object(2), String(3) }
286N/A _internal2Java.put(Type.Node, new JavaType(nodeListClass, 0));
286N/A _internal2Java.put(Type.Node, new JavaType(nodeClass, 1));
286N/A _internal2Java.put(Type.Node, new JavaType(Object.class, 2));
286N/A _internal2Java.put(Type.Node, new JavaType(String.class, 3));
286N/A
286N/A // Type.ResultTree -> { NodeList(0), Node(1), Object(2), String(3) }
286N/A _internal2Java.put(Type.ResultTree, new JavaType(nodeListClass, 0));
286N/A _internal2Java.put(Type.ResultTree, new JavaType(nodeClass, 1));
286N/A _internal2Java.put(Type.ResultTree, new JavaType(Object.class, 2));
286N/A _internal2Java.put(Type.ResultTree, new JavaType(String.class, 3));
286N/A
286N/A _internal2Java.put(Type.Reference, new JavaType(Object.class, 0));
286N/A
286N/A // Possible conversions between Java and internal types
286N/A _java2Internal.put(Boolean.TYPE, Type.Boolean);
286N/A _java2Internal.put(Void.TYPE, Type.Void);
286N/A _java2Internal.put(Character.TYPE, Type.Real);
286N/A _java2Internal.put(Byte.TYPE, Type.Real);
286N/A _java2Internal.put(Short.TYPE, Type.Real);
286N/A _java2Internal.put(Integer.TYPE, Type.Real);
286N/A _java2Internal.put(Long.TYPE, Type.Real);
286N/A _java2Internal.put(Float.TYPE, Type.Real);
286N/A _java2Internal.put(Double.TYPE, Type.Real);
286N/A
286N/A _java2Internal.put(String.class, Type.String);
286N/A
286N/A _java2Internal.put(Object.class, Type.Reference);
286N/A
286N/A // Conversions from org.w3c.dom.Node/NodeList to internal NodeSet
286N/A _java2Internal.put(nodeListClass, Type.NodeSet);
286N/A _java2Internal.put(nodeClass, Type.NodeSet);
286N/A
286N/A // Initialize the extension namespace table
286N/A _extensionNamespaceTable.put(EXT_XALAN, "com.sun.org.apache.xalan.internal.lib.Extensions");
286N/A _extensionNamespaceTable.put(EXSLT_COMMON, "com.sun.org.apache.xalan.internal.lib.ExsltCommon");
286N/A _extensionNamespaceTable.put(EXSLT_MATH, "com.sun.org.apache.xalan.internal.lib.ExsltMath");
286N/A _extensionNamespaceTable.put(EXSLT_SETS, "com.sun.org.apache.xalan.internal.lib.ExsltSets");
286N/A _extensionNamespaceTable.put(EXSLT_DATETIME, "com.sun.org.apache.xalan.internal.lib.ExsltDatetime");
286N/A _extensionNamespaceTable.put(EXSLT_STRINGS, "com.sun.org.apache.xalan.internal.lib.ExsltStrings");
286N/A
286N/A // Initialize the extension function table
286N/A _extensionFunctionTable.put(EXSLT_COMMON + ":nodeSet", "nodeset");
286N/A _extensionFunctionTable.put(EXSLT_COMMON + ":objectType", "objectType");
286N/A _extensionFunctionTable.put(EXT_XALAN + ":nodeset", "nodeset");
286N/A }
286N/A catch (ClassNotFoundException e) {
286N/A System.err.println(e);
286N/A }
286N/A }
286N/A
286N/A public FunctionCall(QName fname, Vector arguments) {
286N/A _fname = fname;
286N/A _arguments = arguments;
286N/A _type = null;
286N/A }
286N/A
286N/A public FunctionCall(QName fname) {
286N/A this(fname, EMPTY_ARG_LIST);
286N/A }
286N/A
286N/A public String getName() {
286N/A return(_fname.toString());
286N/A }
286N/A
570N/A @Override
286N/A public void setParser(Parser parser) {
286N/A super.setParser(parser);
286N/A if (_arguments != null) {
286N/A final int n = _arguments.size();
286N/A for (int i = 0; i < n; i++) {
286N/A final Expression exp = (Expression)_arguments.elementAt(i);
286N/A exp.setParser(parser);
286N/A exp.setParent(this);
286N/A }
286N/A }
286N/A }
286N/A
286N/A public String getClassNameFromUri(String uri)
286N/A {
286N/A String className = (String)_extensionNamespaceTable.get(uri);
286N/A
286N/A if (className != null)
286N/A return className;
286N/A else {
286N/A if (uri.startsWith(JAVA_EXT_XSLTC)) {
286N/A int length = JAVA_EXT_XSLTC.length() + 1;
286N/A return (uri.length() > length) ? uri.substring(length) : EMPTYSTRING;
286N/A }
286N/A else if (uri.startsWith(JAVA_EXT_XALAN)) {
286N/A int length = JAVA_EXT_XALAN.length() + 1;
286N/A return (uri.length() > length) ? uri.substring(length) : EMPTYSTRING;
286N/A }
286N/A else if (uri.startsWith(JAVA_EXT_XALAN_OLD)) {
286N/A int length = JAVA_EXT_XALAN_OLD.length() + 1;
286N/A return (uri.length() > length) ? uri.substring(length) : EMPTYSTRING;
286N/A }
286N/A else {
286N/A int index = uri.lastIndexOf('/');
286N/A return (index > 0) ? uri.substring(index+1) : uri;
286N/A }
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * Type check a function call. Since different type conversions apply,
286N/A * type checking is different for standard and external (Java) functions.
286N/A */
570N/A @Override
286N/A public Type typeCheck(SymbolTable stable)
286N/A throws TypeCheckError
286N/A {
286N/A if (_type != null) return _type;
286N/A
286N/A final String namespace = _fname.getNamespace();
286N/A String local = _fname.getLocalPart();
286N/A
286N/A if (isExtension()) {
286N/A _fname = new QName(null, null, local);
286N/A return typeCheckStandard(stable);
286N/A }
286N/A else if (isStandard()) {
286N/A return typeCheckStandard(stable);
286N/A }
286N/A // Handle extension functions (they all have a namespace)
286N/A else {
286N/A try {
286N/A _className = getClassNameFromUri(namespace);
286N/A
286N/A final int pos = local.lastIndexOf('.');
286N/A if (pos > 0) {
286N/A _isStatic = true;
286N/A if (_className != null && _className.length() > 0) {
286N/A _namespace_format = NAMESPACE_FORMAT_PACKAGE;
286N/A _className = _className + "." + local.substring(0, pos);
286N/A }
286N/A else {
286N/A _namespace_format = NAMESPACE_FORMAT_JAVA;
286N/A _className = local.substring(0, pos);
286N/A }
286N/A
286N/A _fname = new QName(namespace, null, local.substring(pos + 1));
286N/A }
286N/A else {
286N/A if (_className != null && _className.length() > 0) {
286N/A try {
286N/A _clazz = ObjectFactory.findProviderClass(_className, true);
286N/A _namespace_format = NAMESPACE_FORMAT_CLASS;
286N/A }
286N/A catch (ClassNotFoundException e) {
286N/A _namespace_format = NAMESPACE_FORMAT_PACKAGE;
286N/A }
286N/A }
286N/A else
286N/A _namespace_format = NAMESPACE_FORMAT_JAVA;
286N/A
286N/A if (local.indexOf('-') > 0) {
286N/A local = replaceDash(local);
286N/A }
286N/A
286N/A String extFunction = (String)_extensionFunctionTable.get(namespace + ":" + local);
286N/A if (extFunction != null) {
286N/A _fname = new QName(null, null, extFunction);
286N/A return typeCheckStandard(stable);
286N/A }
286N/A else
286N/A _fname = new QName(namespace, null, local);
286N/A }
286N/A
286N/A return typeCheckExternal(stable);
286N/A }
286N/A catch (TypeCheckError e) {
286N/A ErrorMsg errorMsg = e.getErrorMsg();
286N/A if (errorMsg == null) {
286N/A final String name = _fname.getLocalPart();
286N/A errorMsg = new ErrorMsg(ErrorMsg.METHOD_NOT_FOUND_ERR, name);
286N/A }
286N/A getParser().reportError(ERROR, errorMsg);
286N/A return _type = Type.Void;
286N/A }
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * Type check a call to a standard function. Insert CastExprs when needed.
286N/A * If as a result of the insertion of a CastExpr a type check error is
286N/A * thrown, then catch it and re-throw it with a new "this".
286N/A */
286N/A public Type typeCheckStandard(SymbolTable stable) throws TypeCheckError {
286N/A _fname.clearNamespace(); // HACK!!!
286N/A
286N/A final int n = _arguments.size();
286N/A final Vector argsType = typeCheckArgs(stable);
286N/A final MethodType args = new MethodType(Type.Void, argsType);
286N/A final MethodType ptype =
286N/A lookupPrimop(stable, _fname.getLocalPart(), args);
286N/A
286N/A if (ptype != null) {
286N/A for (int i = 0; i < n; i++) {
286N/A final Type argType = (Type) ptype.argsType().elementAt(i);
286N/A final Expression exp = (Expression)_arguments.elementAt(i);
286N/A if (!argType.identicalTo(exp.getType())) {
286N/A try {
286N/A _arguments.setElementAt(new CastExpr(exp, argType), i);
286N/A }
286N/A catch (TypeCheckError e) {
286N/A throw new TypeCheckError(this); // invalid conversion
286N/A }
286N/A }
286N/A }
286N/A _chosenMethodType = ptype;
286N/A return _type = ptype.resultType();
286N/A }
286N/A throw new TypeCheckError(this);
286N/A }
286N/A
286N/A
286N/A
286N/A public Type typeCheckConstructor(SymbolTable stable) throws TypeCheckError{
286N/A final Vector constructors = findConstructors();
286N/A if (constructors == null) {
286N/A // Constructor not found in this class
286N/A throw new TypeCheckError(ErrorMsg.CONSTRUCTOR_NOT_FOUND,
286N/A _className);
286N/A
286N/A }
286N/A
286N/A final int nConstructors = constructors.size();
286N/A final int nArgs = _arguments.size();
286N/A final Vector argsType = typeCheckArgs(stable);
286N/A
286N/A // Try all constructors
286N/A int bestConstrDistance = Integer.MAX_VALUE;
286N/A _type = null; // reset
286N/A for (int j, i = 0; i < nConstructors; i++) {
286N/A // Check if all parameters to this constructor can be converted
286N/A final Constructor constructor =
286N/A (Constructor)constructors.elementAt(i);
286N/A final Class[] paramTypes = constructor.getParameterTypes();
286N/A
286N/A Class extType = null;
286N/A int currConstrDistance = 0;
286N/A for (j = 0; j < nArgs; j++) {
286N/A // Convert from internal (translet) type to external (Java) type
286N/A extType = paramTypes[j];
286N/A final Type intType = (Type)argsType.elementAt(j);
286N/A Object match = _internal2Java.maps(intType, extType);
286N/A if (match != null) {
286N/A currConstrDistance += ((JavaType)match).distance;
286N/A }
286N/A else if (intType instanceof ObjectType) {
286N/A ObjectType objectType = (ObjectType)intType;
286N/A if (objectType.getJavaClass() == extType)
286N/A continue;
286N/A else if (extType.isAssignableFrom(objectType.getJavaClass()))
286N/A currConstrDistance += 1;
286N/A else {
286N/A currConstrDistance = Integer.MAX_VALUE;
286N/A break;
286N/A }
286N/A }
286N/A else {
286N/A // no mapping available
286N/A currConstrDistance = Integer.MAX_VALUE;
286N/A break;
286N/A }
286N/A }
286N/A
286N/A if (j == nArgs && currConstrDistance < bestConstrDistance ) {
286N/A _chosenConstructor = constructor;
286N/A _isExtConstructor = true;
286N/A bestConstrDistance = currConstrDistance;
286N/A
286N/A _type = (_clazz != null) ? Type.newObjectType(_clazz)
286N/A : Type.newObjectType(_className);
286N/A }
286N/A }
286N/A
286N/A if (_type != null) {
286N/A return _type;
286N/A }
286N/A
286N/A throw new TypeCheckError(ErrorMsg.ARGUMENT_CONVERSION_ERR, getMethodSignature(argsType));
286N/A }
286N/A
286N/A
286N/A /**
286N/A * Type check a call to an external (Java) method.
286N/A * The method must be static an public, and a legal type conversion
286N/A * must exist for all its arguments and its return type.
286N/A * Every method of name <code>_fname</code> is inspected
286N/A * as a possible candidate.
286N/A */
286N/A public Type typeCheckExternal(SymbolTable stable) throws TypeCheckError {
286N/A int nArgs = _arguments.size();
286N/A final String name = _fname.getLocalPart();
286N/A
286N/A // check if function is a contructor 'new'
286N/A if (_fname.getLocalPart().equals("new")) {
286N/A return typeCheckConstructor(stable);
286N/A }
286N/A // check if we are calling an instance method
286N/A else {
286N/A boolean hasThisArgument = false;
286N/A
286N/A if (nArgs == 0)
286N/A _isStatic = true;
286N/A
286N/A if (!_isStatic) {
286N/A if (_namespace_format == NAMESPACE_FORMAT_JAVA
286N/A || _namespace_format == NAMESPACE_FORMAT_PACKAGE)
286N/A hasThisArgument = true;
286N/A
286N/A Expression firstArg = (Expression)_arguments.elementAt(0);
286N/A Type firstArgType = (Type)firstArg.typeCheck(stable);
286N/A
286N/A if (_namespace_format == NAMESPACE_FORMAT_CLASS
286N/A && firstArgType instanceof ObjectType
286N/A && _clazz != null
286N/A && _clazz.isAssignableFrom(((ObjectType)firstArgType).getJavaClass()))
286N/A hasThisArgument = true;
286N/A
286N/A if (hasThisArgument) {
286N/A _thisArgument = (Expression) _arguments.elementAt(0);
286N/A _arguments.remove(0); nArgs--;
286N/A if (firstArgType instanceof ObjectType) {
286N/A _className = ((ObjectType) firstArgType).getJavaClassName();
286N/A }
286N/A else
286N/A throw new TypeCheckError(ErrorMsg.NO_JAVA_FUNCT_THIS_REF, name);
286N/A }
286N/A }
286N/A else if (_className.length() == 0) {
286N/A /*
286N/A * Warn user if external function could not be resolved.
286N/A * Warning will _NOT_ be issued is the call is properly
286N/A * wrapped in an <xsl:if> or <xsl:when> element. For details
286N/A * see If.parserContents() and When.parserContents()
286N/A */
286N/A final Parser parser = getParser();
286N/A if (parser != null) {
286N/A reportWarning(this, parser, ErrorMsg.FUNCTION_RESOLVE_ERR,
286N/A _fname.toString());
286N/A }
286N/A unresolvedExternal = true;
286N/A return _type = Type.Int; // use "Int" as "unknown"
286N/A }
286N/A }
286N/A
286N/A final Vector methods = findMethods();
286N/A
286N/A if (methods == null) {
286N/A // Method not found in this class
286N/A throw new TypeCheckError(ErrorMsg.METHOD_NOT_FOUND_ERR, _className + "." + name);
286N/A }
286N/A
286N/A Class extType = null;
286N/A final int nMethods = methods.size();
286N/A final Vector argsType = typeCheckArgs(stable);
286N/A
286N/A // Try all methods to identify the best fit
286N/A int bestMethodDistance = Integer.MAX_VALUE;
286N/A _type = null; // reset internal type
286N/A for (int j, i = 0; i < nMethods; i++) {
286N/A // Check if all paramteters to this method can be converted
286N/A final Method method = (Method)methods.elementAt(i);
286N/A final Class[] paramTypes = method.getParameterTypes();
286N/A
286N/A int currMethodDistance = 0;
286N/A for (j = 0; j < nArgs; j++) {
286N/A // Convert from internal (translet) type to external (Java) type
286N/A extType = paramTypes[j];
286N/A final Type intType = (Type)argsType.elementAt(j);
286N/A Object match = _internal2Java.maps(intType, extType);
286N/A if (match != null) {
286N/A currMethodDistance += ((JavaType)match).distance;
286N/A }
286N/A else {
286N/A // no mapping available
286N/A //
286N/A // Allow a Reference type to match any external (Java) type at
286N/A // the moment. The real type checking is performed at runtime.
286N/A if (intType instanceof ReferenceType) {
286N/A currMethodDistance += 1;
286N/A }
286N/A else if (intType instanceof ObjectType) {
286N/A ObjectType object = (ObjectType)intType;
286N/A if (extType.getName().equals(object.getJavaClassName()))
286N/A currMethodDistance += 0;
286N/A else if (extType.isAssignableFrom(object.getJavaClass()))
286N/A currMethodDistance += 1;
286N/A else {
286N/A currMethodDistance = Integer.MAX_VALUE;
286N/A break;
286N/A }
286N/A }
286N/A else {
286N/A currMethodDistance = Integer.MAX_VALUE;
286N/A break;
286N/A }
286N/A }
286N/A }
286N/A
286N/A if (j == nArgs) {
286N/A // Check if the return type can be converted
286N/A extType = method.getReturnType();
286N/A
286N/A _type = (Type) _java2Internal.get(extType);
286N/A if (_type == null) {
286N/A _type = Type.newObjectType(extType);
286N/A }
286N/A
286N/A // Use this method if all parameters & return type match
286N/A if (_type != null && currMethodDistance < bestMethodDistance) {
286N/A _chosenMethod = method;
286N/A bestMethodDistance = currMethodDistance;
286N/A }
286N/A }
286N/A }
286N/A
286N/A // It is an error if the chosen method is an instance menthod but we don't
286N/A // have a this argument.
286N/A if (_chosenMethod != null && _thisArgument == null &&
286N/A !Modifier.isStatic(_chosenMethod.getModifiers())) {
286N/A throw new TypeCheckError(ErrorMsg.NO_JAVA_FUNCT_THIS_REF, getMethodSignature(argsType));
286N/A }
286N/A
286N/A if (_type != null) {
286N/A if (_type == Type.NodeSet) {
286N/A getXSLTC().setMultiDocument(true);
286N/A }
286N/A return _type;
286N/A }
286N/A
286N/A throw new TypeCheckError(ErrorMsg.ARGUMENT_CONVERSION_ERR, getMethodSignature(argsType));
286N/A }
286N/A
286N/A /**
286N/A * Type check the actual arguments of this function call.
286N/A */
286N/A public Vector typeCheckArgs(SymbolTable stable) throws TypeCheckError {
286N/A final Vector result = new Vector();
286N/A final Enumeration e = _arguments.elements();
286N/A while (e.hasMoreElements()) {
286N/A final Expression exp = (Expression)e.nextElement();
286N/A result.addElement(exp.typeCheck(stable));
286N/A }
286N/A return result;
286N/A }
286N/A
286N/A protected final Expression argument(int i) {
286N/A return (Expression)_arguments.elementAt(i);
286N/A }
286N/A
286N/A protected final Expression argument() {
286N/A return argument(0);
286N/A }
286N/A
286N/A protected final int argumentCount() {
286N/A return _arguments.size();
286N/A }
286N/A
286N/A protected final void setArgument(int i, Expression exp) {
286N/A _arguments.setElementAt(exp, i);
286N/A }
286N/A
286N/A /**
286N/A * Compile the function call and treat as an expression
286N/A * Update true/false-lists.
286N/A */
570N/A @Override
286N/A public void translateDesynthesized(ClassGenerator classGen,
286N/A MethodGenerator methodGen)
286N/A {
286N/A Type type = Type.Boolean;
286N/A if (_chosenMethodType != null)
286N/A type = _chosenMethodType.resultType();
286N/A
286N/A final InstructionList il = methodGen.getInstructionList();
286N/A translate(classGen, methodGen);
286N/A
286N/A if ((type instanceof BooleanType) || (type instanceof IntType)) {
286N/A _falseList.add(il.append(new IFEQ(null)));
286N/A }
286N/A }
286N/A
286N/A
286N/A /**
286N/A * Translate a function call. The compiled code will leave the function's
286N/A * return value on the JVM's stack.
286N/A */
570N/A @Override
286N/A public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
286N/A final int n = argumentCount();
286N/A final ConstantPoolGen cpg = classGen.getConstantPool();
286N/A final InstructionList il = methodGen.getInstructionList();
286N/A final boolean isSecureProcessing = classGen.getParser().getXSLTC().isSecureProcessing();
286N/A int index;
286N/A
286N/A // Translate calls to methods in the BasisLibrary
286N/A if (isStandard() || isExtension()) {
286N/A for (int i = 0; i < n; i++) {
286N/A final Expression exp = argument(i);
286N/A exp.translate(classGen, methodGen);
286N/A exp.startIterator(classGen, methodGen);
286N/A }
286N/A
286N/A // append "F" to the function's name
286N/A final String name = _fname.toString().replace('-', '_') + "F";
286N/A String args = Constants.EMPTYSTRING;
286N/A
286N/A // Special precautions for some method calls
286N/A if (name.equals("sumF")) {
286N/A args = DOM_INTF_SIG;
286N/A il.append(methodGen.loadDOM());
286N/A }
286N/A else if (name.equals("normalize_spaceF")) {
286N/A if (_chosenMethodType.toSignature(args).
286N/A equals("()Ljava/lang/String;")) {
286N/A args = "I"+DOM_INTF_SIG;
286N/A il.append(methodGen.loadContextNode());
286N/A il.append(methodGen.loadDOM());
286N/A }
286N/A }
286N/A
286N/A // Invoke the method in the basis library
286N/A index = cpg.addMethodref(BASIS_LIBRARY_CLASS, name,
286N/A _chosenMethodType.toSignature(args));
286N/A il.append(new INVOKESTATIC(index));
286N/A }
286N/A // Add call to BasisLibrary.unresolved_externalF() to generate
286N/A // run-time error message for unsupported external functions
286N/A else if (unresolvedExternal) {
286N/A index = cpg.addMethodref(BASIS_LIBRARY_CLASS,
286N/A "unresolved_externalF",
286N/A "(Ljava/lang/String;)V");
286N/A il.append(new PUSH(cpg, _fname.toString()));
286N/A il.append(new INVOKESTATIC(index));
286N/A }
286N/A else if (_isExtConstructor) {
286N/A if (isSecureProcessing)
286N/A translateUnallowedExtension(cpg, il);
286N/A
286N/A final String clazz =
286N/A _chosenConstructor.getDeclaringClass().getName();
286N/A Class[] paramTypes = _chosenConstructor.getParameterTypes();
286N/A LocalVariableGen[] paramTemp = new LocalVariableGen[n];
286N/A
286N/A // Backwards branches are prohibited if an uninitialized object is
286N/A // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed.
286N/A // We don't know whether this code might contain backwards branches
286N/A // so we mustn't create the new object until after we've created
286N/A // the suspect arguments to its constructor. Instead we calculate
286N/A // the values of the arguments to the constructor first, store them
286N/A // in temporary variables, create the object and reload the
286N/A // arguments from the temporaries to avoid the problem.
286N/A
286N/A for (int i = 0; i < n; i++) {
286N/A final Expression exp = argument(i);
286N/A Type expType = exp.getType();
286N/A exp.translate(classGen, methodGen);
286N/A // Convert the argument to its Java type
286N/A exp.startIterator(classGen, methodGen);
286N/A expType.translateTo(classGen, methodGen, paramTypes[i]);
286N/A paramTemp[i] =
286N/A methodGen.addLocalVariable("function_call_tmp"+i,
286N/A expType.toJCType(),
286N/A null, null);
286N/A paramTemp[i].setStart(
286N/A il.append(expType.STORE(paramTemp[i].getIndex())));
286N/A
286N/A }
286N/A
286N/A il.append(new NEW(cpg.addClass(_className)));
286N/A il.append(InstructionConstants.DUP);
286N/A
286N/A for (int i = 0; i < n; i++) {
286N/A final Expression arg = argument(i);
286N/A paramTemp[i].setEnd(
286N/A il.append(arg.getType().LOAD(paramTemp[i].getIndex())));
286N/A }
286N/A
286N/A final StringBuffer buffer = new StringBuffer();
286N/A buffer.append('(');
286N/A for (int i = 0; i < paramTypes.length; i++) {
286N/A buffer.append(getSignature(paramTypes[i]));
286N/A }
286N/A buffer.append(')');
286N/A buffer.append("V");
286N/A
286N/A index = cpg.addMethodref(clazz,
286N/A "<init>",
286N/A buffer.toString());
286N/A il.append(new INVOKESPECIAL(index));
286N/A
286N/A // Convert the return type back to our internal type
286N/A (Type.Object).translateFrom(classGen, methodGen,
286N/A _chosenConstructor.getDeclaringClass());
286N/A
286N/A }
286N/A // Invoke function calls that are handled in separate classes
286N/A else {
286N/A if (isSecureProcessing)
286N/A translateUnallowedExtension(cpg, il);
286N/A
286N/A final String clazz = _chosenMethod.getDeclaringClass().getName();
286N/A Class[] paramTypes = _chosenMethod.getParameterTypes();
286N/A
286N/A // Push "this" if it is an instance method
286N/A if (_thisArgument != null) {
286N/A _thisArgument.translate(classGen, methodGen);
286N/A }
286N/A
286N/A for (int i = 0; i < n; i++) {
286N/A final Expression exp = argument(i);
286N/A exp.translate(classGen, methodGen);
286N/A // Convert the argument to its Java type
286N/A exp.startIterator(classGen, methodGen);
286N/A exp.getType().translateTo(classGen, methodGen, paramTypes[i]);
286N/A }
286N/A
286N/A final StringBuffer buffer = new StringBuffer();
286N/A buffer.append('(');
286N/A for (int i = 0; i < paramTypes.length; i++) {
286N/A buffer.append(getSignature(paramTypes[i]));
286N/A }
286N/A buffer.append(')');
286N/A buffer.append(getSignature(_chosenMethod.getReturnType()));
286N/A
286N/A if (_thisArgument != null && _clazz.isInterface()) {
286N/A index = cpg.addInterfaceMethodref(clazz,
286N/A _fname.getLocalPart(),
286N/A buffer.toString());
286N/A il.append(new INVOKEINTERFACE(index, n+1));
286N/A }
286N/A else {
286N/A index = cpg.addMethodref(clazz,
286N/A _fname.getLocalPart(),
286N/A buffer.toString());
286N/A il.append(_thisArgument != null ? (InvokeInstruction) new INVOKEVIRTUAL(index) :
286N/A (InvokeInstruction) new INVOKESTATIC(index));
286N/A }
286N/A
286N/A // Convert the return type back to our internal type
286N/A _type.translateFrom(classGen, methodGen,
286N/A _chosenMethod.getReturnType());
286N/A }
286N/A }
286N/A
570N/A @Override
286N/A public String toString() {
286N/A return "funcall(" + _fname + ", " + _arguments + ')';
286N/A }
286N/A
286N/A public boolean isStandard() {
286N/A final String namespace = _fname.getNamespace();
286N/A return (namespace == null) || (namespace.equals(Constants.EMPTYSTRING));
286N/A }
286N/A
286N/A public boolean isExtension() {
286N/A final String namespace = _fname.getNamespace();
286N/A return (namespace != null) && (namespace.equals(EXT_XSLTC));
286N/A }
286N/A
286N/A /**
286N/A * Returns a vector with all methods named <code>_fname</code>
286N/A * after stripping its namespace or <code>null</code>
286N/A * if no such methods exist.
286N/A */
286N/A private Vector findMethods() {
286N/A
286N/A Vector result = null;
286N/A final String namespace = _fname.getNamespace();
286N/A
286N/A if (_className != null && _className.length() > 0) {
286N/A final int nArgs = _arguments.size();
286N/A try {
286N/A if (_clazz == null) {
286N/A _clazz = ObjectFactory.findProviderClass(_className, true);
286N/A
286N/A if (_clazz == null) {
286N/A final ErrorMsg msg =
286N/A new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className);
286N/A getParser().reportError(Constants.ERROR, msg);
286N/A }
286N/A }
286N/A
286N/A final String methodName = _fname.getLocalPart();
286N/A final Method[] methods = _clazz.getMethods();
286N/A
286N/A for (int i = 0; i < methods.length; i++) {
286N/A final int mods = methods[i].getModifiers();
286N/A // Is it public and same number of args ?
286N/A if (Modifier.isPublic(mods)
286N/A && methods[i].getName().equals(methodName)
286N/A && methods[i].getParameterTypes().length == nArgs)
286N/A {
286N/A if (result == null) {
286N/A result = new Vector();
286N/A }
286N/A result.addElement(methods[i]);
286N/A }
286N/A }
286N/A }
286N/A catch (ClassNotFoundException e) {
286N/A final ErrorMsg msg = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className);
286N/A getParser().reportError(Constants.ERROR, msg);
286N/A }
286N/A }
286N/A return result;
286N/A }
286N/A
286N/A /**
286N/A * Returns a vector with all constructors named <code>_fname</code>
286N/A * after stripping its namespace or <code>null</code>
286N/A * if no such methods exist.
286N/A */
286N/A private Vector findConstructors() {
286N/A Vector result = null;
286N/A final String namespace = _fname.getNamespace();
286N/A
286N/A final int nArgs = _arguments.size();
286N/A try {
286N/A if (_clazz == null) {
286N/A _clazz = ObjectFactory.findProviderClass(_className, true);
286N/A
286N/A if (_clazz == null) {
286N/A final ErrorMsg msg = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className);
286N/A getParser().reportError(Constants.ERROR, msg);
286N/A }
286N/A }
286N/A
286N/A final Constructor[] constructors = _clazz.getConstructors();
286N/A
286N/A for (int i = 0; i < constructors.length; i++) {
286N/A final int mods = constructors[i].getModifiers();
286N/A // Is it public, static and same number of args ?
286N/A if (Modifier.isPublic(mods) &&
286N/A constructors[i].getParameterTypes().length == nArgs)
286N/A {
286N/A if (result == null) {
286N/A result = new Vector();
286N/A }
286N/A result.addElement(constructors[i]);
286N/A }
286N/A }
286N/A }
286N/A catch (ClassNotFoundException e) {
286N/A final ErrorMsg msg = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className);
286N/A getParser().reportError(Constants.ERROR, msg);
286N/A }
286N/A
286N/A return result;
286N/A }
286N/A
286N/A
286N/A /**
286N/A * Compute the JVM signature for the class.
286N/A */
286N/A static final String getSignature(Class clazz) {
286N/A if (clazz.isArray()) {
286N/A final StringBuffer sb = new StringBuffer();
286N/A Class cl = clazz;
286N/A while (cl.isArray()) {
286N/A sb.append("[");
286N/A cl = cl.getComponentType();
286N/A }
286N/A sb.append(getSignature(cl));
286N/A return sb.toString();
286N/A }
286N/A else if (clazz.isPrimitive()) {
286N/A if (clazz == Integer.TYPE) {
286N/A return "I";
286N/A }
286N/A else if (clazz == Byte.TYPE) {
286N/A return "B";
286N/A }
286N/A else if (clazz == Long.TYPE) {
286N/A return "J";
286N/A }
286N/A else if (clazz == Float.TYPE) {
286N/A return "F";
286N/A }
286N/A else if (clazz == Double.TYPE) {
286N/A return "D";
286N/A }
286N/A else if (clazz == Short.TYPE) {
286N/A return "S";
286N/A }
286N/A else if (clazz == Character.TYPE) {
286N/A return "C";
286N/A }
286N/A else if (clazz == Boolean.TYPE) {
286N/A return "Z";
286N/A }
286N/A else if (clazz == Void.TYPE) {
286N/A return "V";
286N/A }
286N/A else {
286N/A final String name = clazz.toString();
286N/A ErrorMsg err = new ErrorMsg(ErrorMsg.UNKNOWN_SIG_TYPE_ERR,name);
286N/A throw new Error(err.toString());
286N/A }
286N/A }
286N/A else {
286N/A return "L" + clazz.getName().replace('.', '/') + ';';
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * Compute the JVM method descriptor for the method.
286N/A */
286N/A static final String getSignature(Method meth) {
286N/A final StringBuffer sb = new StringBuffer();
286N/A sb.append('(');
286N/A final Class[] params = meth.getParameterTypes(); // avoid clone
286N/A for (int j = 0; j < params.length; j++) {
286N/A sb.append(getSignature(params[j]));
286N/A }
286N/A return sb.append(')').append(getSignature(meth.getReturnType()))
286N/A .toString();
286N/A }
286N/A
286N/A /**
286N/A * Compute the JVM constructor descriptor for the constructor.
286N/A */
286N/A static final String getSignature(Constructor cons) {
286N/A final StringBuffer sb = new StringBuffer();
286N/A sb.append('(');
286N/A final Class[] params = cons.getParameterTypes(); // avoid clone
286N/A for (int j = 0; j < params.length; j++) {
286N/A sb.append(getSignature(params[j]));
286N/A }
286N/A return sb.append(")V").toString();
286N/A }
286N/A
286N/A /**
286N/A * Return the signature of the current method
286N/A */
286N/A private String getMethodSignature(Vector argsType) {
286N/A final StringBuffer buf = new StringBuffer(_className);
286N/A buf.append('.').append(_fname.getLocalPart()).append('(');
286N/A
286N/A int nArgs = argsType.size();
286N/A for (int i = 0; i < nArgs; i++) {
286N/A final Type intType = (Type)argsType.elementAt(i);
286N/A buf.append(intType.toString());
286N/A if (i < nArgs - 1) buf.append(", ");
286N/A }
286N/A
286N/A buf.append(')');
286N/A return buf.toString();
286N/A }
286N/A
286N/A /**
286N/A * To support EXSLT extensions, convert names with dash to allowable Java names:
286N/A * e.g., convert abc-xyz to abcXyz.
286N/A * Note: dashes only appear in middle of an EXSLT function or element name.
286N/A */
286N/A protected static String replaceDash(String name)
286N/A {
286N/A char dash = '-';
570N/A final StringBuilder buff = new StringBuilder("");
286N/A for (int i = 0; i < name.length(); i++) {
286N/A if (i > 0 && name.charAt(i-1) == dash)
286N/A buff.append(Character.toUpperCase(name.charAt(i)));
286N/A else if (name.charAt(i) != dash)
286N/A buff.append(name.charAt(i));
286N/A }
286N/A return buff.toString();
286N/A }
286N/A
286N/A /**
286N/A * Translate code to call the BasisLibrary.unallowed_extensionF(String)
286N/A * method.
286N/A */
286N/A private void translateUnallowedExtension(ConstantPoolGen cpg,
286N/A InstructionList il) {
286N/A int index = cpg.addMethodref(BASIS_LIBRARY_CLASS,
286N/A "unallowed_extension_functionF",
286N/A "(Ljava/lang/String;)V");
286N/A il.append(new PUSH(cpg, _fname.toString()));
286N/A il.append(new INVOKESTATIC(index));
286N/A }
286N/A}