code: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the  "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /*
   * $Id$
   */
  
  package org.apache.xalan.xsltc.compiler;
  
  import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  import java.util.Vector;
  
  import org.apache.bcel.generic.ConstantPoolGen;
  import org.apache.bcel.generic.PUSH;
  import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
  import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
  import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
  import org.apache.xalan.xsltc.compiler.util.Type;
  import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
  import org.apache.xalan.xsltc.compiler.util.Util;
  
  /**
   * @author G. Todd Miller 
   * @author Santiago Pericas-Geertsen
   */
  final class FunctionAvailableCall extends FunctionCall {
  
      private Expression _arg; 
      private String     _nameOfFunct = null; 
      private String     _namespaceOfFunct = null; 	
      private boolean    _isFunctionAvailable = false; 
  
      /**
       * Constructs a FunctionAvailableCall FunctionCall. Takes the
       * function name qname, for example, 'function-available', and 
       * a list of arguments where the arguments must be instances of 
       * LiteralExpression. 
       */
      public FunctionAvailableCall(QName fname, Vector arguments) {
  	super(fname, arguments);
  	_arg = (Expression)arguments.elementAt(0);
  	_type = null; 
  
          if (_arg instanceof LiteralExpr) {
  	    LiteralExpr arg = (LiteralExpr) _arg;
              _namespaceOfFunct = arg.getNamespace();
              _nameOfFunct = arg.getValue();
  
              if (!isInternalNamespace()) {
                _isFunctionAvailable = hasMethods();
              }
          }
      }
  
      /**
       * Argument of function-available call must be literal, typecheck
       * returns the type of function-available to be boolean.  
       */
      public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  	if (_type != null) {
  	   return _type;
  	}
  	if (_arg instanceof LiteralExpr) {
  	    return _type = Type.Boolean;
  	}
  	ErrorMsg err = new ErrorMsg(ErrorMsg.NEED_LITERAL_ERR,
  			"function-available", this);
  	throw new TypeCheckError(err);
      }
  
      /**
       * Returns an object representing the compile-time evaluation 
       * of an expression. We are only using this for function-available
       * and element-available at this time.
       */
      public Object evaluateAtCompileTime() {
  	return getResult() ? Boolean.TRUE : Boolean.FALSE;
      }
  
      /**
       * for external java functions only: reports on whether or not
       * the specified method is found in the specifed class. 
       */
      private boolean hasMethods() {
  	
  	// Get the class name from the namespace uri
  	String className = getClassNameFromUri(_namespaceOfFunct);
  	
  	// Get the method name from the argument to function-available
  	String methodName = null;
  	int colonIndex = _nameOfFunct.indexOf(":");
  	if (colonIndex > 0) {
  	  String functionName = _nameOfFunct.substring(colonIndex+1);
  	  int lastDotIndex = functionName.lastIndexOf('.');
  	  if (lastDotIndex > 0) {
  	    methodName = functionName.substring(lastDotIndex+1);
  	    if (className != null && className.length() != 0)
  	      className = className + "." + functionName.substring(0, lastDotIndex);
  	    else
  	      className = functionName.substring(0, lastDotIndex);
  	  }
  	  else
  	    methodName = functionName;
  	}
  	else
  	  methodName = _nameOfFunct;
  	  
  	if (className == null || methodName == null) {
  	    return false;
  	}
  	
  	// Replace the '-' characters in the method name
  	if (methodName.indexOf('-') > 0)
  	  methodName = replaceDash(methodName);
  
  	try {
              final Class clazz = ObjectFactory.findProviderClass(
                  className, ObjectFactory.findClassLoader(), true);
  
  	    if (clazz == null) {
  	    	return false;
  	    }
  
  	    final Method[] methods = clazz.getMethods();
  
  	    for (int i = 0; i < methods.length; i++) {
  		final int mods = methods[i].getModifiers();
  
  		if (Modifier.isPublic(mods) && Modifier.isStatic(mods)
  			&& methods[i].getName().equals(methodName))
  		{
  		    return true;
  		}
  	    }
  	}
  	catch (ClassNotFoundException e) {
  	  return false;
  	}
          return false;   
      }
  
      /**
       * Reports on whether the function specified in the argument to
       * xslt function 'function-available' was found.
       */
      public boolean getResult() {
  	if (_nameOfFunct == null) { 
  	    return false;
  	}
  
          if (isInternalNamespace()) {
              final Parser parser = getParser();
              _isFunctionAvailable = 
  		parser.functionSupported(Util.getLocalName(_nameOfFunct));
          }
   	return _isFunctionAvailable;
      }
      
      /**
       * Return true if the namespace uri is null or it is the XSLTC translet uri.
       */
      private boolean isInternalNamespace() {
      	return (_namespaceOfFunct == null ||
              _namespaceOfFunct.equals(EMPTYSTRING) ||
  	    _namespaceOfFunct.equals(TRANSLET_URI));
      }
  
      /**
       * Calls to 'function-available' are resolved at compile time since 
       * the namespaces declared in the stylsheet are not available at run
       * time. Consequently, arguments to this function must be literals.
       */
      public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
  	final ConstantPoolGen cpg = classGen.getConstantPool();
  	methodGen.getInstructionList().append(new PUSH(cpg, getResult()));
      }
  
  }
  