Package org.apache.jorphan.reflect

Source Code of org.apache.jorphan.reflect.Functor

/*
* 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.
*/

package org.apache.jorphan.reflect;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.apache.jorphan.logging.LoggingManager;
import org.apache.jorphan.util.JMeterError;
import org.apache.log.Logger;

/**
* Implements function call-backs.
*
* Functors may be defined for instance objects or classes.
*
* The method is created on first use, which allows the invokee (class or instance)
* to be omitted from the constructor.
*
* The class name takes precedence over the instance.
*
* If a functor is created with a particular instance, then that is used for all future calls;
* if an object is provided, it is ignored.
* This allows easy override of the table model behaviour.
*
* If an argument list is provided in the constructor, then that is ignored in subsequent invoke() calls.
*
* Usage:
* f = new Functor("methodName")
* o = f.invoke(object) - OR -
* o = f.invoke(object,params)
*
* f2 = new Functor(object,"methodName");
* o = f2.invoke() - OR -
* o = f2.invoke(params)
*
* f3 = new Functor(class,"methodName");
* o = f3.invoke(object) - will be ignored
* o = f3.invoke() - OR -
* o = f3.invoke(params)
* o = f3.invoke(object,params) - object will be ignored
*
*/
public class Functor {
  private static final Logger log = LoggingManager.getLoggerForClass();

  /*
   * If non-null, then any object provided to invoke() is ignored.
   */
  private final Object invokee;

  /*
   * Class to be used to create the Method.
   * Will be non-null if either Class or Object was provided during construction.
   *
   *  Can be used instead of invokee, e.g. when using interfaces.
  */
  private final Class clazz;

  // Methondname must always be provided.
  private final String methodName;

  /*
   * If non-null, then any argument list passed to invoke() will be ignored.
   */
  private Object[] args;

  /*
   * Argument types used to create the method.
   * May be provided explicitly, or derived from the constructor argument list.
   */
  private final Class[] types;

  /*
   * This depends on the class or invokee and either args or types;
   * it is set once by doCreateMethod(), which must be the only method to access it.
  */
  private Method methodToInvoke;
 
  Functor(){
    throw new IllegalArgumentException("Must provide at least one argument");
  }

  /**
   * Create a functor with the invokee and a method name.
   *
   * The invokee will be used in all future invoke calls.
   *
   * @param _invokee object on which to invoke the method
   * @param _methodName method name
   */
  public Functor(Object _invokee, String _methodName) {
    this(null, _invokee, _methodName, null, null);
  }

  /**
   * Create a functor from class and method name.
   * This is useful for methods defined in interfaces.
   *
   * The actual invokee must be provided in all invoke() calls,
   * and must be an instance of the class.
   *
   * @param _clazz class to be used
   * @param _methodName method name
   */
  public Functor(Class _clazz, String _methodName) {
    this(_clazz, null, _methodName, null, null);
  }

  /**
   * Create a functor with the invokee, method name, and argument class types.
   *
   * The invokee will be ignored in any invoke() calls.
   *
   * @param _invokee object on which to invoke the method
   * @param _methodName method name
   * @param _types
   */
  public Functor(Object _invokee, String _methodName, Class[] _types) {
    this(null, _invokee, _methodName, null, _types);
  }

  /**
   * Create a functor with the class, method name, and argument class types.
   *
     * Subsequent invoke() calls must provide the appropriate ivokee object.
     *
   * @param _clazz the class in which to find the method
   * @param _methodName method name
   * @param _types
   */
  public Functor(Class _clazz, String _methodName, Class[] _types) {
    this(_clazz, null, _methodName, null, _types);
  }

  /**
   * Create a functor with just the method name.
   *
   * The invokee and any parameters must be provided in all invoke() calls.
   *
   * @param _methodName method name
   */
  public Functor(String _methodName) {
    this(null, null, _methodName, null, null);
  }

  /**
   * Create a functor with the method name and argument class types.
   *
   * The invokee must be provided in all invoke() calls
   *
   * @param _methodName method name
   * @param _types parameter types
   */
  public Functor(String _methodName, Class[] _types) {
    this(null, null, _methodName, null, _types);
  }

  /**
   * Create a functor with an invokee, method name, and argument values.
   *
   * The invokee will be ignored in any invoke() calls.
   *
   * @param _invokee object on which to invoke the method
   * @param _methodName method name
   * @param _args arguments to be passed to the method
   */
  public Functor(Object _invokee, String _methodName, Object[] _args) {
    this(null, _invokee, _methodName, _args, null);
  }

