Author: hardy.ferentschik
Date: 2009-04-07 16:20:12 -0400 (Tue, 07 Apr 2009)
New Revision: 16271
Added:
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/xml/TestGroup.java
Modified:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/BeanMetaData.java
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/BeanMetaDataImpl.java
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorFactoryImpl.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/ReflectionHelper.java
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/xml/XmlConfigurationTest.java
validator/trunk/hibernate-validator/src/test/resources/META-INF/validation/user-constraints.xml
Log:
HV-112
Parsing and applying class, field and property level constraints.
Modified:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/BeanMetaData.java
===================================================================
---
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/BeanMetaData.java 2009-04-07
17:40:07 UTC (rev 16270)
+++
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/BeanMetaData.java 2009-04-07
20:20:12 UTC (rev 16271)
@@ -33,12 +33,6 @@
public interface BeanMetaData<T> {
/**
- * Initalises the given meta data bean taken its current state into account. An instance
of <code>BeanMetaData</code>
- * won't have any useful state information prior to calling this method.
- */
- void init();
-
- /**
* @return the class of the bean.
*/
Class<T> getBeanClass();
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-04-07
17:40:07 UTC (rev 16270)
+++
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/BeanMetaDataImpl.java 2009-04-07
20:20:12 UTC (rev 16271)
@@ -93,9 +93,6 @@
public BeanMetaDataImpl(Class<T> beanClass, ConstraintHelper constraintHelper) {
this.beanClass = beanClass;
this.constraintHelper = constraintHelper;
- }
-
- public void init() {
createMetaData();
}
Modified:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorFactoryImpl.java
===================================================================
---
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorFactoryImpl.java 2009-04-07
17:40:07 UTC (rev 16270)
+++
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorFactoryImpl.java 2009-04-07
20:20:12 UTC (rev 16271)
@@ -24,6 +24,7 @@
import java.lang.reflect.Array;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
+import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
@@ -113,8 +114,7 @@
*/
public ValidatorContext usingContext() {
return new ValidatorContextImpl(
- constraintValidatorFactory, messageInterpolator, traversableResolver,
- constraintHelper
+ constraintValidatorFactory, messageInterpolator, traversableResolver,
constraintHelper
);
}
@@ -143,8 +143,8 @@
Boolean ignoreAnnotations = bean.isIgnoreAnnotations() == null ? false :
bean.isIgnoreAnnotations();
ignoreAnnotationDefaults.put( beanClass, ignoreAnnotations );
parseClassLevelOverrides( bean.getClassType(), beanClass, defaultPackage );
- parseFieldLevelOverrides( bean.getField() );
- parsePropertyLevelOverrides( bean.getGetter() );
+ parseFieldLevelOverrides( bean.getField(), beanClass, defaultPackage );
+ parsePropertyLevelOverrides( bean.getGetter(), beanClass, defaultPackage );
processedClasses.add( beanClass );
}
}
@@ -165,15 +165,39 @@
}
}
- private void parseFieldLevelOverrides(List<FieldType> fields) {
- for ( FieldType field : fields ) {
- boolean ignoreFieldAnnotation = field.isIgnoreAnnotations() == null ? false :
field.isIgnoreAnnotations();
+ private void parseFieldLevelOverrides(List<FieldType> fields, Class<?>
beanClass, String defaultPackage) {
+ for ( FieldType fieldType : fields ) {
+ String fieldName = fieldType.getName();
+ if ( !ReflectionHelper.containsField( beanClass, fieldName ) ) {
+ throw new ValidationException( beanClass.getName() + " does not contain the
fieldType " + fieldName );
+ }
+ Field field = ReflectionHelper.getField( beanClass, fieldName );
+ boolean ignoreFieldAnnotation = fieldType.isIgnoreAnnotations() == null ? false :
fieldType.isIgnoreAnnotations();
+ if ( ignoreFieldAnnotation ) {
+ ignoreAnnotationOnMember.put( beanClass, null );
+ }
+ for ( ConstraintType constraint : fieldType.getConstraint() ) {
+ MetaConstraint<?, ?> metaConstraint = createMetaConstraint( constraint,
beanClass, field, defaultPackage );
+ addMetaConstraint( beanClass, metaConstraint );
+ }
}
}
- private void parsePropertyLevelOverrides(List<GetterType> getters) {
+ private void parsePropertyLevelOverrides(List<GetterType> getters, Class<?>
beanClass, String defaultPackage) {
for ( GetterType getter : getters ) {
+ String getterName = getter.getName();
+ if ( !ReflectionHelper.containsMethod( beanClass, getterName ) ) {
+ throw new ValidationException( beanClass.getName() + " does not contain the
property " + getterName );
+ }
+ Method method = ReflectionHelper.getMethod( beanClass, getterName );
boolean ignoreGetterAnnotation = getter.isIgnoreAnnotations() == null ? false :
getter.isIgnoreAnnotations();
+ if ( ignoreGetterAnnotation ) {
+ ignoreAnnotationOnMember.put( beanClass, null );
+ }
+ for ( ConstraintType constraint : getter.getConstraint() ) {
+ MetaConstraint<?, ?> metaConstraint = createMetaConstraint( constraint,
beanClass, method, defaultPackage );
+ addMetaConstraint( beanClass, metaConstraint );
+ }
}
}
@@ -186,7 +210,7 @@
ignoreAnnotationOnClass.add( beanClass );
}
for ( ConstraintType constraint : classType.getConstraint() ) {
- MetaConstraint<?, ?> metaConstraint = createMetaConstraint( constraint,
beanClass, defaultPackage );
+ MetaConstraint<?, ?> metaConstraint = createMetaConstraint( constraint,
beanClass, null, defaultPackage );
addMetaConstraint( beanClass, metaConstraint );
}
}
@@ -202,7 +226,7 @@
}
}
- private <A extends Annotation, T> MetaConstraint<?, ?>
createMetaConstraint(ConstraintType constraint, Class<T> beanClass, String
defaultPackage) {
+ private <A extends Annotation, T> MetaConstraint<?, ?>
createMetaConstraint(ConstraintType constraint, Class<T> beanClass, Member member,
String defaultPackage) {
@SuppressWarnings("unchecked")
Class<A> annotationClass = ( Class<A> ) getClass(
constraint.getAnnotation(), defaultPackage );
AnnotationDescriptor<A> annotationDescriptor = new AnnotationDescriptor<A>(
annotationClass );
@@ -224,7 +248,15 @@
ConstraintDescriptorImpl<A> constraintDescriptor = new
ConstraintDescriptorImpl<A>(
annotation, new Class[] { }, constraintHelper
);
- return new MetaConstraint<T, A>( beanClass, constraintDescriptor );
+
+ MetaConstraint<T, A> metaConstraint;
+ if ( member == null ) {
+ metaConstraint = new MetaConstraint<T, A>( beanClass, constraintDescriptor );
+ }
+ else {
+ metaConstraint = new MetaConstraint<T, A>( member, beanClass,
constraintDescriptor );
+ }
+ return metaConstraint;
}
private void checkNameIsValid(String name) {
@@ -423,7 +455,6 @@
for ( MetaConstraint<?, ?> metaConstraint : entry.getValue() ) {
metaData.addMetaConstraint( metaConstraint );
}
- metaData.init();
BeanMetaDataCache.addBeanMetaData( entry.getKey(), metaData );
}
}
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-04-07
17:40:07 UTC (rev 16270)
+++
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java 2009-04-07
20:20:12 UTC (rev 16271)
@@ -554,7 +554,6 @@
BeanMetaDataImpl<T> metadata = BeanMetaDataCache.getBeanMetaData( beanClass );
if ( metadata == null ) {
metadata = new BeanMetaDataImpl<T>( beanClass, constraintHelper );
- metadata.init();
BeanMetaDataCache.addBeanMetaData( beanClass, metadata );
}
return metadata;
Modified:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ReflectionHelper.java
===================================================================
---
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ReflectionHelper.java 2009-04-07
17:40:07 UTC (rev 16270)
+++
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ReflectionHelper.java 2009-04-07
20:20:12 UTC (rev 16271)
@@ -50,24 +50,6 @@
private ReflectionHelper() {
}
-
- /**
- * Checks whether the given annotation is a builtin constraint annotation defined as
defined by the specs.
- *
- * @param annotation the annotation to check
- *
- * @return <code>true</code> if the annotation is a builtin constraint,
<code>false</code> otherwise.
- */
- public static boolean isBuiltInConstraintAnnotation(Annotation annotation) {
- boolean isBuiltInConstraintAnnotation = false;
-
- String packageName = annotation.annotationType().getPackage().getName();
- if ( "javax.validation.constraints".equals( packageName ) ) {
- isBuiltInConstraintAnnotation = true;
- }
- return isBuiltInConstraintAnnotation;
- }
-
@SuppressWarnings("unchecked")
public static <T> T getAnnotationParameter(Annotation annotation, String
parameterName, Class<T> type) {
try {
@@ -269,10 +251,7 @@
}
if ( type instanceof WildcardType ) {
Type[] upperBounds = ( ( WildcardType ) type ).getUpperBounds();
- if ( upperBounds.length == 0 ) {
- return false;
- }
- return isIterable( upperBounds[0] );
+ return upperBounds.length != 0 && isIterable( upperBounds[0] );
}
return false;
}
@@ -291,10 +270,7 @@
}
if ( type instanceof WildcardType ) {
Type[] upperBounds = ( ( WildcardType ) type ).getUpperBounds();
- if ( upperBounds.length == 0 ) {
- return false;
- }
- return isMap( upperBounds[0] );
+ return upperBounds.length != 0 && isMap( upperBounds[0] );
}
return false;
}
@@ -357,7 +333,7 @@
}
/**
- * Checks whether the specified class contains a field or member which matches a given
property.
+ * Checks whether the specified class contains a field or property matching the given
name.
*
* @param clazz The class to check.
* @param property The property name.
@@ -366,24 +342,83 @@
* false</code> otherwise.
*/
public static boolean containsMember(Class<?> clazz, String property) {
+ return containsField( clazz, property ) || containsMethod( clazz, property );
+ }
+
+ /**
+ * Checks whether the specified class contains a field matching the specified name.
+ *
+ * @param clazz The class to check.
+ * @param fieldName The field name.
+ *
+ * @return Returns <code>true</code> if the cass contains a field for the
specified name, <code>
+ * false</code> otherwise.
+ */
+ public static boolean containsField(Class<?> clazz, String fieldName) {
try {
- clazz.getField( property );
+ clazz.getDeclaredField( fieldName );
return true;
}
catch ( NoSuchFieldException e ) {
- // ignore
+ return false;
}
+ }
+ /**
+ * Returns the field with the specified name or <code>null</code> if it does
not exist.
+ *
+ * @param clazz The class to check.
+ * @param fieldName The field name.
+ *
+ * @return Returns the field with the specified name or <code>null</code> if
it does not exist.
+ */
+ public static Field getField(Class<?> clazz, String fieldName) {
try {
- clazz.getMethod( "get" + property.substring( 0, 1 ).toUpperCase() +
property.substring( 1 ) );
+ Field field = clazz.getDeclaredField( fieldName );
+ setAccessibility( field );
+ return field;
+ }
+ catch ( NoSuchFieldException e ) {
+ return null;
+ }
+ }
+
+ /**
+ * Checks whether the specified class contains a method matching the specified name.
+ *
+ * @param clazz The class to check.
+ * @param methodName The method name.
+ *
+ * @return Returns <code>true</code> if the cass contains a property for the
specified name, <code>
+ * false</code> otherwise.
+ */
+ public static boolean containsMethod(Class<?> clazz, String methodName) {
+ try {
+ clazz.getMethod( "get" + methodName.substring( 0, 1 ).toUpperCase() +
methodName.substring( 1 ) );
return true;
}
catch ( NoSuchMethodException e ) {
- // ignore
+ return false;
}
- return false;
}
+ /**
+ * Returns the method with the specified name or <code>null</code> if it
does not exist.
+ *
+ * @param clazz The class to check.
+ * @param methodName The method name.
+ *
+ * @return Returns the method with the specified name or <code>null</code>
if it does not exist.
+ */
+ public static Method getMethod(Class<?> clazz, String methodName) {
+ try {
+ return clazz.getMethod( "get" + methodName.substring( 0, 1 ).toUpperCase() +
methodName.substring( 1 ) );
+ }
+ catch ( NoSuchMethodException e ) {
+ return null;
+ }
+ }
+
public static Class<?> classForName(String name, Class<?> caller) throws
ClassNotFoundException {
try {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Added:
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/xml/TestGroup.java
===================================================================
---
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/xml/TestGroup.java
(rev 0)
+++
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/xml/TestGroup.java 2009-04-07
20:20:12 UTC (rev 16271)
@@ -0,0 +1,24 @@
+// $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.xml;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public interface TestGroup {
+}
Modified:
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/xml/XmlConfigurationTest.java
===================================================================
---
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/xml/XmlConfigurationTest.java 2009-04-07
17:40:07 UTC (rev 16270)
+++
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/xml/XmlConfigurationTest.java 2009-04-07
20:20:12 UTC (rev 16271)
@@ -1,4 +1,4 @@
-// $Id:$
+// $Id$
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
@@ -23,9 +23,9 @@
import org.testng.annotations.Test;
+import org.hibernate.validation.util.TestUtil;
import static org.hibernate.validation.util.TestUtil.assertNumberOfViolations;
import static org.hibernate.validation.util.TestUtil.getValidator;
-import org.hibernate.validation.util.TestUtil;
/**
* @author Hardy Ferentschik
@@ -33,7 +33,7 @@
public class XmlConfigurationTest {
@Test
- public void testValidatorResolutionForMinMax() {
+ public void testClassConstraintDefinedInXml() {
Validator validator = getValidator();
User user = new User();
@@ -45,4 +45,41 @@
constraintViolations = validator.validate( user );
assertNumberOfViolations( constraintViolations, 0 );
}
+
+ @Test
+ public void testPropertyConstraintDefinedInXml() {
+ Validator validator = getValidator();
+
+ User user = new User();
+ user.setConsistent( true );
+ user.setFirstname( "Wolfeschlegelsteinhausenbergerdorff" );
+
+ Set<ConstraintViolation<User>> constraintViolations = validator.validate(
user );
+ assertNumberOfViolations( constraintViolations, 1 );
+ TestUtil.assertConstraintViolation( constraintViolations.iterator().next(), "Size
is limited!" );
+
+ user.setFirstname( "Wolfgang" );
+ constraintViolations = validator.validate( user );
+ assertNumberOfViolations( constraintViolations, 0 );
+ }
+
+ @Test
+ public void testFieldConstraintDefinedInXml() {
+ Validator validator = getValidator();
+
+ User user = new User();
+ user.setConsistent( true );
+ user.setFirstname( "Wolfgang" );
+ user.setLastname( "doe" );
+
+ Set<ConstraintViolation<User>> constraintViolations = validator.validate(
user );
+ assertNumberOfViolations( constraintViolations, 1 );
+ TestUtil.assertConstraintViolation(
+ constraintViolations.iterator().next(), "Last name has to start with with a
capital letter."
+ );
+
+ user.setLastname( "Doe" );
+ constraintViolations = validator.validate( user );
+ assertNumberOfViolations( constraintViolations, 0 );
+ }
}
Modified:
validator/trunk/hibernate-validator/src/test/resources/META-INF/validation/user-constraints.xml
===================================================================
---
validator/trunk/hibernate-validator/src/test/resources/META-INF/validation/user-constraints.xml 2009-04-07
17:40:07 UTC (rev 16270)
+++
validator/trunk/hibernate-validator/src/test/resources/META-INF/validation/user-constraints.xml 2009-04-07
20:20:12 UTC (rev 16271)
@@ -22,9 +22,11 @@
</element>
</constraint>
</class>
- <field name="firstName">
- <constraint annotation="com.acme.app.constraint.LooksLike">
- <element name="patterns">
+ <field name="lastname">
+ <constraint
annotation="javax.validation.constraints.Pattern">
+ <message>Last name has to start with with a capital
letter.</message>
+ <element name="regexp">^[A-Z].*</element>
+ <!--element name="patterns">
<annotation>
<element name="value">myRegExp</element>
<element name="flag">
@@ -34,27 +36,18 @@
<annotation>
<element
name="value">my2ndRegExp</element>
</annotation>
- </element>
+ </element-->
</constraint>
</field>
- <field name="orders">
+ <getter name="firstname">
<valid/>
- <constraint
annotation="com.acme.app.constraint.DiscreteSize">
- <element name="value">
- <value>0</value>
- <value>20</value>
- </element>
- </constraint>
- </field>
- <getter name="orders">
- <valid/>
- <constraint annotation="javax.validation.constraint.Size">
- <message>Size is limited</message>
+ <constraint annotation="javax.validation.constraints.Size">
+ <message>Size is limited!</message>
<groups>
- <value>com.acme.app.model.LightValidation</value>
- <value>javax.persistence.Default</value>
+
<value>org.hibernate.validation.engine.xml.TestGroup</value>
+ <value>javax.validation.groups.Default</value>
</groups>
- <element name="max">30</element>
+ <element name="max">10</element>
</constraint>
</getter>
</bean>