package org.hibernate.validator.internal.metadata.core;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.validation.valueextraction.ValueExtractor;
import org.hibernate.validator.internal.engine.ValidationContext;
import org.hibernate.validator.internal.engine.ValueContext;
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree;
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor;
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorHelper;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.metadata.location.ConstraintLocation;
import org.hibernate.validator.internal.util.StringHelper;
import org.hibernate.validator.internal.util.stereotypes.Immutable;
public class MetaConstraint<A extends Annotation> {
private final ConstraintTree<A> constraintTree;
private final ConstraintLocation location;
@Immutable
private final ValueExtractionPathNode ;
private final int hashCode;
private final boolean isDefinedForOneGroupOnly;
MetaConstraint(ConstraintDescriptorImpl<A> constraintDescriptor, ConstraintLocation location, List<ContainerClassTypeParameterAndExtractor> valueExtractionPath,
Type validatedValueType) {
this.constraintTree = ConstraintTree.of( constraintDescriptor, validatedValueType );
this.location = location;
this.valueExtractionPath = getValueExtractionPath( valueExtractionPath );
this.hashCode = buildHashCode( constraintDescriptor, location );
this.isDefinedForOneGroupOnly = constraintDescriptor.getGroups().size() <= 1;
}
private static ValueExtractionPathNode (List<ContainerClassTypeParameterAndExtractor> valueExtractionPath) {
switch ( valueExtractionPath.size() ) {
case 0: return null;
case 1: return new SingleValueExtractionPathNode( valueExtractionPath.iterator().next() );
default: return new LinkedValueExtractionPathNode( null, valueExtractionPath );
}
}
public final Set<Class<?>> getGroupList() {
return constraintTree.getDescriptor().getGroups();
}
public final boolean isDefinedForOneGroupOnly() {
return isDefinedForOneGroupOnly;
}
public final ConstraintDescriptorImpl<A> getDescriptor() {
return constraintTree.getDescriptor();
}
public final ElementType getElementType() {
return constraintTree.getDescriptor().getElementType();
}
public boolean validateConstraint(ValidationContext<?> validationContext, ValueContext<?, Object> valueContext) {
boolean success = true;
if ( valueExtractionPath != null ) {
Object valueToValidate = valueContext.getCurrentValidatedValue();
if ( valueToValidate != null ) {
TypeParameterValueReceiver receiver = new TypeParameterValueReceiver( validationContext, valueContext, valueExtractionPath );
ValueExtractorHelper.extractValues( valueExtractionPath.getValueExtractorDescriptor(), valueToValidate, receiver );
success = receiver.isSuccess();
}
}
else {
success = doValidateConstraint( validationContext, valueContext );
}
return success;
}
private boolean doValidateConstraint(ValidationContext<?> executionContext, ValueContext<?, ?> valueContext) {
valueContext.setElementType( getElementType() );
boolean validationResult = constraintTree.validateConstraints( executionContext, valueContext );
return validationResult;
}
public ConstraintLocation getLocation() {
return location;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
MetaConstraint<?> that = (MetaConstraint<?>) o;
if ( !constraintTree.getDescriptor().equals( that.constraintTree.getDescriptor() ) ) {
return false;
}
if ( !location.equals( that.location ) ) {
return false;
}
return true;
}
private static int buildHashCode(ConstraintDescriptorImpl<?> constraintDescriptor, ConstraintLocation location) {
final int prime = 31;
int result = 1;
result = prime * result + constraintDescriptor.hashCode();
result = prime * result + location.hashCode();
return result;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "MetaConstraint" );
sb.append( "{constraintType=" ).append( StringHelper.toShortString( constraintTree.getDescriptor().getAnnotation().annotationType() ) );
sb.append( ", location=" ).append( location );
sb.append( ", valueExtractionPath=" ).append( valueExtractionPath );
sb.append( "}" );
return sb.toString();
}
private final class TypeParameterValueReceiver implements ValueExtractor.ValueReceiver {
private final ValidationContext<?> validationContext;
private final ValueContext<?, Object> valueContext;
private boolean success = true;
private ValueExtractionPathNode ;
public (ValidationContext<?> validationContext, ValueContext<?, Object> valueContext, ValueExtractionPathNode currentValueExtractionPathNode) {
this.validationContext = validationContext;
this.valueContext = valueContext;
this.currentValueExtractionPathNode = currentValueExtractionPathNode;
}
@Override
public void value(String nodeName, Object object) {
doValidate( object, nodeName );
}
@Override
public void iterableValue(String nodeName, Object value) {
valueContext.markCurrentPropertyAsIterable();
doValidate( value, nodeName );
}
@Override
public void indexedValue(String nodeName, int index, Object value) {
valueContext.markCurrentPropertyAsIterableAndSetIndex( index );
doValidate( value, nodeName );
}
@Override
public void keyedValue(String nodeName, Object key, Object value) {
valueContext.markCurrentPropertyAsIterableAndSetKey( key );
doValidate( value, nodeName );
}
private void doValidate(Object value, String nodeName) {
ValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();
Class<?> containerClass = currentValueExtractionPathNode.getContainerClass();
if ( containerClass != null ) {
valueContext.setTypeParameter( containerClass, currentValueExtractionPathNode.getTypeParameterIndex() );
}
if ( nodeName != null ) {
valueContext.appendTypeParameterNode( nodeName );
}
valueContext.setCurrentValidatedValue( value );
if ( currentValueExtractionPathNode.hasNext() ) {
if ( value != null ) {
currentValueExtractionPathNode = currentValueExtractionPathNode.getNext();
ValueExtractorDescriptor valueExtractorDescriptor = currentValueExtractionPathNode.getValueExtractorDescriptor();
ValueExtractorHelper.extractValues( valueExtractorDescriptor, value, this );
currentValueExtractionPathNode = currentValueExtractionPathNode.getPrevious();
}
}
else {
success &= doValidateConstraint( validationContext, valueContext );
}
valueContext.resetValueState( originalValueState );
}
public boolean isSuccess() {
return success;
}
}
static final class ContainerClassTypeParameterAndExtractor {
private final Class<?> containerClass;
private final TypeVariable<?> typeParameter;
private final Integer typeParameterIndex;
private final ValueExtractorDescriptor valueExtractorDescriptor;
ContainerClassTypeParameterAndExtractor(Class<?> containerClass, TypeVariable<?> typeParameter, Integer typeParameterIndex, ValueExtractorDescriptor valueExtractorDescriptor) {
this.containerClass = containerClass;
this.typeParameter = typeParameter;
this.typeParameterIndex = typeParameterIndex;
this.valueExtractorDescriptor = valueExtractorDescriptor;
}
@Override
public String toString() {
return "ContainerClassTypeParameterAndExtractor [containerClass=" + containerClass +
", typeParameter=" + typeParameter +
", typeParameterIndex=" + typeParameterIndex +
", valueExtractorDescriptor=" + valueExtractorDescriptor + "]";
}
}
private interface {
boolean ();
ValueExtractionPathNode ();
ValueExtractionPathNode ();
Class<?> ();
TypeVariable<?> ();
Integer ();
ValueExtractorDescriptor ();
}
private static final class implements ValueExtractionPathNode {
private final Class<?> ;
private final TypeVariable<?> ;
private final Integer ;
private final ValueExtractorDescriptor ;
public SingleValueExtractionPathNode(ContainerClassTypeParameterAndExtractor typeParameterAndExtractor) {
this.containerClass = typeParameterAndExtractor.containerClass;
this.typeParameter = typeParameterAndExtractor.typeParameter;
this.typeParameterIndex = typeParameterAndExtractor.typeParameterIndex;
this.valueExtractorDescriptor = typeParameterAndExtractor.valueExtractorDescriptor;
}
@Override
public boolean () {
return false;
}
@Override
public ValueExtractionPathNode () {
throw new NoSuchElementException();
}
@Override
public ValueExtractionPathNode () {
throw new NoSuchElementException();
}
@Override
public Class<?> () {
return containerClass;
}
@Override
public TypeVariable<?> () {
return typeParameter;
}
@Override
public Integer () {
return typeParameterIndex;
}
@Override
public ValueExtractorDescriptor () {
return valueExtractorDescriptor;
}
@Override
public String () {
return "SingleValueExtractionPathNode [containerClass=" + containerClass +
", typeParameter=" + typeParameter +
", valueExtractorDescriptor=" + valueExtractorDescriptor + "]";
}
}
private static final class implements ValueExtractionPathNode {
private final ValueExtractionPathNode ;
private final ValueExtractionPathNode ;
private final Class<?> ;
private final TypeVariable<?> ;
private final Integer ;
private final ValueExtractorDescriptor ;
private ( ValueExtractionPathNode previous, List<ContainerClassTypeParameterAndExtractor> elements) {
ContainerClassTypeParameterAndExtractor first = elements.get( 0 );
this.containerClass = first.containerClass;
this.typeParameter = first.typeParameter;
this.typeParameterIndex = first.typeParameterIndex;
this.valueExtractorDescriptor = first.valueExtractorDescriptor;
this.previous = previous;
if ( elements.size() == 1 ) {
this.next = null;
}
else {
this.next = new LinkedValueExtractionPathNode( this, elements.subList( 1, elements.size() ) );
}
}
@Override
public boolean () {
return next != null;
}
@Override
public ValueExtractionPathNode () {
return previous;
}
@Override
public ValueExtractionPathNode () {
return next;
}
@Override
public Class<?> () {
return containerClass;
}
@Override
public TypeVariable<?> () {
return typeParameter;
}
@Override
public Integer () {
return typeParameterIndex;
}
@Override
public ValueExtractorDescriptor () {
return valueExtractorDescriptor;
}
@Override
public String () {
return "LinkedValueExtractionPathNode [containerClass=" + containerClass +
", typeParameter=" + typeParameter +
", valueExtractorDescriptor=" + valueExtractorDescriptor + "]";
}
}
}