quinta-feira, janeiro 12, 2006

Duck Type em Java

Como o Java é uma linguagem boa e com excelentes APIs, mas muito tosca em algumas coisas básicas (como manipulação de Strings e Coleções), volta e meio fazemos alguns utilitários estúpidos pra facilitar a vida. Aqui vai um dos meus, um simples wrapper para facilitar a chamada de métodos dinâmicos.
package common;

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

/**
 * Executes a method in an object dinamically. It's a wrapper to the java
 * reflection way of execute methods dinamically.
 */
public class DuckType {

  public static  T execute(Object object, String methodName, Object ... parameters) {

    if (object == null) return null;
    if (methodName == null || methodName.equals("")) return null;
    
    Class[] classes = extractClassesFromParameters(parameters);
    
    try {
      
      return (T) executeEasyWay(object, methodName, classes, parameters);

    } catch (NoSuchMethodException e) {
      
      return (T) executeHardWay(object, methodName, classes, parameters);
      
    } catch (SecurityException e) {
      return null;
    } catch (IllegalArgumentException e) {
      return null;
    } catch (IllegalAccessException e) {
      return null;
    } catch (InvocationTargetException e) {
      return null;
    }
  }

  private static Object executeEasyWay(Object object, String methodName, Class[] classes, Object... parameters) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    
    Method method = object.getClass().getMethod(methodName, classes);
    return method.invoke(object, parameters);
  }

  private static Object executeHardWay(Object object, String methodName, Class[] classes, Object... parameters) {
    
    // it didn't find the method in the easy way, trying the hard way
    Method[] methods = object.getClass().getMethods();
    
    for (Method method : methods) {
      Class[] methodTypes = method.getParameterTypes();
      
      // Ignore methods with different names
      if (!methodName.equals(method.getName())) continue;
      
      // Ignore methods with different number of parameters 
      if (methodTypes.length != classes.length) continue;
      
      boolean isMethodExecutable = true;
      for (int i = 0; i < methodTypes.length; i++) {
        Class methodType = (Class) methodTypes[i];
        Class parameterType = classes[i];
        
        if (!methodType.isAssignableFrom(parameterType)) {
          isMethodExecutable = false;
          break;
        }
      }
      
      try {
        
        if (isMethodExecutable) return method.invoke(object, parameters);
        
      } catch (IllegalArgumentException e1) {
        return null;
      } catch (IllegalAccessException e1) {
        return null;
      } catch (InvocationTargetException e1) {
        return null;
      }
    }
    
    return null;
  }

  private static Class[] extractClassesFromParameters(Object[] parameters) {
    
    Class[] classes = new Class[parameters.length];
    
    for (int i = 0; i < parameters.length; i++) {
      Object parameter = parameters[i];
      classes[i] = parameter.getClass();        
    }
    
    return classes;
  }
}

Nenhum comentário:

Postar um comentário