org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable Maven / Gradle / Ivy
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or .
*/
package org.hibernate.validator.internal.properties.javabean;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.validator.internal.properties.Callable;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.ExecutableHelper;
import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
import org.hibernate.validator.internal.util.ReflectionHelper;
import org.hibernate.validator.internal.util.TypeHelper;
/**
* @author Marko Bekhta
* @author Guillaume Smet
*/
public abstract class JavaBeanExecutable implements Callable, JavaBeanAnnotatedConstrainable {
protected final T executable;
private final Type typeForValidatorResolution;
private final boolean hasReturnValue;
private final Type type;
private final List parameters;
JavaBeanExecutable(T executable, boolean hasReturnValue) {
this.executable = executable;
this.type = ReflectionHelper.typeOf( executable );
this.typeForValidatorResolution = ReflectionHelper.boxedType( type );
this.hasReturnValue = hasReturnValue;
this.parameters = getParameters( executable );
}
@Override
public boolean hasReturnValue() {
return hasReturnValue;
}
@Override
public boolean hasParameters() {
return !parameters.isEmpty();
}
@Override
public String getName() {
return executable.getName();
}
@Override
public Class getDeclaringClass() {
return executable.getDeclaringClass();
}
@Override
public Type getTypeForValidatorResolution() {
return typeForValidatorResolution;
}
@Override
public Type getType() {
return type;
}
@Override
public String getParameterName(ExecutableParameterNameProvider parameterNameProvider, int parameterIndex) {
return parameterNameProvider.getParameterNames( executable ).get( parameterIndex );
}
@Override
public boolean isPrivate() {
return Modifier.isPrivate( executable.getModifiers() );
}
@Override
public String getSignature() {
return ExecutableHelper.getSignature( executable );
}
@Override
public Annotation[] getDeclaredAnnotations() {
return executable.getDeclaredAnnotations();
}
@Override
public boolean overrides(ExecutableHelper executableHelper, Callable superTypeMethod) {
return executableHelper.overrides( ( (Method) this.executable ), ( (Method) ( (JavaBeanExecutable) superTypeMethod ).executable ) );
}
@Override
public boolean isResolvedToSameMethodInHierarchy(ExecutableHelper executableHelper, Class mainSubType, Callable superTypeMethod) {
return executableHelper.isResolvedToSameMethodInHierarchy( mainSubType, ( (Method) this.executable ), ( (Method) ( (JavaBeanExecutable) superTypeMethod ).executable ) );
}
@Override
public Type getGenericType() {
return ReflectionHelper.typeOf( executable );
}
@Override
public AnnotatedType getAnnotatedType() {
return executable.getAnnotatedReturnType();
}
@Override
public A getAnnotation(Class annotationClass) {
return executable.getAnnotation( annotationClass );
}
public List getParameters() {
return parameters;
}
@Override
public Type getParameterGenericType(int index) {
return parameters.get( index ).getGenericType();
}
@Override
public int getParameterCount() {
return parameters.size();
}
@Override
public Class[] getParameterTypes() {
return executable.getParameterTypes();
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || this.getClass() != o.getClass() ) {
return false;
}
JavaBeanExecutable that = (JavaBeanExecutable) o;
if ( this.hasReturnValue != that.hasReturnValue ) {
return false;
}
if ( !this.executable.equals( that.executable ) ) {
return false;
}
if ( !this.typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) {
return false;
}
return this.type.equals( that.type );
}
@Override
public int hashCode() {
int result = this.executable.hashCode();
result = 31 * result + this.typeForValidatorResolution.hashCode();
result = 31 * result + ( this.hasReturnValue ? 1 : 0 );
result = 31 * result + this.type.hashCode();
return result;
}
@Override
public String toString() {
return ExecutableHelper.getExecutableAsString(
getDeclaringClass().getSimpleName() + "#" + executable.getName(),
executable.getParameterTypes()
);
}
private static List getParameters(Executable executable) {
if ( executable.getParameterCount() == 0 ) {
return Collections.emptyList();
}
List parameters = new ArrayList<>( executable.getParameterCount() );
Parameter[] parameterArray = executable.getParameters();
Class[] parameterTypes = executable.getParameterTypes();
// getGenericParameterTypes() does not include either the synthetic or the implicit parameters so we need to be
// extra careful
Type[] genericParameterTypes = executable.getGenericParameterTypes();
if ( parameterTypes.length == genericParameterTypes.length ) {
// this is the simple case where both arrays are consistent
// we could do without it but at some point, the behavior of getGenericParameterTypes() might be changed in
// Java and we'd better be ready.
for ( int i = 0; i < parameterArray.length; i++ ) {
parameters.add( new JavaBeanParameter( i, parameterArray[i], parameterTypes[i], getErasedTypeIfTypeVariable( genericParameterTypes[i] ) ) );
}
}
else {
// in this case, we have synthetic or implicit parameters
int explicitlyDeclaredParameterIndex = 0;
for ( int i = 0; i < parameterArray.length; i++ ) {
if ( parameterArray[i].isSynthetic() || parameterArray[i].isImplicit() ) {
// in this case, the parameter is not present in genericParameterTypes
parameters.add( new JavaBeanParameter( i, parameterArray[i], parameterTypes[i], parameterTypes[i] ) );
}
else {
parameters.add( new JavaBeanParameter( i, parameterArray[i], parameterTypes[i],
getErasedTypeIfTypeVariable( genericParameterTypes[explicitlyDeclaredParameterIndex] ) ) );
explicitlyDeclaredParameterIndex++;
}
}
}
return CollectionHelper.toImmutableList( parameters );
}
private static Type getErasedTypeIfTypeVariable(Type genericType) {
if ( genericType instanceof TypeVariable ) {
return TypeHelper.getErasedType( genericType );
}
return genericType;
}
}