Open Source Repository

Home /hibernate/hibernate-3.2.7.ga | Repository Home



org/hibernate/util/ReflectHelper.java
//$Id: ReflectHelper.java 15340 2008-10-13 15:07:52Z [email protected] $
package org.hibernate.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.property.BasicPropertyAccessor;
import org.hibernate.property.DirectPropertyAccessor;
import org.hibernate.property.Getter;
import org.hibernate.property.PropertyAccessor;
import org.hibernate.type.PrimitiveType;
import org.hibernate.type.Type;

/**
 * Utility class for various reflection operations.
 *
 @author Gavin King
 @author Steve Ebersole
 */
public final class ReflectHelper {

  //TODO: this dependency is kinda Bad
  private static final PropertyAccessor BASIC_PROPERTY_ACCESSOR = new BasicPropertyAccessor();
  private static final PropertyAccessor DIRECT_PROPERTY_ACCESSOR = new DirectPropertyAccessor();

  public static final Class[] NO_PARAM_SIGNATURE = new Class[0];
  public static final Object[] NO_PARAMS = new Object[0];

  public static final Class[] SINGLE_OBJECT_PARAM_SIGNATURE = new Class[] { Object.class };

  private static final Method OBJECT_EQUALS;
  private static final Method OBJECT_HASHCODE;

