![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.bval.jsr.metadata.DualBuilder Maven / Gradle / Ivy
/*
* 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 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 activeDelegates() {
return getCustomAnnotationBehavior() == AnnotationBehavior.EXCLUDE ? Stream.of(customDelegate)
: Stream.of(primaryDelegate, customDelegate);
}
Map merge(Function> toMap, BiFunction parallel,
Supplier emptyBuilder) {
final Map primaries = toMap.apply(primaryDelegate);
final Map 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>
implements MetadataBuilder.ForBean {
ForBean(MetadataBuilder.ForBean primaryDelegate, MetadataBuilder.ForBean customDelegate) {
super(null, primaryDelegate, customDelegate);
}
@Override
public MetadataBuilder.ForClass getClass(Meta> meta) {
return new DualBuilder.ForClass<>(this, primaryDelegate.getClass(meta), customDelegate.getClass(meta));
}
@Override
public Map> getFields(Meta> meta) {
return merge(b -> b.getFields(meta), (t, u) -> new DualBuilder.ForContainer<>(this, t, u),
EmptyBuilder.instance()::forContainer);
}
@Override
public Map> getGetters(Meta> meta) {
return merge(b -> b.getGetters(meta), (t, u) -> new DualBuilder.ForContainer<>(this, t, u),
EmptyBuilder.instance()::forContainer);
}
@Override
public Map>> getConstructors(Meta> meta) {
return merge(b -> b.getConstructors(meta), (t, u) -> new DualBuilder.ForExecutable<>(this, t, u),
EmptyBuilder.instance()::forExecutable);
}
@Override
public Map> getMethods(Meta> meta) {
return merge(b -> b.getMethods(meta), (t, u) -> new DualBuilder.ForExecutable<>(this, t, u),
EmptyBuilder.instance()::forExecutable);
}
}
private static class ForElement, E extends AnnotatedElement>
extends Delegator implements MetadataBuilder.ForElement {
ForElement(Delegator> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
super(parent, primaryDelegate, customDelegate);
}
@Override
public final Annotation[] getDeclaredConstraints(Meta meta) {
return activeDelegates().map(d -> d.getDeclaredConstraints(meta)).flatMap(Stream::of)
.toArray(Annotation[]::new);
}
}
private static class ForClass extends ForElement, Class>
implements MetadataBuilder.ForClass {
ForClass(Delegator> parent, MetadataBuilder.ForClass primaryDelegate,
MetadataBuilder.ForClass customDelegate) {
super(parent, primaryDelegate, customDelegate);
}
@Override
public List> getGroupSequence(Meta> meta) {
final List> customGroupSequence = customDelegate.getGroupSequence(meta);
if (customGroupSequence != null) {
return customGroupSequence;
}
return customDelegate.getAnnotationBehavior() == AnnotationBehavior.EXCLUDE ? null
: primaryDelegate.getGroupSequence(meta);
}
}
private static class ForContainer, E extends AnnotatedElement>
extends DualBuilder.ForElement implements MetadataBuilder.ForContainer {
ForContainer(Delegator> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
super(parent, primaryDelegate, customDelegate);
}
@Override
public final boolean isCascade(Meta meta) {
return activeDelegates().anyMatch(d -> d.isCascade(meta));
}
@Override
public final Set getGroupConversions(Meta meta) {
return activeDelegates().map(d -> d.getGroupConversions(meta)).flatMap(Collection::stream)
.collect(ToUnmodifiable.set());
}
@Override
public final Map> getContainerElementTypes(
Meta meta) {
return merge(b -> b.getContainerElementTypes(meta), (t, u) -> new DualBuilder.ForContainer<>(this, t, u),
EmptyBuilder.instance()::forContainer);
}
}
private static class ForExecutable, E extends Executable>
extends Delegator implements MetadataBuilder.ForExecutable {
ForExecutable(Delegator> parent, DELEGATE primaryDelegate, DELEGATE customDelegate) {
super(parent, primaryDelegate, customDelegate);
}
@Override
public MetadataBuilder.ForContainer getReturnValue(Meta meta) {
return new DualBuilder.ForContainer<>(this, primaryDelegate.getReturnValue(meta),
customDelegate.getReturnValue(meta));
}
@Override
public List> getParameters(Meta meta) {
final List> primaries = primaryDelegate.getParameters(meta);
final List> 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 getCrossParameter(Meta meta) {
return new DualBuilder.ForElement, E>(this,
primaryDelegate.getCrossParameter(meta), customDelegate.getCrossParameter(meta));
}
}
private static class CustomWrapper {
private static class ForBean implements MetadataBuilder.ForBean {
private final MetadataBuilder.ForBean wrapped;
private final Map> getters;
private final Map> methods;
ForBean(MetadataBuilder.ForBean wrapped, Map> getters,
Map> 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 getClass(Meta> meta) {
return wrapped.getClass(meta);
}
@Override
public Map> getFields(Meta> meta) {
return wrapped.getFields(meta);
}
@Override
public Map> getGetters(Meta> meta) {
return getters;
}
@Override
public Map>> getConstructors(
Meta> meta) {
return wrapped.getConstructors(meta);
}
@Override
public Map> getMethods(Meta> meta) {
return methods;
}
}
private static class ForGetterMethod implements MetadataBuilder.ForExecutable {
private final MetadataBuilder.ForContainer returnValue;
private ForGetterMethod(MetadataBuilder.ForContainer returnValue) {
super();
this.returnValue = Validate.notNull(returnValue, "returnValue");
}
@Override
public AnnotationBehavior getAnnotationBehavior() {
return returnValue.getAnnotationBehavior();
}
@Override
public MetadataBuilder.ForContainer getReturnValue(Meta meta) {
return returnValue;
}
@Override
public MetadataBuilder.ForElement getCrossParameter(Meta meta) {
return EmptyBuilder.instance().forElement();
}
@Override
public List> getParameters(Meta meta) {
return Collections.emptyList();
}
}
}
public static MetadataBuilder.ForBean forBean(Class beanClass, MetadataBuilder.ForBean primaryDelegate,
MetadataBuilder.ForBean customDelegate, ApacheValidatorFactory validatorFactory) {
return new DualBuilder.ForBean<>(primaryDelegate, wrapCustom(customDelegate, beanClass, validatorFactory));
}
private static MetadataBuilder.ForBean wrapCustom(MetadataBuilder.ForBean customDelegate,
Class beanClass, ApacheValidatorFactory validatorFactory) {
final Meta.ForClass meta = new Meta.ForClass<>(beanClass);
final Map> getters = customDelegate.getGetters(meta);
final Map> 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> 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> mergedMethods = new TreeMap<>(methods);
getters.forEach((k, v) -> {
final Method getter = Methods.getter(beanClass, k);
final Signature signature = Signature.of(getter);
final MetadataBuilder.ForContainer 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);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy