/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.internal.util;

import java.beans.Introspector;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.persistence.Transient;

import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.property.access.internal.PropertyAccessStrategyMixedImpl;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.type.PrimitiveType;
import org.hibernate.type.Type;

Utility class for various reflection operations.
Author:Gavin King, Steve Ebersole, Chris Cranford
/** * Utility class for various reflection operations. * * @author Gavin King * @author Steve Ebersole * @author Chris Cranford */
@SuppressWarnings("unchecked") public final class ReflectHelper { private static final Pattern JAVA_CONSTANT_PATTERN = Pattern.compile( "[a-z\\d]+\\.([A-Z]{1}[a-z\\d]+)+\\$?([A-Z]{1}[a-z\\d]+)*\\.[A-Z_\\$]+", Pattern.UNICODE_CHARACTER_CLASS); 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 = 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.
/** * Disallow instantiation of ReflectHelper. */
private ReflectHelper() { }
Encapsulation of getting hold of a class's equals method.
Params:
  • clazz – The class from which to extract the equals method.
Throws:
Returns:The equals method reference
/** * 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 hashCode method.
Params:
  • clazz – The class from which to extract the hashCode method.
Throws:
Returns:The hashCode method reference
/** * 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 Object.equals override.
Params:
  • clazz – The class to check
Returns:True if clazz defines an equals override.
/** * 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 Object.hashCode override.
Params:
  • clazz – The class to check
Returns:True if clazz defines an hashCode override.
/** * 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.
Params:
  • clazz – The class to check
  • intf – The interface to check it against.
Returns:True if the class does implement the interface, false otherwise.
/** * 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 Class.forName(String, boolean, ClassLoader) using the caller's classloader
Params:
  • name – The class name
  • caller – The class from which this call originated (in order to access that class's loader).
Throws:
Returns:The class reference.
/** * 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 caller) throws ClassNotFoundException { try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if ( classLoader != null ) { return classLoader.loadClass( name ); } } catch ( Throwable ignore ) { } return Class.forName( name, true, caller.getClassLoader() ); }
Perform resolution of a class name.

Same as classForName(String, Class) except that here we delegate to Class.forName(String) if the context classloader lookup is unsuccessful.
Params:
  • name – The class name
Throws:
Returns:The class reference.
Deprecated:Depending on context, either ClassLoaderService or ClassLoaderAccess should be preferred
/** * 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)}. * * @deprecated Depending on context, either {@link org.hibernate.boot.registry.classloading.spi.ClassLoaderService} * or {@link org.hibernate.boot.spi.ClassLoaderAccess} should be preferred */
@Deprecated public static Class classForName(String name) throws ClassNotFoundException { try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if ( classLoader != null ) { return classLoader.loadClass(name); } } catch ( Throwable ignore ) { } return Class.forName( name ); }
Is this member publicly accessible.
Params:
  • clazz – The class which defines the member
  • member – The memeber.
Returns:True if the member is publicly accessible, false otherwise.
/** * 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.
Params:
  • className – The name of the class owning the property.
  • name – The name of the property.
  • classLoaderService – ClassLoader services
Throws:
Returns:The type of the property.
/** * 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. * @param classLoaderService ClassLoader services * * @return The type of the property. * * @throws MappingException Indicates we were unable to locate the property. */
public static Class reflectedPropertyClass( String className, String name, ClassLoaderService classLoaderService) throws MappingException { try { Class clazz = classLoaderService.classForName( className ); return getter( clazz, name ).getReturnType(); } catch ( ClassLoadingException e ) { throw new MappingException( "class " + className + " not found while looking for property: " + name, e ); } }
Attempt to resolve the specified property type through reflection.
Params:
  • clazz – The class owning the property.
  • name – The name of the property.
