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    }