  static {
    Method eq;
    Method hash;
    try {
      eq = extractEqualsMethodObject.class );
      hash = extractHashCodeMethodObject.class );
    }
    catch Exception e ) {
      throw new AssertionFailure"Could not find Object.equals() or Object.hashCode()", e );
    }
    OBJECT_EQUALS = eq;
    OBJECT_HASHCODE = hash;
  }

  /**
   * Disallow instantiation of ReflectHelper.
   */
  private ReflectHelper() {
  }

  /**
   * Encapsulation of getting hold of a class's {@link Object#equals equals}  method.
   *
   @param clazz The class from which to extract the equals method.
   @return The equals method reference
   @throws NoSuchMethodException Should indicate an attempt to extract equals method from interface.
   */
  public static Method extractEqualsMethod(Class clazzthrows NoSuchMethodException {
    return clazz.getMethod"equals", SINGLE_OBJECT_PARAM_SIGNATURE );
  }

  /**
   * Encapsulation of getting hold of a class's {@link Object#hashCode hashCode} method.
   *
   @param clazz The class from which to extract the hashCode method.
   @return The hashCode method reference
   @throws NoSuchMethodException Should indicate an attempt to extract hashCode method from interface.
   */
  public static Method extractHashCodeMethod(Class clazzthrows NoSuchMethodException {
    return clazz.getMethod"hashCode", NO_PARAM_SIGNATURE );
  }

  /**
   * Determine if the given class defines an {@link Object#equals} override.
   *
   @param clazz The class to check
   @return True if clazz defines an equals override.
   */
  public static boolean overridesEquals(Class clazz) {
    Method equals;
    try {
      equals = extractEqualsMethodclazz );
    }
    catch NoSuchMethodException nsme ) {
      return false//its an interface so we can't really tell anything...
    }
    return !OBJECT_EQUALS.equalsequals );
  }

  /**
   * Determine if the given class defines a {@link Object#hashCode} override.
   *
   @param clazz The class to check
   @return True if clazz defines an hashCode override.
   */
  public static boolean overridesHashCode(Class clazz) {
    Method hashCode;
    try {
      hashCode = extractHashCodeMethodclazz );
    }
    catch NoSuchMethodException nsme ) {
      return false//its an interface so we can't really tell anything...
    }
    return !OBJECT_HASHCODE.equalshashCode );
  }

  /**
   * Determine if the given class implements the given interface.
   *
   @param clazz The class to check
   @param intf The interface to check it against.
   @return True if the class does implement the interface, false otherwise.
   */
  public static boolean implementsInterface(Class clazz, Class intf) {
    assert intf.isInterface() "Interface to check was not an interface";
    return intf.isAssignableFromclazz );
  }

  /**
   * Perform resolution of a class name.
   <p/>
   * Here we first check the context classloader, if one, before delegating to
   {@link Class#forName(String, boolean, ClassLoader)} using the caller's classloader
   *
   @param name The class name
   @param caller The class from which this call originated (in order to access that class's loader).
   @return The class reference.
   @throws ClassNotFoundException From {@link Class#forName(String, boolean, ClassLoader)}.
   */
  public static Class classForName(String name, Class callerthrows ClassNotFoundException {
    try {
      ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
      if contextClassLoader != null ) {
        return contextClassLoader.loadClassname );
      }
    }
    catch Throwable ignore ) {
    }
    return Class.forNamename, true, caller.getClassLoader() );
  }

  /**
   * Perform resolution of a class name.
   <p/>
   * Same as {@link #classForName(String, Class)} except that here we delegate to
   {@link Class#forName(String)} if the context classloader lookup is unsuccessful.
   *
   @param name The class name
   @return The class reference.
   @throws ClassNotFoundException From {@link Class#forName(String)}.
   */
  public static Class classForName(String namethrows ClassNotFoundException {
    try {
      ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
      if contextClassLoader != null ) {
        return contextClassLoader.loadClass(name);
      }
    }
    catch Throwable ignore ) {
    }
    return Class.forNamename );
  }

  /**
   * Is this member publicly accessible.
   <p/>
   * Short-hand for {@link #isPublic(Class, Member)} passing the member + {@link Member#getDeclaringClass()}
   *
   @param member The member to check
   @return True if the member is publicly accessible.
   */
  public static boolean isPublic(Member member) {
    return isPublicmember.getDeclaringClass(), member );
  }

  /**
   * Is this member publicly accessible.
   *
   @param clazz The class which defines the member
   @param member The memeber.
   @return True if the member is publicly accessible, false otherwise.
   */
  public static boolean isPublic(Class clazz, Member member) {
    return Modifier.isPublicmember.getModifiers() ) && Modifier.isPublicclazz.getModifiers() );
  }

  /**
   * Attempt to resolve the specified property type through reflection.
   *
   @param className The name of the class owning the property.
   @param name The name of the property.
   @return The type of the property.
   @throws MappingException Indicates we were unable to locate the property.
   */
  public static Class reflectedPropertyClass(String className, String namethrows MappingException {
    try {
      Class clazz = ReflectHelper.classForNameclassName );
      return getterclazz, name ).getReturnType();
    }
    catch ClassNotFoundException cnfe ) {
      throw new MappingException"class " + className + " not found while looking for property: " + name, cnfe );
    }
  }

  private static Getter getter(Class clazz, String namethrows MappingException {
    try {
      return BASIC_PROPERTY_ACCESSOR.getGetterclazz, name );
    }
    catch PropertyNotFoundException pnfe ) {
      return DIRECT_PROPERTY_ACCESSOR.getGetterclazz, name );
    }
  }

  /**
   * Directly retrieve the {@link Getter} reference via the {@link BasicPropertyAccessor}.
   *
   @param theClass The class owning the property
   @param name The name of the property
   @return The getter.
   @throws MappingException Indicates we were unable to locate the property.
   */
  public static Getter getGetter(Class theClass, String namethrows MappingException {
    return BASIC_PROPERTY_ACCESSOR.getGettertheClass, name );
  }

  /**
   * Resolve a constant to its actual value.
   *
   @param name The name
   @return The value
   */
  public static Object getConstantValue(String name) {
    Class clazz;
    try {
      clazz = classForNameStringHelper.qualifiername ) );
    }
    catch Throwable t ) {
      return null;
    }
    try {
      return clazz.getFieldStringHelper.unqualifyname ) ).getnull );
    }
    catch Throwable t ) {
      return null;
    }
  }

  /**
   * Retrieve the default (no arg) constructor from the given class.
   *
   @param clazz The class for which to retrieve the default ctor.
   @return The default constructor.
   @throws PropertyNotFoundException Indicates there was not publicly accessible, no-arg constructor (todo : why PropertyNotFoundException???)
   */
  public static Constructor getDefaultConstructor(Class clazzthrows PropertyNotFoundException {
    if isAbstractClassclazz ) ) {
      return null;
    }

    try {
      Constructor constructor = clazz.getDeclaredConstructorNO_PARAM_SIGNATURE );
      if !isPublicclazz, constructor ) ) {
        constructor.setAccessibletrue );
      }
      return constructor;
    }
    catch NoSuchMethodException nme ) {
      throw new PropertyNotFoundException(
          "Object class [" + clazz.getName() "] must declare a default (no-argument) constructor"
      );
    }
  }

  /**
   * Determine if the given class is declared abstract.
   *
   @param clazz The class to check.
   @return True if the class is abstract, false otherwise.
   */
  public static boolean isAbstractClass(Class clazz) {
    int modifier = clazz.getModifiers();
    return Modifier.isAbstract(modifier|| Modifier.isInterface(modifier);
  }

  /**
   * Determine is the given class is declared final.
   *
   @param clazz The class to check.
   @return True if the class is final, flase otherwise.
   */
  public static boolean isFinalClass(Class clazz) {
    return Modifier.isFinalclazz.getModifiers() );
  }

  /**
   * Retrieve a constructor for the given class, with arguments matching the specified Hibernate mapping
   {@link Type types}.
   *
   @param clazz The class needing instantiation
   @param types The types representing the required ctor param signature
   @return The matching constructor.
   @throws PropertyNotFoundException Indicates we could not locate an appropriate constructor (todo : again with PropertyNotFoundException???)
   */
  public static Constructor getConstructor(Class clazz, Type[] typesthrows PropertyNotFoundException {
    final Constructor[] candidates = clazz.getConstructors();
    for int i = 0; i < candidates.length; i++ ) {
      final Constructor constructor = candidates[i];
      final Class[] params = constructor.getParameterTypes();
      if params.length == types.length ) {
        boolean found = true;
        for int j = 0; j < params.length; j++ ) {
          final boolean ok = params[j].isAssignableFromtypes[j].getReturnedClass() ) || (
              types[jinstanceof PrimitiveType &&
                  params[j== ( ( PrimitiveType types[j] ).getPrimitiveClass()
          );
          if !ok ) {
            found = false;
            break;
          }
        }
        if found ) {
          if !isPublicclazz, constructor ) ) {
            constructor.setAccessibletrue );
          }
          return constructor;
        }
      }
    }
    throw new PropertyNotFoundException"no appropriate constructor in class: " + clazz.getName() );
  }
  
  public static Method getMethod(Class clazz, Method method) {
    try {
      return clazz.getMethodmethod.getName(), method.getParameterTypes() );
    }
    catch (Exception e) {
      return null;
    }
  }

}