| /* |
| * 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.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| 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.function.Function; |
| import java.util.function.Predicate; |
| |
| import javax.validation.metadata.Scope; |
| |
| 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 Meta<Class<?>> hierarchyType; |
| |
| ForBean(MetadataBuilder.ForBean delegate, Class<?> hierarchyType) { |
| super(delegate); |
| this.hierarchyType = new Meta.ForClass(hierarchyType); |
| } |
| |
| @Override |
| public MetadataBuilder.ForClass getClass(Meta<Class<?>> meta) { |
| return new HierarchyDelegate.ForClass(delegate.getClass(hierarchyType), hierarchyType); |
| } |
| |
| @Override |
| public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Meta<Class<?>> meta) { |
| return delegate.getFields(hierarchyType); |
| } |
| |
| @Override |
| public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Meta<Class<?>> meta) { |
| return delegate.getGetters(hierarchyType); |
| } |
| |
| @SuppressWarnings("unlikely-arg-type") |
| @Override |
| public Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> getConstructors(Meta<Class<?>> meta) { |
| // suppress hierarchical ctors: |
| return hierarchyType.equals(meta.getHost()) ? delegate.getConstructors(hierarchyType) |
| : Collections.emptyMap(); |
| } |
| |
| @Override |
| public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Meta<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 Meta<Class<?>> hierarchyType; |
| |
| ForClass(MetadataBuilder.ForClass delegate, Meta<Class<?>> hierarchyType) { |
| super(delegate); |
| this.hierarchyType = hierarchyType; |
| } |
| |
| @Override |
| public Annotation[] getDeclaredConstraints(Meta<Class<?>> meta) { |
| return delegate.getDeclaredConstraints(hierarchyType); |
| } |
| |
| @Override |
| public List<Class<?>> getGroupSequence(Meta<Class<?>> meta) { |
| return delegate.getGroupSequence(hierarchyType); |
| } |
| } |
| } |
| |
| 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, Meta<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, Meta<Class<?>> meta) { |
| return composite.delegates.get(0).getGroupSequence(meta); |
| } |
| } |