http://git-wip-us.apache.org/repos/asf/bval/blob/92c64b3c/bval-jsr/src/main/java/org/apache/bval/jsr/BeanDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/BeanDescriptorImpl.java
b/bval-jsr/src/main/java/org/apache/bval/jsr/BeanDescriptorImpl.java
deleted file mode 100644
index 9d0c32a..0000000
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/BeanDescriptorImpl.java
+++ /dev/null
@@ -1,916 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.apache.bval.jsr;
-
-import org.apache.bval.Validate;
-import org.apache.bval.jsr.groups.Group;
-import org.apache.bval.jsr.groups.GroupConversionDescriptorImpl;
-import org.apache.bval.jsr.util.ClassHelper;
-import org.apache.bval.jsr.xml.AnnotationIgnores;
-import org.apache.bval.model.Features;
-import org.apache.bval.model.Features.Bean;
-import org.apache.bval.model.MetaBean;
-import org.apache.bval.model.MetaConstructor;
-import org.apache.bval.model.MetaMethod;
-import org.apache.bval.model.MetaParameter;
-import org.apache.bval.model.MetaProperty;
-import org.apache.bval.model.Validation;
-import org.apache.bval.util.AccessStrategy;
-import org.apache.bval.util.reflection.Reflection;
-import org.apache.commons.weaver.privilizer.Privilizing;
-import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
-
-import javax.validation.Constraint;
-import javax.validation.ConstraintDeclarationException;
-import javax.validation.ConstraintTarget;
-import javax.validation.Valid;
-import javax.validation.groups.ConvertGroup;
-import javax.validation.metadata.BeanDescriptor;
-import javax.validation.metadata.ConstraintDescriptor;
-import javax.validation.metadata.ConstructorDescriptor;
-import javax.validation.metadata.ExecutableDescriptor;
-import javax.validation.metadata.GroupConversionDescriptor;
-import javax.validation.metadata.MethodDescriptor;
-import javax.validation.metadata.MethodType;
-import javax.validation.metadata.ParameterDescriptor;
-import javax.validation.metadata.PropertyDescriptor;
-import javax.validation.metadata.ReturnValueDescriptor;
-import java.beans.Introspector;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
-
-/**
- * Description: Implements {@link BeanDescriptor}.<br/>
- */
-@Privilizing(@CallTo(Reflection.class))
-public class BeanDescriptorImpl extends ElementDescriptorImpl implements
BeanDescriptor {
- private static final CopyOnWriteArraySet<ConstraintValidation<?>>
NO_CONSTRAINTS =
- new CopyOnWriteArraySet<ConstraintValidation<?>>();
- private static final Validation[] EMPTY_VALIDATION = new Validation[0];
-
- /**
- * The {@link ApacheFactoryContext} (not) used by this
- * {@link BeanDescriptorImpl}
- */
- private final Set<ConstructorDescriptor> constrainedConstructors;
- private final Set<MethodDescriptor> containedMethods;
- private final ExecutableMeta meta;
- private final Boolean isBeanConstrained;
- private final Set<PropertyDescriptor> validatedProperties;
-
- protected BeanDescriptorImpl(final ApacheFactoryContext factoryContext,
final MetaBean metaBean) {
- super(metaBean, metaBean.getBeanClass(), metaBean.getValidations());
-
- Set<PropertyDescriptor> procedureDescriptors =
metaBean.getFeature(Bean.PROPERTIES);
- if (procedureDescriptors == null) {
- procedureDescriptors = new HashSet<PropertyDescriptor>();
- for (final MetaProperty prop : metaBean.getProperties()) {
- if (prop.getValidations().length > 0
- || (prop.getMetaBean() != null ||
prop.getFeature(Features.Property.REF_CASCADE) != null)) {
- procedureDescriptors.add(getPropertyDescriptor(prop));
- }
- }
- procedureDescriptors = metaBean.initFeature(Bean.PROPERTIES,
procedureDescriptors);
- }
-
- ExecutableMeta executables = metaBean.getFeature(Bean.EXECUTABLES);
- if (executables == null) { // caching the result of it is important to
avoid to compute it for each Validator
- executables = new ExecutableMeta(factoryContext, metaBean,
getConstraintDescriptors());
- executables = metaBean.initFeature(Bean.EXECUTABLES, executables);
- }
-
- validatedProperties =
Collections.unmodifiableSet(procedureDescriptors);
- meta = executables;
- isBeanConstrained = meta.isBeanConstrained;
- containedMethods = toConstrained(meta.methodConstraints.values());
- constrainedConstructors =
toConstrained(meta.contructorConstraints.values());
- }
-
- private static void addGroupConvertion(final MetaProperty prop, final
PropertyDescriptorImpl edesc) {
- boolean fieldFound = false;
- boolean methodFound = false;
- Class<?> current = prop.getParentMetaBean().getBeanClass();
- while (current != null && current != Object.class && (!methodFound ||
!fieldFound)) {
- if (!fieldFound) {
- final Field field = Reflection.getDeclaredField(current,
prop.getName());
- if (field != null) {
- processConvertGroup(edesc, field);
- fieldFound = true;
- }
- }
- if (!methodFound) {
- final String name =
Character.toUpperCase(prop.getName().charAt(0)) + prop.getName().substring(1);
- Method m = Reflection.getDeclaredMethod(current, "get" + name);
- if (m == null) {
- final Method isAccessor =
Reflection.getDeclaredMethod(current, "is" + name);
- if (isAccessor != null &&
boolean.class.equals(isAccessor.getReturnType())) {
- m = isAccessor;
- }
- }
- if (m != null) {
- processConvertGroup(edesc, m);
- methodFound = true;
- }
- }
- current = current.getSuperclass();
- }
-
- final Collection<Annotation> annotations =
prop.getFeature(JsrFeatures.Property.ANNOTATIONS_TO_PROCESS);
- if (annotations != null) {
- for (final Annotation a : annotations) {
- if (ConvertGroup.List.class.isInstance(a)) {
- for (final ConvertGroup convertGroup :
ConvertGroup.List.class.cast(a).value()) {
- edesc.addGroupConversion(new
GroupConversionDescriptorImpl(new Group(convertGroup.from()),
- new Group(convertGroup.to())));
- }
- }
- if (ConvertGroup.class.isInstance(a)) {
- final ConvertGroup convertGroup =
ConvertGroup.class.cast(a);
- edesc.addGroupConversion(new
GroupConversionDescriptorImpl(new Group(convertGroup.from()),
- new Group(convertGroup.to())));
- }
- }
- annotations.clear();
- }
- if (!edesc.getGroupConversions().isEmpty() && !edesc.isCascaded()) {
- throw new ConstraintDeclarationException("@Valid is needed for
group conversion");
- }
- }
-
- private static void processConvertGroup(final ElementDescriptorImpl edesc,
final AccessibleObject accessible) {
- final ConvertGroup.List convertGroupList =
accessible.getAnnotation(ConvertGroup.List.class);
- if (convertGroupList != null) {
- for (final ConvertGroup convertGroup : convertGroupList.value()) {
- edesc.addGroupConversion(
- new GroupConversionDescriptorImpl(new
Group(convertGroup.from()), new Group(convertGroup.to())));
- }
- }
- final ConvertGroup convertGroup =
accessible.getAnnotation(ConvertGroup.class);
- if (convertGroup != null) {
- edesc.addGroupConversion(
- new GroupConversionDescriptorImpl(new
Group(convertGroup.from()), new Group(convertGroup.to())));
- }
- }
-
- /**
- * Returns true if the bean involves validation:
- * <ul>
- * <li>a constraint is hosted on the bean itself</li>
- * <li>a constraint is hosted on one of the bean properties, OR</li>
- * <li>a bean property is marked for cascade (<code>@Valid</code>)</li>
- * </ul>
- *
- * @return true if the bean involves validation
- */
- @Override
- public boolean isBeanConstrained() {
- return isBeanConstrained;
- }
-
- /**
- * Return the property level constraints for a given propertyName or
{@code null} if
- * either the property does not exist or has no constraint. The returned
- * object (and associated objects including ConstraintDescriptors) are
- * immutable.
- *
- * @param propertyName property evaluated
- */
- @Override
- public PropertyDescriptor getConstraintsForProperty(String propertyName) {
- if (propertyName == null || propertyName.trim().length() == 0) {
- throw new IllegalArgumentException("propertyName cannot be null or
empty");
- }
- final MetaProperty prop = metaBean.getProperty(propertyName);
- if (prop == null) {
- return null;
- }
- // If no constraints and not cascaded, return null
- if (prop.getValidations().length == 0 &&
prop.getFeature(Features.Property.REF_CASCADE) == null) {
- return null;
- }
- return getPropertyDescriptor(prop);
- }
-
- private PropertyDescriptor getPropertyDescriptor(final MetaProperty prop) {
- PropertyDescriptorImpl edesc =
prop.getFeature(JsrFeatures.Property.PropertyDescriptor);
- if (edesc == null) {
- edesc = new PropertyDescriptorImpl(prop);
- addGroupConvertion(prop, edesc);
- prop.putFeature(JsrFeatures.Property.PropertyDescriptor, edesc);
- }
- return edesc;
- }
-
- /**
- * {@inheritDoc}
- *
- * @return the property descriptors having at least a constraint defined
- */
- @Override
- public Set<PropertyDescriptor> getConstrainedProperties() {
- return Collections.unmodifiableSet(validatedProperties);
- }
-
- public MethodDescriptor getInternalConstraintsForMethod(final String
methodName, final Class<?>... parameterTypes) {
- if (methodName == null) {
- throw new IllegalArgumentException("Method name can't be null");
- }
- return meta.methodConstraints.get(methodName +
Arrays.toString(parameterTypes));
- }
-
- @Override
- public MethodDescriptor getConstraintsForMethod(final String methodName,
final Class<?>... parameterTypes) {
- if (methodName == null) {
- throw new IllegalArgumentException("Method name can't be null");
- }
- final MethodDescriptor methodDescriptor =
- meta.methodConstraints.get(methodName +
Arrays.toString(parameterTypes));
- if (methodDescriptor != null
- && (methodDescriptor.hasConstrainedParameters() ||
methodDescriptor.hasConstrainedReturnValue())) {
- return methodDescriptor;
- }
- return null;
- }
-
- @Override
- public Set<MethodDescriptor> getConstrainedMethods(MethodType methodType,
MethodType... methodTypes) {
- final Set<MethodDescriptor> desc = new HashSet<MethodDescriptor>();
- desc.addAll(filter(containedMethods, methodType));
- if (methodTypes != null) {
- for (final MethodType type : methodTypes) {
- desc.addAll(filter(containedMethods, type));
- }
- }
- return desc;
- }
-
- private static Collection<MethodDescriptor> filter(final
Set<MethodDescriptor> containedMethods,
- final MethodType type) {
- final Collection<MethodDescriptor> list = new
ArrayList<MethodDescriptor>();
- for (final MethodDescriptor d : containedMethods) {
- final boolean getter =
- d.getParameterDescriptors().isEmpty() &&
(d.getName().startsWith("get") || (d.getName().startsWith("is")
- &&
boolean.class.equals(d.getReturnValueDescriptor().getElementClass())));
-
- switch (type) {
- case GETTER:
- if (getter) {
- list.add(d);
- }
- break;
-
- case NON_GETTER:
- if (!getter) {
- list.add(d);
- }
- }
- }
- return list;
- }
-
- @Override
- public ConstructorDescriptor getConstraintsForConstructor(final
Class<?>... parameterTypes) {
- final ConstructorDescriptor descriptor =
meta.contructorConstraints.get(Arrays.toString(parameterTypes));
- if (descriptor != null && (descriptor.hasConstrainedParameters() ||
descriptor.hasConstrainedReturnValue())) {
- return descriptor;
- }
- return null;
- }
-
- @Override
- public Set<ConstructorDescriptor> getConstrainedConstructors() {
- return constrainedConstructors;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return "BeanDescriptorImpl{" + "returnType=" + elementClass + '}';
- }
-
- private static <A extends ExecutableDescriptor> Set<A> toConstrained(final
Collection<A> src) {
- final Set<A> dest = new HashSet<A>();
- for (final A d : src) {
- if (d.hasConstrainedParameters() || d.hasConstrainedReturnValue())
{
- dest.add(d);
- }
- }
- return Collections.unmodifiableSet(dest);
- }
-
- private static class ExecutableMeta {
- private final ApacheFactoryContext factoryContext;
- private final AnnotationProcessor annotationProcessor;
- private final MetaBean metaBean;
- private final Map<String, MethodDescriptor> methodConstraints = new
HashMap<String, MethodDescriptor>();
- private final Map<String, ConstructorDescriptor> contructorConstraints
=
- new HashMap<String, ConstructorDescriptor>();
- private Boolean isBeanConstrained = null;
-
- private ExecutableMeta(final ApacheFactoryContext factoryContext,
final MetaBean metaBean1,
- final Collection<ConstraintDescriptor<?>> constraintDescriptors) {
- this.metaBean = metaBean1;
- this.factoryContext = factoryContext;
- this.annotationProcessor = new
AnnotationProcessor(factoryContext.getFactory());
-
- buildExecutableDescriptors();
-
- boolean hasAnyContraints;
- if (constraintDescriptors.isEmpty()) {
- hasAnyContraints = false;
- for (final MetaProperty mprop : metaBean.getProperties()) {
- if
(!getConstraintDescriptors(mprop.getValidations()).isEmpty()) {
- hasAnyContraints = true;
- break;
- }
- }
- } else {
- hasAnyContraints = true;
- }
-
- // cache isBeanConstrained
- if (hasAnyContraints) {
- isBeanConstrained = true;
- } else {
- isBeanConstrained = false;
- for (final MetaProperty mprop : metaBean.getProperties()) {
- if (mprop.getMetaBean() != null ||
mprop.getFeature(Features.Property.REF_CASCADE) != null) {
- isBeanConstrained = true;
- break;
- }
- }
- }
- }
-
- private void buildConstructorConstraints() throws
InvocationTargetException, IllegalAccessException {
- for (final Constructor<?> cons :
Reflection.getDeclaredConstructors(metaBean.getBeanClass())) {
- final ConstructorDescriptorImpl consDesc = new
ConstructorDescriptorImpl(metaBean, EMPTY_VALIDATION);
-
contructorConstraints.put(Arrays.toString(cons.getParameterTypes()), consDesc);
-
- final List<String> names =
factoryContext.getParameterNameProvider().getParameterNames(cons);
- final boolean isInnerClass =
cons.getDeclaringClass().getEnclosingClass() != null
- &&
!Modifier.isStatic(cons.getDeclaringClass().getModifiers());
-
- final AnnotationIgnores annotationIgnores =
factoryContext.getFactory().getAnnotationIgnores();
-
- {
- final Annotation[][] paramsAnnos =
cons.getParameterAnnotations();
-
- int idx = 0;
- if (isInnerClass) { // paramsAnnos.length =
parameterTypes.length - 1 in this case
- final ParameterDescriptorImpl paramDesc =
- new ParameterDescriptorImpl(metaBean,
EMPTY_VALIDATION, names.get(idx));
- consDesc.getParameterDescriptors().add(paramDesc);
- idx++;
- }
-
- for (final Annotation[] paramAnnos : paramsAnnos) {
- if
(annotationIgnores.isIgnoreAnnotationOnParameter(cons, idx)) {
- consDesc.getParameterDescriptors()
- .add(new ParameterDescriptorImpl(metaBean,
EMPTY_VALIDATION, names.get(idx)));
- } else if (cons.getParameterTypes().length > idx) {
- ParameterAccess access = new
ParameterAccess(cons.getParameterTypes()[idx], idx);
- consDesc.addValidations(
- processAnnotations(consDesc, paramAnnos,
access, idx, names.get(idx)).getValidations());
- } // else anonymous class so that's fine
- idx++;
- }
-
- if (!annotationIgnores.isIgnoreAnnotations(cons)) {
- for (final Annotation anno : cons.getAnnotations()) {
- if (Valid.class.isInstance(anno)) {
- consDesc.setCascaded(true);
- } else {
- processAnnotations(null, consDesc,
cons.getDeclaringClass(), anno);
- }
- }
- }
- }
-
- if (annotationIgnores.isIgnoreAnnotationOnCrossParameter(cons)
- && consDesc.getCrossParameterDescriptor() != null) {
- consDesc.setCrossParameterDescriptor(null);
- }
- if (annotationIgnores.isIgnoreAnnotationOnReturn(cons) &&
consDesc.getReturnValueDescriptor() != null) {
- consDesc.setReturnValueDescriptor(null);
- }
-
- final MetaConstructor metaConstructor =
metaBean.getConstructor(cons);
- if (metaConstructor != null) {
- for (final Annotation anno :
metaConstructor.getAnnotations()) {
- if (Valid.class.isInstance(anno)) {
- consDesc.setCascaded(true);
- } else {
- processAnnotations(null, consDesc,
cons.getDeclaringClass(), anno);
- }
- }
-
- // parameter validations
- final Collection<MetaParameter> paramsAnnos =
metaConstructor.getParameters();
- for (final MetaParameter paramAnnos : paramsAnnos) {
- final int idx = paramAnnos.getIndex();
- final ParameterAccess access = new
ParameterAccess(cons.getParameterTypes()[idx], idx);
- processAnnotations(consDesc,
paramAnnos.getAnnotations(), access, idx, names.get(idx));
- }
- }
-
- if (consDesc.getGroupConversions().isEmpty() ||
consDesc.isCascaded()) {
- ensureNotNullDescriptors(cons.getDeclaringClass(),
consDesc);
- } else {
- throw new ConstraintDeclarationException("@Valid is needed
to define a group conversion");
- }
- }
- }
-
- private void ensureNotNullDescriptors(final Class<?> returnType, final
InvocableElementDescriptor consDesc) {
- // can't be null
- if (consDesc.getCrossParameterDescriptor() == null) {
- consDesc.setCrossParameterDescriptor(new
CrossParameterDescriptorImpl(metaBean, NO_CONSTRAINTS));
- }
- if (consDesc.getReturnValueDescriptor() == null) {
- consDesc.setReturnValueDescriptor(
- new ReturnValueDescriptorImpl(metaBean, returnType,
NO_CONSTRAINTS, consDesc.isCascaded()));
- }
- // enforce it since ReturnValueDescriptor can be created before
cascaded is set to true
- final ReturnValueDescriptorImpl returnValueDescriptor =
-
ReturnValueDescriptorImpl.class.cast(consDesc.getReturnValueDescriptor());
- returnValueDescriptor.setCascaded(consDesc.isCascaded());
- if (returnValueDescriptor.getGroupConversions().isEmpty()) {
- // loop to not forget to map calling addGroupConversion()
- for (final GroupConversionDescriptor c :
consDesc.getGroupConversions()) {
- returnValueDescriptor.addGroupConversion(c);
- }
- }
- }
-
- private void processAnnotations(final Method mtd, final
InvocableElementDescriptor consDesc,
- final Class<?> clazz, final Annotation anno) throws
InvocationTargetException, IllegalAccessException {
- if (mtd == null ||
!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotationOnReturn(mtd))
{
- final ReturnAccess returnAccess = new ReturnAccess(clazz);
- final AppendValidationToList validations = new
AppendValidationToList();
- processAnnotation(anno, consDesc, returnAccess, validations);
- final List<ConstraintValidation<?>> list =
-
removeFromListValidationAppliesTo(validations.getValidations(),
ConstraintTarget.PARAMETERS);
- consDesc.addValidations(list);
-
- ReturnValueDescriptorImpl returnValueDescriptor =
-
ReturnValueDescriptorImpl.class.cast(consDesc.getReturnValueDescriptor());
- if (consDesc.getReturnValueDescriptor() == null) {
- returnValueDescriptor = new
ReturnValueDescriptorImpl(metaBean, clazz, list, consDesc.isCascaded());
- consDesc.setReturnValueDescriptor(returnValueDescriptor);
- } else {
-
returnValueDescriptor.getMutableConstraintDescriptors().addAll(list);
- }
- }
-
- if (mtd == null
- ||
!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotationOnCrossParameter(mtd))
{
- final ParametersAccess parametersAccess = new
ParametersAccess();
- final AppendValidationToList validations = new
AppendValidationToList();
- processAnnotation(anno, consDesc, parametersAccess,
validations);
- final List<ConstraintValidation<?>> list =
-
removeFromListValidationAppliesTo(validations.getValidations(),
ConstraintTarget.RETURN_VALUE);
- consDesc.addValidations(list);
- if (consDesc.getCrossParameterDescriptor() == null) {
- consDesc.setCrossParameterDescriptor(new
CrossParameterDescriptorImpl(metaBean, list));
- } else {
-
CrossParameterDescriptorImpl.class.cast(consDesc.getCrossParameterDescriptor())
- .getMutableConstraintDescriptors().addAll(list);
- }
- }
- }
-
- private static List<ConstraintValidation<?>>
removeFromListValidationAppliesTo(
- final List<ConstraintValidation<?>> validations, final
ConstraintTarget constraint) {
- final Iterator<ConstraintValidation<?>> i = validations.iterator();
- while (i.hasNext()) {
- if (constraint.equals(i.next().getValidationAppliesTo())) {
- i.remove();
- }
- }
- return validations;
- }
-
- private void buildMethodConstraints() throws
InvocationTargetException, IllegalAccessException {
-
- final Class<?> current = metaBean.getBeanClass();
- final List<Class<?>> classHierarchy =
- ClassHelper.fillFullClassHierarchyAsList(new
ArrayList<Class<?>>(), current);
- classHierarchy.remove(current);
-
- for (final Method method : Reflection.getDeclaredMethods(current))
{
- if (Modifier.isStatic(method.getModifiers()) ||
method.isSynthetic()) {
- continue;
- }
-
- final boolean propertyAccessor =
method.getParameterTypes().length == 0
- && (method.getName().startsWith("get") &&
!Void.TYPE.equals(method.getReturnType())
- || method.getName().startsWith("is") &&
Boolean.TYPE.equals(method.getReturnType()));
-
- final String key = method.getName() +
Arrays.toString(method.getParameterTypes());
- MethodDescriptorImpl methodDesc =
MethodDescriptorImpl.class.cast(methodConstraints.get(key));
- if (methodDesc == null) {
- methodDesc = new MethodDescriptorImpl(metaBean,
EMPTY_VALIDATION, method);
- methodConstraints.put(key, methodDesc);
- } else {
- continue;
- }
-
- final Collection<Method> parents = new ArrayList<Method>();
- for (final Class<?> clazz : classHierarchy) {
- final Method overridden =
- Reflection.getDeclaredMethod(clazz, method.getName(),
method.getParameterTypes());
- if (overridden != null) {
- parents.add(overridden);
- processMethod(overridden, methodDesc);
- }
- }
-
- processMethod(method, methodDesc);
-
- ensureNotNullDescriptors(method.getReturnType(), methodDesc);
-
- if (parents != null) {
- if (parents.size() > 1) {
- for (final Method parent : parents) {
- final MethodDescriptor parentDec =
-
factoryContext.getValidator().getConstraintsForClass(parent.getDeclaringClass())
- .getConstraintsForMethod(parent.getName(),
parent.getParameterTypes());
- if (parentDec != null) {
-
ensureNoParameterConstraint(InvocableElementDescriptor.class.cast(parentDec),
- "Parameter constraints can't be defined
for parallel interfaces/parents");
- } else {
-
ensureMethodDoesntDefineParameterConstraint(methodDesc);
- }
-
ensureNoReturnValueAddedInChild(methodDesc.getReturnValueDescriptor(),
parentDec,
- "Return value constraints should be the same
for parent and children");
- }
- } else if (parents.size() == 1) {
- final Method parent = parents.iterator().next();
- final MethodDescriptor parentDesc =
-
factoryContext.getValidator().getConstraintsForClass(parent.getDeclaringClass())
- .getConstraintsForMethod(parent.getName(),
parent.getParameterTypes());
-
ensureNoReturnValueAddedInChild(methodDesc.getReturnValueDescriptor(),
parentDesc,
- "Return value constraints should be at least the
same for parent and children");
-
- if (parentDesc == null) {
-
ensureMethodDoesntDefineParameterConstraint(methodDesc);
- } else {
- final Iterator<ParameterDescriptor> parentPd =
-
parentDesc.getParameterDescriptors().iterator();
- for (final ParameterDescriptor pd :
methodDesc.getParameterDescriptors()) {
- final ParameterDescriptor next =
parentPd.next();
- if (pd.getConstraintDescriptors().size() !=
next.getConstraintDescriptors().size()) {
- throw new ConstraintDeclarationException(
- "child shouldn't get more constraint
than parent");
- }
- if (pd.isCascaded() != next.isCascaded()) { //
@Valid
- throw new ConstraintDeclarationException(
- "child shouldn't get more constraint
than parent");
- }
- }
- }
- }
-
- final Class<?>[] interfaces =
method.getDeclaringClass().getInterfaces();
- final Collection<Method> itfWithThisMethod = new
ArrayList<Method>();
- for (final Class<?> i : interfaces) {
- final Method m = Reflection.getDeclaredMethod(i,
method.getName(), method.getParameterTypes());
- if (m != null) {
- itfWithThisMethod.add(m);
- }
- }
- if (itfWithThisMethod.size() > 1) {
- for (final Method m : itfWithThisMethod) {
- ensureNoConvertGroup(m, "ConvertGroup can't be
used in parallel interfaces");
- }
- } else if (itfWithThisMethod.size() == 1) {
-
ensureNoConvertGroup(itfWithThisMethod.iterator().next(),
- "ConvertGroup can't be used in interface AND
parent class");
- }
-
- int returnValid = 0;
- if (method.getAnnotation(Valid.class) != null) {
- returnValid++;
- }
- for (final Class<?> clazz : classHierarchy) {
- final Method overridden =
- Reflection.getDeclaredMethod(clazz,
method.getName(), method.getParameterTypes());
- if (overridden != null) {
- if (overridden.getAnnotation(Valid.class) != null)
{
- returnValid++;
- }
- }
- }
- if (returnValid > 1
- && !(interfaces.length == returnValid &&
method.getAnnotation(Valid.class) == null)) {
- throw new ConstraintDeclarationException(
- "@Valid on returned value can't be set more than
once");
- }
- }
-
- if (propertyAccessor) {
- final MetaProperty prop =
-
metaBean.getProperty(Introspector.decapitalize(method.getName().substring(3)));
- if (prop != null &&
prop.getFeature(Features.Property.REF_CASCADE) != null) {
- methodDesc.setCascaded(true);
- }
- }
-
- if (!methodDesc.getGroupConversions().isEmpty() &&
!methodDesc.isCascaded()) {
- throw new ConstraintDeclarationException("@Valid is needed
to define a group conversion");
- }
- }
-
- for (final Class<?> parent : classHierarchy) {
- final BeanDescriptorImpl desc =
-
BeanDescriptorImpl.class.cast(factoryContext.getValidator().getConstraintsForClass(parent));
- for (final String s : desc.meta.methodConstraints.keySet()) {
- if (!methodConstraints.containsKey(s)) { // method from
the parent only
- methodConstraints.put(s,
desc.meta.methodConstraints.get(s));
- }
- }
- }
- }
-
- private void
ensureMethodDoesntDefineParameterConstraint(MethodDescriptorImpl methodDesc) {
- for (final ParameterDescriptor pd :
methodDesc.getParameterDescriptors()) {
- if (!pd.getConstraintDescriptors().isEmpty()) {
- throw new ConstraintDeclarationException("child shouldn't
get more constraint than parent");
- }
- if (pd.isCascaded()) { // @Valid
- throw new ConstraintDeclarationException("child shouldn't
get more constraint than parent");
- }
- }
- }
-
- private void ensureNoReturnValueAddedInChild(final
ReturnValueDescriptor returnValueDescriptor,
- final MethodDescriptor parentMtdDesc, final String msg) {
- if (parentMtdDesc == null) {
- return;
- }
- final ReturnValueDescriptor parentReturnDesc =
parentMtdDesc.getReturnValueDescriptor();
- if (parentReturnDesc.isCascaded() &&
!returnValueDescriptor.isCascaded() || parentReturnDesc
- .getConstraintDescriptors().size() >
returnValueDescriptor.getConstraintDescriptors().size()) {
- throw new ConstraintDeclarationException(msg);
- }
- }
-
- private static void ensureNoParameterConstraint(final
InvocableElementDescriptor constraintsForMethod,
- final String msg) {
- for (final ParameterDescriptor parameterDescriptor :
constraintsForMethod.getParameterDescriptors()) {
- if (!parameterDescriptor.getConstraintDescriptors().isEmpty()
|| parameterDescriptor.isCascaded()) {
- throw new ConstraintDeclarationException(msg);
- }
- }
- }
-
- private static void ensureNoConvertGroup(final Method method, final
String msg) {
- for (final Annotation[] annotations :
method.getParameterAnnotations()) {
- for (final Annotation a : annotations) {
- if (ConvertGroup.class.isInstance(a)) {
- throw new ConstraintDeclarationException(msg);
- }
- }
- }
- if (method.getAnnotation(ConvertGroup.class) != null) {
- throw new ConstraintDeclarationException(msg);
- }
- }
-
- private void processMethod(final Method method, final
MethodDescriptorImpl methodDesc)
- throws InvocationTargetException, IllegalAccessException {
- final AnnotationIgnores annotationIgnores =
factoryContext.getFactory().getAnnotationIgnores();
-
- { // reflection
- if (!annotationIgnores.isIgnoreAnnotations(method)) {
- // return value validations and/or cross-parameter
validation
- for (final Annotation anno : method.getAnnotations()) {
- if (anno instanceof Valid || anno instanceof Validate)
{
- methodDesc.setCascaded(true);
- } else {
- processAnnotations(method, methodDesc,
method.getReturnType(), anno);
- }
- }
- }
-
- // parameter validations
- final Annotation[][] paramsAnnos =
method.getParameterAnnotations();
- int idx = 0;
- final List<String> names =
factoryContext.getParameterNameProvider().getParameterNames(method);
- for (final Annotation[] paramAnnos : paramsAnnos) {
- if
(annotationIgnores.isIgnoreAnnotationOnParameter(method, idx)) {
- final ParameterDescriptorImpl parameterDescriptor =
- new ParameterDescriptorImpl(metaBean,
EMPTY_VALIDATION, names.get(idx));
- parameterDescriptor.setIndex(idx);
-
methodDesc.getParameterDescriptors().add(parameterDescriptor);
- } else {
- final ParameterAccess access = new
ParameterAccess(method.getParameterTypes()[idx], idx);
- processAnnotations(methodDesc, paramAnnos, access,
idx, names.get(idx));
- }
- idx++;
- }
- }
-
- if (annotationIgnores.isIgnoreAnnotationOnCrossParameter(method)
- && methodDesc.getCrossParameterDescriptor() != null) {
- methodDesc.setCrossParameterDescriptor(null);
- }
- if (annotationIgnores.isIgnoreAnnotationOnReturn(method) &&
methodDesc.getReturnValueDescriptor() != null) {
- methodDesc.setReturnValueDescriptor(null);
- }
-
- final MetaMethod metaMethod = metaBean.getMethod(method);
- if (metaMethod != null) {
- for (final Annotation anno : metaMethod.getAnnotations()) {
- if (anno instanceof Valid) {
- methodDesc.setCascaded(true);
- } else {
- // set first param as null to force it to be read
- processAnnotations(null, methodDesc,
method.getReturnType(), anno);
- }
- }
-
- // parameter validations
- final Collection<MetaParameter> paramsAnnos =
metaMethod.getParameters();
- final List<String> names =
factoryContext.getParameterNameProvider().getParameterNames(method);
- for (final MetaParameter paramAnnos : paramsAnnos) {
- final int idx = paramAnnos.getIndex();
- final ParameterAccess access = new
ParameterAccess(method.getParameterTypes()[idx], idx);
- processAnnotations(methodDesc,
paramAnnos.getAnnotations(), access, idx, names.get(idx));
- }
- }
- }
-
- private AppendValidationToList
processAnnotations(InvocableElementDescriptor methodDesc,
- Annotation[] paramAnnos, AccessStrategy access, int idx, String
name)
- throws InvocationTargetException, IllegalAccessException {
- final AppendValidationToList validations = new
AppendValidationToList();
- boolean cascaded = false;
-
- Group[] from = null;
- Group[] to = null;
-
- for (final Annotation anno : paramAnnos) {
- if (anno instanceof Valid || anno instanceof Validate) {
- cascaded = true;
- } else if (ConvertGroup.class.isInstance(anno)) {
- final ConvertGroup cg = ConvertGroup.class.cast(anno);
- from = new Group[] { new Group(cg.from()) };
- to = new Group[] { new Group(cg.to()) };
- } else if (ConvertGroup.List.class.isInstance(anno)) {
- final ConvertGroup.List cgl =
ConvertGroup.List.class.cast(anno);
- final ConvertGroup[] groups = cgl.value();
- from = new Group[groups.length];
- to = new Group[groups.length];
- for (int i = 0; i < to.length; i++) {
- from[i] = new Group(groups[i].from());
- to[i] = new Group(groups[i].to());
- }
- } else {
- processConstraint(anno, methodDesc, access, validations);
- }
- }
-
- ParameterDescriptorImpl paramDesc = null;
- for (final ParameterDescriptor pd :
methodDesc.getParameterDescriptors()) {
- if (pd.getIndex() == idx) {
- paramDesc = ParameterDescriptorImpl.class.cast(pd);
- }
- }
-
- if (paramDesc == null) {
- paramDesc = new
ParameterDescriptorImpl(Class.class.cast(access.getJavaType()), // set from
getParameterTypes() so that's a Class<?>
- validations.getValidations().toArray(new
Validation[validations.getValidations().size()]), name);
- paramDesc.setIndex(idx);
- final List<ParameterDescriptor> parameterDescriptors =
methodDesc.getParameterDescriptors();
- if (!parameterDescriptors.contains(paramDesc)) {
- parameterDescriptors.add(paramDesc);
- }
- paramDesc.setCascaded(cascaded);
- } else {
- final List<ConstraintValidation<?>> newValidations =
validations.getValidations();
- for (final ConstraintValidation<?> validation :
newValidations) { // don't add it if exactly the same is already here
- boolean alreadyHere = false;
- for (final ConstraintDescriptor<?> existing :
paramDesc.getMutableConstraintDescriptors()) {
- if (existing.getAnnotation().annotationType()
-
.equals(validation.getAnnotation().annotationType())) { // TODO: make it a bit
finer
- alreadyHere = true;
- break;
- }
- }
- if (!alreadyHere) {
-
paramDesc.getMutableConstraintDescriptors().add(validation);
- }
- }
- if (cascaded) {
- paramDesc.setCascaded(true);
- } // else keep previous config
- }
- if (from != null) {
- if (paramDesc.isCascaded()) {
- for (int i = 0; i < from.length; i++) {
- paramDesc.addGroupConversion(new
GroupConversionDescriptorImpl(from[i], to[i]));
- }
- } else {
- throw new ConstraintDeclarationException("Group conversion
is only relevant for @Valid cases");
- }
- }
- return validations;
- }
-
- private <A extends Annotation> void processAnnotation(final A
annotation, final InvocableElementDescriptor desc,
- final AccessStrategy access, final AppendValidation validations)
- throws InvocationTargetException, IllegalAccessException {
- if
(annotation.annotationType().getName().startsWith("java.lang.annotation.")) {
- return;
- }
-
- if (annotation instanceof Valid || annotation instanceof Validate)
{
- desc.setCascaded(true);
- } else if (ConvertGroup.class.isInstance(annotation) &&
ReturnAccess.class.isInstance(access)) { // access is just tested to ensure to
not read it twice with cross parameter
- final ConvertGroup cg = ConvertGroup.class.cast(annotation);
- desc.addGroupConversion(new GroupConversionDescriptorImpl(new
Group(cg.from()), new Group(cg.to())));
- } else if (ConvertGroup.List.class.isInstance(annotation) &&
ReturnAccess.class.isInstance(access)) {
- final ConvertGroup.List cgl =
ConvertGroup.List.class.cast(annotation);
- for (final ConvertGroup cg : cgl.value()) {
- desc.addGroupConversion(
- new GroupConversionDescriptorImpl(new
Group(cg.from()), new Group(cg.to())));
- }
- } else {
- processConstraint(annotation, desc, access, validations);
- }
- }
-
- private <A extends Annotation> void processConstraint(final A
annotation, final InvocableElementDescriptor desc,
- final AccessStrategy access, final AppendValidation validations)
- throws IllegalAccessException, InvocationTargetException {
- final Constraint vcAnno =
annotation.annotationType().getAnnotation(Constraint.class);
- if (vcAnno == null) {
- /*
- * Multi-valued constraints
- */
- final ConstraintAnnotationAttributes.Worker<? extends
Annotation> worker =
-
ConstraintAnnotationAttributes.VALUE.analyze(annotation.annotationType());
- if (worker.isValid()) {
- final Object value = worker.read(annotation);
- if (Annotation[].class.isInstance(value)) {
- final Annotation[] children =
Annotation[].class.cast(value);
- for (Annotation child : children) {
- processAnnotation(child, desc, access,
validations); // recursion
- }
- }
- }
- } else {
- annotationProcessor.processAnnotation(annotation, null,
- Reflection.primitiveToWrapper((Class<?>)
access.getJavaType()), access, validations, true);
- }
- }
-
- private void buildExecutableDescriptors() {
- try {
- buildMethodConstraints();
- buildConstructorConstraints();
- } catch (final Exception ex) {
- if (RuntimeException.class.isInstance(ex)) {
- throw RuntimeException.class.cast(ex);
- }
- throw new IllegalArgumentException(ex.getMessage(), ex);
- }
- }
- }
-}