@@ -31,8 +31,7 @@
import javax.validation.GroupSequence;
import javax.validation.Valid;

import org.hibernate.validator.group.DefaultGroupSequenceProvider;
import org.hibernate.validator.group.GroupSequenceProvider;
import org.hibernate.validator.internal.engine.groups.DefaultGroupSequenceProviderAdapter;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.core.ConstraintOrigin;
@@ -49,15 +48,25 @@
import org.hibernate.validator.internal.metadata.raw.ConstrainedType;
import org.hibernate.validator.internal.util.ReflectionHelper;
import org.hibernate.validator.internal.util.SoftLimitMRUCache;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;
import org.hibernate.validator.spi.group.GroupSequenceProvider;

import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
import static org.hibernate.validator.internal.util.ReflectionHelper.getMethods;
import static org.hibernate.validator.internal.util.ReflectionHelper.newInstance;

/**
* @author Gunnar Morling
* @author Hardy Ferentschik
*/
@SuppressWarnings("deprecation")
public class AnnotationMetaDataProvider implements MetaDataProvider {

private static final Log log = LoggerFactory.make();

private final ConstraintHelper constraintHelper;
private final SoftLimitMRUCache<Class<?>, BeanConfiguration<?>> configuredBeans;
private final AnnotationProcessingOptions annotationProcessingOptions;
@@ -72,11 +81,14 @@ public AnnotationProcessingOptions getAnnotationProcessingOptions() {
return new AnnotationProcessingOptions();
}

public List<BeanConfiguration<?>> getBeanConfigurationForHierarchy(Class<?> beanClass) {
List<BeanConfiguration<?>> configurations = newArrayList();
public <T> List<BeanConfiguration<? super T>> getBeanConfigurationForHierarchy(Class<T> beanClass) {
List<BeanConfiguration<? super T>> configurations = newArrayList();

for ( Class<?> oneHierarchyClass : ReflectionHelper.computeClassHierarchy( beanClass, true ) ) {
BeanConfiguration<?> configuration = getBeanConfiguration( oneHierarchyClass );
@SuppressWarnings("unchecked")
BeanConfiguration<? super T> configuration = (BeanConfiguration<? super T>) getBeanConfiguration(
oneHierarchyClass
);
if ( configuration != null ) {
configurations.add( configuration );
}
@@ -125,7 +137,7 @@ private <T> BeanConfiguration<T> retrieveBeanConfiguration(Class<T> beanClass) {
beanClass,
propertyMetaData,
getDefaultGroupSequence( beanClass ),
getDefaultGroupSequenceProviderClass( beanClass )
getDefaultGroupSequenceProvider( beanClass )
);
}

@@ -134,9 +146,62 @@ private List<Class<?>> getDefaultGroupSequence(Class<?> beanClass) {
return groupSequenceAnnotation != null ? Arrays.asList( groupSequenceAnnotation.value() ) : null;
}

private Class<? extends DefaultGroupSequenceProvider<?>> getDefaultGroupSequenceProviderClass(Class<?> beanClass) {
private <T> DefaultGroupSequenceProvider<? super T> getDefaultGroupSequenceProvider(Class<T> beanClass) {
GroupSequenceProvider groupSequenceProviderAnnotation = beanClass.getAnnotation( GroupSequenceProvider.class );
return groupSequenceProviderAnnotation != null ? groupSequenceProviderAnnotation.value() : null;

if ( groupSequenceProviderAnnotation != null ) {
return newGroupSequenceProviderClassInstance( beanClass, groupSequenceProviderAnnotation.value() );
}
else {
org.hibernate.validator.group.GroupSequenceProvider deprecatedGroupSequenceProviderAnnotation = beanClass.getAnnotation(
org.hibernate.validator.group.GroupSequenceProvider.class
);

if ( deprecatedGroupSequenceProviderAnnotation != null ) {
return DefaultGroupSequenceProviderAdapter.getInstance(
newDeprecatedGroupSequenceProviderClassInstance(
beanClass,
deprecatedGroupSequenceProviderAnnotation.value()
)
);
}
}

return null;
}

@SuppressWarnings("unchecked")
private <T> DefaultGroupSequenceProvider<? super T> newGroupSequenceProviderClassInstance(Class<T> beanClass, Class<?> providerClass) {
Method[] providerMethods = getMethods( providerClass );
for ( Method method : providerMethods ) {
Class<?>[] paramTypes = method.getParameterTypes();
if ( "getValidationGroups".equals( method.getName() ) && !method.isBridge()
&& paramTypes.length == 1 && paramTypes[0].isAssignableFrom( beanClass ) ) {

return (DefaultGroupSequenceProvider<? super T>) newInstance(
providerClass, "the default group sequence provider"
);
}
}

throw log.getWrongDefaultGroupSequenceProviderTypeException( beanClass.getName() );
}

@SuppressWarnings("unchecked")
private <T> org.hibernate.validator.group.DefaultGroupSequenceProvider<? super T> newDeprecatedGroupSequenceProviderClassInstance(Class<T> beanClass, Class<?> providerClass) {
Method[] providerMethods = getMethods( providerClass );
for ( Method method : providerMethods ) {
Class<?>[] paramTypes = method.getParameterTypes();
if ( "getValidationGroups".equals( method.getName() ) && !method.isBridge()
&& paramTypes.length == 1 && paramTypes[0].isAssignableFrom( beanClass ) ) {

return (org.hibernate.validator.group.DefaultGroupSequenceProvider<? super T>) newInstance(
providerClass, "the default group sequence provider"
);
}
}

throw log.getWrongDefaultGroupSequenceProviderTypeException( beanClass.getName() );
}

private Set<MetaConstraint<?>> getClassLevelConstraints(Class<?> clazz) {
@@ -57,5 +57,5 @@ public interface MetaDataProvider {
* @return A set with the configurations for the complete hierarchy of the
* given type. May be empty, but never {@code null}.
*/
List<BeanConfiguration<?>> getBeanConfigurationForHierarchy(Class<?> beanClass);
<T> List<BeanConfiguration<? super T>> getBeanConfigurationForHierarchy(Class<T> beanClass);
}
@@ -20,13 +20,13 @@
import java.util.Map;
import java.util.Set;

import org.hibernate.validator.group.DefaultGroupSequenceProvider;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.raw.BeanConfiguration;
import org.hibernate.validator.internal.metadata.raw.ConfigurationSource;
import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.ReflectionHelper;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;

import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
@@ -47,11 +47,14 @@ public MetaDataProviderKeyedByClassName(ConstraintHelper constraintHelper) {
this.configuredBeans = newHashMap();
}

public List<BeanConfiguration<?>> getBeanConfigurationForHierarchy(Class<?> beanClass) {
List<BeanConfiguration<?>> configurations = newArrayList();
public <T> List<BeanConfiguration<? super T>> getBeanConfigurationForHierarchy(Class<T> beanClass) {
List<BeanConfiguration<? super T>> configurations = newArrayList();

for ( Class<?> oneHierarchyClass : ReflectionHelper.computeClassHierarchy( beanClass, true ) ) {
BeanConfiguration<?> configuration = getBeanConfiguration( oneHierarchyClass );
@SuppressWarnings("unchecked")
BeanConfiguration<? super T> configuration = (BeanConfiguration<? super T>) getBeanConfiguration(
oneHierarchyClass
);
if ( configuration != null ) {
configurations.add( configuration );
}
@@ -73,7 +76,7 @@ protected <T> BeanConfiguration<T> createBeanConfiguration(ConfigurationSource s
Class<T> beanClass,
Set<? extends ConstrainedElement> constrainableElements,
List<Class<?>> defaultGroupSequence,
Class<? extends DefaultGroupSequenceProvider<?>> defaultGroupSequenceProvider) {
DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider) {
return new BeanConfiguration<T>(
source,
beanClass,
@@ -28,6 +28,7 @@
import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.internal.cfg.context.ConfiguredConstraint;
import org.hibernate.validator.internal.cfg.context.ConstraintMappingContext;
import org.hibernate.validator.internal.engine.groups.DefaultGroupSequenceProviderAdapter;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.core.ConstraintOrigin;
@@ -44,8 +45,10 @@
import org.hibernate.validator.internal.metadata.raw.ConstrainedType;
import org.hibernate.validator.internal.util.CollectionHelper.Partitioner;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.ReflectionHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;

import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
@@ -56,6 +59,7 @@
*
* @author Gunnar Morling
*/
@SuppressWarnings("deprecation")
public class ProgrammaticMetaDataProvider extends MetaDataProviderKeyedByClassName {

private static final Log log = LoggerFactory.make();
@@ -80,32 +84,68 @@ public AnnotationProcessingOptions getAnnotationProcessingOptions() {
*/
private void initProgrammaticConfiguration(ConstraintMappingContext context) {
for ( Class<?> clazz : context.getConfiguredClasses() ) {
initClass( clazz, context );
}
}

Set<ConstrainedElement> constrainedElements =
retrievePropertyMetaData(
context.getConstraintConfig().get( clazz ),
context.getCascadeConfig().get( clazz )
);
private <T> void initClass(Class<T> clazz, ConstraintMappingContext context) {

Set<ConstrainedElement> methodMetaData =
retrieveMethodMetaData(
context.getMethodCascadeConfig().get( clazz ),
context.getMethodConstraintConfig().get( clazz )
);
Set<ConstrainedElement> constrainedElements =
retrievePropertyMetaData(
context.getConstraintConfig().get( clazz ),
context.getCascadeConfig().get( clazz )
);

constrainedElements.addAll( methodMetaData );
Set<ConstrainedElement> methodMetaData =
retrieveMethodMetaData(
context.getMethodCascadeConfig().get( clazz ),
context.getMethodConstraintConfig().get( clazz )
);

addBeanConfiguration(
clazz,
createBeanConfiguration(
ConfigurationSource.API,
clazz,
constrainedElements,
context.getDefaultSequence( clazz ),
context.getDefaultGroupSequenceProvider( clazz )
)
constrainedElements.addAll( methodMetaData );

DefaultGroupSequenceProvider<? super T> sequenceProvider = getDefaultGroupSequenceProvider( clazz, context );

addBeanConfiguration(
clazz,
createBeanConfiguration(
ConfigurationSource.API,
clazz,
constrainedElements,
context.getDefaultSequence( clazz ),
sequenceProvider
)
);
}

private <T> DefaultGroupSequenceProvider<? super T> getDefaultGroupSequenceProvider(Class<T> beanType, ConstraintMappingContext context) {

Class<? extends DefaultGroupSequenceProvider<? super T>> providerClass = context.getDefaultGroupSequenceProvider(
beanType
);

//retrieve provider from new annotation
if ( providerClass != null ) {
DefaultGroupSequenceProvider<? super T> provider = ReflectionHelper.newInstance(
providerClass,
"default group sequence provider"
);
return provider;
}

Class<? extends org.hibernate.validator.group.DefaultGroupSequenceProvider<? super T>> deprecatedProviderClass = context
.getDeprecatedDefaultGroupSequenceProvider( beanType );

//retrieve provider from deprecated annotation and wrap into adapter
if ( deprecatedProviderClass != null ) {
org.hibernate.validator.group.DefaultGroupSequenceProvider<? super T> provider = ReflectionHelper.newInstance(
deprecatedProviderClass,
"default group sequence provider"
);
return DefaultGroupSequenceProviderAdapter.getInstance( provider );
}

return null;
}

private Set<ConstrainedElement> retrievePropertyMetaData(
@@ -313,25 +353,37 @@ private ConstraintMappingContext createMergedMappingContext(Set<ConstraintMappin

private void mergeGroupSequenceAndGroupSequenceProvider(ConstraintMappingContext mergedContext, ConstraintMappingContext context) {
for ( Class<?> clazz : context.getConfiguredClasses() ) {
if ( context.getDefaultGroupSequenceProvider( clazz ) != null ) {
if ( mergedContext.getDefaultGroupSequenceProvider( clazz ) != null ) {
throw log.getMultipleDefinitionOfDefaultGroupSequenceProviderException();
}
mergedContext.addDefaultGroupSequenceProvider(
clazz,
context.getDefaultGroupSequenceProvider( clazz )
);
}
mergeSequenceAndProviderForClass( mergedContext, context, clazz );
}
}

if ( context.getDefaultSequence( clazz ) != null ) {
if ( mergedContext.getDefaultSequence( clazz ) != null ) {
throw log.getMultipleDefinitionOfDefaultGroupSequenceException();
}
mergedContext.addDefaultGroupSequence(
clazz,
context.getDefaultSequence( clazz )
);
private <T> void mergeSequenceAndProviderForClass(ConstraintMappingContext mergedContext, ConstraintMappingContext context, Class<T> clazz) {
if ( context.getDefaultGroupSequenceProvider( clazz ) != null ) {
if ( mergedContext.getDefaultGroupSequenceProvider( clazz ) != null ) {
throw log.getMultipleDefinitionOfDefaultGroupSequenceProviderException();
}
mergedContext.addDefaultGroupSequenceProvider(
clazz,
context.getDefaultGroupSequenceProvider( clazz )
);
}
if ( context.getDeprecatedDefaultGroupSequenceProvider( clazz ) != null ) {
if ( mergedContext.getDeprecatedDefaultGroupSequenceProvider( clazz ) != null ) {
throw log.getMultipleDefinitionOfDefaultGroupSequenceProviderException();
}
mergedContext.addDeprecatedDefaultGroupSequenceProvider(
clazz,
context.getDeprecatedDefaultGroupSequenceProvider( clazz )
);
}
if ( context.getDefaultSequence( clazz ) != null ) {
if ( mergedContext.getDefaultSequence( clazz ) != null ) {
throw log.getMultipleDefinitionOfDefaultGroupSequenceException();
}
mergedContext.addDefaultGroupSequence(
clazz,
context.getDefaultSequence( clazz )
);
}
}

@@ -19,7 +19,7 @@
import java.util.List;
import java.util.Set;

import org.hibernate.validator.group.DefaultGroupSequenceProvider;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;

import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;

@@ -41,7 +41,7 @@

private final List<Class<?>> defaultGroupSequence;

private final Class<? extends DefaultGroupSequenceProvider<?>> defaultGroupSequenceProvider;
private final DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider;

/**
* Creates a new bean configuration.
@@ -60,7 +60,7 @@ public BeanConfiguration(
Class<T> beanClass,
Set<? extends ConstrainedElement> constrainedElements,
List<Class<?>> defaultGroupSequence,
Class<? extends DefaultGroupSequenceProvider<?>> defaultGroupSequenceProvider) {
DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider) {

this.source = source;
this.beanClass = beanClass;
@@ -85,7 +85,7 @@ public List<Class<?>> getDefaultGroupSequence() {
return defaultGroupSequence;
}

public Class<? extends DefaultGroupSequenceProvider<?>> getDefaultGroupSequenceProvider() {
public DefaultGroupSequenceProvider<? super T> getDefaultGroupSequenceProvider() {
return defaultGroupSequenceProvider;
}

@@ -0,0 +1,59 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2012, Red Hat, Inc. and/or its affiliates, 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.validator.spi.group;

import java.util.List;

/**
* This class defines the dynamic group sequence provider contract.
* <p>
* In order to redefine dynamically the default group sequence for a type T, the {@link GroupSequenceProvider} annotation
* must be placed on T, specifying as its value a concrete implementation of {@code DefaultGroupSequenceProvider}, which
* must be parametrized with the type T.
* </p>
* <p>
* If during the validation process the {@code Default} group is validated for T, the actual validated instance
* is passed to the {@code DefaultGroupSequenceProvider} to determine the default group sequence.
* </p>
* <p>
* Note:
* <ul>
* <li>Implementations must provide a public default constructor.</li>
* <li>Implementations must be thread-safe.</li>
* </ul>
*
* @param <T> The type for which an implementation is defined.
*
* @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI
* @author Hardy Ferentschik
*/
public interface DefaultGroupSequenceProvider<T> {

/**
* This method returns the default group sequence for the given instance.
* <p>
* The object parameter allows to dynamically compose the default group sequence in function of the validated value state.
* </p>
*
* @param object the instance being validated. This value can be {@code null} in case this method was called as part of
* {@linkplain javax.validation.Validator#validateValue(Class, String, Object, Class[]) Validator#validateValue}.
*
* @return a list of classes specifying the default group sequence. The same constraints to the redefined group list
* apply as for lists defined via {@code GroupSequence}. In particular the list has to contain the type T.
*/
List<Class<?>> getValidationGroups(T object);
}
@@ -0,0 +1,48 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2012, Red Hat, Inc. and/or its affiliates, 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.validator.spi.group;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* The {@code GroupSequenceProvider} annotation defines the {@code DefaultGroupSequenceProvider}
* class responsible to return the list of classes defining the default group sequence for the annotated type.
* <p>
* Note:
* <ul>
* <li>It is not allowed to use {@code GroupSequenceProvider} and {@link javax.validation.GroupSequence} together on
* the same type.</li>
* <li>{@code GroupSequenceProvider} is a Hibernate Validator specific annotation and not portable.</li>
* </ul>
*
* @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI
* @author Hardy Ferentschik
* @see javax.validation.GroupSequence
*/
@Retention(RUNTIME)
@Target({ TYPE })
public @interface GroupSequenceProvider {

/**
* @return The default group sequence provider implementation.
*/
Class<? extends DefaultGroupSequenceProvider<?>> value();
}
@@ -0,0 +1,25 @@
<!--
~ JBoss, Home of Professional Open Source
~ Copyright 2012, Red Hat, Inc. and/or its affiliates, 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.
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
</head>
<body>
<p>This package provides support for dynamic default group sequence definition.</p>
<p>This package is part of the public Hibernate Validator API.</p>
</body>
</html>