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;
@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;
}
private ReflectHelper() {
}
public static Method (Class clazz) throws NoSuchMethodException {
return clazz.getMethod( "equals", SINGLE_OBJECT_PARAM_SIGNATURE );
}
public static Method (Class clazz) throws NoSuchMethodException {
return clazz.getMethod( "hashCode", NO_PARAM_SIGNATURE );
}
public static boolean overridesEquals(Class clazz) {
Method equals;
try {
equals = extractEqualsMethod( clazz );
}
catch ( NoSuchMethodException nsme ) {
return false;
}
return !OBJECT_EQUALS.equals( equals );
}
public static boolean overridesHashCode(Class clazz) {
Method hashCode;
try {
hashCode = extractHashCodeMethod( clazz );
}
catch ( NoSuchMethodException nsme ) {
return false;
}
return !OBJECT_HASHCODE.equals( hashCode );
}
public static boolean implementsInterface(Class clazz, Class intf) {
assert intf.isInterface() : "Interface to check was not an interface";
return intf.isAssignableFrom( clazz );
}
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() );
}
@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 );
}
public static boolean isPublic(Class clazz, Member member) {
return Modifier.isPublic( member.getModifiers() ) && Modifier.isPublic( clazz.getModifiers() );
}
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 );
}
}
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;
}
}
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"
);
}
}
public static boolean isAbstractClass(Class clazz) {
int modifier = clazz.getModifiers();
return Modifier.isAbstract(modifier) || Modifier.isInterface(modifier);
}
public static boolean isFinalClass(Class clazz) {
return Modifier.isFinal( clazz.getModifiers() );
}
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;
while ( getter == null && checkClass != null ) {
if ( checkClass.equals( Object.class ) ) {
break;
}
getter = getGetterOrNull( checkClass, propertyName );
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 ) {
getter = getGetterOrNull( anInterface.getInterfaces(), propertyName );
}
}
return getter;
}
private static Method getGetterOrNull(Class containerClass, String propertyName) {
for ( Method method : containerClass.getDeclaredMethods() ) {
if ( method.getParameterCount() != 0 ) {
continue;
}
if ( method.isBridge() ) {
continue;
}
if ( method.getAnnotation( Transient.class ) != null ) {
continue;
}
if ( Modifier.isStatic( method.getModifiers() ) ) {
continue;
}
final String methodName = method.getName();
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 ( 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) {
try {
final Method isMethod = containerClass.getDeclaredMethod( "is" + stemName );
if ( !Modifier.isStatic( isMethod.getModifiers() ) && isMethod.getAnnotation( Transient.class ) == null ) {
checkGetAndIsVariants( containerClass, propertyName, getMethod, isMethod );
}
}
catch (NoSuchMethodException ignore) {
}
}
private static void checkGetAndIsVariants(
Class containerClass,
String propertyName,
Method getMethod,
Method isMethod) {
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) {
try {
final Method getMethod = containerClass.getDeclaredMethod( "get" + stemName );
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;
while ( setter == null && checkClass != null ) {
if ( checkClass.equals( Object.class ) ) {
break;
}
setter = setterOrNull( checkClass, propertyName, propertyType );
if ( setter == null ) {
setter = setterOrNull( checkClass.getInterfaces(), propertyName, propertyType );
}
else {
ensureAccessibility( setter );
}
checkClass = checkClass.getSuperclass();
}
return setter;
}
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 ) {
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;
}
public static Method findGetterMethodForFieldAccess(Field field, String propertyName) {
for ( Method method : field.getDeclaringClass().getDeclaredMethods() ) {
if ( method.getParameterCount() != 0 ) {
continue;
}
if ( Modifier.isStatic( method.getModifiers() ) ) {
continue;
}
if ( ! method.getReturnType().isAssignableFrom( field.getType() ) ) {
continue;
}
final String methodName = method.getName();
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 ( 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;
}
}