1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 2017
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  23 
  24 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  25 import com.sun.org.apache.bcel.internal.generic.PUSH;
  26 import com.sun.org.apache.xalan.internal.utils.ObjectFactory;
  27 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  28 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  33 import java.lang.reflect.Method;
  34 import java.lang.reflect.Modifier;
  35 import java.util.List;
  36 
  37 /**
  38  * @author G. Todd Miller
  39  * @author Santiago Pericas-Geertsen
  40  */
  41 final class FunctionAvailableCall extends FunctionCall {
  42 
  43     private Expression _arg;
  44     private String     _nameOfFunct = null;
  45     private String     _namespaceOfFunct = null;
  46     private boolean    _isFunctionAvailable = false;
  47 
  48     /**
  49      * Constructs a FunctionAvailableCall FunctionCall. Takes the
  50      * function name qname, for example, 'function-available', and
  51      * a list of arguments where the arguments must be instances of
  52      * LiteralExpression.
  53      */
  54     public FunctionAvailableCall(QName fname, List<Expression> arguments) {
  55         super(fname, arguments);
  56         _arg = (Expression)arguments.get(0);
  57         _type = null;
  58 
  59         if (_arg instanceof LiteralExpr) {
  60             LiteralExpr arg = (LiteralExpr) _arg;
  61             _namespaceOfFunct = arg.getNamespace();
  62             _nameOfFunct = arg.getValue();
  63 
  64             if (!isInternalNamespace()) {
  65               _isFunctionAvailable = hasMethods();
  66             }
  67         }
  68     }
  69 
  70     /**
  71      * Argument of function-available call must be literal, typecheck
  72      * returns the type of function-available to be boolean.
  73      */
  74     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  75         if (_type != null) {
  76            return _type;
  77         }
  78         if (_arg instanceof LiteralExpr) {
  79             return _type = Type.Boolean;
  80         }
  81         ErrorMsg err = new ErrorMsg(ErrorMsg.NEED_LITERAL_ERR,
  82                         "function-available", this);
  83         throw new TypeCheckError(err);
  84     }
  85 
  86     /**
  87      * Returns an object representing the compile-time evaluation
  88      * of an expression. We are only using this for function-available
  89      * and element-available at this time.
  90      */
  91     public Object evaluateAtCompileTime() {
  92         return getResult() ? Boolean.TRUE : Boolean.FALSE;
  93     }
  94 
  95     /**
  96      * for external java functions only: reports on whether or not
  97      * the specified method is found in the specifed class.
  98      */
  99     private boolean hasMethods() {
 100         LiteralExpr arg = (LiteralExpr)_arg;
 101 
 102         // Get the class name from the namespace uri
 103         String className = getClassNameFromUri(_namespaceOfFunct);
 104 
 105         // Get the method name from the argument to function-available
 106         String methodName = null;
 107         int colonIndex = _nameOfFunct.indexOf(":");
 108         if (colonIndex > 0) {
 109           String functionName = _nameOfFunct.substring(colonIndex+1);
 110           int lastDotIndex = functionName.lastIndexOf('.');
 111           if (lastDotIndex > 0) {
 112             methodName = functionName.substring(lastDotIndex+1);
 113             if (className != null && !className.equals(""))
 114               className = className + "." + functionName.substring(0, lastDotIndex);
 115             else
 116               className = functionName.substring(0, lastDotIndex);
 117           }
 118           else
 119             methodName = functionName;
 120         }
 121         else
 122           methodName = _nameOfFunct;
 123 
 124         if (className == null || methodName == null) {
 125             return false;
 126         }
 127 
 128         // Replace the '-' characters in the method name
 129         if (methodName.indexOf('-') > 0)
 130           methodName = replaceDash(methodName);
 131 
 132         try {
 133             final Class<?> clazz = ObjectFactory.findProviderClass(className, 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 }