  /**
   * Create a functor from method name and arguments.
   * 
   * The class will be determined from the first invoke call.
   * All invoke calls must include a target object;
   * which must be of the same type as the initial invokee.
   *
   * @param _methodName method name
   * @param _args
   */
  public Functor(String _methodName, Object[] _args) {
    this(null, null, _methodName, _args, null);
  }

  /**
   * Create a functor from various different combinations of parameters.
   *
   * @param _clazz class containing the method
   * @param _invokee invokee to use for the method call
   * @param _methodName the method name (required)
   * @param _args arguments to be used
   * @param _types types of arguments to be used
   *
   * @throws IllegalArgumentException if:
   * - methodName is null
   * - both class and invokee are specified
   * - both arguments and types are specified
   */
  private Functor(Class _clazz, Object _invokee, String _methodName, Object[] _args, Class[] _types) {
    if (_methodName == null){
      throw new IllegalArgumentException("Methodname must not be null");
    }
    if (_clazz != null && _invokee != null){
      throw new IllegalArgumentException("Cannot provide both Class and Object");
    }
    if (_args != null && _types != null){
      throw new IllegalArgumentException("Cannot provide both arguments and argument types");
    }
    // If class not provided, default to invokee class, else null
    this.clazz = _clazz != null ? _clazz : (_invokee != null ? _invokee.getClass() : null);
    this.invokee = _invokee;
    this.methodName = _methodName;
    this.args = _args;
    // If types not provided, default to argument types, else null
    this.types = _types != null ? _types : (_args != null ? _getTypes(_args) : null);
  }

  //////////////////////////////////////////
 
  /*
   * Low level invocation routine.
   *
   * Should only be called after any defaults have been applied.
   *
   */
  private Object doInvoke(Class _class, Object _invokee, Object[] _args) {
    Class[] argTypes = getTypes(_args);
    try {
      Method method = doCreateMethod(_class , argTypes);
      if (method == null){
              final String message = "Can't find method "
                  +_class.getName()+"#"+methodName+typesToString(argTypes);
              log.error(message, new Throwable());
              throw new JMeterError(message);                 
      }
      return method.invoke(_invokee, _args);
    } catch (Exception e) {
      final String message = "Trouble functing: "
        +_class.getName()
        +"."+methodName+"(...) : "
        +" invokee: "+_invokee
        +" "+e.getMessage();
      log.warn(message, e);
      throw new JMeterError(message,e);
    }
  }

  /**
   * Invoke a Functor, which must have been created with either a class name or object.
   *
   * @return the object if any
   */
  public Object invoke() {
    if (invokee == null) {
      throw new IllegalStateException("Cannot call invoke() - invokee not known");
    }
    // If invokee was provided, then clazz has been set up
    return doInvoke(clazz, invokee, getArgs());
  }

  /**
   * Invoke the method on a given object.
   *
   * @param p_invokee - provides the object to call; ignored if the class or object were provided to the constructor
   * @return the value
   */
  public Object invoke(Object p_invokee) {
    return invoke(p_invokee, getArgs());
  }

  /**
   * Invoke the method with the provided parameters.
   *
   * The invokee must have been provided in the constructor.
   *
   * @param p_args parameters for the method
   * @return the value
   */
  public Object invoke(Object[] p_args) {
    if (invokee == null){
      throw new IllegalStateException("Invokee was not provided in constructor");
    }
    // If invokee was provided, then clazz has been set up
    return doInvoke(clazz, invokee, args != null? args : p_args);
  }

  /**
   * Invoke the method on the invokee with the provided parameters.
   *
   * The invokee must agree with the class (if any) provided at construction time.
   *
   * If the invokee was provided at construction time, then this invokee will be ignored.
   * If actual arguments were provided at construction time, then arguments will be ignored.
   *
   */
  public Object invoke(Object p_invokee, Object[] p_args) {
    return doInvoke(clazz != null ? clazz : p_invokee.getClass(), // Use constructor class if present
               invokee != null ? invokee : p_invokee, // use invokee if provided
            args != null? args : p_args);// use argumenrs if provided
  }

