| /* |
| * 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.groups.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); |
| |
| final int parameterCount = parameterCounts.iterator().next().intValue(); |
| return IntStream.range(0, parameterCount) |
| .mapToObj(n -> new CompositeBuilder.ForContainer<>( |
| parameterLists.stream().map(l -> l.get(n)).collect(Collectors.toList()))) |
| .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); |
| } |
| } |