You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by mb...@apache.org on 2018/02/25 19:38:25 UTC
[5/6] bval git commit: clean up no-longer-used code from JSR module
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);
- }
- }
- }
-}