  /*
   * Low-level (recursive) routine to define the method - if not already defined.
   * Synchronized to protect access to methodToInvoke.
   */
  private synchronized Method doCreateMethod(Class p_class, Class[] p_types) {
    if (log.isDebugEnabled()){
        log.debug("doCreateMethod() using "+this.toString()
          +"class="
        + p_class.getName()
        + " types: " + Arrays.asList(p_types));
    }
    if (methodToInvoke == null) {
      try {
        methodToInvoke = p_class.getMethod(methodName, p_types);
      } catch (Exception e) {
        for (int i = 0; i < p_types.length; i++) {
          Class primitive = getPrimitive(p_types[i]);
          if (primitive != null) {
            methodToInvoke = doCreateMethod(p_class, getNewArray(i, primitive, p_types));
            if (methodToInvoke != null) {
              return methodToInvoke;
            }
          }
          Class[] interfaces = p_types[i].getInterfaces();
          for (int j = 0; j < interfaces.length; j++) {
            methodToInvoke = doCreateMethod(p_class,getNewArray(i, interfaces[j], p_types));
            if (methodToInvoke != null) {
              return methodToInvoke;
            }
          }
          Class parent = p_types[i].getSuperclass();
          if (parent != null) {
            methodToInvoke = doCreateMethod(p_class,getNewArray(i, parent, p_types));
            if (methodToInvoke != null) {
              return methodToInvoke;
            }
          }
        }
      }
    }
    return methodToInvoke;
  }

  /**
   * Check if a read Functor method is valid.
   *
   * @deprecated ** for use by Unit test code only **
   *
   * @return true if method exists
   */
  public boolean checkMethod(Object _invokee){
    Method m = null;
    try {
        m = doCreateMethod(_invokee.getClass(), getTypes(args));
    } catch (Exception e){
      // ignored
    }
    return null != m;
  }

  /**
   * Check if a write Functor method is valid.
   *
   * @deprecated ** for use by Unit test code only **
   *
   * @return true if method exists
   */
  public boolean checkMethod(Object _invokee, Class c){
    Method m = null;
    try {
        m = doCreateMethod(_invokee.getClass(), new Class[]{c});
    } catch (Exception e){
      // ignored
    }
    return null != m;
  }

  public String toString(){
    StringBuffer sb = new StringBuffer(100);
    if (clazz != null){
      sb.append(clazz.getName());
    }
    if (invokee != null){
        sb.append("@");
        sb.append(System.identityHashCode(invokee));
    }
    sb.append(".");
    sb.append(methodName);
    typesToString(sb,types);
    return sb.toString();
  }

  private void typesToString(StringBuffer sb,Class[] _types) {
    sb.append("(");
    if (_types != null){
      for(int i=0; i < _types.length; i++){
        if (i>0) {
            sb.append(",");
        }
        sb.append(_types[i].getName());
      }
    }
    sb.append(")");
  }

  private String typesToString(Class[] argTypes) {
    StringBuffer sb = new StringBuffer();
    typesToString(sb,argTypes);
    return sb.toString();
  }

  private Class getPrimitive(Class t) {
    if (t==null) {
        return null;
    }
    if (t.equals(Integer.class)) {
      return int.class;
    } else if (t.equals(Long.class)) {
      return long.class;
    } else if (t.equals(Double.class)) {
      return double.class;
    } else if (t.equals(Float.class)) {
      return float.class;
    } else if (t.equals(Byte.class)) {
      return byte.class;
    } else if (t.equals(Boolean.class)) {
      return boolean.class;
    } else if (t.equals(Short.class)) {
      return short.class;
    } else if (t.equals(Character.class)) {
      return char.class;
    }
    return null;
  }

  private Class[] getNewArray(int i, Class replacement, Class[] orig) {
    Class[] newArray = new Class[orig.length];
    for (int j = 0; j < newArray.length; j++) {
      if (j == i) {
        newArray[j] = replacement;
      } else {
        newArray[j] = orig[j];       
      }
    }
    return newArray;
  }

  private Class[] getTypes(Object[] _args) {
    if (types == null)
    {
      return _getTypes(_args);
    }
    return types;
  }

  private static Class[] _getTypes(Object[] _args) {
    Class[] _types;
    if (_args != null) {
      _types = new Class[_args.length];
      for (int i = 0; i < _args.length; i++) {
        _types[i] = _args[i].getClass();
      }
    } else {
      _types = new Class[0];
    }
    return _types;
  }

  private Object[] getArgs() {
    if (args == null) {
      args = new Object[0];
    }
    return args;
  }
}
TOP

Related Classes of org.apache.jorphan.reflect.Functor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.