package org.hibernate.validator.internal.metadata;
import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.Option.IDENTITY_COMPARISONS;
import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.ReferenceType.SOFT;
import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.hibernate.validator.internal.engine.ConstraintCreationContext;
import org.hibernate.validator.internal.engine.MethodValidationConfiguration;
import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData;
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder;
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl;
import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider;
import org.hibernate.validator.internal.metadata.provider.MetaDataProvider;
import org.hibernate.validator.internal.metadata.raw.BeanConfiguration;
import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.ExecutableHelper;
import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper;
import org.hibernate.validator.internal.util.stereotypes.Immutable;
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
public class BeanMetaDataManagerImpl implements BeanMetaDataManager {
private static final int DEFAULT_INITIAL_CAPACITY = 16;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
@Immutable
private final List<MetaDataProvider> metaDataProviders;
private final ConstraintCreationContext constraintCreationContext;
private final ExecutableParameterNameProvider parameterNameProvider;
private final ConcurrentReferenceHashMap<Class<?>, BeanMetaData<?>> beanMetaDataCache;
private final ExecutableHelper executableHelper;
private final BeanMetaDataClassNormalizer beanMetaDataClassNormalizer;
private final ValidationOrderGenerator validationOrderGenerator;
private final MethodValidationConfiguration methodValidationConfiguration;
public BeanMetaDataManagerImpl(ConstraintCreationContext constraintCreationContext,
ExecutableHelper executableHelper,
ExecutableParameterNameProvider parameterNameProvider,
JavaBeanHelper javaBeanHelper,
BeanMetaDataClassNormalizer beanMetaDataClassNormalizer,
ValidationOrderGenerator validationOrderGenerator,
List<MetaDataProvider> optionalMetaDataProviders,
MethodValidationConfiguration methodValidationConfiguration) {
this.constraintCreationContext = constraintCreationContext;
this.executableHelper = executableHelper;
this.parameterNameProvider = parameterNameProvider;
this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer;
this.validationOrderGenerator = validationOrderGenerator;
this.methodValidationConfiguration = methodValidationConfiguration;
this.beanMetaDataCache = new ConcurrentReferenceHashMap<>(
DEFAULT_INITIAL_CAPACITY,
DEFAULT_LOAD_FACTOR,
DEFAULT_CONCURRENCY_LEVEL,
SOFT,
SOFT,
EnumSet.of( IDENTITY_COMPARISONS )
);
AnnotationProcessingOptions annotationProcessingOptions = getAnnotationProcessingOptionsFromNonDefaultProviders( optionalMetaDataProviders );
AnnotationMetaDataProvider defaultProvider = new AnnotationMetaDataProvider(
constraintCreationContext,
javaBeanHelper,
annotationProcessingOptions
);
List<MetaDataProvider> tmpMetaDataProviders = new ArrayList<>( optionalMetaDataProviders.size() + 1 );
tmpMetaDataProviders.add( defaultProvider );
tmpMetaDataProviders.addAll( optionalMetaDataProviders );
this.metaDataProviders = CollectionHelper.toImmutableList( tmpMetaDataProviders );
}
@Override
@SuppressWarnings("unchecked")
public <T> BeanMetaData<T> getBeanMetaData(Class<T> beanClass) {
Contracts.assertNotNull( beanClass, MESSAGES.beanTypeCannotBeNull() );
Class<? super T> normalizedBeanClass = beanMetaDataClassNormalizer.normalize( beanClass );
BeanMetaData<? super T> beanMetaData = (BeanMetaData<? super T>) beanMetaDataCache.get( normalizedBeanClass );
if ( beanMetaData != null ) {
return (BeanMetaData<T>) beanMetaData;
}
beanMetaData = createBeanMetaData( normalizedBeanClass );
BeanMetaData<? super T> previousBeanMetaData =
(BeanMetaData<? super T>) beanMetaDataCache.putIfAbsent( normalizedBeanClass, beanMetaData );
if ( previousBeanMetaData != null ) {
return (BeanMetaData<T>) previousBeanMetaData;
}
return (BeanMetaData<T>) beanMetaData;
}
@Override
public void clear() {
beanMetaDataCache.clear();
}
public int numberOfCachedBeanMetaDataInstances() {
return beanMetaDataCache.size();
}
private <T> BeanMetaDataImpl<T> createBeanMetaData(Class<T> clazz) {
BeanMetaDataBuilder<T> builder = BeanMetaDataBuilder.getInstance(
constraintCreationContext, executableHelper, parameterNameProvider,
validationOrderGenerator, clazz, methodValidationConfiguration );
for ( MetaDataProvider provider : metaDataProviders ) {
for ( BeanConfiguration<? super T> beanConfiguration : getBeanConfigurationForHierarchy( provider, clazz ) ) {
builder.add( beanConfiguration );
}
}
return builder.build();
}
private AnnotationProcessingOptions getAnnotationProcessingOptionsFromNonDefaultProviders(List<MetaDataProvider> optionalMetaDataProviders) {
AnnotationProcessingOptions options = new AnnotationProcessingOptionsImpl();
for ( MetaDataProvider metaDataProvider : optionalMetaDataProviders ) {
options.merge( metaDataProvider.getAnnotationProcessingOptions() );
}
return options;
}
private <T> List<BeanConfiguration<? super T>> getBeanConfigurationForHierarchy(MetaDataProvider provider, Class<T> beanClass) {
List<BeanConfiguration<? super T>> configurations = newArrayList();
for ( Class<? super T> clazz : ClassHierarchyHelper.getHierarchy( beanClass ) ) {
BeanConfiguration<? super T> configuration = provider.getBeanConfiguration( clazz );
if ( configuration != null ) {
configurations.add( configuration );
}
}
return configurations;
}
}