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 }