blob: 7ca5ebd38cba0e9b2707ace700327c4bba052ce8 [file] [log] [blame]
/*
* 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.TreeMap;
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.ApacheValidatorFactory;
import org.apache.bval.jsr.groups.GroupConversion;
import org.apache.bval.jsr.util.Methods;
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<T> extends DualBuilder.Delegator<MetadataBuilder.ForBean<T>>
implements MetadataBuilder.ForBean<T> {
ForBean(MetadataBuilder.ForBean<T> primaryDelegate, MetadataBuilder.ForBean<T> customDelegate) {
super(null, primaryDelegate, customDelegate);
}
@Override
public MetadataBuilder.ForClass<T> getClass(Meta<Class<T>> meta) {
return new DualBuilder.ForClass<>(this, primaryDelegate.getClass(meta), customDelegate.getClass(meta));
}
@Override
public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Meta<Class<T>> 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(Meta<Class<T>> 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<? extends T>>> getConstructors(Meta<Class<T>> 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(Meta<Class<T>> 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(Meta<E> meta) {
return activeDelegates().map(d -> d.getDeclaredConstraints(meta)).flatMap(Stream::of)
.toArray(Annotation[]::new);
}
}
private static class ForClass<T> extends ForElement<MetadataBuilder.ForClass<T>, Class<T>>
implements MetadataBuilder.ForClass<T> {
ForClass(Delegator<?> parent, MetadataBuilder.ForClass<T> primaryDelegate,
MetadataBuilder.ForClass<T> customDelegate) {
super(parent, primaryDelegate, customDelegate);
}
@Override
public List<Class<?>> getGroupSequence(Meta<Class<T>> 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(Meta<E> meta) {
return activeDelegates().anyMatch(d -> d.isCascade(meta));
}
@Override
public final Set<GroupConversion> getGroupConversions(Meta<E> meta) {
return activeDelegates().map(d -> d.getGroupConversions(meta)).flatMap(Collection::stream)
.collect(ToUnmodifiable.set());
}
@Override
public final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
Meta<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(Meta<E> meta) {
return new DualBuilder.ForContainer<>(this, primaryDelegate.getReturnValue(meta),
customDelegate.getReturnValue(meta));
}
@Override
public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Meta<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(Meta<E> meta) {
return new DualBuilder.ForElement<MetadataBuilder.ForElement<E>, E>(this,
primaryDelegate.getCrossParameter(meta), customDelegate.getCrossParameter(meta));
}
}
private static class CustomWrapper {
private static class ForBean<T> implements MetadataBuilder.ForBean<T> {
private final MetadataBuilder.ForBean<T> wrapped;
private final Map<String, MetadataBuilder.ForContainer<Method>> getters;
private final Map<Signature, MetadataBuilder.ForExecutable<Method>> methods;
ForBean(MetadataBuilder.ForBean<T> wrapped, Map<String, MetadataBuilder.ForContainer<Method>> getters,
Map<Signature, MetadataBuilder.ForExecutable<Method>> methods) {
super();
this.wrapped = Validate.notNull(wrapped, "wrapped");
this.getters = Validate.notNull(getters, "getters");
this.methods = Validate.notNull(methods, "methods");
}
@Override
public AnnotationBehavior getAnnotationBehavior() {
return wrapped.getAnnotationBehavior();
}
@Override
public MetadataBuilder.ForClass<T> getClass(Meta<Class<T>> meta) {
return wrapped.getClass(meta);
}
@Override
public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Meta<Class<T>> meta) {
return wrapped.getFields(meta);
}
@Override
public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Meta<Class<T>> meta) {
return getters;
}
@Override
public Map<Signature, MetadataBuilder.ForExecutable<Constructor<? extends T>>> getConstructors(
Meta<Class<T>> meta) {
return wrapped.getConstructors(meta);
}
@Override
public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Meta<Class<T>> meta) {
return methods;
}
}
private static class ForGetterMethod implements MetadataBuilder.ForExecutable<Method> {
private final MetadataBuilder.ForContainer<Method> returnValue;
private ForGetterMethod(MetadataBuilder.ForContainer<Method> returnValue) {
super();
this.returnValue = Validate.notNull(returnValue, "returnValue");
}
@Override
public AnnotationBehavior getAnnotationBehavior() {
return returnValue.getAnnotationBehavior();
}
@Override
public MetadataBuilder.ForContainer<Method> getReturnValue(Meta<Method> meta) {
return returnValue;
}
@Override
public MetadataBuilder.ForElement<Method> getCrossParameter(Meta<Method> meta) {
return EmptyBuilder.instance().forElement();
}
@Override
public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Meta<Method> meta) {
return Collections.emptyList();
}
}
}
public static <T> MetadataBuilder.ForBean<T> forBean(Class<T> beanClass, MetadataBuilder.ForBean<T> primaryDelegate,
MetadataBuilder.ForBean<T> customDelegate, ApacheValidatorFactory validatorFactory) {
return new DualBuilder.ForBean<>(primaryDelegate, wrapCustom(customDelegate, beanClass, validatorFactory));
}
private static <T> MetadataBuilder.ForBean<T> wrapCustom(MetadataBuilder.ForBean<T> customDelegate,
Class<T> beanClass, ApacheValidatorFactory validatorFactory) {
final Meta.ForClass<T> meta = new Meta.ForClass<>(beanClass);
final Map<String, MetadataBuilder.ForContainer<Method>> getters = customDelegate.getGetters(meta);
final Map<Signature, MetadataBuilder.ForExecutable<Method>> methods = customDelegate.getMethods(meta);
if (getters.isEmpty() && methods.keySet().stream().noneMatch(Signature::isGetter)) {
// nothing to merge
return customDelegate;
}
final CompositeBuilder composite =
CompositeBuilder.with(validatorFactory, AnnotationBehaviorMergeStrategy.consensus());
final Map<String, MetadataBuilder.ForContainer<Method>> mergedGetters = new TreeMap<>(getters);
methods.forEach((k, v) -> {
if (k.isGetter()) {
mergedGetters.compute(Methods.propertyName(k.getName()), (p, d) -> {
final Method getter = Methods.getter(beanClass, p);
return Stream.of(d, v.getReturnValue(new Meta.ForMethod(getter))).filter(Objects::nonNull)
.collect(composite.composeContainer());
});
}
});
final Map<Signature, MetadataBuilder.ForExecutable<Method>> mergedMethods = new TreeMap<>(methods);
getters.forEach((k, v) -> {
final Method getter = Methods.getter(beanClass, k);
final Signature signature = Signature.of(getter);
final MetadataBuilder.ForContainer<Method> rv;
if (methods.containsKey(signature)) {
rv = Stream.of(methods.get(signature).getReturnValue(new Meta.ForMethod(getter)), v)
.collect(composite.composeContainer());
} else {
rv = v;
}
mergedMethods.put(signature, new CustomWrapper.ForGetterMethod(rv));
});
return new CustomWrapper.ForBean<>(customDelegate, mergedGetters, mergedMethods);
}
}