Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and/or its affiliates, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hibernate.validator.internal.metadata.provider;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.GroupSequence;
import javax.validation.ParameterNameProvider;
import javax.validation.Valid;
import javax.validation.groups.ConvertGroup;
import org.hibernate.validator.group.GroupSequenceProvider;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.core.ConstraintOrigin;
import org.hibernate.validator.internal.metadata.core.MetaConstraint;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType;
import org.hibernate.validator.internal.metadata.location.BeanConstraintLocation;
import org.hibernate.validator.internal.metadata.location.CrossParameterConstraintLocation;
import org.hibernate.validator.internal.metadata.location.ExecutableConstraintLocation;
import org.hibernate.validator.internal.metadata.raw.BeanConfiguration;
import org.hibernate.validator.internal.metadata.raw.ConfigurationSource;
import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable;
import org.hibernate.validator.internal.metadata.raw.ConstrainedField;
import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter;
import org.hibernate.validator.internal.metadata.raw.ConstrainedType;
import org.hibernate.validator.internal.metadata.raw.ExecutableElement;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.CollectionHelper.Partitioner;
import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap;
import org.hibernate.validator.internal.util.ReflectionHelper;
import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;
import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
import static org.hibernate.validator.internal.util.CollectionHelper.partition;
import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.ReferenceType.SOFT;
import static org.hibernate.validator.internal.util.ReflectionHelper.getMethods;
import static org.hibernate.validator.internal.util.ReflectionHelper.newInstance;
/**
* {@code MetaDataProvider} which reads the metadata from annotations which is the default configuration source.
*
* @author Gunnar Morling
* @author Hardy Ferentschik
*/
public class AnnotationMetaDataProvider implements MetaDataProvider {
private static final Log log = LoggerFactory.make();
/**
* The default initial capacity for this cache.
*/
static final int DEFAULT_INITIAL_CAPACITY = 16;
private final ConstraintHelper constraintHelper;
private final ConcurrentReferenceHashMap, BeanConfiguration> configuredBeans;
private final AnnotationProcessingOptions annotationProcessingOptions;
private final ParameterNameProvider parameterNameProvider;
public AnnotationMetaDataProvider(ConstraintHelper constraintHelper,
ParameterNameProvider parameterNameProvider,
AnnotationProcessingOptions annotationProcessingOptions) {
this.constraintHelper = constraintHelper;
this.parameterNameProvider = parameterNameProvider;
this.annotationProcessingOptions = annotationProcessingOptions;
configuredBeans = new ConcurrentReferenceHashMap, BeanConfiguration>(
DEFAULT_INITIAL_CAPACITY,
SOFT,
SOFT
);
}
@Override
public AnnotationProcessingOptions getAnnotationProcessingOptions() {
return new AnnotationProcessingOptionsImpl();
}
@Override
public List> getBeanConfigurationForHierarchy(Class beanClass) {
List> configurations = newArrayList();
for ( Class hierarchyClass : ClassHierarchyHelper.getHierarchy( beanClass ) ) {
BeanConfiguration configuration = getBeanConfiguration( hierarchyClass );
if ( configuration != null ) {
configurations.add( configuration );
}
}
return configurations;
}
private BeanConfiguration getBeanConfiguration(Class beanClass) {
@SuppressWarnings("unchecked")
BeanConfiguration configuration = (BeanConfiguration) configuredBeans.get( beanClass );
if ( configuration != null ) {
return configuration;
}
configuration = retrieveBeanConfiguration( beanClass );
configuredBeans.put( beanClass, configuration );
return configuration;
}
/**
* @param beanClass The bean class for which to retrieve the meta data
*
* @return Retrieves constraint related meta data from the annotations of the given type.
*/
private BeanConfiguration retrieveBeanConfiguration(Class beanClass) {
Set constrainedElements = getFieldMetaData( beanClass );
constrainedElements.addAll( getMethodMetaData( beanClass ) );
constrainedElements.addAll( getConstructorMetaData( beanClass ) );
//TODO GM: currently class level constraints are represented by a PropertyMetaData. This
//works but seems somewhat unnatural
Set> classLevelConstraints = getClassLevelConstraints( beanClass );
if ( !classLevelConstraints.isEmpty() ) {
ConstrainedType classLevelMetaData =
new ConstrainedType(
ConfigurationSource.ANNOTATION,
new BeanConstraintLocation( beanClass ),
classLevelConstraints
);
constrainedElements.add( classLevelMetaData );
}
return new BeanConfiguration(
ConfigurationSource.ANNOTATION,
beanClass,
constrainedElements,
getDefaultGroupSequence( beanClass ),
getDefaultGroupSequenceProvider( beanClass )
);
}
private List> getDefaultGroupSequence(Class beanClass) {
GroupSequence groupSequenceAnnotation = beanClass.getAnnotation( GroupSequence.class );
return groupSequenceAnnotation != null ? Arrays.asList( groupSequenceAnnotation.value() ) : null;
}
private DefaultGroupSequenceProvider getDefaultGroupSequenceProvider(Class beanClass) {
GroupSequenceProvider groupSequenceProviderAnnotation = beanClass.getAnnotation( GroupSequenceProvider.class );
if ( groupSequenceProviderAnnotation != null ) {
@SuppressWarnings("unchecked")
Class> providerClass = (Class>) groupSequenceProviderAnnotation
.value();
return newGroupSequenceProviderClassInstance( beanClass, providerClass );
}
return null;
}
private DefaultGroupSequenceProvider newGroupSequenceProviderClassInstance(Class beanClass, Class> providerClass) {
Method[] providerMethods = getMethods( providerClass );
for ( Method method : providerMethods ) {
Class[] paramTypes = method.getParameterTypes();
if ( "getValidationGroups".equals( method.getName() ) && !method.isBridge()
&& paramTypes.length == 1 && paramTypes[0].isAssignableFrom( beanClass ) ) {
return newInstance(
providerClass, "the default group sequence provider"
);
}
}
throw log.getWrongDefaultGroupSequenceProviderTypeException( beanClass.getName() );
}
private Set> getClassLevelConstraints(Class clazz) {
if ( annotationProcessingOptions.areClassLevelConstraintsIgnoredFor( clazz ) ) {
return Collections.emptySet();
}
Set> classLevelConstraints = newHashSet();
// HV-262
List> classMetaData = findClassLevelConstraints( clazz );
for ( ConstraintDescriptorImpl constraintDescription : classMetaData ) {
classLevelConstraints.add( createMetaConstraint( clazz, constraintDescription ) );
}
return classLevelConstraints;
}
private Set getFieldMetaData(Class beanClass) {
Set propertyMetaData = newHashSet();
for ( Field field : ReflectionHelper.getDeclaredFields( beanClass ) ) {
// HV-172
if ( Modifier.isStatic( field.getModifiers() ) ||
annotationProcessingOptions.areMemberConstraintsIgnoredFor( field ) ||
field.isSynthetic() ) {
continue;
}
propertyMetaData.add( findPropertyMetaData( field ) );
}
return propertyMetaData;
}
private ConstrainedField findPropertyMetaData(Field field) {
Set> constraints = convertToMetaConstraints(
findConstraints( field, ElementType.FIELD ),
field
);
Map, Class> groupConversions = getGroupConversions(
field.getAnnotation( ConvertGroup.class ),
field.getAnnotation( ConvertGroup.List.class )
);
boolean isCascading = field.isAnnotationPresent( Valid.class );
return new ConstrainedField(
ConfigurationSource.ANNOTATION,
new BeanConstraintLocation( field ),
constraints,
groupConversions,
isCascading
);
}
private Set> convertToMetaConstraints(List> constraintDescriptors, Field field) {
Set> constraints = newHashSet();
for ( ConstraintDescriptorImpl constraintDescription : constraintDescriptors ) {
constraints.add( createMetaConstraint( field, constraintDescription ) );
}
return constraints;
}
private Set getConstructorMetaData(Class clazz) {
List declaredConstructors = ExecutableElement.forConstructors(
ReflectionHelper.getDeclaredConstructors( clazz )
);
return getMetaData( declaredConstructors );
}
private Set getMethodMetaData(Class clazz) {
List declaredMethods = ExecutableElement.forMethods(
ReflectionHelper.getDeclaredMethods( clazz )
);
return getMetaData( declaredMethods );
}
private Set getMetaData(List executableElements) {
Set executableMetaData = newHashSet();
for ( ExecutableElement executable : executableElements ) {
// HV-172; ignoring synthetic methods (inserted by the compiler), as they can't have any constraints
// anyway and possibly hide the actual method with the same signature in the built meta model
Member member = executable.getMember();
if ( Modifier.isStatic( member.getModifiers() ) || member.isSynthetic() ) {
continue;
}
executableMetaData.add( findExecutableMetaData( executable ) );
}
return executableMetaData;
}
/**
* Finds all constraint annotations defined for the given method or constructor.
*
* @param executable The executable element to check for constraints annotations.
*
* @return A meta data object describing the constraints specified for the
* given element.
*/
private ConstrainedExecutable findExecutableMetaData(ExecutableElement executable) {
List parameterConstraints = getParameterMetaData( executable );
AccessibleObject member = executable.getAccessibleObject();
Map>> executableConstraints = partition(
findConstraints(
executable.getMember(),
executable.getElementType()
), byType()
);
Set> crossParameterConstraints;
if ( annotationProcessingOptions.areCrossParameterConstraintsIgnoredFor( executable.getMember() ) ) {
crossParameterConstraints = Collections.emptySet();
}
else {
crossParameterConstraints = convertToMetaConstraints(
executableConstraints.get( ConstraintType.CROSS_PARAMETER ),
executable
);
}
Set> returnValueConstraints;
Map, Class> groupConversions;
boolean isCascading;
if ( annotationProcessingOptions.areReturnValueConstraintsIgnoredFor( executable.getMember() ) ) {
returnValueConstraints = Collections.emptySet();
groupConversions = Collections.emptyMap();
isCascading = false;
}
else {
returnValueConstraints = convertToMetaConstraints(
executableConstraints.get( ConstraintType.GENERIC ),
executable
);
groupConversions = getGroupConversions(
member.getAnnotation( ConvertGroup.class ),
member.getAnnotation( ConvertGroup.List.class )
);
isCascading = executable.getAccessibleObject().isAnnotationPresent( Valid.class );
}
return new ConstrainedExecutable(
ConfigurationSource.ANNOTATION,
new ExecutableConstraintLocation( executable ),
parameterConstraints,
crossParameterConstraints,
returnValueConstraints,
groupConversions,
isCascading
);
}
private Set> convertToMetaConstraints(List> constraintsDescriptors, ExecutableElement executable) {
if ( constraintsDescriptors == null ) {
return Collections.emptySet();
}
Set> constraints = newHashSet();
for ( ConstraintDescriptorImpl constraintDescriptor : constraintsDescriptors ) {
constraints.add(
constraintDescriptor.getConstraintType() == ConstraintType.GENERIC ?
createReturnValueMetaConstraint( executable, constraintDescriptor ) :
createCrossParameterMetaConstraint( executable, constraintDescriptor )
);
}
return constraints;
}
/**
* Retrieves constraint related meta data for the parameters of the given
* executable.
*
* @param executable The executable of interest.
*
* @return A list with parameter meta data for the given executable.
*/
private List getParameterMetaData(ExecutableElement executable) {
List metaData = newArrayList();
List parameterNames = executable.getParameterNames( parameterNameProvider );
int i = 0;
for ( Annotation[] parameterAnnotations : executable.getParameterAnnotations() ) {
boolean parameterIsCascading = false;
String parameterName = parameterNames.get( i );
Set> parameterConstraints = newHashSet();
ConvertGroup groupConversion = null;
ConvertGroup.List groupConversionList = null;
if ( annotationProcessingOptions.areParameterConstraintsIgnoredFor( executable.getMember(), i ) ) {
metaData.add(
new ConstrainedParameter(
ConfigurationSource.ANNOTATION,
new ExecutableConstraintLocation( executable, i ),
parameterName,
parameterConstraints,
getGroupConversions( groupConversion, groupConversionList ),
false
)
);
i++;
continue;
}
for ( Annotation parameterAnnotation : parameterAnnotations ) {
//1. collect constraints if this annotation is a constraint annotation
List> constraints = findConstraintAnnotations(
executable.getMember(), parameterAnnotation, ElementType.PARAMETER
);
for ( ConstraintDescriptorImpl constraintDescriptorImpl : constraints ) {
parameterConstraints.add(
createParameterMetaConstraint(
executable, i, constraintDescriptorImpl
)
);
}
//2. mark parameter as cascading if this annotation is the @Valid annotation
if ( parameterAnnotation.annotationType().equals( Valid.class ) ) {
parameterIsCascading = true;
}
//3. determine group conversions
else if ( parameterAnnotation.annotationType().equals( ConvertGroup.class ) ) {
groupConversion = (ConvertGroup) parameterAnnotation;
}
else if ( parameterAnnotation.annotationType().equals( ConvertGroup.List.class ) ) {
groupConversionList = (ConvertGroup.List) parameterAnnotation;
}
}
metaData.add(
new ConstrainedParameter(
ConfigurationSource.ANNOTATION,
new ExecutableConstraintLocation( executable, i ),
parameterName,
parameterConstraints,
getGroupConversions( groupConversion, groupConversionList ),
parameterIsCascading
)
);
i++;
}
return metaData;
}
/**
* Finds all constraint annotations defined for the given member and returns them in a list of
* constraint descriptors.
*
* @param member The member to check for constraints annotations.
* @param type The element type the constraint/annotation is placed on.
*
* @return A list of constraint descriptors for all constraint specified for the given member.
*/
private List> findConstraints(Member member, ElementType type) {
List> metaData = newArrayList();
for ( Annotation annotation : ( (AccessibleObject) member ).getDeclaredAnnotations() ) {
metaData.addAll( findConstraintAnnotations( member, annotation, type ) );
}
return metaData;
}
/**
* Finds all constraint annotations defined for the given class and returns them in a list of
* constraint descriptors.
*
* @param beanClass The class to check for constraints annotations.
*
* @return A list of constraint descriptors for all constraint specified on the given class.
*/
private List> findClassLevelConstraints(Class beanClass) {
List> metaData = newArrayList();
for ( Annotation annotation : beanClass.getDeclaredAnnotations() ) {
metaData.addAll( findConstraintAnnotations( null, annotation, ElementType.TYPE ) );
}
return metaData;
}
/**
* Examines the given annotation to see whether it is a single- or multi-valued constraint annotation.
*
* @param annotation The annotation to examine
* @param type the element type on which the annotation/constraint is placed on
*
* @return A list of constraint descriptors or the empty list in case annotation
is neither a
* single nor multi-valued annotation.
*/
private List> findConstraintAnnotations(Member member,
A annotation,
ElementType type) {
List> constraintDescriptors = newArrayList();
List constraints = newArrayList();
Class annotationType = annotation.annotationType();
if ( constraintHelper.isConstraintAnnotation( annotationType ) ) {
constraints.add( annotation );
}
else if ( constraintHelper.isMultiValueConstraint( annotationType ) ) {
constraints.addAll( constraintHelper.getMultiValueConstraints( annotation ) );
}
for ( Annotation constraint : constraints ) {
final ConstraintDescriptorImpl constraintDescriptor = buildConstraintDescriptor(
member, constraint, type
);
constraintDescriptors.add( constraintDescriptor );
}
return constraintDescriptors;
}
private Map, Class> getGroupConversions(ConvertGroup groupConversion, ConvertGroup.List groupConversionList) {
Map, Class> groupConversions = newHashMap();
if ( groupConversion != null ) {
groupConversions.put( groupConversion.from(), groupConversion.to() );
}
if ( groupConversionList != null ) {
for ( ConvertGroup conversion : groupConversionList.value() ) {
if ( groupConversions.containsKey( conversion.from() ) ) {
throw log.getMultipleGroupConversionsForSameSourceException(
conversion.from(),
CollectionHelper.>asSet(
groupConversions.get( conversion.from() ),
conversion.to()
)
);
}
groupConversions.put( conversion.from(), conversion.to() );
}
}
return groupConversions;
}
private Partitioner> byType() {
return new Partitioner>() {
@Override
public ConstraintType getPartition(ConstraintDescriptorImpl v) {
return v.getConstraintType();
}
};
}
private MetaConstraint createMetaConstraint(Class declaringClass,
ConstraintDescriptorImpl descriptor) {
return new MetaConstraint ( descriptor, new BeanConstraintLocation( declaringClass ) );
}
private MetaConstraint createMetaConstraint(Member member,
ConstraintDescriptorImpl descriptor) {
return new MetaConstraint ( descriptor, new BeanConstraintLocation( member ) );
}
private MetaConstraint createParameterMetaConstraint(ExecutableElement member,
int parameterIndex,
ConstraintDescriptorImpl descriptor) {
return new MetaConstraint ( descriptor, new ExecutableConstraintLocation( member, parameterIndex ) );
}
private MetaConstraint createReturnValueMetaConstraint(ExecutableElement member,
ConstraintDescriptorImpl descriptor) {
return new MetaConstraint ( descriptor, new ExecutableConstraintLocation( member ) );
}
private MetaConstraint createCrossParameterMetaConstraint(ExecutableElement member,
ConstraintDescriptorImpl descriptor) {
return new MetaConstraint ( descriptor, new CrossParameterConstraintLocation( member ) );
}
private ConstraintDescriptorImpl buildConstraintDescriptor(Member member,
A annotation,
ElementType type) {
return new ConstraintDescriptorImpl (
member,
annotation,
constraintHelper,
type,
ConstraintOrigin.DEFINED_LOCALLY
);
}
}