[hibernate-commits] Hibernate SVN: r15773 - in validator/trunk: hibernate-validator/src/main/java/org/hibernate/validation/engine and 5 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Tue Jan 13 09:46:35 EST 2009


Author: hardy.ferentschik
Date: 2009-01-13 09:46:34 -0500 (Tue, 13 Jan 2009)
New Revision: 15773

Added:
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConstraintTree.java
Modified:
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/BeanMetaDataImpl.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaConstraint.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidationContext.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorBuilderImpl.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorFactoryImpl.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/constraints/composition/GermanZipcode.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ConstraintCompositionTest.java
   validator/trunk/hibernate-validator/src/test/resources/log4j.properties
   validator/trunk/pom.xml
   validator/trunk/validation-api/src/main/java/javax/validation/Validator.java
Log:
BVAL-31 - Added support for @ReportAsViolationFromCompositeConstraint

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/BeanMetaDataImpl.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/BeanMetaDataImpl.java	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/BeanMetaDataImpl.java	2009-01-13 14:46:34 UTC (rev 15773)
@@ -32,6 +32,8 @@
 import javax.validation.PropertyDescriptor;
 import javax.validation.Valid;
 import javax.validation.ValidationException;
+import javax.validation.MessageResolver;
+import javax.validation.ConstraintFactory;
 
 import org.slf4j.Logger;
 
@@ -47,13 +49,16 @@
  * instantiate an instance of this class and delegate the metadata extraction to it.
  *
  * @author Hardy Ferentschik
