WeakCastUtils.java

package org.cyclopsgroup.jmxterm.utils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import javax.naming.OperationNotSupportedException;
import org.apache.commons.lang3.Validate;

/**
 * Utility that cast object into given interface(s) even though class doesn't implement interface(s)
 *
 * @author <a href="mailto:jiaqi.guo@gmail.com">Jiaqi Guo</a>
 */
public final class WeakCastUtils {
  /**
   * Cast object into multiple interfaces
   *
   * @param original The interface of the from instance
   * @param from Object to cast
   * @param interfaces Interfaces to cast to
   * @param classLoader ClassLoader to load methods for invocation
   * @return Result that implements given interfaces
   * @throws SecurityException Allows exception related to security.
   * @throws NoSuchMethodException Allows exception due to wrong method.
   */
  public static Object cast(
      final Class<?> original,
      final Object from,
      final Class<?>[] interfaces,
      ClassLoader classLoader)
      throws SecurityException, NoSuchMethodException {
    Validate.notNull(from, "Invocation target can't be NULL");
    Validate.notNull(interfaces, "Interfaces can't be NULL");
    Validate.notNull(classLoader, "ClassLoader can't be NULL");
    final Map<Method, Method> methodMap = new HashMap<Method, Method>();
    for (Class<?> interfase : interfaces) {
      Validate.isTrue(interfase.isInterface(), interfase + " is not an interface");
      for (Method fromMethod : interfase.getMethods()) {
        Method toMethod = original.getMethod(fromMethod.getName(), fromMethod.getParameterTypes());
        methodMap.put(fromMethod, toMethod);
      }
    }
    return Proxy.newProxyInstance(
        classLoader,
        interfaces,
        new InvocationHandler() {
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Method toMethod = methodMap.get(method);
            if (toMethod == null) {
              throw new OperationNotSupportedException(
                  "Method " + method + " isn't implemented in " + from.getClass());
            }
            try {
              if ((toMethod.getModifiers() & Modifier.STATIC) == 0) {
                return toMethod.invoke(from, args);
              }
              return toMethod.invoke(null, args);
            } catch (InvocationTargetException e) {
              throw e.getCause();
            }
          }
        });
  }

  /**
   * Cast object to one given interface using ClassLoader of interface
   *
   * @param <T> Type of interface
   * @param from Object to cast
   * @param interfase Interface to cast to
   * @return Result that implements interface
   * @throws SecurityException Allows exception related to security.
   * @throws NoSuchMethodException Allows exception due to wrong method.
   */
  public static <T> T cast(Object from, Class<T> interfase)
      throws SecurityException, NoSuchMethodException {
    return cast(from.getClass(), from, interfase, interfase.getClassLoader());
  }

  /**
   * Cast object to one given interface using ClassLoader of interface
   *
   * @param original The interface of the from instance
   * @param <T> Type of interface
   * @param from Object to cast
   * @param interfase Interface to cast to
   * @return Result that implements interface
   * @throws SecurityException Allows exception related to security.
   * @throws NoSuchMethodException Allows exception due to wrong method.
   */
  public static <T> T cast(Class<?> original, Object from, Class<T> interfase)
      throws SecurityException, NoSuchMethodException {
    return cast(original, from, interfase, interfase.getClassLoader());
  }

  /**
   * Cast object to one given interface
   *
   * @param <T> Type of interface.
   * @param original The interface of the from instance.
   * @param from Object to cast.
   * @param interfase Interface to cast to.
   * @param classLoader Class loader to load invocation methods.
   * @return Result that implements interface.
   * @throws SecurityException Allows exception related to security.
   * @throws NoSuchMethodException Allows exception due to wrong method.
   */
  @SuppressWarnings("unchecked")
  public static <T> T cast(
      Class<?> original, Object from, Class<T> interfase, ClassLoader classLoader)
      throws SecurityException, NoSuchMethodException {
    Validate.notNull(interfase, "Interface can't be NULL");
    return (T) cast(original, from, new Class<?>[] {interfase}, classLoader);
  }

  /**
   * Cast static methods of a class to given interfaces
   *
   * @param from From class
   * @param interfaces To interfaces
   * @param classLoader Class loader
   * @return Casted result
   * @throws SecurityException Allows exception related to security.
   * @throws NoSuchMethodException Allows exception due to wrong method.
   */
  public static Object staticCast(
      final Class<?> from, final Class<?>[] interfaces, ClassLoader classLoader)
      throws SecurityException, NoSuchMethodException {
    Validate.notNull(from, "Invocation target type can't be NULL");
    Validate.notNull(interfaces, "Interfaces can't be NULL");
    Validate.notNull(classLoader, "ClassLoader can't be NULL");
    final Map<Method, Method> methodMap = new HashMap<Method, Method>();
    for (Class<?> interfase : interfaces) {
      Validate.isTrue(interfase.isInterface(), interfase + " is not an interface");
      for (Method fromMethod : interfase.getMethods()) {
        Method toMethod = from.getMethod(fromMethod.getName(), fromMethod.getParameterTypes());
        if ((toMethod.getModifiers() & Modifier.STATIC) == 0) {
          throw new NoSuchMethodException("Method " + toMethod + " isn't static");
        }
        methodMap.put(fromMethod, toMethod);
      }
    }
    return Proxy.newProxyInstance(
        classLoader,
        interfaces,
        new InvocationHandler() {
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Method toMethod = methodMap.get(method);
            if (toMethod == null) {
              throw new OperationNotSupportedException(
                  "Method " + method + " isn't implemented in " + from.getClass());
            }
            try {
              return toMethod.invoke(null, args);
            } catch (InvocationTargetException e) {
              throw e.getCause();
            }
          }
        });
  }

  /**
   * Cast static class to one given interface
   *
   * @param <T> Type of interface to casts to
   * @param from Type of static class to casts from
   * @param interfase Interface to cast to
   * @return Result that implements interface
   * @throws SecurityException Allows exception related to security.
   * @throws NoSuchMethodException Allows exception due to wrong method.
   */
  public static <T> T staticCast(Class<?> from, Class<T> interfase)
      throws SecurityException, NoSuchMethodException {
    return staticCast(from, interfase, interfase.getClassLoader());
  }

  /**
   * Cast static class to one given interface
   *
   * @param <T> Type of interface to casts to
   * @param from Type of static class to casts from
   * @param interfase Interface to cast to
   * @param classLoader Class loader to load result
   * @return Result that implements interface
   * @throws SecurityException Allows exception related to security.
   * @throws NoSuchMethodException Allows exception due to wrong method.
   */
  @SuppressWarnings("unchecked")
  public static <T> T staticCast(Class<?> from, Class<T> interfase, ClassLoader classLoader)
      throws SecurityException, NoSuchMethodException {
    return (T) staticCast(from, new Class<?>[] {interfase}, classLoader);
  }

  private WeakCastUtils() {}
}