Package fr.imag.adele.apam.apform.impl

Source Code of fr.imag.adele.apam.apform.impl.InstanceCallback

package fr.imag.adele.apam.apform.impl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.apache.felix.ipojo.ConfigurationException;
import org.apache.felix.ipojo.parser.MethodMetadata;
import org.apache.felix.ipojo.parser.PojoMetadata;
import org.apache.felix.ipojo.util.Callback;


/**
* This is the base class of all APAM callbacks that are invoked on component instances.
*
* All callbacks are methods declared in the component implementation, with an optional
* parameter whose type depends on the kind of callback.
*
* To allow some basic type checking at invocation, we use the parameter type to represent
* the type of the argument.
*
* @author vega
*
*/
public abstract class InstanceCallback<T> extends Callback {

  /**
   * The associated Apam component instance to be injected
   */
  protected final ApamInstanceManager instance;

  /**
   * Whether invocation requires an argument
   */
    protected boolean requiresArgument;

  /**
   * The type of the required argument
   */
    protected Class<?> argumentType;
   
   
  protected InstanceCallback(ApamInstanceManager instance, String method) throws ConfigurationException {
   
    super(method,(String[])null,false,instance);
   
    this.instance  = instance;
  }

  /**
   * Uses introspection to search for the callback method in the instrumented class of the
   * component implementation
   */
  @Override
  protected void searchMethod() throws NoSuchMethodException {
   
   
    m_methodObj     = null;
    requiresArgument  = false;
    argumentType    = null;
   
    /*
     * Try to find the declared method with the specified name that best matches the callback signature.
     * Try first the locally declared methods, and only if not found search publicly declared methods
     * of super classes.
     *
     * NOTE Notice that we force class loading of the implementation class (and its super classes) by using
     * reflection to search for methods.
     */
   
    Method[] candidates = instance.getClazz().getDeclaredMethods();
        int match = searchMethod(candidates);
        if (match == -1) {
          candidates = instance.getClazz().getMethods();
          match = searchMethod(candidates);
        }
       
        if (match == -1)
          throw new NoSuchMethodException(getMethod());
       
        m_methodObj = candidates[match];
        if (! m_methodObj.isAccessible()) {
            m_methodObj.setAccessible(true);
        }
       
        requiresArgument  = m_methodObj.getParameterTypes().length == 1;
        argumentType    = requiresArgument ? m_methodObj.getParameterTypes()[0] : null;
  }

  /**
   * Search the method associated with this callback in the specified array.
   *
      * We are looking for a method with an optional, single, parameter of the expected type.
      *
      * NOTE that if the same method name is defined with different signatures, we try to use the
      * most specific version.
   *
   */
  protected int searchMethod(Method[] methods) {

          Method candidate       = null;
      int candidateIndex      = -1;
          Class<?> candidateParameter  = null;
         
          for (int index = 0; index < methods.length; index++) {
       
            Method method = methods[index];
           
            /*
             * Skip all methods not matching the name, or the number and type
             * of parameter
             */
            if (! method.getName().equals(getMethod()))
          continue;

            Class<?>[] parameters = method.getParameterTypes();
            if (parameters.length > 1)
              continue;

               Class<?> parameter = parameters.length == 1 ? parameters[0] : null;
               if (parameter != null && ! isExpectedParameter(parameter))
              continue;
           
               /*
                * Register the candidate, but continue searching for a better match
                */
            if (candidate == null) {
              candidate       = method;
              candidateIndex    = index;
              candidateParameter  = parameter;
              continue;
            }
           
            /*
             * If we find a method with a parameter, prefer it
             */
            if (candidateParameter == null && parameter != null) {
              candidate       = method;
              candidateIndex    = index;
              candidateParameter  = parameter;
              continue;
            }
           
            /*
             * If we find a method with a parameter, and the current candidate also has a parameter
             * prefer the method with the most specific type
             */
            if (candidateParameter != null && parameter != null && candidateParameter.isAssignableFrom(parameter)) {
              candidate       = method;
              candidateIndex    = index;
              candidateParameter  = parameter;
              continue;
            }
      }
         
          return candidateIndex;

  }
 
  /**
   * Whether invocation actually requires an argument or not
   */
  public final boolean requiresArgument() {
    return requiresArgument;
  }

  /**
   * The actual type of the parameter, if required
   */
  public final Class<?> getArgumentType() {
    return argumentType;
  }
 
  /**
   * Invokes the callback method with the specified optional argument.
   *
   * If the argument doesn't match the parameter type, we try to perform automatic
   * casting (specifically defined for each kind of callback)
   */
  public Object invoke(T argument) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        if (m_methodObj == null) {
            searchMethod();
        }

        Object actualArgument = requiresArgument ? argumentType.isInstance(argument) ? argument : cast(argument): null;

    return call(requiresArgument ? new Object[] {actualArgument} : new Object[0]);
  }

  /**
   * Casts the argument of the callback, in cases where it doesn't match the parameter type of the method.
   *
   * NOTE IMPORTANT If casting is not possible, this method should return the unmodified argument. In this
   * way if there are errors in the declarations the user will get a class cast exception.
   */
  protected Object cast(T argument) {
    return argument;
  }

  /**
   * The iPOJO metadata associated with the callback method to be invoked
   */
  public MethodMetadata getMethodMetadata() {
   
    PojoMetadata metadata   = instance.getFactory().getPojoMetadata();
    String[] arguments    = requiresArgument ? new String[] {m_methodObj.getParameterTypes()[0].getCanonicalName()}: new String[0];
   
    return metadata.getMethod(m_methodObj.getName(),arguments);
  }
 
  /**
   * Test whether the specified method is the one invoked by this callback
   */
  public boolean invokes(Method method)  {
    return m_methodObj.equals(method);
  }
 
 
  /**
   * Whether this is the expected parameter type of the callback.
   *
   * This method must be overridden by kinds of callbacks depending on the specific method
   * signature required.
   */
  protected abstract boolean isExpectedParameter(Class<?> parameterType);

}
TOP

Related Classes of fr.imag.adele.apam.apform.impl.InstanceCallback

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.