Author: hardy.ferentschik
Date: 2009-03-18 07:08:10 -0400 (Wed, 18 Mar 2009)
New Revision: 16179
Modified:
validator/trunk/hibernate-validator/pom.xml
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ExecutionContext.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/ValidatorImpl.java
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/IdentitySet.java
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/graphnavigation/GraphNavigationTest.java
validator/trunk/hibernate-validator/src/test/resources/log4j.properties
Log:
HV-126 added support for one validation per group AND path
Modified: validator/trunk/hibernate-validator/pom.xml
===================================================================
--- validator/trunk/hibernate-validator/pom.xml 2009-03-18 10:28:42 UTC (rev 16178)
+++ validator/trunk/hibernate-validator/pom.xml 2009-03-18 11:08:10 UTC (rev 16179)
@@ -22,7 +22,7 @@
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
- <version>1.0.CR1-SNAPSHOT</version>
+ <version>1.0.CR1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
Modified:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ExecutionContext.java
===================================================================
---
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ExecutionContext.java 2009-03-18
10:28:42 UTC (rev 16178)
+++
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ExecutionContext.java 2009-03-18
11:08:10 UTC (rev 16179)
@@ -19,8 +19,11 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.Stack;
import javax.validation.ConstraintDescriptor;
import javax.validation.ConstraintValidatorContext;
@@ -51,12 +54,17 @@
private final T rootBean;
/**
- * Maps for each group to an identity set to keep track of already validated objects. We
have to make sure
- * that each object gets only validated once (per group).
+ * Maps a group to an identity set to keep track of already validated objects. We have
to make sure
+ * that each object gets only validated once per group and property path.
*/
private final Map<Class<?>, IdentitySet> processedObjects;
/**
+ * Maps an object to a list of paths in which it has been invalidated.
+ */
+ private final Map<Object, Set<String>> processedPaths;
+
+ /**
* A list of all failing constraints so far.
*/
private final List<ConstraintViolationImpl<T>> failingConstraintViolations;
@@ -83,6 +91,13 @@
private Stack<Object> beanStack = new Stack<Object>();
/**
+ * Flag indicating whether an object can only be validated once per group or once per
group AND validation path.
+ *
+ * @todo Make this boolean a configurable item.
+ */
+ private boolean allowOneValidationPerPath = true;
+
+ /**
* The message resolver which should be used in this context.
*/
private final MessageInterpolator messageInterpolator;
@@ -109,6 +124,7 @@
beanStack.push( object );
processedObjects = new HashMap<Class<?>, IdentitySet>();
+ processedPaths = new IdentityHashMap<Object, Set<String>>();
propertyPath = new ArrayList<String>();
failingConstraintViolations = new ArrayList<ConstraintViolationImpl<T>>();
}
@@ -168,31 +184,42 @@
public void setCurrentGroup(Class<?> currentGroup) {
this.currentGroup = currentGroup;
+ markProcessed();
}
- public void markProcessedForCurrentGroup() {
- if ( processedObjects.containsKey( currentGroup ) ) {
- processedObjects.get( currentGroup ).add( beanStack.peek() );
+ /**
+ * Returns <code>true</code> if the specified value has already been
validated, <code>false</code> otherwise.
+ * Each object can only be validated once per group and validation path. The flag {@link
#allowOneValidationPerPath}
+ * determines whether an object can only be validated once per group or once per group
and validation path.�
+ *
+ * @param value The value to be validated.
+ *
+ * @return Returns <code>true</code> if the specified value has already been
validated, <code>false</code> otherwise.
+ */
+ public boolean isAlreadyValidated(Object value) {
+ boolean alreadyValidated;
+ alreadyValidated = isAlreadyValidatedForCurrentGroup( value );
+
+ if ( alreadyValidated && allowOneValidationPerPath ) {
+ alreadyValidated = isAlreadyValidatedForPath( value );
}
- else {
- IdentitySet set = new IdentitySet();
- set.add( beanStack.peek() );
- processedObjects.put( currentGroup, set );
+ return alreadyValidated;
+ }
+
+ private boolean isAlreadyValidatedForPath(Object value) {
+ for ( String path : processedPaths.get( value ) ) {
+ if ( path.contains( peekPropertyPath() ) || peekPropertyPath().contains( path ) ) {
+ return true;
+ }
}
+ return false;
}
- public boolean isValidatedAgainstCurrentGroup(Object value) {
+ private boolean isAlreadyValidatedForCurrentGroup(Object value) {
final IdentitySet objectsProcessedInCurrentGroups = processedObjects.get( currentGroup
);
return objectsProcessedInCurrentGroups != null &&
objectsProcessedInCurrentGroups.contains( value );
}
- private void addConstraintFailure(ConstraintViolationImpl<T>
failingConstraintViolation) {
- int i = failingConstraintViolations.indexOf( failingConstraintViolation );
- if ( i == -1 ) {
- failingConstraintViolations.add( failingConstraintViolation );
- }
- }
-
public void addConstraintFailures(List<ConstraintViolationImpl<T>>
failingConstraintViolations) {
for ( ConstraintViolationImpl<T> violation : failingConstraintViolations ) {
addConstraintFailure( violation );
@@ -226,15 +253,16 @@
public void markCurrentPropertyAsIndexed() {
String property = peekProperty();
property += "[]";
- popProperty();
+ propertyPath.remove( propertyPath.size() - 1 );
pushProperty( property );
}
- public void replacePropertyIndex(String index) {
+ public void setPropertyIndex(String index) {
+ String property = peekProperty();
+
// replace the last occurance of [<oldIndex>] with [<index>]
- String property = peekProperty();
property = property.replaceAll( "\\[[0-9]*\\]$", "[" + index +
"]" );
- popProperty();
+ propertyPath.remove( propertyPath.size() - 1 );
pushProperty( property );
}
@@ -297,12 +325,48 @@
);
}
+ private void markProcessed() {
+ markProcessForCurrentGroup();
+ if ( allowOneValidationPerPath ) {
+ markProcessedForCurrentPath();
+ }
+ }
+
+ private void markProcessedForCurrentPath() {
+ if ( processedPaths.containsKey( peekCurrentBean() ) ) {
+ processedPaths.get( peekCurrentBean() ).add( peekPropertyPath() );
+ }
+ else {
+ Set<String> set = new HashSet<String>();
+ set.add( peekPropertyPath() );
+ processedPaths.put( peekCurrentBean(), set );
+ }
+ }
+
+ private void markProcessForCurrentGroup() {
+ if ( processedObjects.containsKey( currentGroup ) ) {
+ processedObjects.get( currentGroup ).add( peekCurrentBean() );
+ }
+ else {
+ IdentitySet set = new IdentitySet();
+ set.add( peekCurrentBean() );
+ processedObjects.put( currentGroup, set );
+ }
+ }
+
+ private void addConstraintFailure(ConstraintViolationImpl<T>
failingConstraintViolation) {
+ int i = failingConstraintViolations.indexOf( failingConstraintViolation );
+ if ( i == -1 ) {
+ failingConstraintViolations.add( failingConstraintViolation );
+ }
+ }
+
class ValidatedProperty {
private final List<ErrorMessage> errorMessages = new
ArrayList<ErrorMessage>( 3 );
private final String property;
private final String propertyParent;
- private ConstraintDescriptor constraintDescriptor;
+ private ConstraintDescriptor<?> constraintDescriptor;
private boolean defaultDisabled;
@@ -311,11 +375,11 @@
this.propertyParent = propertyParent;
}
- public void setConstraintDescriptor(ConstraintDescriptor constraintDescriptor) {
+ public void setConstraintDescriptor(ConstraintDescriptor<?> constraintDescriptor)
{
this.constraintDescriptor = constraintDescriptor;
}
- public ConstraintDescriptor getConstraintDescriptor() {
+ public ConstraintDescriptor<?> getConstraintDescriptor() {
return constraintDescriptor;
}
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-03-18
10:28:42 UTC (rev 16178)
+++
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaConstraint.java 2009-03-18
11:08:10 UTC (rev 16179)
@@ -21,7 +21,6 @@
import java.lang.annotation.ElementType;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
-import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
@@ -46,11 +45,6 @@
private final ConstraintTree<A> constraintTree;
/**
- * The type the constraint was defined on.
- */
- private final Type type;
-
- /**
* The member the constraint was defined on.
*/
private final Member member;
@@ -71,12 +65,11 @@
*/
private final Class<T> beanClass;
- public MetaConstraint(Type type, ConstraintDescriptor<A> constraintDescriptor) {
- this.type = type;
+ public MetaConstraint(Class<T> beanClass, ConstraintDescriptor<A>
constraintDescriptor) {
this.elementType = ElementType.TYPE;
this.member = null;
this.propertyName = "";
- this.beanClass = ( Class<T> ) type.getClass();
+ this.beanClass = beanClass;
constraintTree = new ConstraintTree<A>( constraintDescriptor );
}
@@ -90,7 +83,6 @@
else {
throw new IllegalArgumentException( "Non allowed member type: " + member );
}
- this.type = null;
this.member = member;
this.propertyName = ReflectionHelper.getPropertyName( member );
this.beanClass = beanClass;
@@ -170,7 +162,7 @@
Type t;
switch ( elementType ) {
case TYPE: {
- t = type;
+ t = beanClass;
break;
}
default: {
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-03-18
10:28:42 UTC (rev 16178)
+++
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java 2009-03-18
11:08:10 UTC (rev 16179)
@@ -201,7 +201,7 @@
validateCascadedConstraints( context );
}
- // process sequences depth-first to guarantee that groups following a violation within
a group won't get executed.
+ // process group sequences depth-first to guarantee that groups following a violation
within a group won't get executed.
Iterator<List<Group>> sequenceIterator = groupChain.getSequenceIterator();
while ( sequenceIterator.hasNext() ) {
List<Group> sequence = sequenceIterator.next();
@@ -270,7 +270,6 @@
}
executionContext.popProperty();
}
- executionContext.markProcessedForCurrentGroup();
return validationSuccessful;
}
@@ -339,14 +338,11 @@
actualValue = ( ( Map.Entry ) actualValue ).getValue();
}
- if ( !context.isValidatedAgainstCurrentGroup( actualValue ) ) {
- context.replacePropertyIndex( propertyIndex );
-
+ if ( !context.isAlreadyValidated( actualValue ) ) {
+ context.setPropertyIndex( propertyIndex );
context.pushCurrentBean( actualValue );
- validateInContext(
- context,
- groupChainGenerator.getGroupChainFor( Arrays.asList( new Class<?>[] {
context.getCurrentGroup() } ) )
- );
+ GroupChain groupChain = groupChainGenerator.getGroupChainFor( Arrays.asList( new
Class<?>[] { context.getCurrentGroup() } ) );
+ validateInContext( context, groupChain );
context.popCurrentBean();
}
i++;
Modified:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/IdentitySet.java
===================================================================
---
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/IdentitySet.java 2009-03-18
10:28:42 UTC (rev 16178)
+++
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/IdentitySet.java 2009-03-18
11:08:10 UTC (rev 16179)
@@ -24,7 +24,7 @@
import java.util.Set;
/**
- * Set that compares object by identity rather than equality.
+ * Set that compares object by identity rather than equality. Wraps around a
<code>IdentityHashMap</code>
*
* @author Emmanuel Bernard
*/
@@ -104,4 +104,11 @@
public Object[] toArray(Object[] a) {
return map.keySet().toArray( a );
}
+
+ @Override
+ public String toString() {
+ return "IdentitySet{" +
+ "map=" + map +
+ '}';
+ }
}
Modified:
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/graphnavigation/GraphNavigationTest.java
===================================================================
---
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/graphnavigation/GraphNavigationTest.java 2009-03-18
10:28:42 UTC (rev 16178)
+++
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/graphnavigation/GraphNavigationTest.java 2009-03-18
11:08:10 UTC (rev 16179)
@@ -1,4 +1,4 @@
-// $Id:$
+// $Id$
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
@@ -58,6 +58,6 @@
Validator validator = TestUtil.getValidator();
Set<ConstraintViolation<Order>> constraintViolations = validator.validate(
order );
- assertEquals( "Wrong number of constraints", 1, constraintViolations.size()
);
+ assertEquals( "Wrong number of constraints", 3, constraintViolations.size()
);
}
}
Modified: validator/trunk/hibernate-validator/src/test/resources/log4j.properties
===================================================================
--- validator/trunk/hibernate-validator/src/test/resources/log4j.properties 2009-03-18
10:28:42 UTC (rev 16178)
+++ validator/trunk/hibernate-validator/src/test/resources/log4j.properties 2009-03-18
11:08:10 UTC (rev 16179)
@@ -21,6 +21,5 @@
log4j.rootLogger=debug, stdout
log4j.logger.org.hibernate.validation.engine.ValidatorImpl=trace
-log4j.logger.org.hibernate.validation.engine.ConstraintTree=trace
-org.hibernate.validation.engine.ResourceBundleMessageInterpolator=info
-
+#log4j.logger.org.hibernate.validation.engine.ConstraintTree=trace
+log4j.logger.org.hibernate.validation.engine.ResourceBundleMessageInterpolator=info