/* * Modified org.hibernate.util.ReflectHelper that avoids calling loadClass for known missing classes. * New code is in classForName() and classForNameInternal(). */ package org.hibernate.util; 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; import java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; /** * 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; private static final Map classes = new HashMap(); static { Method eq; Method hash; try { eq = extractEqualsMethod( Object.class ); hash = extractHashCodeMethod( Object.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 clazz) throws 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 clazz) throws 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 = extractEqualsMethod( clazz ); } catch ( NoSuchMethodException nsme ) { return false; //its an interface so we can't really tell anything... } return !OBJECT_EQUALS.equals( equals ); } /** * 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 = extractHashCodeMethod( clazz ); } catch ( NoSuchMethodException nsme ) { return false; //its an interface so we can't really tell anything... } return !OBJECT_HASHCODE.equals( hashCode ); } /** * 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.isAssignableFrom( clazz ); } /** * Perform resolution of a class name. *

* 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 caller) throws ClassNotFoundException { String key = name + "-" + (caller == null ? "null" : caller.getName()); Object classOrException = classes.get(key); if (classOrException == null) { classOrException = classForNameInternal(name, caller); classes.put(key, classOrException); } if (classOrException instanceof ClassNotFoundException) { throw (ClassNotFoundException) classOrException; } return (Class) classOrException; } /** * Perform resolution of a class name. *

* 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 name) throws ClassNotFoundException { return classForName(name, null); } private static Object classForNameInternal(String name, Class caller) { try { ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if ( contextClassLoader != null ) { return contextClassLoader.loadClass( name ); } } catch ( Throwable ignore ) { } try { if (caller != null) { return Class.forName( name, true, caller.getClassLoader() ); } else { return Class.forName( name ); } } catch (ClassNotFoundException e) { return e; } } /** * Is this member publicly accessible. *

* 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 isPublic( member.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.isPublic( member.getModifiers() ) && Modifier.isPublic( clazz.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 name) throws MappingException { try { Class clazz = ReflectHelper.classForName( className ); return getter( clazz, 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 name) throws MappingException { try { return BASIC_PROPERTY_ACCESSOR.getGetter( clazz, name ); } catch ( PropertyNotFoundException pnfe ) { return DIRECT_PROPERTY_ACCESSOR.getGetter( clazz, 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 name) throws MappingException { return BASIC_PROPERTY_ACCESSOR.getGetter( theClass, 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 = classForName( StringHelper.qualifier( name ) ); } catch ( Throwable t ) { return null; } try { return clazz.getField( StringHelper.unqualify( name ) ).get( null ); } 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 clazz) throws PropertyNotFoundException { if ( isAbstractClass( clazz ) ) { return null; } try { Constructor constructor = clazz.getDeclaredConstructor( NO_PARAM_SIGNATURE ); if ( !isPublic( clazz, constructor ) ) { constructor.setAccessible( true ); } 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.isFinal( clazz.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[] types) throws 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].isAssignableFrom( types[j].getReturnedClass() ) || ( types[j] instanceof PrimitiveType && params[j] == ( ( PrimitiveType ) types[j] ).getPrimitiveClass() ); if ( !ok ) { found = false; break; } } if ( found ) { if ( !isPublic( clazz, constructor ) ) { constructor.setAccessible( true ); } return constructor; } } } throw new PropertyNotFoundException( "no appropriate constructor in class: " + clazz.getName() ); } public static Method getMethod(Class clazz, Method method) { try { return clazz.getMethod( method.getName(), method.getParameterTypes() ); } catch (Exception e) { return null; } } }