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/21 20:25:00 UTC
[04/11] bval git commit: implement BV 2.0 against existing BVal unit
tests
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
new file mode 100644
index 0000000..37082d4
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlBuilder.java
@@ -0,0 +1,694 @@
+/*
+ * 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.metadata;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedParameterizedType;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ConstraintTarget;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.xml.bind.JAXBElement;
+
+import org.apache.bval.jsr.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.jsr.util.AnnotationsManager;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.jsr.xml.AnnotationProxyBuilder;
+import org.apache.bval.jsr.xml.AnnotationType;
+import org.apache.bval.jsr.xml.BeanType;
+import org.apache.bval.jsr.xml.ClassType;
+import org.apache.bval.jsr.xml.ConstraintMappingsType;
+import org.apache.bval.jsr.xml.ConstraintType;
+import org.apache.bval.jsr.xml.ConstructorType;
+import org.apache.bval.jsr.xml.ContainerElementTypeType;
+import org.apache.bval.jsr.xml.CrossParameterType;
+import org.apache.bval.jsr.xml.ElementType;
+import org.apache.bval.jsr.xml.FieldType;
+import org.apache.bval.jsr.xml.GetterType;
+import org.apache.bval.jsr.xml.GroupConversionType;
+import org.apache.bval.jsr.xml.GroupSequenceType;
+import org.apache.bval.jsr.xml.GroupsType;
+import org.apache.bval.jsr.xml.MethodType;
+import org.apache.bval.jsr.xml.ParameterType;
+import org.apache.bval.jsr.xml.PayloadType;
+import org.apache.bval.jsr.xml.ReturnValueType;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.ObjectUtils;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+@Privilizing(@CallTo(Reflection.class))
+public class XmlBuilder {
+ //@formatter:off
+ public enum Version {
+ v10("1.0"), v11("1.1"), v20("2.0");
+
+ final BigDecimal number;
+ private final String id;
+
+ private Version(String number) {
+ this.id = number;
+ this.number = new BigDecimal(number);
+ }
+
+ public String getId() {
+ return id;
+ }
+ }
+ //@formatter:on
+
+ private class ForBean implements MetadataBuilder.ForBean {
+
+ private final BeanType descriptor;
+
+ ForBean(BeanType descriptor) {
+ super();
+ this.descriptor = Validate.notNull(descriptor, "descriptor");
+ }
+
+ Class<?> getBeanClass() {
+ return resolveClass(descriptor.getClazz());
+ }
+
+ @Override
+ public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+ return new XmlBuilder.ForClass(descriptor.getClassType());
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+ return descriptor.getField().stream()
+ .collect(ToUnmodifiable.map(FieldType::getName, XmlBuilder.ForField::new));
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+ return descriptor.getGetter().stream()
+ .collect(ToUnmodifiable.map(GetterType::getName, XmlBuilder.ForGetter::new));
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+ if (!atLeast(Version.v11)) {
+ return Collections.emptyMap();
+ }
+ final Function<ConstructorType, Class<?>[]> params = ct -> ct.getParameter().stream()
+ .map(ParameterType::getType).map(XmlBuilder.this::resolveClass).toArray(Class[]::new);
+
+ final Function<ConstructorType, Signature> signature =
+ ct -> new Signature(meta.getHost().getSimpleName(), params.apply(ct));
+
+ return descriptor.getConstructor().stream()
+ .collect(Collectors.toMap(signature, XmlBuilder.ForConstructor::new));
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+ if (!atLeast(Version.v11)) {
+ return Collections.emptyMap();
+ }
+ final Function<MethodType, Class<?>[]> params = mt -> mt.getParameter().stream().map(ParameterType::getType)
+ .map(XmlBuilder.this::resolveClass).toArray(Class[]::new);
+
+ final Function<MethodType, Signature> signature = mt -> new Signature(mt.getName(), params.apply(mt));
+
+ return descriptor.getMethod().stream().collect(Collectors.toMap(signature, XmlBuilder.ForMethod::new));
+ }
+
+ @Override
+ public final AnnotationBehavior getAnnotationBehavior() {
+ return descriptor.getIgnoreAnnotations() ? AnnotationBehavior.EXCLUDE : AnnotationBehavior.INCLUDE;
+ }
+ }
+
+ private class NonRootLevel<SELF extends NonRootLevel<SELF, D>, D> implements HasAnnotationBehavior {
+ protected final D descriptor;
+ private Lazy<Boolean> getIgnoreAnnotations;
+
+ public NonRootLevel(D descriptor) {
+ super();
+ this.descriptor = Validate.notNull(descriptor, "descriptor");
+ }
+
+ @Override
+ public final AnnotationBehavior getAnnotationBehavior() {
+ return Optional.ofNullable(getIgnoreAnnotations).map(Lazy::get)
+ .map(b -> b.booleanValue() ? AnnotationBehavior.EXCLUDE : AnnotationBehavior.INCLUDE)
+ .orElse(AnnotationBehavior.ABSTAIN);
+ }
+
+ @SuppressWarnings("unchecked")
+ final SELF withGetIgnoreAnnotations(Function<D, Boolean> getIgnoreAnnotations) {
+ Validate.notNull(getIgnoreAnnotations);
+ this.getIgnoreAnnotations = new Lazy<>(() -> getIgnoreAnnotations.apply(descriptor));
+ return (SELF) this;
+ }
+ }
+
+ private class ForElement<SELF extends XmlBuilder.ForElement<SELF, E, D>, E extends AnnotatedElement, D>
+ extends NonRootLevel<SELF, D> implements MetadataBuilder.ForElement<E> {
+
+ private Lazy<Annotation[]> getDeclaredConstraints;
+
+ ForElement(D descriptor) {
+ super(descriptor);
+ }
+
+ @Override
+ public final Annotation[] getDeclaredConstraints(Metas<E> meta) {
+ return lazy(getDeclaredConstraints, "getDeclaredConstraints");
+ }
+
+ final SELF withGetConstraintTypes(Function<D, List<ConstraintType>> getConstraintTypes) {
+ return withGetDeclaredConstraints(getConstraintTypes
+ .andThen(l -> l.stream().map(XmlBuilder.this::createConstraint).toArray(Annotation[]::new)));
+ }
+
+ @SuppressWarnings("unchecked")
+ final SELF withGetDeclaredConstraints(Function<D, Annotation[]> getDeclaredConstraints) {
+ this.getDeclaredConstraints = new Lazy<>(() -> getDeclaredConstraints.apply(descriptor));
+ return (SELF) this;
+ }
+ }
+
+ private class ForClass extends ForElement<ForClass, Class<?>, ClassType> implements MetadataBuilder.ForClass {
+
+ ForClass(ClassType descriptor) {
+ super(descriptor);
+ this.withGetConstraintTypes(ct -> ct.getConstraint());
+ }
+
+ @Override
+ public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+ final GroupSequenceType groupSequence = descriptor.getGroupSequence();
+ return groupSequence == null ? null
+ : groupSequence.getValue().stream().map(XmlBuilder.this::resolveClass).collect(ToUnmodifiable.list());
+ }
+ }
+
+ private class ForContainer<SELF extends XmlBuilder.ForContainer<SELF, E, D>, E extends AnnotatedElement, D>
+ extends XmlBuilder.ForElement<SELF, E, D> implements MetadataBuilder.ForContainer<E> {
+
+ private Lazy<Boolean> isCascade;
+ private Lazy<Set<GroupConversion>> getGroupConversions;
+ private Lazy<List<ContainerElementTypeType>> getContainerElementTypes;
+
+ ForContainer(D descriptor) {
+ super(descriptor);
+ }
+
+ @Override
+ public boolean isCascade(Metas<E> meta) {
+ return lazy(isCascade, "isCascade").booleanValue();
+ }
+
+ @Override
+ public Set<GroupConversion> getGroupConversions(Metas<E> meta) {
+ return lazy(getGroupConversions, "getGroupConversions");
+ }
+
+ @Override
+ public Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+ Metas<E> meta) {
+ if (!atLeast(Version.v20)) {
+ return Collections.emptyMap();
+ }
+ final List<ContainerElementTypeType> elements = lazy(getContainerElementTypes, "getContainerElementTypes");
+ final AnnotatedType annotatedType = meta.getAnnotatedType();
+ final E host = meta.getHost();
+
+ if (annotatedType instanceof AnnotatedParameterizedType) {
+ final AnnotatedType[] actualTypeArguments =
+ ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();
+
+ return elements.stream().collect(ToUnmodifiable.map(cet -> {
+ Integer typeArgumentIndex = cet.getTypeArgumentIndex();
+ if (typeArgumentIndex == null) {
+ Exceptions.raiseIf(actualTypeArguments.length > 1, ValidationException::new,
+ "Missing required type argument index for %s", host);
+ typeArgumentIndex = Integer.valueOf(0);
+ }
+ return new ContainerElementKey((AnnotatedParameterizedType) annotatedType, typeArgumentIndex);
+ }, XmlBuilder.ForContainerElementType::new));
+ }
+ Exceptions.raiseUnless(elements.isEmpty(), ValidationException::new,
+ "Illegally specified %d container element type(s) for %s", elements.size(), host);
+
+ return Collections.emptyMap();
+ }
+
+ @SuppressWarnings("unchecked")
+ SELF withGetValid(Function<D, String> getValid) {
+ Validate.notNull(getValid);
+ this.isCascade = new Lazy<>(() -> getValid.apply(descriptor) != null);
+ return (SELF) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ SELF withGetGroupConversions(Function<D, List<GroupConversionType>> getGroupConversions) {
+ Validate.notNull(getGroupConversions);
+
+ this.getGroupConversions = new Lazy<>(() -> {
+ return getGroupConversions.apply(descriptor).stream().map(gc -> {
+ final Class<?> source = resolveClass(gc.getFrom());
+ final Class<?> target = resolveClass(gc.getTo());
+ return GroupConversion.from(source).to(target);
+ }).collect(ToUnmodifiable.set());
+ });
+ return (SELF) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ SELF withGetContainerElementTypes(Function<D, List<ContainerElementTypeType>> getContainerElementTypes) {
+ Validate.notNull(getContainerElementTypes);
+ this.getContainerElementTypes = new Lazy<>(() -> getContainerElementTypes.apply(descriptor));
+ return (SELF) this;
+ }
+ }
+
+ private class ForContainerElementType
+ extends ForContainer<ForContainerElementType, AnnotatedType, ContainerElementTypeType> {
+
+ ForContainerElementType(ContainerElementTypeType descriptor) {
+ super(descriptor);
+ this.withGetConstraintTypes(ContainerElementTypeType::getConstraint)
+ .withGetValid(ContainerElementTypeType::getValid)
+ .withGetGroupConversions(ContainerElementTypeType::getConvertGroup)
+ .withGetContainerElementTypes(ContainerElementTypeType::getContainerElementType);
+ }
+ }
+
+ private class ForField extends XmlBuilder.ForContainer<ForField, Field, FieldType> {
+
+ ForField(FieldType descriptor) {
+ super(descriptor);
+ this.withGetIgnoreAnnotations(FieldType::getIgnoreAnnotations)
+ .withGetConstraintTypes(FieldType::getConstraint).withGetValid(FieldType::getValid)
+ .withGetGroupConversions(FieldType::getConvertGroup)
+ .withGetContainerElementTypes(FieldType::getContainerElementType);
+ }
+ }
+
+ private class ForGetter extends XmlBuilder.ForContainer<ForGetter, Method, GetterType> {
+
+ ForGetter(GetterType descriptor) {
+ super(descriptor);
+ this.withGetIgnoreAnnotations(GetterType::getIgnoreAnnotations)
+ .withGetConstraintTypes(GetterType::getConstraint).withGetValid(GetterType::getValid)
+ .withGetGroupConversions(GetterType::getConvertGroup)
+ .withGetContainerElementTypes(GetterType::getContainerElementType);
+ }
+ }
+
+ private abstract class ForExecutable<SELF extends ForExecutable<SELF, E, D>, E extends Executable, D>
+ extends NonRootLevel<SELF, D> implements MetadataBuilder.ForExecutable<E> {
+
+ Lazy<ReturnValueType> getReturnValue;
+ Lazy<CrossParameterType> getCrossParameter;
+ Lazy<List<ParameterType>> getParameters;
+
+ ForExecutable(D descriptor) {
+ super(descriptor);
+ }
+
+ @Override
+ public MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta) {
+ return new XmlBuilder.ForCrossParameter<>(lazy(getCrossParameter, "getCrossParameter"));
+ }
+
+ @Override
+ public MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta) {
+ return new XmlBuilder.ForReturnValue<>(lazy(getReturnValue, "getReturnValue"));
+ }
+
+ @Override
+ public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<E> meta) {
+ return lazy(getParameters, "getParameters").stream().map(XmlBuilder.ForParameter::new)
+ .collect(Collectors.toList());
+ }
+
+ @SuppressWarnings("unchecked")
+ SELF withGetReturnValue(Function<D, ReturnValueType> getReturnValue) {
+ Validate.notNull(getReturnValue);
+ this.getReturnValue = new Lazy<>(() -> getReturnValue.apply(descriptor));
+ return (SELF) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ SELF withGetCrossParameter(Function<D, CrossParameterType> getCrossParameter) {
+ Validate.notNull(getCrossParameter);
+ this.getCrossParameter = new Lazy<>(() -> getCrossParameter.apply(descriptor));
+ return (SELF) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ SELF withGetParameters(Function<D, List<ParameterType>> getParameters) {
+ Validate.notNull(getParameters);
+ this.getParameters = new Lazy<>(() -> getParameters.apply(descriptor));
+ return (SELF) this;
+ }
+ }
+
+ private class ForConstructor extends ForExecutable<ForConstructor, Constructor<?>, ConstructorType> {
+
+ ForConstructor(ConstructorType descriptor) {
+ super(descriptor);
+ this.withGetIgnoreAnnotations(ConstructorType::getIgnoreAnnotations)
+ .withGetReturnValue(ConstructorType::getReturnValue)
+ .withGetCrossParameter(ConstructorType::getCrossParameter)
+ .withGetParameters(ConstructorType::getParameter);
+ }
+ }
+
+ private class ForMethod extends ForExecutable<ForMethod, Method, MethodType> {
+
+ ForMethod(MethodType descriptor) {
+ super(descriptor);
+ this.withGetIgnoreAnnotations(MethodType::getIgnoreAnnotations)
+ .withGetReturnValue(MethodType::getReturnValue).withGetCrossParameter(MethodType::getCrossParameter)
+ .withGetParameters(MethodType::getParameter);
+ }
+ }
+
+ private class ForParameter extends ForContainer<ForParameter, Parameter, ParameterType> {
+
+ ForParameter(ParameterType descriptor) {
+ super(descriptor);
+ this.withGetIgnoreAnnotations(ParameterType::getIgnoreAnnotations)
+ .withGetConstraintTypes(ParameterType::getConstraint).withGetValid(ParameterType::getValid)
+ .withGetGroupConversions(ParameterType::getConvertGroup)
+ .withGetContainerElementTypes(ParameterType::getContainerElementType);
+ }
+ }
+
+ private class ForCrossParameter<E extends Executable>
+ extends ForElement<ForCrossParameter<E>, E, CrossParameterType> {
+
+ ForCrossParameter(CrossParameterType descriptor) {
+ super(descriptor);
+ this.withGetIgnoreAnnotations(CrossParameterType::getIgnoreAnnotations)
+ .withGetDeclaredConstraints(d -> d.getConstraint().stream()
+ .map(ct -> createConstraint(ct, ConstraintTarget.PARAMETERS)).toArray(Annotation[]::new));
+ }
+ }
+
+ private class ForReturnValue<E extends Executable> extends ForContainer<ForReturnValue<E>, E, ReturnValueType> {
+
+ ForReturnValue(ReturnValueType descriptor) {
+ super(descriptor);
+ this.withGetDeclaredConstraints(d -> d.getConstraint().stream()
+ .map(ct -> createConstraint(ct, ConstraintTarget.RETURN_VALUE)).toArray(Annotation[]::new))
+ .withGetContainerElementTypes(d -> d.getContainerElementType());
+ }
+ }
+
+ private static final Set<ConstraintAnnotationAttributes> RESERVED_PARAMS = Collections
+ .unmodifiableSet(EnumSet.of(ConstraintAnnotationAttributes.GROUPS, ConstraintAnnotationAttributes.MESSAGE,
+ ConstraintAnnotationAttributes.PAYLOAD, ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO));
+
+ static final <T> T lazy(Lazy<T> lazy, String name) {
+ Validate.validState(lazy != null, "%s not set", name);
+ return lazy.get();
+ }
+
+ private final ConstraintMappingsType constraintMappings;
+ private final BigDecimal version;
+
+ public XmlBuilder(ConstraintMappingsType constraintMappings) {
+ super();
+ this.constraintMappings = constraintMappings;
+ Validate.notNull(constraintMappings, "constraintMappings");
+
+ BigDecimal v;
+ try {
+ v = new BigDecimal(constraintMappings.getVersion());
+ } catch (NumberFormatException e) {
+ v = Version.v10.number;
+ }
+ this.version = v;
+ }
+
+ public Map<Class<?>, MetadataBuilder.ForBean> forBeans() {
+ return constraintMappings.getBean().stream().map(XmlBuilder.ForBean::new)
+ .collect(ToUnmodifiable.map(XmlBuilder.ForBean::getBeanClass, Function.identity()));
+ }
+
+ public String getDefaultPackage() {
+ return constraintMappings.getDefaultPackage();
+ }
+
+ boolean atLeast(Version v) {
+ return version.compareTo(v.number) >= 0;
+ }
+
+ <T> Class<T> resolveClass(String className) {
+ return loadClass(toQualifiedClassName(className));
+ }
+
+ private String toQualifiedClassName(String className) {
+ if (isQualifiedClass(className)) {
+ return className;
+ }
+ if (className.startsWith("[L") && className.endsWith(";")) {
+ return "[L" + getDefaultPackage() + "." + className.substring(2);
+ }
+ return getDefaultPackage() + "." + className;
+ }
+
+ private boolean isQualifiedClass(String clazz) {
+ return clazz.indexOf('.') >= 0;
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> Class<T> loadClass(final String fqn) {
+ ClassLoader loader = Reflection.getClassLoader(XmlBuilder.class);
+ if (loader == null) {
+ loader = getClass().getClassLoader();
+ }
+ try {
+ return (Class<T>) Class.forName(fqn, true, loader);
+ } catch (ClassNotFoundException ex) {
+ throw Exceptions.create(ValidationException::new, ex, "Unable to load class: %d", fqn);
+ }
+ }
+
+ private Class<?>[] loadClasses(Supplier<Stream<String>> classNames) {
+ return streamClasses(classNames).toArray(Class[]::new);
+ }
+
+ private Stream<Class<?>> streamClasses(Supplier<Stream<String>> classNames) {
+ return classNames.get().map(this::loadClass);
+ }
+
+ private <A extends Annotation, T> A createConstraint(final ConstraintType constraint) {
+ return createConstraint(constraint, ConstraintTarget.IMPLICIT);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <A extends Annotation, T> A createConstraint(final ConstraintType constraint, ConstraintTarget target) {
+
+ final Class<A> annotationClass = (Class<A>) loadClass(toQualifiedClassName(constraint.getAnnotation()));
+ final AnnotationProxyBuilder<A> annoBuilder = new AnnotationProxyBuilder<A>(annotationClass);
+
+ if (constraint.getMessage() != null) {
+ annoBuilder.setMessage(constraint.getMessage());
+ }
+ annoBuilder.setGroups(getGroups(constraint.getGroups()));
+ annoBuilder.setPayload(getPayload(constraint.getPayload()));
+
+ if (AnnotationsManager.declaresAttribute(annotationClass,
+ ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.getAttributeName())) {
+ annoBuilder.setValidationAppliesTo(target);
+ }
+
+ for (final ElementType elementType : constraint.getElement()) {
+ final String name = elementType.getName();
+ checkValidName(name);
+
+ final Class<?> returnType = getAnnotationParameterType(annotationClass, name);
+ final Object elementValue = getElementValue(elementType, returnType);
+ annoBuilder.setValue(name, elementValue);
+ }
+ return annoBuilder.createAnnotation();
+ }
+
+ private void checkValidName(String name) {
+ Exceptions.raiseIf(RESERVED_PARAMS.stream().map(ConstraintAnnotationAttributes::getAttributeName)
+ .anyMatch(Predicate.isEqual(name)), ValidationException::new, "%s is a reserved parameter name.", name);
+ }
+
+ private <A extends Annotation> Class<?> getAnnotationParameterType(final Class<A> annotationClass,
+ final String name) {
+ final Method m = Reflection.getPublicMethod(annotationClass, name);
+ Exceptions.raiseIf(m == null, ValidationException::new,
+ "Annotation of type %s does not contain a parameter %s.", annotationClass.getName(), name);
+ return m.getReturnType();
+ }
+
+ private Object getElementValue(ElementType elementType, Class<?> returnType) {
+ removeEmptyContentElements(elementType);
+
+ final List<Serializable> content = elementType.getContent();
+ final int sz = content.size();
+ if (returnType.isArray()) {
+ final Object result = Array.newInstance(returnType.getComponentType(), sz);
+ for (int i = 0; i < sz; i++) {
+ Array.set(result, i, getSingleValue(content.get(i), returnType.getComponentType()));
+ }
+ return result;
+ }
+ Exceptions.raiseIf(sz != 1, ValidationException::new,
+ "Attempt to specify an array where single value is expected.");
+
+ return getSingleValue(content.get(0), returnType);
+ }
+
+ private void removeEmptyContentElements(ElementType elementType) {
+ for (Iterator<Serializable> iter = elementType.getContent().iterator(); iter.hasNext();) {
+ final Serializable content = iter.next();
+ if (content instanceof String && ((String) content).matches("[\\n ].*")) {
+ iter.remove();
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object getSingleValue(Serializable serializable, Class<?> returnType) {
+ if (serializable instanceof String) {
+ return convertToResultType(returnType, (String) serializable);
+ }
+ if (serializable instanceof JAXBElement<?>) {
+ final JAXBElement<?> elem = (JAXBElement<?>) serializable;
+ if (String.class.equals(elem.getDeclaredType())) {
+ return convertToResultType(returnType, (String) elem.getValue());
+ }
+ if (AnnotationType.class.equals(elem.getDeclaredType())) {
+ AnnotationType annotationType = (AnnotationType) elem.getValue();
+ try {
+ return createAnnotation(annotationType, (Class<? extends Annotation>) returnType);
+ } catch (ClassCastException e) {
+ throw new ValidationException("Unexpected parameter value");
+ }
+ }
+ }
+ throw new ValidationException("Unexpected parameter value");
+ }
+
+ private Object convertToResultType(Class<?> returnType, String value) {
+ /**
+ * Class is represented by the fully qualified class name of the class. spec: Note that if the raw string is
+ * unqualified, default package is taken into account.
+ */
+ if (String.class.equals(returnType)) {
+ return value;
+ }
+ if (Class.class.equals(returnType)) {
+ return resolveClass(value);
+ }
+ if (returnType.isEnum()) {
+ try {
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ final Enum e = Enum.valueOf(returnType.asSubclass(Enum.class), value);
+ return e;
+ } catch (IllegalArgumentException e) {
+ throw new ConstraintDeclarationException(e);
+ }
+ }
+ if (Byte.class.equals(returnType) || byte.class.equals(returnType)) {
+ // spec mandates it:
+ return Byte.parseByte(value);
+ }
+ if (Short.class.equals(returnType) || short.class.equals(returnType)) {
+ return Short.parseShort(value);
+ }
+ if (Integer.class.equals(returnType) || int.class.equals(returnType)) {
+ return Integer.parseInt(value);
+ }
+ if (Long.class.equals(returnType) || long.class.equals(returnType)) {
+ return Long.parseLong(value);
+ }
+ if (Float.class.equals(returnType) || float.class.equals(returnType)) {
+ return Float.parseFloat(value);
+ }
+ if (Double.class.equals(returnType) || double.class.equals(returnType)) {
+ return Double.parseDouble(value);
+ }
+ if (Boolean.class.equals(returnType) || boolean.class.equals(returnType)) {
+ return Boolean.parseBoolean(value);
+ }
+ if (Character.class.equals(returnType) || char.class.equals(returnType)) {
+ Exceptions.raiseIf(value.length() > 1, ConstraintDeclarationException::new,
+ "a char must have a length of 1");
+ return value.charAt(0);
+ }
+ return Exceptions.raise(ValidationException::new, "Unknown annotation value type %s", returnType.getName());
+ }
+
+ private <A extends Annotation> Annotation createAnnotation(AnnotationType annotationType, Class<A> returnType) {
+ final AnnotationProxyBuilder<A> metaAnnotation = new AnnotationProxyBuilder<>(returnType);
+ for (ElementType elementType : annotationType.getElement()) {
+ final String name = elementType.getName();
+ metaAnnotation.setValue(name, getElementValue(elementType, getAnnotationParameterType(returnType, name)));
+ }
+ return metaAnnotation.createAnnotation();
+ }
+
+ private Class<?>[] getGroups(GroupsType groupsType) {
+ if (groupsType == null) {
+ return ObjectUtils.EMPTY_CLASS_ARRAY;
+ }
+ return loadClasses(groupsType.getValue()::stream);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Class<? extends Payload>[] getPayload(PayloadType payloadType) {
+ if (payloadType == null) {
+ return (Class<? extends Payload>[]) ObjectUtils.EMPTY_CLASS_ARRAY;
+ }
+ return streamClasses(payloadType.getValue()::stream).peek(pc -> {
+ Exceptions.raiseUnless(Payload.class.isAssignableFrom(pc), ConstraintDeclarationException::new,
+ "Specified payload class %s does not implement %s", pc.getName(), Payload.class.getName());
+ }).<Class<? extends Payload>> map(pc -> pc.asSubclass(Payload.class)).toArray(Class[]::new);
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlValidationMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlValidationMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlValidationMappingProvider.java
new file mode 100644
index 0000000..a47d1c6
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/XmlValidationMappingProvider.java
@@ -0,0 +1,64 @@
+/*
+ * 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.metadata;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import javax.validation.ConstraintValidator;
+
+import org.apache.bval.jsr.xml.ValidatedByType;
+import org.apache.bval.util.Validate;
+
+public class XmlValidationMappingProvider extends ClassLoadingValidatorMappingProvider {
+ private static final Logger log = Logger.getLogger(XmlValidationMappingProvider.class.getName());
+
+ private final Map<Class<? extends Annotation>, ValidatedByType> config;
+ private final Function<String, String> classNameTransformer;
+
+ public XmlValidationMappingProvider(Map<Class<? extends Annotation>, ValidatedByType> validatorMappings,
+ Function<String, String> classNameTransformer) {
+ super();
+ this.config = Validate.notNull(validatorMappings, "validatorMappings");
+ this.classNameTransformer = Validate.notNull(classNameTransformer, "classNameTransformer");
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ public <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType) {
+ final ValidatedByType validatedByType = config.get(constraintType);
+ if (validatedByType == null) {
+ return null;
+ }
+ return new ValidatorMapping<>("XML descriptor",
+ load(validatedByType.getValue().stream().map(String::trim).map(classNameTransformer),
+ (Class<ConstraintValidator<A, ?>>) (Class) ConstraintValidator.class,
+ e -> log.log(Level.SEVERE, "exception loading XML-declared constraint validators", e))
+ .collect(Collectors.toList()),
+ toAnnotationBehavior(validatedByType));
+ }
+
+ private AnnotationBehavior toAnnotationBehavior(ValidatedByType validatedByType) {
+ final Boolean includeExistingValidators = validatedByType.getIncludeExistingValidators();
+ return includeExistingValidators == null ? AnnotationBehavior.ABSTAIN
+ : includeExistingValidators.booleanValue() ? AnnotationBehavior.INCLUDE : AnnotationBehavior.EXCLUDE;
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/parameter/DefaultParameterNameProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/parameter/DefaultParameterNameProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/parameter/DefaultParameterNameProvider.java
index 2b43bcd..dec9ae8 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/parameter/DefaultParameterNameProvider.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/parameter/DefaultParameterNameProvider.java
@@ -18,30 +18,29 @@
*/
package org.apache.bval.jsr.parameter;
-import javax.validation.ParameterNameProvider;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
import java.lang.reflect.Method;
-import java.util.ArrayList;
+import java.lang.reflect.Parameter;
import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.ParameterNameProvider;
public class DefaultParameterNameProvider implements ParameterNameProvider {
- private static final String ARG = "arg";
+
+ private static List<String> parameterNames(Executable exe) {
+ return Stream.of(exe.getParameters()).map(Parameter::getName).collect(Collectors.toList());
+ }
@Override
public List<String> getParameterNames(Constructor<?> constructor) {
- return names(constructor.getParameterTypes().length);
+ return parameterNames(constructor);
}
@Override
public List<String> getParameterNames(Method method) {
- return names(method.getParameterTypes().length);
- }
-
- private static List<String> names(final int length) {
- final List<String> list = new ArrayList<String>();
- for (int i = 0; i < length; i++) {
- list.add(ARG + i);
- }
- return list;
+ return parameterNames(method);
}
}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/resolver/CachingTraversableResolver.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/resolver/CachingTraversableResolver.java b/bval-jsr/src/main/java/org/apache/bval/jsr/resolver/CachingTraversableResolver.java
index 05639c7..2f212de 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/resolver/CachingTraversableResolver.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/resolver/CachingTraversableResolver.java
@@ -21,6 +21,7 @@ import javax.validation.TraversableResolver;
import java.lang.annotation.ElementType;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
/**
* Cache results of a delegated traversable resovler to optimize calls
@@ -34,7 +35,7 @@ import java.util.Map;
*/
public class CachingTraversableResolver implements TraversableResolver, CachingRelevant {
private TraversableResolver delegate;
- private Map<CacheEntry, CacheEntry> cache = new HashMap<CacheEntry, CacheEntry>();
+ private Map<CacheEntry, CacheEntry> cache = new HashMap<>();
/**
* Convenience method to check whether caching is necessary on a given {@link TraversableResolver}.
@@ -61,11 +62,8 @@ public class CachingTraversableResolver implements TraversableResolver, CachingR
* @see #needsCaching(TraversableResolver)
*/
public static TraversableResolver cacheFor(TraversableResolver traversableResolver) {
- if (needsCaching(traversableResolver)) {
- return new CachingTraversableResolver(traversableResolver);
- } else {
- return traversableResolver;
- }
+ return needsCaching(traversableResolver) ? new CachingTraversableResolver(traversableResolver)
+ : traversableResolver;
}
/**
@@ -158,15 +156,14 @@ public class CachingTraversableResolver implements TraversableResolver, CachingR
if (this == o) {
return true;
}
- if (o == null || getClass() != o.getClass()) {
+ if (o == null || !getClass().equals(o.getClass())) {
return false;
}
CacheEntry that = (CacheEntry) o;
- return elementType == that.elementType && path.equals(that.path) && type.equals(that.type)
- && !(object != null ? !object.equals(that.object) : that.object != null) && node.equals(that.node);
-
+ return elementType == that.elementType && Objects.equals(path, that.path) && Objects.equals(type, that.type)
+ && Objects.equals(object, that.object) && Objects.equals(node, that.node);
}
/**
@@ -178,12 +175,7 @@ public class CachingTraversableResolver implements TraversableResolver, CachingR
}
private int buildHashCode() {
- int result = object != null ? object.hashCode() : 0;
- result = 31 * result + node.hashCode();
- result = 31 * result + type.hashCode();
- result = 31 * result + path.hashCode();
- result = 31 * result + elementType.hashCode();
- return result;
+ return Objects.hash(object, node, type, path, elementType);
}
}
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
new file mode 100644
index 0000000..b53b513
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/AnnotationsManager.java
@@ -0,0 +1,359 @@
+/*
+ * 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.util;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Repeatable;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintTarget;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.validation.constraintvalidation.ValidationTarget;
+
+import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.ConfigurationImpl;
+import org.apache.bval.jsr.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo;
+import org.apache.bval.jsr.groups.Group;
+import org.apache.bval.jsr.groups.Groups;
+import org.apache.bval.jsr.groups.GroupsComputer;
+import org.apache.bval.jsr.metadata.Metas;
+import org.apache.bval.jsr.xml.AnnotationProxyBuilder;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.StringUtils;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+/**
+ * Manages (constraint) annotations according to the BV spec.
+ *
+ * @since 2.0
+ */
+@Privilizing(@CallTo(Reflection.class))
+public class AnnotationsManager {
+ private static final class OverriddenAnnotationSpecifier {
+ final Class<? extends Annotation> annotationType;
+ final boolean impliesSingleComposingConstraint;
+ final int constraintIndex;
+
+ OverriddenAnnotationSpecifier(OverridesAttribute annotation) {
+ this(annotation.constraint(), annotation.constraintIndex());
+ }
+
+ OverriddenAnnotationSpecifier(Class<? extends Annotation> annotationType, int constraintIndex) {
+ super();
+ this.annotationType = annotationType;
+ this.impliesSingleComposingConstraint = constraintIndex < 0;
+ this.constraintIndex = Math.max(constraintIndex, 0);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null || !obj.getClass().equals(getClass())) {
+ return false;
+ }
+ final OverriddenAnnotationSpecifier other = (OverriddenAnnotationSpecifier) obj;
+ return Objects.equals(annotationType, other.annotationType) && constraintIndex == other.constraintIndex;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(annotationType, constraintIndex);
+ }
+ }
+
+ private static class Composition {
+ final Lazy<Map<OverriddenAnnotationSpecifier, Map<String, String>>> overrides = new Lazy<>(HashMap::new);
+ final Annotation[] components;
+
+ Composition(Class<? extends Annotation> annotationType) {
+ // TODO detect recursion
+ components = getDeclaredConstraints(annotationType);
+
+ if (!isComposed()) {
+ return;
+ }
+ final Map<Class<? extends Annotation>, AtomicInteger> constraintCounts = new HashMap<>();
+ for (Annotation a : components) {
+ constraintCounts.computeIfAbsent(a.annotationType(), k -> new AtomicInteger()).incrementAndGet();
+ }
+ // create a map of overridden constraints to overridden attributes:
+ for (Method m : Reflection.getDeclaredMethods(annotationType)) {
+ final String from = m.getName();
+ for (OverridesAttribute overridesAttribute : m.getDeclaredAnnotationsByType(OverridesAttribute.class)) {
+ final String to =
+ Optional.of(overridesAttribute.name()).filter(StringUtils::isNotBlank).orElse(from);
+
+ final OverriddenAnnotationSpecifier spec = new OverriddenAnnotationSpecifier(overridesAttribute);
+ final int count = constraintCounts.get(spec.annotationType).get();
+
+ if (spec.impliesSingleComposingConstraint) {
+ Exceptions.raiseUnless(count == 1, ConstraintDefinitionException::new,
+ "Expected a single composing %s constraint", spec.annotationType);
+ } else {
+ Exceptions.raiseUnless(count > spec.constraintIndex, ConstraintDefinitionException::new,
+ "Expected at least %s composing %s constraints", spec.constraintIndex + 1,
+ spec.annotationType);
+ }
+ final Map<String, String> attributeMapping =
+ overrides.get().computeIfAbsent(spec, k -> new HashMap<>());
+
+ Exceptions.raiseIf(attributeMapping.containsKey(to), ConstraintDefinitionException::new,
+ "Attempt to override %s#%s() index %d from multiple sources", overridesAttribute.constraint(),
+ to, overridesAttribute.constraintIndex());
+
+ attributeMapping.put(to, from);
+ }
+ }
+ }
+
+ boolean isComposed() {
+ return components.length > 0;
+ }
+
+ Annotation[] getComponents(Annotation source) {
+ final Class<?>[] groups =
+ ConstraintAnnotationAttributes.GROUPS.analyze(source.annotationType()).read(source);
+
+ final Class<? extends Payload>[] payload =
+ ConstraintAnnotationAttributes.PAYLOAD.analyze(source.annotationType()).read(source);
+
+ final Optional<ConstraintTarget> constraintTarget =
+ Optional.of(source.annotationType()).map(ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO::analyze)
+ .filter(ConstraintAnnotationAttributes.Worker::isValid).map(w -> w.read(source));
+
+ final Map<Class<? extends Annotation>, AtomicInteger> constraintCounts = new HashMap<>();
+
+ return Stream.of(components).map(c -> {
+ final int index =
+ constraintCounts.computeIfAbsent(c.annotationType(), k -> new AtomicInteger()).getAndIncrement();
+
+ final AnnotationProxyBuilder<Annotation> proxyBuilder = new AnnotationProxyBuilder<>(c);
+
+ proxyBuilder.setGroups(groups);
+ proxyBuilder.setPayload(payload);
+ constraintTarget.ifPresent(proxyBuilder::setValidationAppliesTo);
+
+ overrides.optional().map(o -> o.get(new OverriddenAnnotationSpecifier(c.annotationType(), index)))
+ .ifPresent(m -> {
+ final Map<String, Object> sourceAttributes = readAttributes(source);
+ m.forEach((k, v) -> proxyBuilder.setValue(k, sourceAttributes.get(v)));
+ });
+ return proxyBuilder.isChanged() ? proxyBuilder.createAnnotation() : c;
+ }).toArray(Annotation[]::new);
+ }
+ }
+
+ public static Map<String, Object> readAttributes(Annotation a) {
+ final Lazy<Map<String, Object>> result = new Lazy<>(LinkedHashMap::new);
+
+ Stream.of(Reflection.getDeclaredMethods(a.annotationType())).filter(m -> m.getParameterCount() == 0)
+ .forEach(m -> {
+ final boolean mustUnset = Reflection.setAccessible(m, true);
+ try {
+ result.get().put(m.getName(), m.invoke(a));
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ Exceptions.raise(ValidationException::new, e, "Caught exception reading attributes of %s", a);
+ } finally {
+ if (mustUnset) {
+ Reflection.setAccessible(m, false);
+ }
+ }
+ });
+ return result.optional().map(Collections::unmodifiableMap).orElseGet(Collections::emptyMap);
+ }
+
+ /**
+ * Meta-annotation aware.
+ *
+ * @param e
+ * @param t
+ * @return {@code boolean}
+ * @see AnnotatedElement#isAnnotationPresent(Class)
+ */
+ public static boolean isAnnotationPresent(AnnotatedElement e, Class<? extends Annotation> t) {
+ if (e.isAnnotationPresent(t)) {
+ return true;
+ }
+ return Stream.of(e.getAnnotations()).map(Annotation::annotationType).anyMatch(a -> isAnnotationPresent(a, t));
+ }
+
+ /**
+ * Get declared annotations with a particular meta-annotation.
+ *
+ * @param e
+ * @param meta
+ * @return {@link Annotation}[]
+ */
+ public static Annotation[] getDeclared(AnnotatedElement e, Class<? extends Annotation> meta) {
+ return Stream.of(e.getDeclaredAnnotations()).filter(ann -> isAnnotationPresent(ann.annotationType(), meta))
+ .toArray(Annotation[]::new);
+ }
+
+ /**
+ * Accounts for {@link Constraint} meta-annotation AND {@link Repeatable}
+ * constraint annotations.
+ *
+ * @param meta
+ * @return Annotation[]
+ */
+ public static Annotation[] getDeclaredConstraints(Metas<?> meta) {
+ final Annotation[] result = getDeclaredConstraints(meta.getHost());
+ final Class<?> dc = meta.getDeclaringClass();
+ if (dc.isInterface()) {
+ final GroupsComputer groupsComputer = new GroupsComputer();
+ // ensure interface group is implied by Default group:
+ Stream.of(result).map(c -> {
+ final Groups groups = groupsComputer
+ .computeGroups(ConstraintAnnotationAttributes.GROUPS.analyze(c.annotationType()).read(c));
+ if (groups.getGroups().stream().anyMatch(Group::isDefault)) {
+ final Set<Class<?>> groupClasses = groups.getGroups().stream().map(Group::getGroup)
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ if (groupClasses.add(dc)) {
+ final AnnotationProxyBuilder<?> proxyBuilder = new AnnotationProxyBuilder<>(c);
+ proxyBuilder.setGroups(groupClasses.toArray(new Class[groupClasses.size()]));
+ return proxyBuilder.createAnnotation();
+ }
+ }
+ return c;
+ }).toArray(n -> result);
+ }
+ return result;
+ }
+
+ private static Annotation[] getDeclaredConstraints(AnnotatedElement e) {
+ return Stream.of(e.getDeclaredAnnotations()).flatMap((Function<Annotation, Stream<Annotation>>) a -> {
+ final ConstraintAnnotationAttributes.Worker<? extends Annotation> analyzer =
+ ConstraintAnnotationAttributes.VALUE.analyze(a.annotationType());
+ if (analyzer.isValid()) {
+ return Stream.of(analyzer.<Annotation[]> read(a));
+ }
+ return Stream.of(a);
+ }).filter(a -> a.annotationType().isAnnotationPresent(Constraint.class)).toArray(Annotation[]::new);
+ }
+
+ public static boolean declaresAttribute(Class<? extends Annotation> annotationType, String name) {
+ try {
+ annotationType.getDeclaredMethod(name);
+ return true;
+ } catch (NoSuchMethodException | SecurityException e) {
+ return false;
+ }
+ }
+
+ private final ApacheValidatorFactory validatorFactory;
+ private final LRUCache<Class<? extends Annotation>, Composition> compositions;
+
+ public AnnotationsManager(ApacheValidatorFactory validatorFactory) {
+ super();
+ this.validatorFactory = Validate.notNull(validatorFactory);
+ final String cacheSize =
+ validatorFactory.getProperties().get(ConfigurationImpl.Properties.CONSTRAINTS_CACHE_SIZE);
+ try {
+ compositions = new LRUCache<>(Integer.parseInt(cacheSize));
+ } catch (NumberFormatException e) {
+ throw Exceptions.create(IllegalStateException::new, e,
+ "Cannot parse value %s for configuration property %s", cacheSize,
+ ConfigurationImpl.Properties.CONSTRAINTS_CACHE_SIZE);
+ }
+ }
+
+ /**
+ * Retrieve the composing constraints for the specified constraint
+ * {@link Annotation}.
+ *
+ * @param a
+ * @return {@link Annotation}[]
+ */
+ public Annotation[] getComposingConstraints(Annotation a) {
+ return getComposition(a.annotationType()).getComponents(a);
+ }
+
+ /**
+ * Learn whether {@code a} is composed.
+ *
+ * @param a
+ * @return {@code boolean}
+ */
+ public boolean isComposed(Annotation a) {
+ return getComposition(a.annotationType()).isComposed();
+ }
+
+ /**
+ * Get the supported targets for {@code constraintType}.
+ *
+ * @param constraintType
+ * @return {@link Set} of {@link ValidationTarget}
+ */
+ public <A extends Annotation> Set<ValidationTarget> supportedTargets(Class<A> constraintType) {
+ final Set<ConstraintValidatorInfo<A>> constraintValidatorInfo =
+ validatorFactory.getConstraintsCache().getConstraintValidatorInfo(constraintType);
+ final Stream<Set<ValidationTarget>> s;
+ if (constraintValidatorInfo.isEmpty()) {
+ // must be for composition:
+ s = Stream.of(new Composition(constraintType).components).map(Annotation::annotationType)
+ .map(this::supportedTargets);
+ } else {
+ s = constraintValidatorInfo.stream().map(ConstraintValidatorInfo::getSupportedTargets);
+ }
+ return s.flatMap(Collection::stream)
+ .collect(Collectors.toCollection(() -> EnumSet.noneOf(ValidationTarget.class)));
+ }
+
+ private Composition getComposition(Class<? extends Annotation> annotationType) {
+ return compositions.computeIfAbsent(annotationType, ct -> {
+ final Set<ValidationTarget> composedTargets = supportedTargets(annotationType);
+ final Composition result = new Composition(annotationType);
+ Stream.of(result.components).map(Annotation::annotationType).forEach(at -> {
+ final Set<ValidationTarget> composingTargets = supportedTargets(at);
+ Exceptions.raiseIf(Collections.disjoint(composingTargets, composedTargets),
+ ConstraintDefinitionException::new,
+ "Attempt to compose %s of %s but validator types are incompatible", annotationType.getName(),
+ at.getName());
+ });
+ return result;
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java
index 9d3bd85..73c82a6 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ClassHelper.java
@@ -20,7 +20,11 @@ package org.apache.bval.jsr.util;
import java.io.Serializable;
import java.security.AccessController;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Common operations on classes that do not require an {@link AccessController}.
@@ -28,6 +32,7 @@ import java.util.List;
* @author Carlos Vara
*/
public class ClassHelper {
+ private static final Set<Class<?>> IGNORED_TYPES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(null,Object.class,Serializable.class,Cloneable.class)));
private ClassHelper() {
// No instances please
@@ -42,10 +47,7 @@ public class ClassHelper {
* @param clazz
*/
public static List<Class<?>> fillFullClassHierarchyAsList(List<Class<?>> allClasses, Class<?> clazz) {
- if (clazz == null || clazz == Object.class || clazz == Serializable.class || clazz == Cloneable.class) {
- return allClasses;
- }
- if (allClasses.contains(clazz)) {
+ if (IGNORED_TYPES.contains(clazz) || allClasses.contains(clazz)) {
return allClasses;
}
allClasses.add(clazz);
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java
new file mode 100644
index 0000000..c0cff10
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderCustomizableContextImpl.java
@@ -0,0 +1,77 @@
+/*
+ * 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.util;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeContextBuilder;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext;
+
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
+
+public class ContainerElementNodeBuilderCustomizableContextImpl
+ implements ContainerElementNodeBuilderCustomizableContext {
+ private final ConstraintValidatorContextImpl<?> context;
+ private final String template;
+ private final PathImpl path;
+ private NodeImpl node;
+
+ public ContainerElementNodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl<?> context, String template,
+ PathImpl path, String name, Class<?> containerType, Integer typeArgumentIndex) {
+ super();
+ this.context = context;
+ this.path = path;
+ this.template = template;
+ this.node = new NodeImpl.ContainerElementNodeImpl(name, containerType, typeArgumentIndex);
+ }
+
+ @Override
+ public ContainerElementNodeContextBuilder inIterable() {
+ node.setInIterable(true);
+ return new ContainerElementNodeContextBuilderImpl(context, template, path, node);
+ }
+
+ @Override
+ public NodeBuilderCustomizableContext addPropertyNode(String name) {
+ path.addNode(node);
+ return new NodeBuilderCustomizableContextImpl(context, template, path, name);
+ }
+
+ @Override
+ public LeafNodeBuilderCustomizableContext addBeanNode() {
+ path.addNode(node);
+ return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
+ }
+
+ @Override
+ public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+ Integer typeArgumentIndex) {
+ path.addNode(node);
+ node = new NodeImpl.ContainerElementNodeImpl(name, containerType, typeArgumentIndex);
+ return this;
+ }
+
+ @Override
+ public ConstraintValidatorContext addConstraintViolation() {
+ context.addError(template, path);
+ return context;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java
new file mode 100644
index 0000000..6077d87
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeBuilderDefinedContextImpl.java
@@ -0,0 +1,65 @@
+/*
+ * 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.util;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderDefinedContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext;
+
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
+
+public class ContainerElementNodeBuilderDefinedContextImpl implements ContainerElementNodeBuilderDefinedContext {
+ private final ConstraintValidatorContextImpl<?> context;
+ private final String template;
+ private final PathImpl path;
+
+ ContainerElementNodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl<?> context, String template,
+ PathImpl path) {
+ super();
+ this.context = context;
+ this.template = template;
+ this.path = path;
+ }
+
+ @Override
+ public NodeBuilderCustomizableContext addPropertyNode(String name) {
+ return new NodeBuilderCustomizableContextImpl(context, template, path, name);
+ }
+
+ @Override
+ public LeafNodeBuilderCustomizableContext addBeanNode() {
+ return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
+ }
+
+ @Override
+ public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+ Integer typeArgumentIndex) {
+ return new ContainerElementNodeBuilderCustomizableContextImpl(context, name, path, name, containerType,
+ typeArgumentIndex);
+ }
+
+ @Override
+ public ConstraintValidatorContext addConstraintViolation() {
+ context.addError(template, path);
+ return context;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java
new file mode 100644
index 0000000..f05ef76
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/ContainerElementNodeContextBuilderImpl.java
@@ -0,0 +1,85 @@
+/*
+ * 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.util;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderDefinedContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeContextBuilder;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext;
+
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
+
+public class ContainerElementNodeContextBuilderImpl implements ContainerElementNodeContextBuilder {
+ private final ConstraintValidatorContextImpl<?> context;
+ private final String template;
+ private final PathImpl path;
+ private final NodeImpl node;
+
+ ContainerElementNodeContextBuilderImpl(ConstraintValidatorContextImpl<?> context, String template,
+ PathImpl path, NodeImpl node) {
+ super();
+ this.context = context;
+ this.template = template;
+ this.path = path;
+ this.node = node;
+ }
+
+ @Override
+ public ContainerElementNodeBuilderDefinedContext atKey(Object key) {
+ node.setKey(key);
+ path.addNode(node);
+ return new ContainerElementNodeBuilderDefinedContextImpl(context, template, path);
+ }
+
+ @Override
+ public ContainerElementNodeBuilderDefinedContext atIndex(Integer index) {
+ node.setIndex(index);
+ path.addNode(node);
+ return new ContainerElementNodeBuilderDefinedContextImpl(context, template, path);
+ }
+
+ @Override
+ public NodeBuilderCustomizableContext addPropertyNode(String name) {
+ path.addNode(node);
+ return new NodeBuilderCustomizableContextImpl(context, name, path, name);
+ }
+
+ @Override
+ public LeafNodeBuilderCustomizableContext addBeanNode() {
+ path.addNode(node);
+ return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
+ }
+
+ @Override
+ public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+ Integer typeArgumentIndex) {
+ path.addNode(node);
+ return new ContainerElementNodeBuilderCustomizableContextImpl(context, template, path, name, containerType,
+ typeArgumentIndex);
+ }
+
+ @Override
+ public ConstraintValidatorContext addConstraintViolation() {
+ context.addError(template, path);
+ return context;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java
index 611a9d6..57f7cf4 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/IOs.java
@@ -33,25 +33,20 @@ public class IOs {
if (stream == null) {
return null;
}
-
- // force ByteArrayOutputStream since we close the stream ATM
- /*if (stream.markSupported()) {
- return stream;
- } else {*/
- try {
+ try (InputStream in = stream) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final byte[] buffer = new byte[1024];
int length;
- while ((length = stream.read(buffer)) != -1) {
+ while ((length = in.read(buffer)) != -1) {
baos.write(buffer, 0, length);
}
return new ByteArrayInputStream(baos.toByteArray());
} catch (final IOException e) {
throw new RuntimeException(e);
}
- /*}*/
}
+ //TODO see if needed
public static void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
new file mode 100644
index 0000000..48fcd7d
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LRUCache.java
@@ -0,0 +1,41 @@
+/*
+ * 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.util;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class LRUCache<K, V> extends LinkedHashMap<K, V> {
+ private static final long serialVersionUID = 1L;
+
+ private final int maximumCapacity;
+
+ public LRUCache(int maximumCapacity) {
+ super(16, 0.75f, true);
+ if (maximumCapacity < 1) {
+ throw new IllegalArgumentException("maximumCapacity must be > 0");
+ }
+ this.maximumCapacity = maximumCapacity;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+ return super.removeEldestEntry(eldest) || size() >= maximumCapacity;
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java
index efa9aeb..99305be 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/LeafNodeBuilderCustomizableContextImpl.java
@@ -18,9 +18,10 @@
*/
package org.apache.bval.jsr.util;
-import org.apache.bval.jsr.ConstraintValidatorContextImpl;
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext;
import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderDefinedContext;
import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeContextBuilder;
@@ -43,8 +44,7 @@ public class LeafNodeBuilderCustomizableContextImpl
}
@Override
- public LeafNodeBuilderDefinedContext atIndex(
- Integer index) {
+ public LeafNodeBuilderDefinedContext atIndex(Integer index) {
node.setIndex(index);
return definedContext;
}
@@ -55,16 +55,16 @@ public class LeafNodeBuilderCustomizableContextImpl
}
}
- private final ConstraintValidatorContextImpl context;
+ private final ConstraintValidatorContextImpl<?> context;
private final PathImpl path;
private final String template;
private final NodeImpl node;
- public LeafNodeBuilderCustomizableContextImpl(final ConstraintValidatorContextImpl parent, String messageTemplate,
- PathImpl propertyPath) {
- context = parent;
- template = messageTemplate;
- path = propertyPath;
+ public LeafNodeBuilderCustomizableContextImpl(final ConstraintValidatorContextImpl<?> context, String template,
+ PathImpl path) {
+ this.context = context;
+ this.template = template;
+ this.path = path;
node = new NodeImpl.BeanNodeImpl();
}
@@ -81,4 +81,9 @@ public class LeafNodeBuilderCustomizableContextImpl
return context;
}
+ @Override
+ public LeafNodeBuilderCustomizableContext inContainer(Class<?> containerType, Integer typeArgumentIndex) {
+ node.inContainer(containerType, typeArgumentIndex);
+ return this;
+ }
}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java
new file mode 100644
index 0000000..9f98311
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/Methods.java
@@ -0,0 +1,45 @@
+/*
+ * 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.util;
+
+import java.beans.Introspector;
+import java.lang.reflect.Method;
+
+import org.apache.bval.util.Validate;
+
+public final class Methods {
+ public static boolean isGetter(Method m) {
+ if (m.getParameterCount() > 0) {
+ return false;
+ }
+ // TODO look for capital letter after verb?
+ if (Boolean.TYPE.equals(m.getReturnType()) && m.getName().startsWith("is")) {
+ return true;
+ }
+ return !Void.TYPE.equals(m.getReturnType()) && m.getName().startsWith("get");
+ }
+
+ public static String propertyName(Method getter) {
+ Validate.isTrue(isGetter(getter), "%s is not a getter", getter);
+ final String name = getter.getName();
+ final String suffix = name.startsWith("is") ? name.substring(2) : name.substring(3);
+ return Introspector.decapitalize(suffix);
+ }
+
+ private Methods() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java
index ca058fc..6ec977c 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderCustomizableContextImpl.java
@@ -18,38 +18,40 @@
*/
package org.apache.bval.jsr.util;
-import org.apache.bval.jsr.ConstraintValidatorContextImpl;
-
import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext;
import javax.validation.ElementKind;
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
+
/**
* Description: implementation of {@link javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext}.<br/>
*/
public final class NodeBuilderCustomizableContextImpl
implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext {
- private final ConstraintValidatorContextImpl parent;
- private final String messageTemplate;
- private final PathImpl propertyPath;
+ private final ConstraintValidatorContextImpl<?> context;
+ private final String template;
+ private final PathImpl path;
private NodeImpl node;
/**
* Create a new NodeBuilderCustomizableContextImpl instance.
- * @param contextImpl
+ * @param context
* @param template
* @param path
* @param name
*/
- public NodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl contextImpl, String template, PathImpl path,
+ public NodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl<?> context, String template, PathImpl path,
String name) {
- parent = contextImpl;
- messageTemplate = template;
- propertyPath = path;
+ this.context = context;
+ this.template = template;
+ this.path = path;
- if (propertyPath.isRootPath() || propertyPath.getLeafNode().getKind() != null) {
+ if (path.isRootPath() || path.getLeafNode().getKind() != null) {
node = new NodeImpl.PropertyNodeImpl(name);
} else {
- node = propertyPath.removeLeafNode();
+ node = path.removeLeafNode();
node.setName(name);
node.setKind(ElementKind.PROPERTY); // enforce it
}
@@ -61,7 +63,7 @@ public final class NodeBuilderCustomizableContextImpl
@Override
public ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder inIterable() {
node.setInIterable(true);
- return new NodeContextBuilderImpl(parent, messageTemplate, propertyPath, node);
+ return new NodeContextBuilderImpl(context, template, path, node);
}
/**
@@ -75,15 +77,15 @@ public final class NodeBuilderCustomizableContextImpl
@Override
public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode(
String name) {
- propertyPath.addNode(node);
+ path.addNode(node);
node = new NodeImpl.PropertyNodeImpl(name);
return this;
}
@Override
public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() {
- propertyPath.addNode(node);
- return new LeafNodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath);
+ path.addNode(node);
+ return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
}
/**
@@ -91,10 +93,25 @@ public final class NodeBuilderCustomizableContextImpl
*/
@Override
public ConstraintValidatorContext addConstraintViolation() {
- propertyPath.addNode(node);
+ path.addNode(node);
node = null;
- parent.addError(messageTemplate, propertyPath);
- return parent;
+ context.addError(template, path);
+ return context;
+ }
+
+ @Override
+ public NodeBuilderCustomizableContext inContainer(Class<?> containerClass, Integer typeArgumentIndex) {
+ path.getLeafNode().inContainer(containerClass, typeArgumentIndex);
+ return this;
+ }
+
+ @Override
+ public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+ Integer typeArgumentIndex) {
+ path.addNode(node);
+ node = new NodeImpl.ContainerElementNodeImpl(name, containerType, typeArgumentIndex);
+ return new ContainerElementNodeBuilderCustomizableContextImpl(context, template, path, name, containerType,
+ typeArgumentIndex);
}
}
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java
index 5ce20b5..f695e84 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/util/NodeBuilderDefinedContextImpl.java
@@ -18,18 +18,19 @@
*/
package org.apache.bval.jsr.util;
-import org.apache.bval.jsr.ConstraintValidatorContextImpl;
-
import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext;
+
+import org.apache.bval.jsr.job.ConstraintValidatorContextImpl;
/**
* Description: Implementation of {@link NodeBuilderDefinedContext}.<br/>
*/
public final class NodeBuilderDefinedContextImpl
implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext {
- private final ConstraintValidatorContextImpl parent;
- private final String messageTemplate;
- private final PathImpl propertyPath;
+ private final ConstraintValidatorContextImpl context;
+ private final String template;
+ private final PathImpl path;
/**
* Create a new NodeBuilderDefinedContextImpl instance.
@@ -38,9 +39,9 @@ public final class NodeBuilderDefinedContextImpl
* @param path
*/
public NodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl contextImpl, String template, PathImpl path) {
- parent = contextImpl;
- messageTemplate = template;
- propertyPath = path;
+ this.context = contextImpl;
+ this.template = template;
+ this.path = path;
}
/**
@@ -54,12 +55,12 @@ public final class NodeBuilderDefinedContextImpl
@Override
public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode(
String name) {
- return new NodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath, name);
+ return new NodeBuilderCustomizableContextImpl(context, template, path, name);
}
@Override
public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() {
- return new LeafNodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath);
+ return new LeafNodeBuilderCustomizableContextImpl(context, template, path);
}
/**
@@ -67,7 +68,14 @@ public final class NodeBuilderDefinedContextImpl
*/
@Override
public ConstraintValidatorContext addConstraintViolation() {
- parent.addError(messageTemplate, propertyPath);
- return parent;
+ context.addError(template, path);
+ return context;
+ }
+
+ @Override
+ public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType,
+ Integer typeArgumentIndex) {
+ return new ContainerElementNodeBuilderCustomizableContextImpl(context, template, path, name, containerType,
+ typeArgumentIndex);
}
}