001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: FunctionAvailableCall.java 1225364 2011-12-28 22:45:16Z mrglavas $ 020 */ 021 022 package org.apache.xalan.xsltc.compiler; 023 024 import java.lang.reflect.Method; 025 import java.lang.reflect.Modifier; 026 import java.util.Vector; 027 028 import org.apache.bcel.generic.ConstantPoolGen; 029 import org.apache.bcel.generic.PUSH; 030 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 031 import org.apache.xalan.xsltc.compiler.util.ErrorMsg; 032 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 033 import org.apache.xalan.xsltc.compiler.util.Type; 034 import org.apache.xalan.xsltc.compiler.util.TypeCheckError; 035 import org.apache.xalan.xsltc.compiler.util.Util; 036 037 /** 038 * @author G. Todd Miller 039 * @author Santiago Pericas-Geertsen 040 */ 041 final class FunctionAvailableCall extends FunctionCall { 042 043 private Expression _arg; 044 private String _nameOfFunct = null; 045 private String _namespaceOfFunct = null; 046 private boolean _isFunctionAvailable = false; 047 048 /** 049 * Constructs a FunctionAvailableCall FunctionCall. Takes the 050 * function name qname, for example, 'function-available', and 051 * a list of arguments where the arguments must be instances of 052 * LiteralExpression. 053 */ 054 public FunctionAvailableCall(QName fname, Vector arguments) { 055 super(fname, arguments); 056 _arg = (Expression)arguments.elementAt(0); 057 _type = null; 058 059 if (_arg instanceof LiteralExpr) { 060 LiteralExpr arg = (LiteralExpr) _arg; 061 _namespaceOfFunct = arg.getNamespace(); 062 _nameOfFunct = arg.getValue(); 063 064 if (!isInternalNamespace()) { 065 _isFunctionAvailable = hasMethods(); 066 } 067 } 068 } 069 070 /** 071 * Argument of function-available call must be literal, typecheck 072 * returns the type of function-available to be boolean. 073 */ 074 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 075 if (_type != null) { 076 return _type; 077 } 078 if (_arg instanceof LiteralExpr) { 079 return _type = Type.Boolean; 080 } 081 ErrorMsg err = new ErrorMsg(ErrorMsg.NEED_LITERAL_ERR, 082 "function-available", this); 083 throw new TypeCheckError(err); 084 } 085 086 /** 087 * Returns an object representing the compile-time evaluation 088 * of an expression. We are only using this for function-available 089 * and element-available at this time. 090 */ 091 public Object evaluateAtCompileTime() { 092 return getResult() ? Boolean.TRUE : Boolean.FALSE; 093 } 094 095 /** 096 * for external java functions only: reports on whether or not 097 * the specified method is found in the specifed class. 098 */ 099 private boolean hasMethods() { 100 101 // Get the class name from the namespace uri 102 String className = getClassNameFromUri(_namespaceOfFunct); 103 104 // Get the method name from the argument to function-available 105 String methodName = null; 106 int colonIndex = _nameOfFunct.indexOf(":"); 107 if (colonIndex > 0) { 108 String functionName = _nameOfFunct.substring(colonIndex+1); 109 int lastDotIndex = functionName.lastIndexOf('.'); 110 if (lastDotIndex > 0) { 111 methodName = functionName.substring(lastDotIndex+1); 112 if (className != null && className.length() != 0) 113 className = className + "." + functionName.substring(0, lastDotIndex); 114 else 115 className = functionName.substring(0, lastDotIndex); 116 } 117 else 118 methodName = functionName; 119 } 120 else 121 methodName = _nameOfFunct; 122 123 if (className == null || methodName == null) { 124 return false; 125 } 126 127 // Replace the '-' characters in the method name 128 if (methodName.indexOf('-') > 0) 129 methodName = replaceDash(methodName); 130 131 try { 132 final Class clazz = ObjectFactory.findProviderClass( 133 className, ObjectFactory.findClassLoader(), true); 134 135 if (clazz == null) { 136 return false; 137 } 138 139 final Method[] methods = clazz.getMethods(); 140 141 for (int i = 0; i < methods.length; i++) { 142 final int mods = methods[i].getModifiers(); 143 144 if (Modifier.isPublic(mods) && Modifier.isStatic(mods) 145 && methods[i].getName().equals(methodName)) 146 { 147 return true; 148 } 149 } 150 } 151 catch (ClassNotFoundException e) { 152 return false; 153 } 154 return false; 155 } 156 157 /** 158 * Reports on whether the function specified in the argument to 159 * xslt function 'function-available' was found. 160 */ 161 public boolean getResult() { 162 if (_nameOfFunct == null) { 163 return false; 164 } 165 166 if (isInternalNamespace()) { 167 final Parser parser = getParser(); 168 _isFunctionAvailable = 169 parser.functionSupported(Util.getLocalName(_nameOfFunct)); 170 } 171 return _isFunctionAvailable; 172 } 173 174 /** 175 * Return true if the namespace uri is null or it is the XSLTC translet uri. 176 */ 177 private boolean isInternalNamespace() { 178 return (_namespaceOfFunct == null || 179 _namespaceOfFunct.equals(EMPTYSTRING) || 180 _namespaceOfFunct.equals(TRANSLET_URI)); 181 } 182 183 /** 184 * Calls to 'function-available' are resolved at compile time since 185 * the namespaces declared in the stylsheet are not available at run 186 * time. Consequently, arguments to this function must be literals. 187 */ 188 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 189 final ConstantPoolGen cpg = classGen.getConstantPool(); 190 methodGen.getInstructionList().append(new PUSH(cpg, getResult())); 191 } 192 193 }