org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bean-validator Show documentation
Show all versions of bean-validator Show documentation
JSR 380's RI, Hibernate Validator version ${hibernate-validator.version} and its dependencies repackaged as OSGi bundle
/*
* 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.engine.valueextraction;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.AnnotatedArrayType;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.validation.valueextraction.ExtractedValue;
import javax.validation.valueextraction.UnwrapByDefault;
import javax.validation.valueextraction.ValueExtractor;
import org.hibernate.validator.internal.util.ReflectionHelper;
import org.hibernate.validator.internal.util.StringHelper;
import org.hibernate.validator.internal.util.TypeHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
/**
* Describes a {@link ValueExtractor}.
*
* @author Gunnar Morling
* @author Guillaume Smet
*/
public class ValueExtractorDescriptor {
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
private final Key key;
private final ValueExtractor valueExtractor;
private final boolean unwrapByDefault;
private final Optional> extractedType;
public ValueExtractorDescriptor(ValueExtractor valueExtractor) {
AnnotatedParameterizedType valueExtractorDefinition = getValueExtractorDefinition( valueExtractor.getClass() );
this.key = new Key(
getContainerType( valueExtractorDefinition, valueExtractor.getClass() ),
getExtractedTypeParameter( valueExtractorDefinition, valueExtractor.getClass() )
);
this.valueExtractor = valueExtractor;
this.unwrapByDefault = hasUnwrapByDefaultAnnotation( valueExtractor.getClass() );
this.extractedType = getExtractedType( valueExtractorDefinition );
}
@SuppressWarnings("rawtypes")
private static TypeVariable getExtractedTypeParameter(AnnotatedParameterizedType valueExtractorDefinition,
Class extractorImplementationType) {
AnnotatedType containerType = valueExtractorDefinition.getAnnotatedActualTypeArguments()[0];
Class containerTypeRaw = (Class) TypeHelper.getErasedType( containerType.getType() );
TypeVariable extractedTypeParameter = null;
if ( containerType.isAnnotationPresent( ExtractedValue.class ) ) {
if ( containerType instanceof AnnotatedArrayType ) {
extractedTypeParameter = new ArrayElement( (AnnotatedArrayType) containerType );
}
else {
extractedTypeParameter = AnnotatedObject.INSTANCE;
}
}
if ( containerType instanceof AnnotatedParameterizedType ) {
AnnotatedParameterizedType parameterizedExtractedType = (AnnotatedParameterizedType) containerType;
int i = 0;
for ( AnnotatedType typeArgument : parameterizedExtractedType.getAnnotatedActualTypeArguments() ) {
if ( !TypeHelper.isUnboundWildcard( typeArgument.getType() ) ) {
throw LOG.getOnlyUnboundWildcardTypeArgumentsSupportedForContainerTypeOfValueExtractorException( extractorImplementationType );
}
if ( typeArgument.isAnnotationPresent( ExtractedValue.class ) ) {
if ( extractedTypeParameter != null ) {
throw LOG.getValueExtractorDeclaresExtractedValueMultipleTimesException( extractorImplementationType );
}
if ( !void.class.equals( typeArgument.getAnnotation( ExtractedValue.class ).type() ) ) {
throw LOG.getExtractedValueOnTypeParameterOfContainerTypeMayNotDefineTypeAttributeException( extractorImplementationType );
}
extractedTypeParameter = containerTypeRaw.getTypeParameters()[i];
}
i++;
}
}
if ( extractedTypeParameter == null ) {
throw LOG.getValueExtractorFailsToDeclareExtractedValueException( extractorImplementationType );
}
return extractedTypeParameter;
}
private static Optional> getExtractedType(AnnotatedParameterizedType valueExtractorDefinition) {
AnnotatedType containerType = valueExtractorDefinition.getAnnotatedActualTypeArguments()[0];
if ( containerType.isAnnotationPresent( ExtractedValue.class ) ) {
Class extractedType = containerType.getAnnotation( ExtractedValue.class ).type();
if ( !void.class.equals( extractedType ) ) {
return Optional.of( ReflectionHelper.boxedType( extractedType ) );
}
}
return Optional.empty();
}
@SuppressWarnings("rawtypes")
private static Class getContainerType(AnnotatedParameterizedType valueExtractorDefinition, Class extractorImplementationType) {
AnnotatedType containerType = valueExtractorDefinition.getAnnotatedActualTypeArguments()[0];
return TypeHelper.getErasedReferenceType( containerType.getType() );
}
private static AnnotatedParameterizedType getValueExtractorDefinition(Class extractorImplementationType) {
List valueExtractorAnnotatedTypes = new ArrayList<>();
determineValueExtractorDefinitions( valueExtractorAnnotatedTypes, extractorImplementationType );
if ( valueExtractorAnnotatedTypes.size() == 1 ) {
return (AnnotatedParameterizedType) valueExtractorAnnotatedTypes.get( 0 );
}
else if ( valueExtractorAnnotatedTypes.size() > 1 ) {
throw LOG.getParallelDefinitionsOfValueExtractorsException( extractorImplementationType );
}
else {
throw new AssertionError( extractorImplementationType.getName() + " should be a subclass of " + ValueExtractor.class.getSimpleName() );
}
}
private static void determineValueExtractorDefinitions(List valueExtractorDefinitions, Class extractorImplementationType) {
if ( !ValueExtractor.class.isAssignableFrom( extractorImplementationType ) ) {
return;
}
Class superClass = extractorImplementationType.getSuperclass();
if ( superClass != null && !Object.class.equals( superClass ) ) {
determineValueExtractorDefinitions( valueExtractorDefinitions, superClass );
}
for ( Class implementedInterface : extractorImplementationType.getInterfaces() ) {
if ( !ValueExtractor.class.equals( implementedInterface ) ) {
determineValueExtractorDefinitions( valueExtractorDefinitions, implementedInterface );
}
}
for ( AnnotatedType annotatedInterface : extractorImplementationType.getAnnotatedInterfaces() ) {
if ( ValueExtractor.class.equals( ReflectionHelper.getClassFromType( annotatedInterface.getType() ) ) ) {
valueExtractorDefinitions.add( annotatedInterface );
}
}
}
private static boolean hasUnwrapByDefaultAnnotation(Class extractorImplementationType) {
return extractorImplementationType.isAnnotationPresent( UnwrapByDefault.class );
}
public Key getKey() {
return key;
}
public Class getContainerType() {
return key.containerType;
}
public TypeVariable getExtractedTypeParameter() {
return key.extractedTypeParameter;
}
public Optional> getExtractedType() {
return extractedType;
}
public ValueExtractor getValueExtractor() {
return valueExtractor;
}
public boolean isUnwrapByDefault() {
return unwrapByDefault;
}
@Override
public String toString() {
return "ValueExtractorDescriptor [key=" + key + ", valueExtractor=" + valueExtractor + ", unwrapByDefault=" + unwrapByDefault + "]";
}
public static class Key {
private final Class containerType;
private final TypeVariable extractedTypeParameter;
private final int hashCode;
public Key(Class containerType, TypeVariable extractedTypeParameter) {
this.containerType = containerType;
this.extractedTypeParameter = extractedTypeParameter;
this.hashCode = buildHashCode( containerType, extractedTypeParameter );
}
private static int buildHashCode(Type containerType, TypeVariable extractedTypeParameter) {
final int prime = 31;
int result = 1;
result = prime * result + containerType.hashCode();
result = prime * result + extractedTypeParameter.hashCode();
return result;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
Key other = (Key) obj;
return containerType.equals( other.containerType ) &&
extractedTypeParameter.equals( other.extractedTypeParameter );
}
@Override
public String toString() {
return "Key [containerType=" + StringHelper.toShortString( containerType ) + ", extractedTypeParameter=" + extractedTypeParameter + "]";
}
}
}