All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 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