- *         FIXME create an interface for MetadataProvider
  */
 
 public class BeanMetaDataImpl<T> implements BeanMetaData<T> {
 
 	private static final Logger log = LoggerFactory.make();
 
+	private final MessageResolver messageResolver;
+
+	private final ConstraintFactory constraintFactory;
+
 	/**
 	 * The root bean class for this validator.
 	 */
@@ -90,8 +95,10 @@
 	 */
 	private Map<Class<?>, List<Class<?>>> groupSequences = new HashMap<Class<?>, List<Class<?>>>();
 
-	public BeanMetaDataImpl(Class<T> beanClass) {
+	public BeanMetaDataImpl(Class<T> beanClass, MessageResolver messageResolver, ConstraintFactory constraintFactory) {
 		this.beanClass = beanClass;
+		this.messageResolver = messageResolver;
+		this.constraintFactory = constraintFactory;
 		createMetaData();
 	}
 
@@ -190,7 +197,7 @@
 			List<ConstraintDescriptorImpl> fieldMetadata = findFieldLevelConstraints( field );
 			for ( ConstraintDescriptorImpl constraintDescription : fieldMetadata ) {
 				ReflectionHelper.setAccessibility( field );
-				MetaConstraint metaConstraint = new MetaConstraint( field, constraintDescription );
+				MetaConstraint metaConstraint = new MetaConstraint( field, constraintDescription, messageResolver, constraintFactory );
 				metaConstraintList.add( metaConstraint );
 			}
 			if ( field.isAnnotationPresent( Valid.class ) ) {
@@ -205,7 +212,7 @@
 			List<ConstraintDescriptorImpl> methodMetadata = findMethodLevelConstraints( method );
 			for ( ConstraintDescriptorImpl constraintDescription : methodMetadata ) {
 				ReflectionHelper.setAccessibility( method );
-				MetaConstraint metaConstraint = new MetaConstraint( method, constraintDescription );
+				MetaConstraint metaConstraint = new MetaConstraint( method, constraintDescription, messageResolver, constraintFactory );
 				metaConstraintList.add( metaConstraint );
 			}
 			if ( method.isAnnotationPresent( Valid.class ) ) {
@@ -218,7 +225,7 @@
 	private void initClassConstraints(Class clazz) {
 		List<ConstraintDescriptorImpl> classMetadata = findClassLevelConstraints( clazz );
 		for ( ConstraintDescriptorImpl constraintDescription : classMetadata ) {
-			MetaConstraint metaConstraint = new MetaConstraint( clazz, constraintDescription );
+			MetaConstraint metaConstraint = new MetaConstraint( clazz, constraintDescription, messageResolver, constraintFactory );
 			metaConstraintList.add( metaConstraint );
 		}
 	}

Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConstraintTree.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConstraintTree.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConstraintTree.java	2009-01-13 14:46:34 UTC (rev 15773)
@@ -0,0 +1,164 @@
+// $Id$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, 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.validation.engine;
+
+import java.util.List;
+import java.util.ArrayList;
+import javax.validation.Constraint;
+import javax.validation.ConstraintDescriptor;
+import javax.validation.ValidationException;
+import javax.validation.ConstraintFactory;
+import javax.validation.MessageResolver;
+
+import org.slf4j.Logger;
+
+import org.hibernate.validation.impl.ConstraintViolationImpl;
+import org.hibernate.validation.util.LoggerFactory;
+
+/**
+ * Due to constraint conposition a single constraint annotation can lead to a whole constraint tree beeing validated.
+ * This class encapsulates such a tree for a single constraint annotation.
+ *
+ * @author Hardy Ferentschik
+ */
+public class ConstraintTree {
+
+	private static final Logger log = LoggerFactory.make();
+
+	private final ConstraintTree parent;
+	private final List<ConstraintTree> children;
+	private Constraint constraint;
+	private final ConstraintDescriptor descriptor;
+	private final ConstraintFactory constraintFactory;
+	private final MessageResolver messageResolver;
+
+	public ConstraintTree(ConstraintDescriptor descriptor, ConstraintFactory constraintFactory, MessageResolver messageResolver) {
+		this( descriptor, null, constraintFactory, messageResolver );
+	}
+
+	private ConstraintTree(ConstraintDescriptor descriptor, ConstraintTree parent, ConstraintFactory constraintFactory, MessageResolver messageResolver) {
+		this.parent = parent;
+		this.descriptor = descriptor;
+		this.constraintFactory = constraintFactory;
+		this.messageResolver = messageResolver;
+		this.constraint = getConstraint( descriptor );
+		children = new ArrayList<ConstraintTree>( descriptor.getComposingConstraints().size() );
+
+		for ( ConstraintDescriptor composingDescriptor : descriptor.getComposingConstraints() ) {
+			ConstraintTree treeNode = new ConstraintTree(
+					composingDescriptor, this, constraintFactory, messageResolver
+			);
+			children.add( treeNode );
+		}
+	}
+
+	public boolean isRoot() {
+		return parent == null;
+	}
+
+	public ConstraintTree getParent() {
+		return parent;
+	}
+
+	public List<ConstraintTree> getChildren() {
+		return children;
+	}
+
+	public boolean hasChildren() {
+		return children.size() > 0;
+	}
+
+	public Constraint getConstraint() {
+		return constraint;
+	}
+
+	public ConstraintDescriptor getDescriptor() {
+		return descriptor;
+	}
+
+	public <T> void validateConstraints(Object value, Class<T> beanClass, ValidationContext<T> validationContext) {
+		for ( ConstraintTree tree : getChildren() ) {
+			tree.validateConstraints( value, beanClass, validationContext );
+		}
+
+		final Object leafBeanInstance = validationContext.peekValidatedObject();
+		ConstraintContextImpl constraintContext = new ConstraintContextImpl( descriptor );
+		if ( log.isTraceEnabled() ) {
+			log.trace( "Validating value {} against constraint defined by {}", value, descriptor );
+		}
+		if ( !constraint.isValid( value, constraintContext ) ) {
+			for ( ConstraintContextImpl.ErrorMessage error : constraintContext.getErrorMessages() ) {
+				final String message = error.getMessage();
+				createConstraintViolation( value, beanClass, validationContext, leafBeanInstance, message, descriptor );
+			}
+		}
+		if ( reportAsSingleViolation()
+				&& validationContext.getFailingConstraints().size() > 0 ) {
+			validationContext.clearFailingConstraints();
+			final String message = ( String ) getParent().getDescriptor().getParameters().get( "message" );
+			createConstraintViolation(
+					value, beanClass, validationContext, leafBeanInstance, message, getParent().descriptor
+			);
+		}
+	}
+
+	private boolean reportAsSingleViolation() {
+		return getParent() != null
+				&& getParent().getDescriptor().isReportAsViolationFromCompositeConstraint();
+	}
+
+	private <T> void createConstraintViolation(Object value, Class<T> beanClass, ValidationContext<T> validationContext, Object leafBeanInstance, String message, ConstraintDescriptor descriptor) {
+		String interpolatedMessage = messageResolver.interpolate(
+				message,
+				descriptor,
+				leafBeanInstance
+		);
+		ConstraintViolationImpl<T> failingConstraintViolation = new ConstraintViolationImpl<T>(
+				message,
+				interpolatedMessage,
+				validationContext.getRootBean(),
+				beanClass,
+				leafBeanInstance,
+				value,
+				validationContext.peekPropertyPath(), //FIXME use error.getProperty()
+				validationContext.getCurrentGroup(),
+				descriptor
+		);
+		validationContext.addConstraintFailure( failingConstraintViolation );
+	}
+
+	@SuppressWarnings("unchecked")
+	private Constraint getConstraint(ConstraintDescriptor descriptor) {
+		Constraint constraint;
+		try {
+			//unchecked
+			constraint = constraintFactory.getInstance( descriptor.getConstraintClass() );
+		}
+		catch ( RuntimeException e ) {
+			throw new ValidationException( "Unable to instantiate " + descriptor.getConstraintClass(), e );
+		}
+
+		try {
+			constraint.initialize( descriptor.getAnnotation() );
+		}
+		catch ( RuntimeException e ) {
+			throw new ValidationException( "Unable to intialize " + constraint.getClass().getName(), e );
+		}
+		return constraint;
+	}
+}


Property changes on: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConstraintTree.java
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaConstraint.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaConstraint.java	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaConstraint.java	2009-01-13 14:46:34 UTC (rev 15773)
@@ -21,10 +21,11 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import javax.validation.ConstraintDescriptor;
 import javax.validation.ValidationException;
-import javax.validation.ConstraintDescriptor;
+import javax.validation.MessageResolver;
+import javax.validation.ConstraintFactory;
 
-import org.hibernate.validation.impl.ConstraintDescriptorImpl;
 import org.hibernate.validation.util.ReflectionHelper;
 
 /**
@@ -36,9 +37,9 @@
 public class MetaConstraint {
 
 	/**
-	 * The constraint specific meta data.
+	 * The constraint tree created from the constraint annotation.
 	 */
-	private final ConstraintDescriptorImpl descriptor;
+	private final ConstraintTree constraintTree;
 
 	/**
 	 * The type (class) the constraint was defined on. <code>null</code> if the constraint was specified on method or
@@ -69,31 +70,34 @@
 	 */
 	private final ElementType elementType;
 
-	public MetaConstraint(Type t, ConstraintDescriptorImpl constraintDescriptor) {
-		this.type = t;
-		this.method = null;
-		this.field = null;
-		this.descriptor = constraintDescriptor;
-		this.elementType = ElementType.TYPE;
-		this.propertyName = "";
+	public MetaConstraint(Type t, ConstraintDescriptor constraintDescriptor, MessageResolver messageResolver, ConstraintFactory factory) {
+		this( t, null, null, ElementType.FIELD, constraintDescriptor, "", messageResolver, factory );
 	}
 
-	public MetaConstraint(Method m, ConstraintDescriptorImpl constraintDescriptor) {
-		this.method = m;
-		this.type = null;
-		this.field = null;
-		this.descriptor = constraintDescriptor;
-		this.elementType = ElementType.METHOD;
-		this.propertyName = ReflectionHelper.getPropertyName( m );
+	public MetaConstraint(Method m, ConstraintDescriptor constraintDescriptor, MessageResolver messageResolver, ConstraintFactory factory) {
+		this(
+				null,
+				m,
+				null,
+				ElementType.METHOD,
+				constraintDescriptor,
+				ReflectionHelper.getPropertyName( m ),
+				messageResolver,
+				factory
+		);
 	}
 
-	public MetaConstraint(Field f, ConstraintDescriptorImpl constraintDescriptor) {
+	public MetaConstraint(Field f, ConstraintDescriptor constraintDescriptor, MessageResolver messageResolver, ConstraintFactory factory) {
+		this( null, null, f, ElementType.FIELD, constraintDescriptor, f.getName(), messageResolver, factory );
+	}
+
+	private MetaConstraint(Type t, Method m, Field f, ElementType elementType, ConstraintDescriptor constraintDescriptor, String property, MessageResolver messageResolver, ConstraintFactory factory) {
+		this.type = t;
+		this.method = m;
 		this.field = f;
-		this.method = null;
-		this.type = null;
-		this.descriptor = constraintDescriptor;
-		this.elementType = ElementType.FIELD;
-		this.propertyName = f.getName();
+		this.elementType = elementType;
+		this.propertyName = property;
+		constraintTree = new ConstraintTree( constraintDescriptor, factory, messageResolver );
 	}
 
 	/**
@@ -140,7 +144,7 @@
 	}
 
 	public ConstraintDescriptor getDescriptor() {
-		return descriptor;
+		return constraintTree.getDescriptor();
 	}
 
 	public Method getMethod() {
@@ -163,6 +167,20 @@
 		return elementType;
 	}
 
+	public ConstraintTree getConstraintTree() {
+		return constraintTree;
+	}
+
+	public <T> void validateConstraint(Class beanClass, ValidationContext<T> validationContext) {
+		final Object leafBeanInstance = validationContext.peekValidatedObject();
+		Object value = getValue( leafBeanInstance );
+		constraintTree.validateConstraints( value, beanClass, validationContext );
+	}
+
+	public <T> void validateConstraint(Class beanClass, Object value, ValidationContext<T> validationContext) {
+		constraintTree.validateConstraints( value, beanClass, validationContext );
+	}
+
 	private Type typeOfAnnoatedElement() {
 		Type t;
 		switch ( elementType ) {

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidationContext.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidationContext.java	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidationContext.java	2009-01-13 14:46:34 UTC (rev 15773)
@@ -143,6 +143,10 @@
 		return failingConstraintViolations;
 	}
 
+	public void clearFailingConstraints() {
+		failingConstraintViolations.clear();
+	}
+
 	/**
 	 * Adds a new level to the current property path of this context.
 	 *

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java	2009-01-13 14:46:34 UTC (rev 15773)
@@ -27,24 +27,15 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.ListIterator;
 import javax.validation.BeanDescriptor;
-import javax.validation.Constraint;
 import javax.validation.ConstraintDescriptor;
-import javax.validation.ConstraintFactory;
 import javax.validation.ConstraintViolation;
-import javax.validation.MessageResolver;
-import javax.validation.TraversableResolver;
-import javax.validation.ValidationException;
 import javax.validation.Validator;
 import javax.validation.groups.Default;
 
-import org.slf4j.Logger;
-
 import org.hibernate.validation.impl.ConstraintViolationImpl;
 import org.hibernate.validation.util.PropertyIterator;
 import org.hibernate.validation.util.ReflectionHelper;
-import org.hibernate.validation.util.LoggerFactory;
 
 /**
  * The main Bean Validation class.
@@ -54,7 +45,6 @@
  * @todo Make all properties transient for serializability.
  */
 public class ValidatorImpl implements Validator {
-	private static final Logger log = LoggerFactory.make();
 	private static final Set<Class<?>> INDEXABLE_CLASS = new HashSet<Class<?>>();
 	private static final Class<?>[] DEFAULT_GROUP = new Class<?>[] { Default.class };
 
@@ -64,22 +54,10 @@
 		INDEXABLE_CLASS.add( String.class );
 	}
 
-	/**
-	 * Message resolver used  for interpolating error messages.
-	 */
-	private final MessageResolver messageResolver;
-
 	private final ValidatorFactoryImplementor factory;
 
-	private final ConstraintFactory constraintFactory;
-	private final TraversableResolver traversableResolver;
-
-	public ValidatorImpl(ValidatorFactoryImplementor factory, MessageResolver messageResolver,
-						 TraversableResolver traversableResolver, ConstraintFactory constraintFactory) {
+	public ValidatorImpl(ValidatorFactoryImplementor factory) {
 		this.factory = factory;
-		this.messageResolver = messageResolver;
-		this.traversableResolver = traversableResolver;
-		this.constraintFactory = constraintFactory;
 	}
 
 	/**
@@ -149,6 +127,7 @@
 				( BeanMetaData<T> ) factory.getBeanMetaData( validationContext.peekValidatedObjectType() );
 		for ( MetaConstraint metaConstraint : beanMetaData.geMetaConstraintList() ) {
 			ConstraintDescriptor mainConstraintDescriptor = metaConstraint.getDescriptor();
+
 			validationContext.pushProperty( metaConstraint.getPropertyName() );
 
 			if ( !validationContext.needsValidation( mainConstraintDescriptor.getGroups() ) ) {
@@ -156,48 +135,7 @@
 				continue;
 			}
 
-			final Object leafBeanInstance = validationContext.peekValidatedObject();
-			Object value = metaConstraint.getValue( leafBeanInstance );
-
-			// we have to check the main constraint and all composing constraints
-			List<ConstraintDescriptor> descriptors = new ArrayList<ConstraintDescriptor>();
-			descriptors.add( mainConstraintDescriptor );
-			// use a list iterator so that we can keep modifying the underlying list
-			ListIterator<ConstraintDescriptor> listIter = descriptors.listIterator();
-			while ( listIter.hasNext() ) {
-				ConstraintDescriptor descriptor = listIter.next();
-				ConstraintContextImpl constraintContext = new ConstraintContextImpl( descriptor );
-				if ( log.isTraceEnabled() ) {
-					log.trace( "Validating value {} against constraint defined by {}", value, descriptor );
-				}
-				if ( !getConstraint( descriptor ).isValid( value, constraintContext ) ) {
-					for ( ConstraintContextImpl.ErrorMessage error : constraintContext.getErrorMessages() ) {
-						final String message = error.getMessage();
-						String interpolatedMessage = messageResolver.interpolate(
-								message,
-								descriptor,
-								leafBeanInstance
-						);
-						ConstraintViolationImpl<T> failingConstraintViolation = new ConstraintViolationImpl<T>(
-								message,
-								interpolatedMessage,
-								validationContext.getRootBean(),
-								beanMetaData.getBeanClass(),
-								leafBeanInstance,
-								value,
-								validationContext.peekPropertyPath(), //FIXME use error.getProperty()
-								validationContext.getCurrentGroup(),
-								descriptor
-						);
-						validationContext.addConstraintFailure( failingConstraintViolation );
-					}
-				}
-				// check for composing constraints and add them to the list. The list iterator needs to set back each time!
-				for ( ConstraintDescriptor composingDescriptor : descriptor.getComposingConstraints() ) {
-					listIter.add( composingDescriptor );
-					listIter = descriptors.listIterator( listIter.previousIndex() );
-				}
-			}
+			metaConstraint.validateConstraint( beanMetaData.getBeanClass(), validationContext );
 			validationContext.popProperty();
 		}
 		validationContext.markProcessedForCurrentGroup();
@@ -281,7 +219,6 @@
 		}
 	}
 
-
 	/**
 	 * {@inheritDoc}
 	 */
@@ -291,7 +228,6 @@
 		return new HashSet<ConstraintViolation<T>>( failingConstraintViolations );
 	}
 
-
 	private <T> void validateProperty(T object, PropertyIterator propertyIter, List<ConstraintViolationImpl<T>> failingConstraintViolations, Class<?>... groups) {
 		if ( object == null ) {
 			throw new IllegalArgumentException( "Validated object cannot be null" );
@@ -299,9 +235,10 @@
 		@SuppressWarnings("unchecked")
 		final Class<T> beanType = ( Class<T> ) object.getClass();
 
-		DesrciptorValueWrapper wrapper = getConstraintDescriptorAndValueForPath( beanType, propertyIter, object );
+		Set<MetaConstraint> metaConstraints = new HashSet<MetaConstraint>();
+		getMetaConstraintsForPath( beanType, propertyIter, metaConstraints );
 
-		if ( wrapper == null ) {
+		if ( metaConstraints.size() == 0 ) {
 			return;
 		}
 
@@ -318,33 +255,14 @@
 
 			for ( Class<?> expandedGroup : expandedGroups ) {
 
-				if ( !wrapper.descriptor.getGroups().contains( expandedGroup ) ) {
-					continue;
-				}
-
-				ConstraintContextImpl contextImpl = new ConstraintContextImpl( wrapper.descriptor );
-				if ( !getConstraint( wrapper.descriptor ).isValid( wrapper.value, contextImpl ) ) {
-
-					for ( ConstraintContextImpl.ErrorMessage error : contextImpl.getErrorMessages() ) {
-						final String message = error.getMessage();
-						String interpolatedMessage = messageResolver.interpolate(
-								message,
-								wrapper.descriptor,
-								wrapper.value
-						);
-						ConstraintViolationImpl<T> failingConstraintViolation = new ConstraintViolationImpl<T>(
-								message,
-								interpolatedMessage,
-								object,
-								beanType,
-								object,
-								wrapper.value,
-								propertyIter.getOriginalProperty(), //FIXME use error.getProperty()
-								group,
-								wrapper.descriptor
-						);
-						addFailingConstraint( failingConstraintViolations, failingConstraintViolation );
+				for ( MetaConstraint metaConstraint : metaConstraints ) {
+					if ( !metaConstraint.getDescriptor().getGroups().contains( expandedGroup ) ) {
+						continue;
 					}
+
+					ValidationContext<T> context = new ValidationContext<T>( object );
+					metaConstraint.validateConstraint( object.getClass(), context );
+					failingConstraintViolations.addAll( context.getFailingConstraints() );
 				}
 
 				if ( isGroupSequence && failingConstraintViolations.size() > 0 ) {
@@ -367,13 +285,11 @@
 		return factory.getBeanMetaData( clazz ).getBeanDescriptor();
 	}
 
-	/**
-	 * @todo Implement composing constraints.
-	 */
-	private <T> void validateValue(Class<T> beanType, Object object, PropertyIterator propertyIter, List<ConstraintViolationImpl<T>> failingConstraintViolations, Class<?>... groups) {
-		ConstraintDescriptor constraintDescriptor = getConstraintDescriptorForPath( beanType, propertyIter );
+	private <T> void validateValue(Class<T> beanType, Object value, PropertyIterator propertyIter, List<ConstraintViolationImpl<T>> failingConstraintViolations, Class<?>... groups) {
+		Set<MetaConstraint> metaConstraints = new HashSet<MetaConstraint>();
+		getMetaConstraintsForPath( beanType, propertyIter, metaConstraints );
 
-		if ( constraintDescriptor == null ) {
+		if ( metaConstraints.size() == 0 ) {
 			return;
 		}
 
@@ -390,33 +306,15 @@
 
 			for ( Class<?> expandedGroup : expandedGroups ) {
 
-				if ( !constraintDescriptor.getGroups().contains( expandedGroup ) ) {
-					continue;
-				}
-
-				ConstraintContextImpl contextImpl = new ConstraintContextImpl( constraintDescriptor );
-				if ( !getConstraint( constraintDescriptor ).isValid( object, contextImpl ) ) {
-					for ( ConstraintContextImpl.ErrorMessage error : contextImpl.getErrorMessages() ) {
-						final String message = error.getMessage();
-						String interpolatedMessage = messageResolver.interpolate(
-								message,
-								constraintDescriptor,
-								object
-						);
-						ConstraintViolationImpl<T> failingConstraintViolation = new ConstraintViolationImpl<T>(
-								message,
-								interpolatedMessage,
-								null,
-								null,
-								null,
-								object,
-								propertyIter.getOriginalProperty(),  //FIXME use error.getProperty()
-								null,
-								//FIXME why is this a null group!! Used to be "" string should it be Default. Looks weird
-								constraintDescriptor
-						);
-						addFailingConstraint( failingConstraintViolations, failingConstraintViolation );
+				for ( MetaConstraint metaConstraint : metaConstraints ) {
+					if ( !metaConstraint.getDescriptor().getGroups().contains( expandedGroup ) ) {
+						continue;
 					}
+
+					ValidationContext<T> context = new ValidationContext<T>( ( T ) value );
+					context.pushProperty( propertyIter.getOriginalProperty() );
+					metaConstraint.validateConstraint( beanType, value, context );
+					failingConstraintViolations.addAll( context.getFailingConstraints() );
 				}
 
 				if ( isGroupSequence && failingConstraintViolations.size() > 0 ) {
@@ -427,7 +325,7 @@
 	}
 
 	/**
-	 * Returns the constraint descriptor for the given path relative to the specified validator.
+	 * Collects all <code>MetaConstraint</code>s which match the given path relative to the specified root class.
 	 * <p>
 	 * This method does not traverse an actual object, but rather tries to resolve the porperty generically.
 	 * </p>
@@ -438,20 +336,16 @@
 	 *
 	 * @param clazz the class type to check for constraints.
 	 * @param propertyIter an instance of <code>PropertyIterator</code>
-	 *
-	 * @return The constraint descriptor matching the given path.
+	 * @param metaConstraints Set of <code>MetaConstraint</code>s to collect all matching constraints.
 	 */
-	private ConstraintDescriptor getConstraintDescriptorForPath(Class<?> clazz, PropertyIterator propertyIter) {
-
-		ConstraintDescriptor matchingConstraintDescriptor = null;
+	private void getMetaConstraintsForPath(Class<?> clazz, PropertyIterator propertyIter, Set<MetaConstraint> metaConstraints) {
 		propertyIter.split();
 
 		if ( !propertyIter.hasNext() ) {
 			List<MetaConstraint> metaConstraintList = factory.getBeanMetaData( clazz ).geMetaConstraintList();
 			for ( MetaConstraint metaConstraint : metaConstraintList ) {
-				ConstraintDescriptor constraintDescriptor = metaConstraint.getDescriptor();
 				if ( metaConstraint.getPropertyName().equals( propertyIter.getHead() ) ) {
-					matchingConstraintDescriptor = constraintDescriptor;
+					metaConstraints.add( metaConstraint );
 				}
 			}
 		}
@@ -467,67 +361,12 @@
 							continue;
 						}
 					}
-
-					matchingConstraintDescriptor = getConstraintDescriptorForPath( ( Class<?> ) type, propertyIter );
+					getMetaConstraintsForPath( ( Class<?> ) type, propertyIter, metaConstraints );
 				}
 			}
 		}
-
-		return matchingConstraintDescriptor;
 	}
 
-
-	private DesrciptorValueWrapper getConstraintDescriptorAndValueForPath(Class<?> clazz, PropertyIterator propertyIter, Object value) {
-
-		DesrciptorValueWrapper wrapper = null;
-		propertyIter.split();
-
-
-		// bottom out - there is only one token left
-		if ( !propertyIter.hasNext() ) {
-			List<MetaConstraint> metaConstraintList = factory.getBeanMetaData( clazz ).geMetaConstraintList();
-			for ( MetaConstraint metaConstraint : metaConstraintList ) {
-				ConstraintDescriptor constraintDescriptor = metaConstraint.getDescriptor();
-				if ( metaConstraint.getPropertyName().equals( propertyIter.getHead() ) ) {
-					return new DesrciptorValueWrapper(
-							constraintDescriptor, metaConstraint.getValue( value )
-					);
-				}
-			}
-		}
-		else {
-			List<Member> cascadedMembers = factory.getBeanMetaData( clazz ).getCascadedMembers();
-			for ( Member m : cascadedMembers ) {
-				if ( ReflectionHelper.getPropertyName( m ).equals( propertyIter.getHead() ) ) {
-					Object newValue;
-					if ( propertyIter.isIndexed() ) {
-						newValue = ReflectionHelper.getValue( m, value );
-					}
-					else {
-						newValue = ReflectionHelper.getIndexedValue( value, propertyIter.getIndex() );
-					}
-					wrapper = getConstraintDescriptorAndValueForPath(
-							newValue.getClass(), propertyIter, newValue
-					);
-				}
-			}
-		}
-
-		return wrapper;
-	}
-
-
-	private <T> void addFailingConstraint(List<ConstraintViolationImpl<T>> failingConstraintViolations, ConstraintViolationImpl<T> failingConstraintViolation) {
-		int i = failingConstraintViolations.indexOf( failingConstraintViolation );
-		if ( i == -1 ) {
-			failingConstraintViolations.add( failingConstraintViolation );
-		}
-		else {
-			failingConstraintViolations.get( i ).addGroups( failingConstraintViolation.getGroups() );
-		}
-	}
-
-
 	/**
 	 * Checks whether the provided group name is a group sequence and if so expands the group name and add the expanded
 	 * groups names to <code>expandedGroupName</code>.
@@ -556,33 +395,4 @@
 		}
 		return isGroupSequence;
 	}
-
-	private Constraint getConstraint(ConstraintDescriptor descriptor) {
-		Constraint constraint;
-		try {
-			//unchecked
-			constraint = constraintFactory.getInstance( descriptor.getConstraintClass() );
-		}
-		catch ( RuntimeException e ) {
-			throw new ValidationException( "Unable to instantiate " + descriptor.getConstraintClass(), e );
-		}
-
-		try {
-			constraint.initialize( descriptor.getAnnotation() );
-		}
-		catch ( RuntimeException e ) {
-			throw new ValidationException( "Unable to intialize " + constraint.getClass().getName(), e );
-		}
-		return constraint;
-	}
-
-	private class DesrciptorValueWrapper {
-		final ConstraintDescriptor descriptor;
-		final Object value;
-
-		DesrciptorValueWrapper(ConstraintDescriptor descriptor, Object value) {
-			this.descriptor = descriptor;
-			this.value = value;
-		}
-	}
 }

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorBuilderImpl.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorBuilderImpl.java	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorBuilderImpl.java	2009-01-13 14:46:34 UTC (rev 15773)
@@ -1,10 +1,9 @@
 package org.hibernate.validation.impl;
 
-import javax.validation.ValidatorBuilder;
 import javax.validation.MessageResolver;
 import javax.validation.TraversableResolver;
 import javax.validation.Validator;
-import javax.validation.ConstraintFactory;
+import javax.validation.ValidatorBuilder;
 
 import org.hibernate.validation.engine.ValidatorImpl;
 
@@ -17,16 +16,13 @@
 	private final MessageResolver factoryMessageResolver;
 	private final TraversableResolver factoryTraversableResolver;
 	private final ValidatorFactoryImpl validatorFactory;
-	private final ConstraintFactory constraintFactory;
 
 	public ValidatorBuilderImpl(ValidatorFactoryImpl validatorFactory,
 								MessageResolver factoryMessageResolver,
-								TraversableResolver factoryTraversableResolver,
-								ConstraintFactory constraintFactory) {
+								TraversableResolver factoryTraversableResolver) {
 		this.validatorFactory = validatorFactory;
 		this.factoryMessageResolver = factoryMessageResolver;
 		this.factoryTraversableResolver = factoryTraversableResolver;
-		this.constraintFactory = constraintFactory;
 		messageResolver(factoryMessageResolver);
 		traversableResolver(factoryTraversableResolver);
 	}
@@ -52,6 +48,6 @@
 	}
 
 	public Validator getValidator() {
-		return new ValidatorImpl( validatorFactory, messageResolver, traversableResolver, constraintFactory );
+		return new ValidatorImpl( validatorFactory );
 	}
 }

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorFactoryImpl.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorFactoryImpl.java	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/impl/ValidatorFactoryImpl.java	2009-01-13 14:46:34 UTC (rev 15773)
@@ -63,7 +63,7 @@
 	}
 
 	public ValidatorBuilder defineValidatorState() {
-		return new ValidatorBuilderImpl(this, messageResolver, traversableResolver, constraintFactory);
+		return new ValidatorBuilderImpl(this, messageResolver, traversableResolver);
 	}
 
 	public <T> BeanMetaDataImpl<T> getBeanMetaData(Class<T> beanClass) {
@@ -72,7 +72,7 @@
 		@SuppressWarnings( "unchecked")
 		BeanMetaDataImpl<T> metadata = ( BeanMetaDataImpl<T> ) metadataProviders.get(beanClass);
 		if (metadata == null) {
-			metadata = new BeanMetaDataImpl<T>(beanClass);
+			metadata = new BeanMetaDataImpl<T>(beanClass, messageResolver, constraintFactory);
 			metadataProviders.put( beanClass, metadata );
 		}
 		return metadata;

Modified: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/constraints/composition/GermanZipcode.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/constraints/composition/GermanZipcode.java	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/constraints/composition/GermanZipcode.java	2009-01-13 14:46:34 UTC (rev 15773)
@@ -25,6 +25,7 @@
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 import java.lang.annotation.Target;
 import javax.validation.ConstraintValidator;
+import javax.validation.ReportAsViolationFromCompositeConstraint;
 
 /**
  * Constraint used to test nested composing constraints.
@@ -36,6 +37,7 @@
 @Documented
 @Target({ METHOD, FIELD, TYPE })
 @Retention(RUNTIME)
+ at ReportAsViolationFromCompositeConstraint
 public @interface GermanZipcode {
 	public abstract String message() default "Falsche Postnummer.";
 

Modified: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ConstraintCompositionTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ConstraintCompositionTest.java	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ConstraintCompositionTest.java	2009-01-13 14:46:34 UTC (rev 15773)
@@ -24,15 +24,15 @@
 import static org.junit.Assert.fail;
 import org.junit.Test;
 
-import static org.hibernate.validation.util.TestUtil.getValidator;
-import static org.hibernate.validation.util.TestUtil.assertNumberOfViolations;
-import static org.hibernate.validation.util.TestUtil.assertConstraintViolation;
-
 import org.hibernate.validation.constraints.NotNullConstraint;
+import org.hibernate.validation.constraints.PatternConstraint;
 import org.hibernate.validation.constraints.SizeConstraint;
-import org.hibernate.validation.constraints.PatternConstraint;
+import org.hibernate.validation.constraints.composition.GermanZipcodeConstraint;
 import org.hibernate.validation.eg.FrenchAddress;
 import org.hibernate.validation.eg.GermanAddress;
+import static org.hibernate.validation.util.TestUtil.assertConstraintViolation;
+import static org.hibernate.validation.util.TestUtil.assertNumberOfViolations;
+import static org.hibernate.validation.util.TestUtil.getValidator;
 
 /**
  * Tests for composing constraints.
@@ -147,11 +147,33 @@
 		assertNumberOfViolations( constraintViolations, 1 );
 		assertConstraintViolation(
 				constraintViolations.iterator().next(),
-				"may not be null",
-				NotNullConstraint.class,
+				"Falsche Postnummer.",
+				GermanZipcodeConstraint.class,
 				GermanAddress.class,
 				null,
 				"zipCode"
 		);
 	}
+
+	@Test
+	public void testOnlySingleConstraintViolation() {
+		Validator validator = getValidator();
+
+		GermanAddress address = new GermanAddress();
+		address.setAddressline1( "Rathausstrasse 5" );
+		address.setAddressline2( "3ter Stock" );
+		address.setCity( "Karlsruhe" );
+		address.setZipCode( "abc" );
+		// actually three composing constraints fail, but due to @ReportAsViolationFromCompositeConstraint only one will be reported.
+		Set<ConstraintViolation<GermanAddress>> constraintViolations = validator.validate( address );
+		assertNumberOfViolations( constraintViolations, 1 );
+		assertConstraintViolation(
+				constraintViolations.iterator().next(),
+				"Falsche Postnummer.",
+				GermanZipcodeConstraint.class,
+				GermanAddress.class,
+				"abc",
+				"zipCode"
+		);
+	}
 }
\ No newline at end of file

Modified: validator/trunk/hibernate-validator/src/test/resources/log4j.properties
===================================================================
--- validator/trunk/hibernate-validator/src/test/resources/log4j.properties	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/hibernate-validator/src/test/resources/log4j.properties	2009-01-13 14:46:34 UTC (rev 15773)
@@ -20,6 +20,7 @@
 ### set log levels - for more verbose logging change 'info' to 'debug' ###
 log4j.rootLogger=debug, stdout
 
-log4j.logger.org.hibernate.validation.engine.ValidatorImpl=trace
+log4j.logger.org.hibernate.validation.engine.ValidatorImpl=debug
+log4j.logger.org.hibernate.validation.engine.ConstraintTree=trace
 org.hibernate.validation.impl.ResourceBundleMessageResolver=info
 

Modified: validator/trunk/pom.xml
===================================================================
--- validator/trunk/pom.xml	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/pom.xml	2009-01-13 14:46:34 UTC (rev 15773)
@@ -59,6 +59,11 @@
                 <artifactId>validation-api</artifactId>
                 <version>${version}</version>
             </dependency>
+            <!--dependency>
+                <groupId>com.google.code.guice</groupId>
+                <artifactId>guice</artifactId>
+                <version>1.0</version>
+            </dependency-->
             <dependency>
                 <groupId>org.slf4j</groupId>
                 <artifactId>slf4j-api</artifactId>

Modified: validator/trunk/validation-api/src/main/java/javax/validation/Validator.java
===================================================================
--- validator/trunk/validation-api/src/main/java/javax/validation/Validator.java	2009-01-12 22:07:51 UTC (rev 15772)
+++ validator/trunk/validation-api/src/main/java/javax/validation/Validator.java	2009-01-13 14:46:34 UTC (rev 15773)
@@ -17,7 +17,6 @@
 */
 package javax.validation;
 
-import java.io.Serializable;
 import java.util.Set;
 
 /**
@@ -30,7 +29,7 @@
  */
 public interface Validator {
 	/**
-	 * validate all constraints on object
+	 * Validates all constraints on object.
 	 *
 	 * @param object object to validate
 	 * @param groups groups targeted for validation
@@ -43,7 +42,7 @@
 	<T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);
 
 	/**
-	 * validate all constraints on &lt;code&gt;propertyName&lt;/code&gt; property of object
+	 * Validates all constraints on &lt;code&gt;propertyName&lt;/code&gt; property of object
 	 *
 	 * @param object object to validate
 	 * @param propertyName property to validate (ie field and getter constraints)
@@ -59,11 +58,12 @@
 													 Class<?>... groups);
 
 	/**
-	 * validate all constraints on <code>propertyName</code> property
+	 * Validates all constraints on <code>propertyName</code> property
 	 * if the property value is <code>value</code>
 	 * <p/>
 	 * TODO express limitations of ConstraintViolation in this case
 	 *
+	 * @param beanType the bean type
 	 * @param propertyName property to validate
 	 * @param value property value to validate
 	 * @param groups groups targeted for validation
@@ -83,6 +83,7 @@
      * are immutable.
 	 *
 	 * @param clazz class type evaluated
+	 * @return the bean descriptor for the specified class.
 	 */
 	BeanDescriptor getConstraintsForClass(Class<?> clazz);
 }




More information about the hibernate-commits mailing list