Throws:
Returns:The type of the property.
/** * Attempt to resolve the specified property type through reflection. * * @param clazz 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(Class clazz, String name) throws MappingException { return getter( clazz, name ).getReturnType(); } private static Getter getter(Class clazz, String name) throws MappingException { return PropertyAccessStrategyMixedImpl.INSTANCE.buildPropertyAccess( clazz, name ).getGetter(); } public static Object getConstantValue(String name, SessionFactoryImplementor factory) { boolean conventionalJavaConstants = factory.getSessionFactoryOptions().isConventionalJavaConstants(); Class clazz; try { if ( conventionalJavaConstants && !JAVA_CONSTANT_PATTERN.matcher( name ).find() ) { return null; } ClassLoaderService classLoaderService = factory.getServiceRegistry().getService( ClassLoaderService.class ); clazz = classLoaderService.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.
Params:
  • clazz – The class for which to retrieve the default ctor.
Throws:
  • PropertyNotFoundException – Indicates there was not publicly accessible, no-arg constructor (todo : why PropertyNotFoundException???)
Returns:The default constructor.
/** * 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 <T> Constructor<T> getDefaultConstructor(Class<T> clazz) throws PropertyNotFoundException { if ( isAbstractClass( clazz ) ) { return null; } try { Constructor<T> constructor = clazz.getDeclaredConstructor( NO_PARAM_SIGNATURE ); ensureAccessibility( constructor ); 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.
Params:
  • clazz – The class to check.
Returns:True if the class is abstract, false otherwise.
/** * 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.
Params:
  • clazz – The class to check.
Returns:True if the class is final, flase otherwise.
/** * 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 types.
Params:
  • clazz – The class needing instantiation
  • types – The types representing the required ctor param signature
Throws:
  • PropertyNotFoundException – Indicates we could not locate an appropriate constructor (todo : again with PropertyNotFoundException???)
Returns:The matching constructor.
/** * 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(); Constructor constructor = null; int numberOfMatchingConstructors = 0; for ( final Constructor candidate : candidates ) { final Class[] params = candidate.getParameterTypes(); if ( params.length == types.length ) { boolean found = true; for ( int j = 0; j < params.length; j++ ) { final boolean ok = types[j] == null || params[j].isAssignableFrom( types[j].getReturnedClass() ) || ( types[j] instanceof PrimitiveType && params[j] == ( (PrimitiveType) types[j] ).getPrimitiveClass() ); if ( !ok ) { found = false; break; } } if ( found ) { numberOfMatchingConstructors ++; ensureAccessibility( candidate ); constructor = candidate; } } } if ( numberOfMatchingConstructors == 1 ) { 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; } } public static Field findField(Class containerClass, String propertyName) { if ( containerClass == null ) { throw new IllegalArgumentException( "Class on which to find field [" + propertyName + "] cannot be null" ); } else if ( containerClass == Object.class ) { throw new IllegalArgumentException( "Illegal attempt to locate field [" + propertyName + "] on Object.class" ); } Field field = locateField( containerClass, propertyName ); if ( field == null ) { throw new PropertyNotFoundException( String.format( Locale.ROOT, "Could not locate field name [%s] on class [%s]", propertyName, containerClass.getName() ) ); } ensureAccessibility( field ); return field; } public static void ensureAccessibility(AccessibleObject accessibleObject) { if ( accessibleObject.isAccessible() ) { return; } accessibleObject.setAccessible( true ); } private static Field locateField(Class clazz, String propertyName) { if ( clazz == null || Object.class.equals( clazz ) ) { return null; } try { Field field = clazz.getDeclaredField( propertyName ); if ( !isStaticField( field ) ) { return field; } return locateField( clazz.getSuperclass(), propertyName ); } catch ( NoSuchFieldException nsfe ) { return locateField( clazz.getSuperclass(), propertyName ); } } private static boolean isStaticField(Field field) { return field != null && ( field.getModifiers() & Modifier.STATIC ) == Modifier.STATIC; } public static Method findGetterMethod(Class containerClass, String propertyName) { Class checkClass = containerClass; Method getter = null; // check containerClass, and then its super types (if any) while ( getter == null && checkClass != null ) { if ( checkClass.equals( Object.class ) ) { break; } getter = getGetterOrNull( checkClass, propertyName ); // if no getter found yet, check all implemented interfaces if ( getter == null ) { getter = getGetterOrNull( checkClass.getInterfaces(), propertyName ); } checkClass = checkClass.getSuperclass(); } if ( getter == null ) { throw new PropertyNotFoundException( String.format( Locale.ROOT, "Could not locate getter method for property [%s#%s]", containerClass.getName(), propertyName ) ); } ensureAccessibility( getter ); return getter; } private static Method getGetterOrNull(Class[] interfaces, String propertyName) { Method getter = null; for ( int i = 0; getter == null && i < interfaces.length; ++i ) { final Class anInterface = interfaces[i]; getter = getGetterOrNull( anInterface, propertyName ); if ( getter == null ) { // if no getter found yet, check all implemented interfaces of interface getter = getGetterOrNull( anInterface.getInterfaces(), propertyName ); } } return getter; } private static Method getGetterOrNull(Class containerClass, String propertyName) { for ( Method method : containerClass.getDeclaredMethods() ) { // if the method has parameters, skip it if ( method.getParameterCount() != 0 ) { continue; } // if the method is a "bridge", skip it if ( method.isBridge() ) { continue; } if ( method.getAnnotation( Transient.class ) != null ) { continue; } if ( Modifier.isStatic( method.getModifiers() ) ) { continue; } final String methodName = method.getName(); // try "get" if ( methodName.startsWith( "get" ) ) { final String stemName = methodName.substring( 3 ); final String decapitalizedStemName = Introspector.decapitalize( stemName ); if ( stemName.equals( propertyName ) || decapitalizedStemName.equals( propertyName ) ) { verifyNoIsVariantExists( containerClass, propertyName, method, stemName ); return method; } } // if not "get", then try "is" if ( methodName.startsWith( "is" ) ) { final String stemName = methodName.substring( 2 ); String decapitalizedStemName = Introspector.decapitalize( stemName ); if ( stemName.equals( propertyName ) || decapitalizedStemName.equals( propertyName ) ) { verifyNoGetVariantExists( containerClass, propertyName, method, stemName ); return method; } } } return null; } private static void verifyNoIsVariantExists( Class containerClass, String propertyName, Method getMethod, String stemName) { // verify that the Class does not also define a method with the same stem name with 'is' try { final Method isMethod = containerClass.getDeclaredMethod( "is" + stemName ); if ( !Modifier.isStatic( isMethod.getModifiers() ) && isMethod.getAnnotation( Transient.class ) == null ) { // No such method should throw the caught exception. So if we get here, there was // such a method. checkGetAndIsVariants( containerClass, propertyName, getMethod, isMethod ); } } catch (NoSuchMethodException ignore) { } } private static void checkGetAndIsVariants( Class containerClass, String propertyName, Method getMethod, Method isMethod) { // Check the return types. If they are the same, its ok. If they are different // we are in a situation where we could not reasonably know which to use. if ( !isMethod.getReturnType().equals( getMethod.getReturnType() ) ) { throw new MappingException( String.format( Locale.ROOT, "In trying to locate getter for property [%s], Class [%s] defined " + "both a `get` [%s] and `is` [%s] variant", propertyName, containerClass.getName(), getMethod.toString(), isMethod.toString() ) ); } } private static void verifyNoGetVariantExists( Class containerClass, String propertyName, Method isMethod, String stemName) { // verify that the Class does not also define a method with the same stem name with 'is' try { final Method getMethod = containerClass.getDeclaredMethod( "get" + stemName ); // No such method should throw the caught exception. So if we get here, there was // such a method. if ( !Modifier.isStatic( getMethod.getModifiers() ) && getMethod.getAnnotation( Transient.class ) == null ) { checkGetAndIsVariants( containerClass, propertyName, getMethod, isMethod ); } } catch (NoSuchMethodException ignore) { } } public static Method getterMethodOrNull(Class containerJavaType, String propertyName) { try { return findGetterMethod( containerJavaType, propertyName ); } catch (PropertyNotFoundException e) { return null; } } public static Method setterMethodOrNull(final Class containerClass, final String propertyName, final Class propertyType) { Class checkClass = containerClass; Method setter = null; // check containerClass, and then its super types (if any) while ( setter == null && checkClass != null ) { if ( checkClass.equals( Object.class ) ) { break; } setter = setterOrNull( checkClass, propertyName, propertyType ); // if no setter found yet, check all implemented interfaces if ( setter == null ) { setter = setterOrNull( checkClass.getInterfaces(), propertyName, propertyType ); } else { ensureAccessibility( setter ); } checkClass = checkClass.getSuperclass(); } return setter; // might be null } public static Method findSetterMethod(final Class containerClass, final String propertyName, final Class propertyType) { final Method setter = setterMethodOrNull( containerClass, propertyName, propertyType ); if ( setter == null ) { throw new PropertyNotFoundException( String.format( Locale.ROOT, "Could not locate setter method for property [%s#%s]", containerClass.getName(), propertyName ) ); } return setter; } private static Method setterOrNull(Class[] interfaces, String propertyName, Class propertyType) { Method setter = null; for ( int i = 0; setter == null && i < interfaces.length; ++i ) { final Class anInterface = interfaces[i]; setter = setterOrNull( anInterface, propertyName, propertyType ); if ( setter == null ) { // if no setter found yet, check all implemented interfaces of interface setter = setterOrNull( anInterface.getInterfaces(), propertyName, propertyType ); } } return setter; } private static Method setterOrNull(Class theClass, String propertyName, Class propertyType) { Method potentialSetter = null; for ( Method method : theClass.getDeclaredMethods() ) { final String methodName = method.getName(); if ( method.getParameterCount() == 1 && methodName.startsWith( "set" ) ) { final String testOldMethod = methodName.substring( 3 ); final String testStdMethod = Introspector.decapitalize( testOldMethod ); if ( testStdMethod.equals( propertyName ) || testOldMethod.equals( propertyName ) ) { potentialSetter = method; if ( propertyType == null || method.getParameterTypes()[0].equals( propertyType ) ) { break; } } } } return potentialSetter; }
Similar to getterMethodOrNull, except that here we are just looking for the corresponding getter for a field (defined as field access) if one exists. We do not look at supers, although conceivably the super could declare the method as an abstract - but again, that is such an edge case...
/** * Similar to {@link #getterMethodOrNull}, except that here we are just looking for the * corresponding getter for a field (defined as field access) if one exists. * * We do not look at supers, although conceivably the super could declare the method * as an abstract - but again, that is such an edge case... */
public static Method findGetterMethodForFieldAccess(Field field, String propertyName) { for ( Method method : field.getDeclaringClass().getDeclaredMethods() ) { // if the method has parameters, skip it if ( method.getParameterCount() != 0 ) { continue; } if ( Modifier.isStatic( method.getModifiers() ) ) { continue; } if ( ! method.getReturnType().isAssignableFrom( field.getType() ) ) { continue; } final String methodName = method.getName(); // try "get" if ( methodName.startsWith( "get" ) ) { final String stemName = methodName.substring( 3 ); final String decapitalizedStemName = Introspector.decapitalize( stemName ); if ( stemName.equals( propertyName ) || decapitalizedStemName.equals( propertyName ) ) { return method; } } // if not "get", then try "is" if ( methodName.startsWith( "is" ) ) { final String stemName = methodName.substring( 2 ); String decapitalizedStemName = Introspector.decapitalize( stemName ); if ( stemName.equals( propertyName ) || decapitalizedStemName.equals( propertyName ) ) { return method; } } } return null; } }