/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.internal.metadata.descriptor;
import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.validation.groups.Default;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.ElementDescriptor;
import javax.validation.metadata.Scope;
import org.hibernate.validator.internal.engine.groups.Group;
import org.hibernate.validator.internal.engine.groups.ValidationOrder;
import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
import org.hibernate.validator.internal.metadata.core.ConstraintOrigin;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.TypeHelper;
import org.hibernate.validator.internal.util.stereotypes.Immutable;
/**
* Describes a validated element (class, field or property).
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
* @author Gunnar Morling
*/
public abstract class ElementDescriptorImpl implements ElementDescriptor, Serializable {
/**
* The type of the element
*/
private final Class<?> type;
@Immutable
private final Set<ConstraintDescriptorImpl<?>> constraintDescriptors;
private final boolean defaultGroupSequenceRedefined;
@Immutable
private final List<Class<?>> defaultGroupSequence;
public ElementDescriptorImpl(Type type,
Set<ConstraintDescriptorImpl<?>> constraintDescriptors,
boolean defaultGroupSequenceRedefined,
List<Class<?>> defaultGroupSequence) {
this.type = (Class<?>) TypeHelper.getErasedType( type );
this.constraintDescriptors = CollectionHelper.toImmutableSet( constraintDescriptors );
this.defaultGroupSequenceRedefined = defaultGroupSequenceRedefined;
this.defaultGroupSequence = CollectionHelper.toImmutableList( defaultGroupSequence );
}
@Override
public final boolean hasConstraints() {
return constraintDescriptors.size() != 0;
}
@Override
public final Class<?> getElementClass() {
return type;
}
@Override
public final Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
return findConstraints().getConstraintDescriptors();
}
@Override
public final ConstraintFinder findConstraints() {
return new ConstraintFinderImpl();
}
private class ConstraintFinderImpl implements ConstraintFinder {
private List<Class<?>> groups;
private final Set<ConstraintOrigin> definedInSet;
private final Set<ElementType> elementTypes;
ConstraintFinderImpl() {
elementTypes = new HashSet<ElementType>();
elementTypes.add( ElementType.TYPE );
elementTypes.add( ElementType.METHOD );
elementTypes.add( ElementType.CONSTRUCTOR );
elementTypes.add( ElementType.FIELD );
elementTypes.add( ElementType.TYPE_USE );
//for a bean descriptor there will be no parameter constraints, so we can safely add this element type here
elementTypes.add( ElementType.PARAMETER );
definedInSet = newHashSet();
definedInSet.add( ConstraintOrigin.DEFINED_LOCALLY );
definedInSet.add( ConstraintOrigin.DEFINED_IN_HIERARCHY );
groups = Collections.emptyList();
}
@Override
public ConstraintFinder unorderedAndMatchingGroups(Class<?>... classes) {
this.groups = newArrayList();
for ( Class<?> clazz : classes ) {
if ( Default.class.equals( clazz ) && defaultGroupSequenceRedefined ) {
this.groups.addAll( defaultGroupSequence );
}
else {
groups.add( clazz );
}
}
return this;
}
@Override
public ConstraintFinder lookingAt(Scope visibility) {
if ( visibility.equals( Scope.LOCAL_ELEMENT ) ) {
definedInSet.remove( ConstraintOrigin.DEFINED_IN_HIERARCHY );
}
return this;
}
@Override
public ConstraintFinder declaredOn(ElementType... elementTypes) {
this.elementTypes.clear();
this.elementTypes.addAll( Arrays.asList( elementTypes ) );
return this;
}
@Override
public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
Set<ConstraintDescriptor<?>> matchingDescriptors = new HashSet<ConstraintDescriptor<?>>();
findMatchingDescriptors( matchingDescriptors );
return CollectionHelper.toImmutableSet( matchingDescriptors );
}
@Override
public boolean hasConstraints() {
return getConstraintDescriptors().size() != 0;
}
private void addMatchingDescriptorsForGroup(Class<?> group, Set<ConstraintDescriptor<?>> matchingDescriptors) {
for ( ConstraintDescriptorImpl<?> descriptor : constraintDescriptors ) {
if ( definedInSet.contains( descriptor.getDefinedOn() ) && elementTypes.contains( descriptor.getElementType() )
&& descriptor.getGroups().contains( group ) ) {
matchingDescriptors.add( descriptor );
}
}
}
private void findMatchingDescriptors(Set<ConstraintDescriptor<?>> matchingDescriptors) {
if ( !groups.isEmpty() ) {
ValidationOrder validationOrder = new ValidationOrderGenerator().getValidationOrder( groups );
Iterator<Group> groupIterator = validationOrder.getGroupIterator();
while ( groupIterator.hasNext() ) {
Group g = groupIterator.next();
addMatchingDescriptorsForGroup( g.getDefiningClass(), matchingDescriptors );
}
}
else {
for ( ConstraintDescriptorImpl<?> descriptor : constraintDescriptors ) {
if ( definedInSet.contains( descriptor.getDefinedOn() ) && elementTypes.contains( descriptor.getElementType() ) ) {
matchingDescriptors.add( descriptor );
}
}
}
}
}
}