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 21:02:00 UTC
[10/17] bval git commit: BV2: new metadata model
BV2: new metadata model
Project: http://git-wip-us.apache.org/repos/asf/bval/repo
Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/59bd964b
Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/59bd964b
Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/59bd964b
Branch: refs/heads/bv2
Commit: 59bd964bb20dc465c42123315c8d0f6e760bb728
Parents: 40fc20f
Author: Matt Benson <mb...@apache.org>
Authored: Wed Feb 21 14:50:31 2018 -0600
Committer: Matt Benson <mb...@apache.org>
Committed: Wed Feb 21 14:59:57 2018 -0600
----------------------------------------------------------------------
.../bval/jsr/metadata/AnnotationBehavior.java | 35 +
.../AnnotationBehaviorMergeStrategy.java | 54 ++
...otationDeclaredValidatorMappingProvider.java | 42 ++
.../ClassLoadingValidatorMappingProvider.java | 48 ++
.../bval/jsr/metadata/CompositeBuilder.java | 227 ++++++
.../CompositeValidatorMappingProvider.java | 42 ++
.../bval/jsr/metadata/ContainerElementKey.java | 175 +++++
.../apache/bval/jsr/metadata/DualBuilder.java | 243 +++++++
.../metadata/DualValidationMappingProvider.java | 50 ++
.../apache/bval/jsr/metadata/EmptyBuilder.java | 183 +++++
.../jsr/metadata/HasAnnotationBehavior.java | 24 +
.../bval/jsr/metadata/HierarchyBuilder.java | 235 +++++++
.../bval/jsr/metadata/MetadataBuilder.java | 98 +++
.../bval/jsr/metadata/MetadataBuilders.java | 41 ++
.../org/apache/bval/jsr/metadata/Metas.java | 324 +++++++++
.../bval/jsr/metadata/ReflectionBuilder.java | 272 ++++++++
.../org/apache/bval/jsr/metadata/Signature.java | 75 ++
.../bval/jsr/metadata/ValidatorMapping.java | 121 ++++
.../jsr/metadata/ValidatorMappingProvider.java | 51 ++
.../apache/bval/jsr/metadata/XmlBuilder.java | 694 +++++++++++++++++++
.../metadata/XmlValidationMappingProvider.java | 64 ++
21 files changed, 3098 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java
new file mode 100644
index 0000000..56ed4f0
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehavior.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.apache.bval.jsr.metadata.MetadataBuilder;
+
+/**
+ * Models the behavior of a {@link MetadataBuilder} with regard to bean validation annotations.
+ *
+ * @see DualBuilder
+ */
+public enum AnnotationBehavior implements AnnotationBehaviorMergeStrategy {
+ //@formatter:off
+ INCLUDE, EXCLUDE, ABSTAIN;
+ //@formatter:on
+
+ @Override
+ public AnnotationBehavior apply(Iterable<? extends HasAnnotationBehavior> t) {
+ return this;
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java
new file mode 100644
index 0000000..bfd16c5
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationBehaviorMergeStrategy.java
@@ -0,0 +1,54 @@
+/*
+ * 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.util.EnumSet;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.bval.util.Validate;
+
+@FunctionalInterface
+public interface AnnotationBehaviorMergeStrategy
+ extends Function<Iterable<? extends HasAnnotationBehavior>, AnnotationBehavior> {
+
+ public static AnnotationBehaviorMergeStrategy first() {
+ return coll -> {
+ final Iterator<? extends HasAnnotationBehavior> iterator = coll.iterator();
+ return iterator.hasNext() ? iterator.next().getAnnotationBehavior() : AnnotationBehavior.ABSTAIN;
+ };
+ }
+
+ public static AnnotationBehaviorMergeStrategy consensus() {
+ return coll -> {
+ final Stream.Builder<HasAnnotationBehavior> b = Stream.builder();
+ coll.forEach(b);
+ final Set<AnnotationBehavior> annotationBehaviors =
+ b.build().map(HasAnnotationBehavior::getAnnotationBehavior).filter(Objects::nonNull)
+ .filter(Predicate.isEqual(AnnotationBehavior.ABSTAIN).negate())
+ .collect(Collectors.toCollection(() -> EnumSet.noneOf(AnnotationBehavior.class)));
+ Validate.validState(annotationBehaviors.size() <= 1,
+ "Conflicting annotation inclusion behaviors found among %s", coll);
+ return annotationBehaviors.isEmpty() ? AnnotationBehavior.ABSTAIN : annotationBehaviors.iterator().next();
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationDeclaredValidatorMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationDeclaredValidatorMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationDeclaredValidatorMappingProvider.java
new file mode 100644
index 0000000..b2126ac
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/AnnotationDeclaredValidatorMappingProvider.java
@@ -0,0 +1,42 @@
+/*
+ * 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.Arrays;
+import java.util.List;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+
+import org.apache.bval.util.Validate;
+
+public class AnnotationDeclaredValidatorMappingProvider extends ValidatorMappingProvider {
+ public static final AnnotationDeclaredValidatorMappingProvider INSTANCE =
+ new AnnotationDeclaredValidatorMappingProvider();
+
+ @Override
+ protected <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType) {
+ Validate.notNull(constraintType);
+ Validate.isTrue(constraintType.isAnnotationPresent(Constraint.class),
+ "%s does not represent a validation constraint", constraintType);
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ final List<Class<? extends ConstraintValidator<A, ?>>> validatorTypes =
+ (List) Arrays.asList(constraintType.getAnnotation(Constraint.class).validatedBy());
+ return new ValidatorMapping<>("@Constraint.validatedBy()", validatorTypes);
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ClassLoadingValidatorMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ClassLoadingValidatorMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ClassLoadingValidatorMappingProvider.java
new file mode 100644
index 0000000..e636a8a
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ClassLoadingValidatorMappingProvider.java
@@ -0,0 +1,48 @@
+/*
+ * 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.util.Objects;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+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 abstract class ClassLoadingValidatorMappingProvider extends ValidatorMappingProvider {
+
+ protected final <T> Stream<Class<? extends T>> load(Stream<String> classNames, Class<T> assignableTo,
+ Consumer<? super ClassNotFoundException> handleException) {
+ return classNames.map(className -> {
+ try {
+ return Reflection.toClass(className, getClassLoader());
+ } catch (ClassNotFoundException e) {
+ handleException.accept(e);
+ return (Class<?>) null;
+ }
+ }).filter(Objects::nonNull).map(c -> (Class<? extends T>) c.asSubclass(assignableTo));
+ }
+
+ protected ClassLoader getClassLoader() {
+ final ClassLoader classloader = Thread.currentThread().getContextClassLoader();
+ return classloader == null ? getClass().getClassLoader() : classloader;
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
new file mode 100644
index 0000000..52a7407
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeBuilder.java
@@ -0,0 +1,227 @@
+/*
+ * 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.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+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.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Validate;
+
+public class CompositeBuilder {
+
+ class Delegator<DELEGATE extends HasAnnotationBehavior> implements HasAnnotationBehavior {
+
+ protected final List<DELEGATE> delegates;
+
+ Delegator(List<DELEGATE> delegates) {
+ this.delegates = Validate.notNull(delegates, "delegates");
+ Validate.isTrue(!delegates.isEmpty(), "no delegates specified");
+ Validate.isTrue(delegates.stream().noneMatch(Objects::isNull), "One or more supplied delegates was null");
+ }
+
+ @Override
+ public AnnotationBehavior getAnnotationBehavior() {
+ return annotationBehaviorStrategy.apply(delegates);
+ }
+
+ <K, D> Map<K, D> merge(Function<DELEGATE, Map<K, D>> toMap, Function<List<D>, D> merge) {
+ final List<Map<K, D>> maps = delegates.stream().map(toMap).collect(Collectors.toList());
+
+ final Function<? super K, ? extends D> valueMapper = k -> {
+ final List<D> mappedDelegates =
+ maps.stream().map(m -> m.get(k)).filter(Objects::nonNull).collect(Collectors.toList());
+ return mappedDelegates.size() == 1 ? mappedDelegates.get(0) : merge.apply(mappedDelegates);
+ };
+
+ return maps.stream().map(Map::keySet).flatMap(Collection::stream).distinct()
+ .collect(Collectors.toMap(Function.identity(), valueMapper));
+ }
+ }
+
+ private class ForBean extends CompositeBuilder.Delegator<MetadataBuilder.ForBean>
+ implements MetadataBuilder.ForBean {
+
+ ForBean(List<MetadataBuilder.ForBean> delegates) {
+ super(delegates);
+ }
+
+ @Override
+ public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+ return new CompositeBuilder.ForClass(
+ delegates.stream().map(d -> d.getClass(meta)).collect(Collectors.toList()));
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+ return merge(b -> b.getFields(meta), CompositeBuilder.ForContainer::new);
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+ return merge(b -> b.getGetters(meta), CompositeBuilder.ForContainer::new);
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+ return merge(b -> b.getConstructors(meta), CompositeBuilder.ForExecutable::new);
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+ return merge(b -> b.getMethods(meta), CompositeBuilder.ForExecutable::new);
+ }
+ }
+
+ class ForElement<DELEGATE extends MetadataBuilder.ForElement<E>, E extends AnnotatedElement>
+ extends Delegator<DELEGATE> implements MetadataBuilder.ForElement<E> {
+
+ ForElement(List<DELEGATE> delegates) {
+ super(delegates);
+ }
+
+ @Override
+ public Map<Scope, Annotation[]> getConstraintsByScope(Metas<E> meta) {
+ return CompositeBuilder.this.getConstraintsByScope(this, meta);
+ }
+
+ @Override
+ public final Annotation[] getDeclaredConstraints(Metas<E> meta) {
+ return delegates.stream().map(d -> d.getDeclaredConstraints(meta)).flatMap(Stream::of)
+ .toArray(Annotation[]::new);
+ }
+ }
+
+ class ForClass extends ForElement<MetadataBuilder.ForClass, Class<?>> implements MetadataBuilder.ForClass {
+
+ ForClass(List<MetadataBuilder.ForClass> delegates) {
+ super(delegates);
+ }
+
+ @Override
+ public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+ return CompositeBuilder.this.getGroupSequence(this, meta);
+ }
+ }
+
+ private class ForContainer<DELEGATE extends MetadataBuilder.ForContainer<E>, E extends AnnotatedElement>
+ extends CompositeBuilder.ForElement<DELEGATE, E> implements MetadataBuilder.ForContainer<E> {
+
+ ForContainer(List<DELEGATE> delegates) {
+ super(delegates);
+ }
+
+ @Override
+ public final boolean isCascade(Metas<E> meta) {
+ return delegates.stream().anyMatch(d -> d.isCascade(meta));
+ }
+
+ @Override
+ public final Set<GroupConversion> getGroupConversions(Metas<E> meta) {
+ return delegates.stream().map(d -> d.getGroupConversions(meta)).flatMap(Collection::stream)
+ .collect(ToUnmodifiable.set());
+ }
+
+ @Override
+ public final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+ Metas<E> meta) {
+ return merge(b -> b.getContainerElementTypes(meta), CompositeBuilder.ForContainer::new);
+ }
+ }
+
+ private class ForExecutable<DELEGATE extends MetadataBuilder.ForExecutable<E>, E extends Executable>
+ extends Delegator<DELEGATE> implements MetadataBuilder.ForExecutable<E> {
+
+ ForExecutable(List<DELEGATE> delegates) {
+ super(delegates);
+ }
+
+ @Override
+ public MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta) {
+ return new CompositeBuilder.ForContainer<>(
+ delegates.stream().map(d -> d.getReturnValue(meta)).collect(Collectors.toList()));
+ }
+
+ @Override
+ public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<E> meta) {
+ final List<List<MetadataBuilder.ForContainer<Parameter>>> parameterLists =
+ delegates.stream().map(d -> d.getParameters(meta)).collect(Collectors.toList());
+
+ final Set<Integer> parameterCounts = parameterLists.stream().map(List::size).collect(Collectors.toSet());
+ Validate.validState(parameterCounts.size() == 1, "Mismatched parameter counts: %s", parameterCounts);
+
+ return IntStream.range(0, parameterCounts.iterator().next().intValue())
+ .mapToObj(n -> new CompositeBuilder.ForContainer<>(parameterLists.get(n)))
+ .collect(ToUnmodifiable.list());
+ }
+
+ @Override
+ public MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta) {
+ return new CompositeBuilder.ForElement<MetadataBuilder.ForElement<E>, E>(
+ delegates.stream().map(d -> d.getCrossParameter(meta)).collect(Collectors.toList()));
+ }
+ }
+
+ public static CompositeBuilder with(AnnotationBehaviorMergeStrategy annotationBehaviorStrategy) {
+ return new CompositeBuilder(annotationBehaviorStrategy);
+ }
+
+ private final AnnotationBehaviorMergeStrategy annotationBehaviorStrategy;
+
+ CompositeBuilder(AnnotationBehaviorMergeStrategy annotationBehaviorMergeStrategy) {
+ super();
+ this.annotationBehaviorStrategy =
+ Validate.notNull(annotationBehaviorMergeStrategy, "annotationBehaviorMergeStrategy");
+ }
+
+ public Collector<MetadataBuilder.ForBean, ?, MetadataBuilder.ForBean> compose() {
+ return Collectors.collectingAndThen(Collectors.toList(), CompositeBuilder.ForBean::new);
+ }
+
+ protected <E extends AnnotatedElement> Map<Scope, Annotation[]> getConstraintsByScope(
+ CompositeBuilder.ForElement<? extends MetadataBuilder.ForElement<E>, E> composite, Metas<E> meta) {
+ return Collections.singletonMap(Scope.LOCAL_ELEMENT, composite.getDeclaredConstraints(meta));
+ }
+
+ protected List<Class<?>> getGroupSequence(CompositeBuilder.ForClass composite, Metas<Class<?>> meta) {
+ final List<List<Class<?>>> groupSequence =
+ composite.delegates.stream().map(d -> d.getGroupSequence(meta)).collect(Collectors.toList());
+ Validate.validState(groupSequence.size() <= 1,
+ "group sequence returned from multiple composite class metadata builders");
+ return groupSequence.isEmpty() ? null : groupSequence.get(0);
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeValidatorMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeValidatorMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeValidatorMappingProvider.java
new file mode 100644
index 0000000..9808f89
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/CompositeValidatorMappingProvider.java
@@ -0,0 +1,42 @@
+/*
+ * 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.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.bval.util.Validate;
+
+public class CompositeValidatorMappingProvider extends ValidatorMappingProvider {
+
+ private final List<ValidatorMappingProvider> delegates;
+
+ public CompositeValidatorMappingProvider(List<ValidatorMappingProvider> delegates) {
+ super();
+ this.delegates = Validate.notNull(delegates, "delegates");
+ Validate.isTrue(!delegates.isEmpty(), "no delegates specified");
+ Validate.isTrue(delegates.stream().noneMatch(Objects::isNull), "One or more supplied delegates was null");
+ }
+
+ @Override
+ protected <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType) {
+ return ValidatorMapping.merge(delegates.stream().map(d -> d.doGetValidatorMapping(constraintType))
+ .filter(Objects::nonNull).collect(Collectors.toList()), AnnotationBehaviorMergeStrategy.consensus());
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java
new file mode 100644
index 0000000..322a4ef
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/ContainerElementKey.java
@@ -0,0 +1,175 @@
+/*
+ * 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.reflect.AnnotatedParameterizedType;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.TypeVariable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import javax.validation.valueextraction.ExtractedValue;
+import javax.validation.valueextraction.ValueExtractor;
+import javax.validation.valueextraction.ValueExtractorDefinitionException;
+
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.LazyInt;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.TypeUtils;
+
+public class ContainerElementKey implements Comparable<ContainerElementKey> {
+ private static Logger log = Logger.getLogger(ContainerElementKey.class.getName());
+
+ public static ContainerElementKey forValueExtractor(ValueExtractor<?> extractor) {
+ @SuppressWarnings("rawtypes")
+ final Class<? extends ValueExtractor> extractorType = extractor.getClass();
+ final Lazy<Set<ContainerElementKey>> result = new Lazy<>(HashSet::new);
+
+ Stream.of(extractorType.getAnnotatedInterfaces()).filter(AnnotatedParameterizedType.class::isInstance)
+ .map(AnnotatedParameterizedType.class::cast)
+ .filter(apt -> ValueExtractor.class.equals(((ParameterizedType) apt.getType()).getRawType()))
+ .forEach(decl -> {
+ final AnnotatedType containerType = decl.getAnnotatedActualTypeArguments()[0];
+
+ if (containerType.isAnnotationPresent(ExtractedValue.class)) {
+ Exceptions.raiseIf(void.class.equals(containerType.getAnnotation(ExtractedValue.class).type()),
+ ValueExtractorDefinitionException::new, "%s does not specify %s type for %s", extractorType,
+ ExtractedValue.class.getSimpleName(), containerType);
+ result.get().add(new ContainerElementKey(containerType, null));
+ }
+ Optional.of(containerType).filter(AnnotatedParameterizedType.class::isInstance)
+ .map(AnnotatedParameterizedType.class::cast)
+ .map(AnnotatedParameterizedType::getAnnotatedActualTypeArguments).ifPresent(args -> {
+ IntStream.range(0, args.length).forEach(n -> {
+ if (args[n].isAnnotationPresent(ExtractedValue.class)) {
+ if (void.class.equals(args[n].getAnnotation(ExtractedValue.class).type())) {
+ log.warning(String.format("Ignoring non-default %s type specified for %s by %s",
+ ExtractedValue.class.getSimpleName(), containerType, extractorType));
+ }
+ result.get().add(new ContainerElementKey(containerType, Integer.valueOf(n)));
+ }
+ });
+ });
+ });
+ return result.optional().filter(s -> s.size() == 1)
+ .orElseThrow(() -> new ValueExtractorDefinitionException(extractorType.getName())).iterator().next();
+ }
+
+ private static Integer validTypeArgumentIndex(Integer typeArgumentIndex, Class<?> containerClass) {
+ if (typeArgumentIndex != null) {
+ final int i = typeArgumentIndex.intValue();
+ Validate.isTrue(i >= 0 && i < containerClass.getTypeParameters().length,
+ "type argument index %d is invalid for container type %s", typeArgumentIndex, containerClass);
+ }
+ return typeArgumentIndex;
+ }
+
+ private final Integer typeArgumentIndex;
+ private final Class<?> containerClass;
+ private final LazyInt hashCode = new LazyInt(() -> Objects.hash(getContainerClass(), getTypeArgumentIndex()));
+ private final Lazy<String> toString = new Lazy<>(() -> String.format("%s: %s<[%d]>",
+ ContainerElementKey.class.getSimpleName(), getContainerClass().getName(), getTypeArgumentIndex()));
+ private final AnnotatedType annotatedType;
+
+ public ContainerElementKey(AnnotatedType containerType, Integer typeArgumentIndex) {
+ super();
+ Validate.notNull(containerType, "containerType");
+ this.containerClass = TypeUtils.getRawType(containerType.getType(), null);
+ this.typeArgumentIndex = validTypeArgumentIndex(typeArgumentIndex, containerClass);
+ this.annotatedType = typeArgumentIndex == null ? containerType : ((AnnotatedParameterizedType) containerType)
+ .getAnnotatedActualTypeArguments()[typeArgumentIndex.intValue()];
+ }
+
+ public Class<?> getContainerClass() {
+ return containerClass;
+ }
+
+ public Integer getTypeArgumentIndex() {
+ return typeArgumentIndex;
+ }
+
+ public AnnotatedType getAnnotatedType() {
+ return annotatedType;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj == this || Optional.ofNullable(obj).filter(ContainerElementKey.class::isInstance)
+ .map(ContainerElementKey.class::cast)
+ .filter(
+ cek -> Objects.equals(containerClass, cek.containerClass) && typeArgumentIndex == cek.typeArgumentIndex)
+ .isPresent();
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode.getAsInt();
+ }
+
+ @Override
+ public String toString() {
+ return toString.get();
+ }
+
+ @Override
+ public int compareTo(ContainerElementKey o) {
+ return Comparator.comparing(ContainerElementKey::containerClassName)
+ .thenComparing(Comparator.comparing(ContainerElementKey::getTypeArgumentIndex)).compare(this, o);
+ }
+
+ public Set<ContainerElementKey> getAssignableKeys() {
+ final Lazy<Set<ContainerElementKey>> result = new Lazy<>(LinkedHashSet::new);
+
+ if (typeArgumentIndex != null) {
+ final TypeVariable<?> var = containerClass.getTypeParameters()[typeArgumentIndex.intValue()];
+
+ Stream
+ .concat(Stream.of(containerClass.getAnnotatedSuperclass()),
+ Stream.of(containerClass.getAnnotatedInterfaces()))
+ .filter(AnnotatedParameterizedType.class::isInstance).map(AnnotatedParameterizedType.class::cast)
+ .forEach(t -> {
+ final AnnotatedType[] args = t.getAnnotatedActualTypeArguments();
+
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].getType().equals(var)) {
+ result.get().add(new ContainerElementKey(t, Integer.valueOf(i)));
+ }
+ }
+ });
+ }
+ return result.optional().map(Collections::unmodifiableSet).orElseGet(Collections::emptySet);
+ }
+
+ public boolean represents(TypeVariable<?> var) {
+ return Optional.ofNullable(typeArgumentIndex)
+ .map(index -> getContainerClass().getTypeParameters()[index.intValue()]).filter(var::equals).isPresent();
+ }
+
+ private String containerClassName() {
+ return getContainerClass().getName();
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualBuilder.java
new file mode 100644
index 0000000..269d953
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualBuilder.java
@@ -0,0 +1,243 @@
+/*
+ * 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.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+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.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.util.Validate;
+
+/**
+ * Maintains two metadata builds in parallel. The "primary" build is assumed to be the reflection/annotation-based build
+ * and is subject to the {@link AnnotationBehavior} prescribed by the "custom" build.
+ */
+public class DualBuilder {
+
+ private static class Delegator<DELEGATE extends HasAnnotationBehavior> implements HasAnnotationBehavior {
+
+ private final Delegator<?> parent;
+ protected final DELEGATE primaryDelegate;
+ protected final DELEGATE customDelegate;
+
+ Delegator(Delegator<?> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
+ this.parent = parent;
+ this.primaryDelegate = Validate.notNull(primaryDelegate, "primaryDelegate");
+ this.customDelegate = Validate.notNull(customDelegate, "customDelegate");
+ }
+
+ AnnotationBehavior getCustomAnnotationBehavior() {
+ final AnnotationBehavior annotationBehavior = customDelegate.getAnnotationBehavior();
+ Validate.validState(annotationBehavior != null, "null %s returned from %s",
+ AnnotationBehavior.class.getSimpleName(), customDelegate);
+ if (annotationBehavior == AnnotationBehavior.ABSTAIN && parent != null) {
+ return parent.getCustomAnnotationBehavior();
+ }
+ return annotationBehavior;
+ }
+
+ protected Stream<DELEGATE> activeDelegates() {
+ return getCustomAnnotationBehavior() == AnnotationBehavior.EXCLUDE ? Stream.of(customDelegate)
+ : Stream.of(primaryDelegate, customDelegate);
+ }
+
+ <K, D> Map<K, D> merge(Function<DELEGATE, Map<K, D>> toMap, BiFunction<D, D, D> parallel,
+ Supplier<D> emptyBuilder) {
+
+ final Map<K, D> primaries = toMap.apply(primaryDelegate);
+ final Map<K, D> customs = toMap.apply(customDelegate);
+
+ if (primaries.isEmpty() && customs.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ final Function<? super K, ? extends D> valueMapper = k -> {
+ final D primary = primaries.get(k);
+ final D custom = customs.get(k);
+
+ if (custom == null) {
+ if (primary != null) {
+ switch (getCustomAnnotationBehavior()) {
+ case INCLUDE:
+ case ABSTAIN:
+ return primary;
+ default:
+ break;
+ }
+ }
+ return emptyBuilder.get();
+ }
+ return parallel.apply(primary, custom);
+ };
+ return Stream.of(primaries, customs).map(Map::keySet).flatMap(Collection::stream).distinct()
+ .collect(Collectors.toMap(Function.identity(), valueMapper));
+ }
+ }
+
+ private static class ForBean extends DualBuilder.Delegator<MetadataBuilder.ForBean>
+ implements MetadataBuilder.ForBean {
+
+ ForBean(MetadataBuilder.ForBean primaryDelegate, MetadataBuilder.ForBean customDelegate) {
+ super(null, primaryDelegate, customDelegate);
+ }
+
+ @Override
+ public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+ return new DualBuilder.ForClass(this, primaryDelegate.getClass(meta), customDelegate.getClass(meta));
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+ return merge(b -> b.getFields(meta), (t, u) -> new DualBuilder.ForContainer<>(this, t, u),
+ EmptyBuilder.instance()::forContainer);
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+ return merge(b -> b.getGetters(meta), (t, u) -> new DualBuilder.ForContainer<>(this, t, u),
+ EmptyBuilder.instance()::forContainer);
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+ return merge(b -> b.getConstructors(meta), (t, u) -> new DualBuilder.ForExecutable<>(this, t, u),
+ EmptyBuilder.instance()::forExecutable);
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+ return merge(b -> b.getMethods(meta), (t, u) -> new DualBuilder.ForExecutable<>(this, t, u),
+ EmptyBuilder.instance()::forExecutable);
+ }
+ }
+
+ private static class ForElement<DELEGATE extends MetadataBuilder.ForElement<E>, E extends AnnotatedElement>
+ extends Delegator<DELEGATE> implements MetadataBuilder.ForElement<E> {
+
+ ForElement(Delegator<?> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
+ super(parent, primaryDelegate, customDelegate);
+ }
+
+ @Override
+ public final Annotation[] getDeclaredConstraints(Metas<E> meta) {
+ return activeDelegates().map(d -> d.getDeclaredConstraints(meta)).flatMap(Stream::of)
+ .toArray(Annotation[]::new);
+ }
+ }
+
+ private static class ForClass extends ForElement<MetadataBuilder.ForClass, Class<?>>
+ implements MetadataBuilder.ForClass {
+
+ ForClass(Delegator<?> parent, MetadataBuilder.ForClass primaryDelegate,
+ MetadataBuilder.ForClass customDelegate) {
+ super(parent, primaryDelegate, customDelegate);
+ }
+
+ @Override
+ public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+ final List<Class<?>> customGroupSequence = customDelegate.getGroupSequence(meta);
+ if (customGroupSequence != null) {
+ return customGroupSequence;
+ }
+ return customDelegate.getAnnotationBehavior() == AnnotationBehavior.EXCLUDE ? null
+ : primaryDelegate.getGroupSequence(meta);
+ }
+ }
+
+ private static class ForContainer<DELEGATE extends MetadataBuilder.ForContainer<E>, E extends AnnotatedElement>
+ extends DualBuilder.ForElement<DELEGATE, E> implements MetadataBuilder.ForContainer<E> {
+
+ ForContainer(Delegator<?> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
+ super(parent, primaryDelegate, customDelegate);
+ }
+
+ @Override
+ public final boolean isCascade(Metas<E> meta) {
+ return activeDelegates().anyMatch(d -> d.isCascade(meta));
+ }
+
+ @Override
+ public final Set<GroupConversion> getGroupConversions(Metas<E> meta) {
+ return activeDelegates().map(d -> d.getGroupConversions(meta)).flatMap(Collection::stream)
+ .collect(ToUnmodifiable.set());
+ }
+
+ @Override
+ public final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+ Metas<E> meta) {
+ return merge(b -> b.getContainerElementTypes(meta), (t, u) -> new DualBuilder.ForContainer<>(this, t, u),
+ EmptyBuilder.instance()::forContainer);
+ }
+ }
+
+ private static class ForExecutable<DELEGATE extends MetadataBuilder.ForExecutable<E>, E extends Executable>
+ extends Delegator<DELEGATE> implements MetadataBuilder.ForExecutable<E> {
+
+ ForExecutable(Delegator<?> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
+ super(parent, primaryDelegate, customDelegate);
+ }
+
+ @Override
+ public MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta) {
+ return new DualBuilder.ForContainer<>(this, primaryDelegate.getReturnValue(meta),
+ customDelegate.getReturnValue(meta));
+ }
+
+ @Override
+ public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<E> meta) {
+
+ final List<MetadataBuilder.ForContainer<Parameter>> primaries = primaryDelegate.getParameters(meta);
+ final List<MetadataBuilder.ForContainer<Parameter>> customs = customDelegate.getParameters(meta);
+
+ Validate.validState(primaries.size() == customs.size(), "Mismatched parameter counts: %d vs. %d",
+ primaries.size(), customs.size());
+
+ return IntStream.range(0, primaries.size())
+ .mapToObj(n -> new DualBuilder.ForContainer<>(this, primaries.get(n), customs.get(n)))
+ .collect(ToUnmodifiable.list());
+ }
+
+ @Override
+ public MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta) {
+ return new DualBuilder.ForElement<MetadataBuilder.ForElement<E>, E>(this,
+ primaryDelegate.getCrossParameter(meta), customDelegate.getCrossParameter(meta));
+ }
+ }
+
+ public static MetadataBuilder.ForBean forBean(MetadataBuilder.ForBean primaryDelegate,
+ MetadataBuilder.ForBean customDelegate) {
+ return new DualBuilder.ForBean(primaryDelegate, customDelegate);
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualValidationMappingProvider.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualValidationMappingProvider.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualValidationMappingProvider.java
new file mode 100644
index 0000000..e5b5038
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/DualValidationMappingProvider.java
@@ -0,0 +1,50 @@
+/*
+ * 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.Arrays;
+
+import org.apache.bval.util.Validate;
+
+public class DualValidationMappingProvider extends ValidatorMappingProvider {
+ private final ValidatorMappingProvider primaryDelegate;
+ private final ValidatorMappingProvider secondaryDelegate;
+
+ public DualValidationMappingProvider(ValidatorMappingProvider primary, ValidatorMappingProvider secondary) {
+ super();
+ this.primaryDelegate = Validate.notNull(primary, "primary delegate");
+ this.secondaryDelegate = Validate.notNull(secondary, "secondary delegate");
+ }
+
+ @Override
+ protected <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType) {
+
+ final ValidatorMapping<A> secondaryMapping = secondaryDelegate.doGetValidatorMapping(constraintType);
+ if (secondaryMapping == null) {
+ return primaryDelegate.doGetValidatorMapping(constraintType);
+ }
+ final AnnotationBehavior annotationBehavior = secondaryMapping.getAnnotationBehavior();
+
+ if (annotationBehavior == AnnotationBehavior.EXCLUDE) {
+ return secondaryMapping;
+ }
+ return ValidatorMapping.merge(
+ Arrays.asList(primaryDelegate.doGetValidatorMapping(constraintType), secondaryMapping),
+ AnnotationBehaviorMergeStrategy.consensus());
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/EmptyBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/EmptyBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/EmptyBuilder.java
new file mode 100644
index 0000000..c95f6d7
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/EmptyBuilder.java
@@ -0,0 +1,183 @@
+/*
+ * 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.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+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.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.ObjectUtils;
+import org.apache.bval.util.Validate;
+
+public class EmptyBuilder {
+ private static final Map<AnnotationBehavior, EmptyBuilder> INSTANCES = new EnumMap<>(AnnotationBehavior.class);
+
+ public static EmptyBuilder instance() {
+ return instance(AnnotationBehavior.ABSTAIN);
+ }
+
+ public static EmptyBuilder instance(AnnotationBehavior annotationBehavior) {
+ return INSTANCES.computeIfAbsent(annotationBehavior, EmptyBuilder::new);
+ }
+
+ private class Level implements HasAnnotationBehavior {
+
+ @Override
+ public final AnnotationBehavior getAnnotationBehavior() {
+ return annotationBehavior;
+ }
+ }
+
+ private class ForBean extends Level implements MetadataBuilder.ForBean {
+ private final Lazy<EmptyBuilder.ForClass> forClass = new Lazy<>(EmptyBuilder.ForClass::new);
+
+ @Override
+ public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+ return forClass.get();
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+ }
+
+ private class ForElement<E extends AnnotatedElement> extends Level implements MetadataBuilder.ForElement<E> {
+
+ @Override
+ public final Annotation[] getDeclaredConstraints(Metas<E> meta) {
+ return ObjectUtils.EMPTY_ANNOTATION_ARRAY;
+ }
+ }
+
+ private class ForClass extends ForElement<Class<?>> implements MetadataBuilder.ForClass {
+
+ @Override
+ public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+ return null;
+ }
+ }
+
+ private class ForContainer<E extends AnnotatedElement> extends ForElement<E>
+ implements MetadataBuilder.ForContainer<E> {
+
+ @Override
+ public boolean isCascade(Metas<E> meta) {
+ return false;
+ }
+
+ @Override
+ public Set<GroupConversion> getGroupConversions(Metas<E> meta) {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+ Metas<E> meta) {
+ return Collections.emptyMap();
+ }
+ }
+
+ private class ForExecutable<E extends Executable> extends Level implements MetadataBuilder.ForExecutable<E> {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta) {
+ return forElement.get();
+ }
+
+ @Override
+ public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<E> meta) {
+ return Collections.emptyList();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta) {
+ return forContainer.get();
+ }
+ }
+
+ private final AnnotationBehavior annotationBehavior;
+ private final Lazy<EmptyBuilder.ForBean> forBean;
+ @SuppressWarnings("rawtypes")
+ private final Lazy<EmptyBuilder.ForContainer> forContainer;
+ @SuppressWarnings("rawtypes")
+ private final Lazy<EmptyBuilder.ForExecutable> forExecutable;
+ @SuppressWarnings("rawtypes")
+ private final Lazy<EmptyBuilder.ForElement> forElement;
+
+ private EmptyBuilder(AnnotationBehavior annotationBehavior) {
+ super();
+ this.annotationBehavior = Validate.notNull(annotationBehavior, "annotationBehavior");
+ forBean = new Lazy<>(EmptyBuilder.ForBean::new);
+ forContainer = new Lazy<>(EmptyBuilder.ForContainer::new);
+ forExecutable = new Lazy<>(EmptyBuilder.ForExecutable::new);
+ forElement = new Lazy<>(EmptyBuilder.ForElement::new);
+ }
+
+ public MetadataBuilder.ForBean forBean() {
+ return forBean.get();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends AnnotatedElement> MetadataBuilder.ForContainer<E> forContainer() {
+ return forContainer.get();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Executable> MetadataBuilder.ForExecutable<E> forExecutable() {
+ return forExecutable.get();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends AnnotatedElement> MetadataBuilder.ForElement<E> forElement() {
+ return forElement.get();
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HasAnnotationBehavior.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HasAnnotationBehavior.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HasAnnotationBehavior.java
new file mode 100644
index 0000000..2060954
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HasAnnotationBehavior.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+public interface HasAnnotationBehavior {
+
+ default AnnotationBehavior getAnnotationBehavior() {
+ return AnnotationBehavior.ABSTAIN;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
new file mode 100644
index 0000000..35276ea
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/HierarchyBuilder.java
@@ -0,0 +1,235 @@
+/*
+ * 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.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+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 javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.bval.util.reflection.Reflection.Interfaces;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+@Privilizing(@CallTo(Reflection.class))
+public class HierarchyBuilder extends CompositeBuilder {
+ private static abstract class HierarchyDelegate<T> {
+ final T delegate;
+
+ HierarchyDelegate(T delegate) {
+ super();
+ this.delegate = Validate.notNull(delegate, "delegate");
+ }
+
+ static class ForBean extends HierarchyDelegate<MetadataBuilder.ForBean> implements MetadataBuilder.ForBean {
+ final Metas<Class<?>> hierarchyType;
+
+ ForBean(MetadataBuilder.ForBean delegate, Class<?> hierarchyType) {
+ super(delegate);
+ this.hierarchyType = new Metas.ForClass(hierarchyType);
+ }
+
+ @Override
+ public MetadataBuilder.ForClass getClass(Metas<Class<?>> meta) {
+ return new HierarchyDelegate.ForClass(delegate.getClass(hierarchyType), hierarchyType);
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Metas<Class<?>> meta) {
+ return delegate.getFields(hierarchyType);
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Metas<Class<?>> meta) {
+ return delegate.getGetters(hierarchyType);
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta) {
+ return delegate.getConstructors(hierarchyType);
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Metas<Class<?>> meta) {
+ final Map<Signature, MetadataBuilder.ForExecutable<Method>> m = delegate.getMethods(hierarchyType);
+
+ return m;
+ }
+ }
+
+ static class ForClass extends HierarchyDelegate<MetadataBuilder.ForClass> implements MetadataBuilder.ForClass {
+
+ final Metas<Class<?>> hierarchyType;
+
+ ForClass(MetadataBuilder.ForClass delegate, Metas<Class<?>> hierarchyType) {
+ super(delegate);
+ this.hierarchyType = hierarchyType;
+ }
+
+ @Override
+ public Annotation[] getDeclaredConstraints(Metas<Class<?>> meta) {
+ return delegate.getDeclaredConstraints(hierarchyType);
+ }
+
+ @Override
+ public List<Class<?>> getGroupSequence(Metas<Class<?>> meta) {
+ return delegate.getGroupSequence(hierarchyType);
+ }
+ }
+
+ static class ForGetter extends HierarchyDelegate<MetadataBuilder.ForContainer<Method>>
+ implements MetadataBuilder.ForContainer<Method> {
+ final Metas.ForMethod meta;
+
+ ForGetter(MetadataBuilder.ForContainer<Method> delegate,
+ org.apache.bval.jsr.metadata.Metas.ForMethod meta) {
+ super(delegate);
+ this.meta = Validate.notNull(meta, "meta");
+ }
+
+ @Override
+ public Annotation[] getDeclaredConstraints(Metas<Method> meta) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isCascade(Metas<Method> meta) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public Set<GroupConversion> getGroupConversions(Metas<Method> meta) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Map<ContainerElementKey, org.apache.bval.jsr.metadata.MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+ Metas<Method> meta) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+ }
+
+ static class ForMethod extends HierarchyDelegate<MetadataBuilder.ForExecutable<Method>>
+ implements MetadataBuilder.ForExecutable<Method> {
+ final Metas.ForMethod meta;
+
+ public ForMethod(MetadataBuilder.ForExecutable<Method> delegate, Metas.ForMethod meta) {
+ super(delegate);
+ this.meta = Validate.notNull(meta, "meta");
+ }
+
+ @Override
+ public MetadataBuilder.ForContainer<Method> getReturnValue(Metas<Method> meta) {
+
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public MetadataBuilder.ForElement<Method> getCrossParameter(Metas<Method> meta) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Metas<Method> meta) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+ }
+ }
+
+ private final Function<Class<?>, MetadataBuilder.ForBean> getBeanBuilder;
+
+ public HierarchyBuilder(Function<Class<?>, MetadataBuilder.ForBean> getBeanBuilder) {
+ super(AnnotationBehaviorMergeStrategy.first());
+ this.getBeanBuilder = Validate.notNull(getBeanBuilder, "getBeanBuilder function was null");
+ }
+
+ public MetadataBuilder.ForBean forBean(Class<?> beanClass) {
+ final List<MetadataBuilder.ForBean> delegates = new ArrayList<>();
+
+ /*
+ * First add the delegate for the requested bean class, forcing to empty if absent. This is important for the
+ * same reason that we use the #first() AnnotationBehaviorMergeStrategy: namely, that custom metadata overrides
+ * only from the immediately available mapping per the BV spec.
+ */
+ delegates.add(Optional.of(beanClass).map(getBeanBuilder).orElseGet(() -> EmptyBuilder.instance().forBean()));
+
+ // iterate the hierarchy, skipping the first (i.e. beanClass handled
+ // above)
+ final Iterator<Class<?>> hierarchy = Reflection.hierarchy(beanClass, Interfaces.INCLUDE).iterator();
+ hierarchy.next();
+
+ // skip Object.class; skip null/empty hierarchy builders, mapping others
+ // to HierarchyDelegate
+ hierarchy
+ .forEachRemaining(t -> Optional.of(t).filter(Predicate.isEqual(Object.class).negate()).map(getBeanBuilder)
+ .filter(b -> !b.isEmpty()).map(b -> new HierarchyDelegate.ForBean(b, t)).ifPresent(delegates::add));
+
+ // if we have nothing but empty builders (which should only happen for
+ // absent custom metadata), return empty:
+ if (delegates.stream().allMatch(MetadataBuilder.ForBean::isEmpty)) {
+ return EmptyBuilder.instance().forBean();
+ }
+ return delegates.stream().collect(compose());
+ }
+
+ @Override
+ protected <E extends AnnotatedElement> Map<Scope, Annotation[]> getConstraintsByScope(
+ CompositeBuilder.ForElement<? extends MetadataBuilder.ForElement<E>, E> composite, Metas<E> meta) {
+
+ final Iterator<? extends MetadataBuilder.ForElement<E>> iter = composite.delegates.iterator();
+
+ final Map<Scope, Annotation[]> result = new EnumMap<>(Scope.class);
+ result.put(Scope.LOCAL_ELEMENT, iter.next().getDeclaredConstraints(meta));
+
+ if (iter.hasNext()) {
+ final List<Annotation> hierarchyConstraints = new ArrayList<>();
+ iter.forEachRemaining(d -> Collections.addAll(hierarchyConstraints, d.getDeclaredConstraints(meta)));
+ result.put(Scope.HIERARCHY, hierarchyConstraints.toArray(new Annotation[hierarchyConstraints.size()]));
+ }
+ return result;
+ }
+
+ @Override
+ protected List<Class<?>> getGroupSequence(CompositeBuilder.ForClass composite, Metas<Class<?>> meta) {
+ return composite.delegates.get(0).getGroupSequence(meta);
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilder.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilder.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilder.java
new file mode 100644
index 0000000..7dbdcbc
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilder.java
@@ -0,0 +1,98 @@
+/*
+ * 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.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+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.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.metadata.Scope;
+
+import org.apache.bval.jsr.descriptor.GroupConversion;
+
+/**
+ * Common interface for populating the Bean Validation descriptors from various sources. Most implementations should
+ * concern themselves with a single level of an inheritance hierarchy.
+ */
+public final class MetadataBuilder {
+
+ public interface ForBean extends HasAnnotationBehavior {
+ MetadataBuilder.ForClass getClass(Metas<Class<?>> meta);
+
+ Map<String, ForContainer<Field>> getFields(Metas<Class<?>> meta);
+
+ /**
+ * Returned keys are property names per XML mapping spec.
+ *
+ * @param meta
+ * @return {@link Map}
+ */
+ Map<String, ForContainer<Method>> getGetters(Metas<Class<?>> meta);
+
+ Map<Signature, ForExecutable<Constructor<?>>> getConstructors(Metas<Class<?>> meta);
+
+ Map<Signature, ForExecutable<Method>> getMethods(Metas<Class<?>> meta);
+
+ default boolean isEmpty() {
+ return false;
+ }
+ }
+
+ public interface ForElement<E extends AnnotatedElement> extends HasAnnotationBehavior {
+
+ Annotation[] getDeclaredConstraints(Metas<E> meta);
+
+ default Map<Scope, Annotation[]> getConstraintsByScope(Metas<E> meta) {
+ return Collections.singletonMap(Scope.LOCAL_ELEMENT, getDeclaredConstraints(meta));
+ }
+ }
+
+ public interface ForClass extends ForElement<Class<?>> {
+
+ List<Class<?>> getGroupSequence(Metas<Class<?>> meta);
+ }
+
+ public interface ForContainer<E extends AnnotatedElement> extends MetadataBuilder.ForElement<E> {
+
+ boolean isCascade(Metas<E> meta);
+
+ Set<GroupConversion> getGroupConversions(Metas<E> meta);
+
+ Map<ContainerElementKey, ForContainer<AnnotatedType>> getContainerElementTypes(Metas<E> meta);
+ }
+
+ public interface ForExecutable<E extends Executable> extends HasAnnotationBehavior {
+
+ MetadataBuilder.ForContainer<E> getReturnValue(Metas<E> meta);
+
+ MetadataBuilder.ForElement<E> getCrossParameter(Metas<E> meta);
+
+ List<ForContainer<Parameter>> getParameters(Metas<E> meta);
+ }
+
+ private MetadataBuilder() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilders.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilders.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilders.java
new file mode 100644
index 0000000..aa301a4
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/MetadataBuilders.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.metadata;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.bval.util.Validate;
+
+public class MetadataBuilders {
+
+ private final Map<Class<?>, List<MetadataBuilder.ForBean>> beanBuilders = new ConcurrentHashMap<>();
+
+ public <T> void registerCustomBuilder(Class<?> bean, MetadataBuilder.ForBean builder) {
+ Validate.notNull(bean, "bean");
+ Validate.notNull(builder, "builder");
+ beanBuilders.computeIfAbsent(bean, c -> new ArrayList<>()).add(builder);
+ }
+
+ public List<MetadataBuilder.ForBean> getCustomBuilders(Class<?> bean) {
+ final List<MetadataBuilder.ForBean> list = beanBuilders.get(bean);
+ return list == null ? Collections.emptyList() : Collections.unmodifiableList(list);
+ }
+}
http://git-wip-us.apache.org/repos/asf/bval/blob/59bd964b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Metas.java
----------------------------------------------------------------------
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Metas.java b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Metas.java
new file mode 100644
index 0000000..667c404
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/metadata/Metas.java
@@ -0,0 +1,324 @@
+/*
+ * 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.lang.annotation.ElementType;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+import java.util.Objects;
+
+import javax.validation.constraintvalidation.ValidationTarget;
+
+import org.apache.bval.util.Validate;
+
+/**
+ * Validation class model.
+ *
+ * @param <E>
+ */
+// TODO rename to Meta; delete old type of that name
+public abstract class Metas<E extends AnnotatedElement> {
+
+ public static class ForClass extends Metas<Class<?>> {
+
+ public ForClass(Class<?> host) {
+ super(host, ElementType.TYPE);
+ }
+
+ @Override
+ public final Class<?> getDeclaringClass() {
+ return getHost();
+ }
+
+ @Override
+ public Type getType() {
+ return getHost();
+ }
+
+ @Override
+ public AnnotatedType getAnnotatedType() {
+ return new AnnotatedType() {
+
+ @Override
+ public Annotation[] getDeclaredAnnotations() {
+ return getHost().getDeclaredAnnotations();
+ }
+
+ @Override
+ public Annotation[] getAnnotations() {
+ return getHost().getAnnotations();
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+ return getHost().getAnnotation(annotationClass);
+ }
+
+ @Override
+ public Type getType() {
+ return getHost();
+ }
+ };
+ }
+
+ @Override
+ public String getName() {
+ return getHost().getName();
+ }
+ }
+
+ public static abstract class ForMember<M extends Member & AnnotatedElement> extends Metas<M> {
+
+ protected ForMember(M host, ElementType elementType) {
+ super(host, elementType);
+ }
+
+ @Override
+ public Class<?> getDeclaringClass() {
+ return getHost().getDeclaringClass();
+ }
+ }
+
+ public static class ForField extends ForMember<Field> {
+
+ public ForField(Field host) {
+ super(host, ElementType.FIELD);
+ }
+
+ @Override
+ public Type getType() {
+ return getHost().getGenericType();
+ }
+
+ @Override
+ public AnnotatedType getAnnotatedType() {
+ return getHost().getAnnotatedType();
+ }
+
+ @Override
+ public String getName() {
+ return getHost().getName();
+ }
+ }
+
+ public static abstract class ForExecutable<E extends Executable> extends ForMember<E> {
+
+ protected ForExecutable(E host, ElementType elementType) {
+ super(host, elementType);
+ }
+
+ @Override
+ public AnnotatedType getAnnotatedType() {
+ return getHost().getAnnotatedReturnType();
+ }
+ }
+
+ public static class ForConstructor extends ForExecutable<Constructor<?>> {
+
+ public ForConstructor(Constructor<?> host) {
+ super(host, ElementType.CONSTRUCTOR);
+ }
+
+ @Override
+ public Type getType() {
+ return getHost().getDeclaringClass();
+ }
+
+ @Override
+ public String getName() {
+ return getHost().getDeclaringClass().getSimpleName();
+ }
+ }
+
+ public static class ForMethod extends ForExecutable<Method> {
+
+ public ForMethod(Method host) {
+ super(host, ElementType.METHOD);
+ }
+
+ @Override
+ public Type getType() {
+ return getHost().getGenericReturnType();
+ }
+
+ @Override
+ public String getName() {
+ return getHost().getName();
+ }
+ }
+
+ public static class ForCrossParameter<E extends Executable> extends Metas.ForExecutable<E> {
+
+ public ForCrossParameter(Metas<E> parent) {
+ super(parent.getHost(), parent.getElementType());
+ }
+
+ @Override
+ public Type getType() {
+ return Object[].class;
+ }
+
+ @Override
+ public String getName() {
+ return "<cross parameter>";
+ }
+
+ @Override
+ public ValidationTarget getValidationTarget() {
+ return ValidationTarget.PARAMETERS;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s(%s of %s)", getStringPrefix(), getName(), getHost());
+ }
+ }
+
+ public static class ForParameter extends Metas<Parameter> {
+
+ private final String name;
+
+ public ForParameter(Parameter host, String name) {
+ super(host, ElementType.PARAMETER);
+ this.name = Validate.notNull(name, "name");
+ }
+
+ @Override
+ public Type getType() {
+ return getHost().getType();
+ }
+
+ @Override
+ public Class<?> getDeclaringClass() {
+ return getHost().getDeclaringExecutable().getDeclaringClass();
+ }
+
+ @Override
+ public AnnotatedType getAnnotatedType() {
+ return getHost().getAnnotatedType();
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public static class ForContainerElement extends Metas<AnnotatedType> {
+
+ private final Metas<?> parent;
+ private final ContainerElementKey key;
+
+ public ForContainerElement(Metas<?> parent, ContainerElementKey key) {
+ super(key.getAnnotatedType(), ElementType.TYPE_USE);
+ this.parent = Validate.notNull(parent, "parent");
+ this.key = Validate.notNull(key, "key");
+ }
+
+ @Override
+ public Type getType() {
+ return getHost().getType();
+ }
+
+ @Override
+ public Class<?> getDeclaringClass() {
+ return parent.getDeclaringClass();
+ }
+
+ @Override
+ public AnnotatedType getAnnotatedType() {
+ return key.getAnnotatedType();
+ }
+
+ public Integer getTypeArgumentIndex() {
+ return Integer.valueOf(key.getTypeArgumentIndex());
+ }
+
+ @Override
+ public String getName() {
+ return key.toString();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s(%s of %s)", getStringPrefix(), key, getHost());
+ }
+ }
+
+ private final E host;
+ private final ElementType elementType;
+
+ protected Metas(E host, ElementType elementType) {
+ super();
+ this.host = Validate.notNull(host, "host");
+ this.elementType = Validate.notNull(elementType, "elementType");
+ }
+
+ public E getHost() {
+ return host;
+ }
+
+ public ElementType getElementType() {
+ return elementType;
+ }
+
+ public abstract Type getType();
+
+ public abstract Class<?> getDeclaringClass();
+
+ public abstract AnnotatedType getAnnotatedType();
+
+ public abstract String getName();
+
+ public ValidationTarget getValidationTarget() {
+ return ValidationTarget.ANNOTATED_ELEMENT;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s(%s)", getStringPrefix(), host);
+ }
+
+ protected String getStringPrefix() {
+ return Metas.class.getSimpleName() + '.' + getClass().getSimpleName();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!obj.getClass().equals(getClass())) {
+ return false;
+ }
+ return Objects.equals(((Metas<?>) obj).getHost(), getHost());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getHost());
+ }
+}