Reflection Java

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
import java.lang.reflect.Array;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;
/**
 * A collection of Class utilities.
 * 
 * @version $Revision: 2787 $
 * @author Jason Dillon
 * @author Scott Stark
 * @author Dimitris Andreadis
 */
@SuppressWarnings("unchecked")
public final class Classes {
  /** The string used to separator packages */
  public static final String PACKAGE_SEPARATOR = ".";
  /** The characther used to separator packages */
  public static final char PACKAGE_SEPARATOR_CHAR = '.';
  /** The default package name. */
  public static final String DEFAULT_PACKAGE_NAME = "";
  /**
   * Describe the class of an object
   * 
   * @param object
   *          the object
   * @return the description
   */
  public static String getDescription(Object object) {
    StringBuffer buffer = new StringBuffer();
    describe(buffer, object);
    return buffer.toString();
  }
  /**
   * Describe the class of an object
   * 
   * @param buffer
   *          the string buffer
   * @param object
   *          the object
   */
  public static void describe(StringBuffer buffer, Object object) {
    if (object == null)
      buffer.append("**null**");
    else
      describe(buffer, object.getClass());
  }
  /**
   * Describe the class
   * 
   * @param buffer
   *          the string buffer
   * @param clazz
   *          the clazz
   */
  public static void describe(StringBuffer buffer, Class clazz) {
    if (clazz == null)
      buffer.append("**null**");
    else {
      buffer.append("{class=").append(clazz.getName());
      Class[] intfs = clazz.getInterfaces();
      if (intfs.length > 0) {
        buffer.append(" intfs=");
        for (int i = 0; i < intfs.length; ++i) {
          buffer.append(intfs[i].getName());
          if (i < intfs.length - 1)
            buffer.append(", ");
        }
      }
      buffer.append("}");
    }
  }
  /**
   * Get the short name of the specified class by striping off the package name.
   * 
   * @param classname
   *          Class name.
   * @return Short class name.
   */
  public static String stripPackageName(final String classname) {
    int idx = classname.lastIndexOf(PACKAGE_SEPARATOR);
    if (idx != -1)
      return classname.substring(idx + 1, classname.length());
    return classname;
  }
  /**
   * Get the short name of the specified class by striping off the package name.
   * 
   * @param type
   *          Class name.
   * @return Short class name.
   */
  public static String stripPackageName(final Class type) {
    return stripPackageName(type.getName());
  }
  /**
   * Get the package name of the specified class.
   * 
   * @param classname
   *          Class name.
   * @return Package name or "" if the classname is in the default
   *         package.
   * 
   * @throws EmptyStringException
   *           Classname is an empty string.
   */
  public static String getPackageName(final String classname) {
    if (classname.length() == 0)
      System.out.println("Empty String Exception");
    int index = classname.lastIndexOf(PACKAGE_SEPARATOR);
    if (index != -1)
      return classname.substring(0, index);
    return "";
  }
  /**
   * Get the package name of the specified class.
   * 
   * @param type
   *          Class.
   * @return Package name.
   */
  public static String getPackageName(final Class type) {
    return getPackageName(type.getName());
  }
  // ///////////////////////////////////////////////////////////////////////
  // Primitives //
  // ///////////////////////////////////////////////////////////////////////
  /** Primitive type name -> class map. */
  private static final Map PRIMITIVE_NAME_TYPE_MAP = new HashMap();
  /** Setup the primitives map. */
  static {
    PRIMITIVE_NAME_TYPE_MAP.put("boolean", Boolean.TYPE);
    PRIMITIVE_NAME_TYPE_MAP.put("byte", Byte.TYPE);
    PRIMITIVE_NAME_TYPE_MAP.put("char", Character.TYPE);
    PRIMITIVE_NAME_TYPE_MAP.put("short", Short.TYPE);
    PRIMITIVE_NAME_TYPE_MAP.put("int", Integer.TYPE);
    PRIMITIVE_NAME_TYPE_MAP.put("long", Long.TYPE);
    PRIMITIVE_NAME_TYPE_MAP.put("float", Float.TYPE);
    PRIMITIVE_NAME_TYPE_MAP.put("double", Double.TYPE);
  }
  /**
   * Get the primitive type for the given primitive name.
   * 
   * 


   * For example, "boolean" returns Boolean.TYPE and so on...
   * 
   * @param name
   *          Primitive type name (boolean, int, byte, ...)
   * @return Primitive type or null.
   * 
   * @exception IllegalArgumentException
   *              Type is not a primitive class
   */
  public static Class getPrimitiveTypeForName(final String name) {
    return (Class) PRIMITIVE_NAME_TYPE_MAP.get(name);
  }
  /** Map of primitive types to their wrapper classes */
  private static final Class[] PRIMITIVE_WRAPPER_MAP = { Boolean.TYPE, Boolean.class, Byte.TYPE,
      Byte.class, Character.TYPE, Character.class, Double.TYPE, Double.class, Float.TYPE,
      Float.class, Integer.TYPE, Integer.class, Long.TYPE, Long.class, Short.TYPE, Short.class, };
  /**
   * Get the wrapper class for the given primitive type.
   * 
   * @param type
   *          Primitive class.
   * @return Wrapper class for primitive.
   * 
   * @exception IllegalArgumentException
   *              Type is not a primitive class
   */
  public static Class getPrimitiveWrapper(final Class type) {
    if (!type.isPrimitive()) {
      throw new IllegalArgumentException("type is not a primitive class");
    }
    for (int i = 0; i < PRIMITIVE_WRAPPER_MAP.length; i += 2) {
      if (type.equals(PRIMITIVE_WRAPPER_MAP[i]))
        return PRIMITIVE_WRAPPER_MAP[i + 1];
    }
    // should never get here, if we do then PRIMITIVE_WRAPPER_MAP
    // needs to be updated to include the missing mapping
    System.out.println("Unreachable Statement Exception");
    return null;
  }
  /**
   * Check if the given class is a primitive wrapper class.
   * 
   * @param type
   *          Class to check.
   * @return True if the class is a primitive wrapper.
   */
  public static boolean isPrimitiveWrapper(final Class type) {
    for (int i = 0; i < PRIMITIVE_WRAPPER_MAP.length; i += 2) {
      if (type.equals(PRIMITIVE_WRAPPER_MAP[i + 1])) {
        return true;
      }
    }
    return false;
  }
  /**
   * Check if the given class is a primitive class or a primitive wrapper class.
   * 
   * @param type
   *          Class to check.
   * @return True if the class is a primitive or primitive wrapper.
   */
  public static boolean isPrimitive(final Class type) {
    if (type.isPrimitive() || isPrimitiveWrapper(type)) {
      return true;
    }
    return false;
  }
  /**
   * Check type against boolean, byte, char, short, int, long, float, double.
   * 
   * @param type
   *          The java type name
   * @return true if this is a primative type name.
   */
  public static boolean isPrimitive(final String type) {
    return PRIMITIVE_NAME_TYPE_MAP.containsKey(type);
  }
  /**
   * Instantiate a java class object
   * 
   * @param expected
   *          the expected class type
   * @param property
   *          the system property defining the class
   * @param defaultClassName
   *          the default class name
   * @return the instantiated object
   */
  public static Object instantiate(Class expected, String property, String defaultClassName) {
    String className = getProperty(property, defaultClassName);
    Class clazz = null;
    try {
      clazz = loadClass(className);
    } catch (ClassNotFoundException e) {
      throw new RuntimeException("Cannot load class " + className, e);
    }
    Object result = null;
    try {
      result = clazz.newInstance();
    } catch (InstantiationException e) {
      throw new RuntimeException("Error instantiating " + className, e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Error instantiating " + className, e);
    }
    if (expected.isAssignableFrom(clazz) == false)
      throw new RuntimeException("Class " + className + " from classloader "
          + clazz.getClassLoader() + " is not of the expected class " + expected + " loaded from "
          + expected.getClassLoader());
    return result;
  }
  // ///////////////////////////////////////////////////////////////////////
  // Class Loading //
  // ///////////////////////////////////////////////////////////////////////
  /**
   * This method acts equivalently to invoking
   * Thread.currentThread().getContextClassLoader().loadClass(className);
   * but it also supports primitive types and array classes of object types or
   * primitive types.
   * 
   * @param className
   *          the qualified name of the class or the name of primitive type or
   *          array in the same format as returned by the
   *          java.lang.Class.getName() method.
   * @return the Class object for the requested className
   * 
   * @throws ClassNotFoundException
   *           when the classLoader can not find the requested
   *           class
   */
  public static Class loadClass(String className) throws ClassNotFoundException {
    return loadClass(className, Thread.currentThread().getContextClassLoader());
  }
  /**
   * This method acts equivalently to invoking classLoader.loadClass(className)
   * but it also supports primitive types and array classes of object types or
   * primitive types.
   * 
   * @param className
   *          the qualified name of the class or the name of primitive type or
   *          array in the same format as returned by the
   *          java.lang.Class.getName() method.
   * @param classLoader
   *          the ClassLoader used to load classes
   * @return the Class object for the requested className
   * 
   * @throws ClassNotFoundException
   *           when the classLoader can not find the requested
   *           class
   */
  public static Class loadClass(String className, ClassLoader classLoader)
      throws ClassNotFoundException {
    // ClassLoader.loadClass() does not handle primitive types:
    //
    // B byte
    // C char
    // D double
    // F float
    // I int
    // J long
    // S short
    // Z boolean
    // V void
    //
    if (className.length() == 1) {
      char type = className.charAt(0);
      if (type == 'B')
        return Byte.TYPE;
      if (type == 'C')
        return Character.TYPE;
      if (type == 'D')
        return Double.TYPE;
      if (type == 'F')
        return Float.TYPE;
      if (type == 'I')
        return Integer.TYPE;
      if (type == 'J')
        return Long.TYPE;
      if (type == 'S')
        return Short.TYPE;
      if (type == 'Z')
        return Boolean.TYPE;
      if (type == 'V')
        return Void.TYPE;
      // else throw...
      throw new ClassNotFoundException(className);
    }
    // Check for a primative type
    if (isPrimitive(className) == true)
      return (Class) Classes.PRIMITIVE_NAME_TYPE_MAP.get(className);
    // Check for the internal vm format: Lclassname;
    if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';')
      return classLoader.loadClass(className.substring(1, className.length() - 1));
    // first try - be optimistic
    // this will succeed for all non-array classes and array classes that have
    // already been resolved
    //
    try {
      return classLoader.loadClass(className);
    } catch (ClassNotFoundException e) {
      // if it was non-array class then throw it
      if (className.charAt(0) != '[')
        throw e;
    }
    // we are now resolving array class for the first time
    // count opening braces
    int arrayDimension = 0;
    while (className.charAt(arrayDimension) == '[')
      arrayDimension++;
    // resolve component type - use recursion so that we can resolve primitive
    // types also
    Class componentType = loadClass(className.substring(arrayDimension), classLoader);
    // construct array class
    return Array.newInstance(componentType, new int[arrayDimension]).getClass();
  }
  /**
   * Get a system property
   * 
   * @param name
   *          the property name
   * @param defaultValue
   *          the default value
   */
  private static String getProperty(final String name, final String defaultValue) {
    return (String) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        return System.getProperty(name, defaultValue);
      }
    });
  }
}