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

org.apache.bval.jsr.metadata.CompositeBuilder 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.function.BiFunction;
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 jakarta.validation.ElementKind;
import jakarta.validation.ParameterNameProvider;

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.Exceptions;
import org.apache.bval.util.Validate;
import org.apache.bval.util.reflection.Reflection;

public class CompositeBuilder {

    class Delegator implements HasAnnotationBehavior {

        protected final List delegates;

        Delegator(List 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);
        }

         Map merge(Function> toMap, Function, D> merge) {
            return merge(toMap, (k, l) -> merge.apply(l));
        }

         Map merge(Function> toMap, BiFunction, D> merge) {
            final List> maps =
                delegates.stream().map(toMap).filter(m -> !m.isEmpty()).collect(Collectors.toList());

            final Function valueMapper = k -> {
                final List mappedDelegates =
                    maps.stream().map(m -> m.get(k)).filter(Objects::nonNull).collect(Collectors.toList());
                return mappedDelegates.size() == 1 ? mappedDelegates.get(0) : merge.apply(k, mappedDelegates);
            };
            return maps.stream().map(Map::keySet).flatMap(Collection::stream).distinct()
                .collect(Collectors.toMap(Function.identity(), valueMapper));
        }
    }

    class ForBean extends CompositeBuilder.Delegator>
        implements MetadataBuilder.ForBean {

        ForBean(List> delegates) {
            super(delegates);
        }

        @Override
        public MetadataBuilder.ForClass getClass(Meta> meta) {
            return new CompositeBuilder.ForClass<>(
                delegates.stream().map(d -> d.getClass(meta)).collect(Collectors.toList()));
        }

        @Override
        public Map> getFields(Meta> meta) {
            return merge(b -> b.getFields(meta), (f, l) -> {
                final Field fld = Reflection.find(meta.getHost(), t -> Reflection.getDeclaredField(t, f));
                if (fld == null) {
                    Exceptions.raise(IllegalStateException::new, "Could not find field %s of %s", f, meta.getHost());
                }
                return forContainer(l, new Meta.ForField(fld), ElementKind.PROPERTY);
            });
        }

        @Override
        public Map> getGetters(Meta> meta) {
            return merge(b -> b.getGetters(meta), (g, l) -> {
                final Method getter = Methods.getter(meta.getHost(), g);
                if (getter == null) {
                    Exceptions.raise(IllegalStateException::new, "Could not find getter for property %s of %s", g,
                        meta.getHost());
                }
                return forContainer(l, new Meta.ForMethod(getter), ElementKind.PROPERTY);
            });
        }

        @Override
        public Map>> getConstructors(Meta> meta) {
            return merge(b -> b.getConstructors(meta),
                d -> new CompositeBuilder.ForExecutable<>(d, ParameterNameProvider::getParameterNames));
        }

        @Override
        public Map> getMethods(Meta> meta) {
            return merge(b -> b.getMethods(meta),
                d -> new CompositeBuilder.ForExecutable<>(d, ParameterNameProvider::getParameterNames));
        }
    }

    class ForElement, E extends AnnotatedElement>
        extends Delegator implements MetadataBuilder.ForElement {

        ForElement(List delegates) {
            super(delegates);
        }

        @Override
        public Map, Annotation[]> getConstraintDeclarationMap(Meta meta) {
            return CompositeBuilder.this.getConstraintDeclarationMap(this, meta);
        }

        @Override
        public final Annotation[] getDeclaredConstraints(Meta meta) {
            return delegates.stream().map(d -> d.getDeclaredConstraints(meta)).flatMap(Stream::of)
                .toArray(Annotation[]::new);
        }
    }

    class ForClass extends ForElement, Class> implements MetadataBuilder.ForClass {

        ForClass(List> delegates) {
            super(delegates);
        }

        @Override
        public List> getGroupSequence(Meta> meta) {
            return CompositeBuilder.this.getGroupSequence(this, meta);
        }
    }

    class ForContainer, E extends AnnotatedElement>
        extends CompositeBuilder.ForElement implements MetadataBuilder.ForContainer {

        ForContainer(List delegates) {
            super(delegates);
        }

        @Override
        public boolean isCascade(Meta meta) {
            return delegates.stream().anyMatch(d -> d.isCascade(meta));
        }

        @Override
        public Set getGroupConversions(Meta meta) {
            return delegates.stream().map(d -> d.getGroupConversions(meta)).flatMap(Collection::stream)
                .collect(ToUnmodifiable.set());
        }

        @Override
        public Map> getContainerElementTypes(
            Meta meta) {
            return merge(b -> b.getContainerElementTypes(meta),
                (k, l) -> forContainer(l, new Meta.ForContainerElement(meta, k), ElementKind.CONTAINER_ELEMENT));
        }
    }

    class ForExecutable, E extends Executable>
        extends Delegator implements MetadataBuilder.ForExecutable {

        private final BiFunction> getParameterNames;

        ForExecutable(List delegates, BiFunction> getParameterNames) {
            super(delegates);
            this.getParameterNames = Validate.notNull(getParameterNames, "getParameterNames");
        }

        @Override
        public MetadataBuilder.ForContainer getReturnValue(Meta meta) {
            return CompositeBuilder.this.forContainer(
                delegates.stream().map(d -> d.getReturnValue(meta)).collect(Collectors.toList()), meta,
                ElementKind.RETURN_VALUE);
        }

        @Override
        public List> getParameters(Meta meta) {
            final List>> parameterLists =
                delegates.stream().map(d -> d.getParameters(meta)).collect(Collectors.toList());

            final Set 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();
            final List> metaParams = getMetaParameters(meta, getParameterNames);
            return IntStream.range(0, parameterCount).mapToObj(n -> {
                return forContainer(parameterLists.stream().map(l -> l.get(n)).collect(Collectors.toList()),
                    metaParams.get(n), ElementKind.PARAMETER);
            }).collect(ToUnmodifiable.list());
        }

        @Override
        public MetadataBuilder.ForElement getCrossParameter(Meta meta) {
            return forCrossParameter(
                delegates.stream().map(d -> d.getCrossParameter(meta)).collect(Collectors.toList()), meta);
        }
    }

    public static CompositeBuilder with(ApacheValidatorFactory validatorFactory,
        AnnotationBehaviorMergeStrategy annotationBehaviorStrategy) {
        return new CompositeBuilder(validatorFactory, annotationBehaviorStrategy);
    }

    private final AnnotationBehaviorMergeStrategy annotationBehaviorStrategy;
    protected final ApacheValidatorFactory validatorFactory;

    protected CompositeBuilder(ApacheValidatorFactory validatorFactory,
        AnnotationBehaviorMergeStrategy annotationBehaviorMergeStrategy) {
        super();
        this.annotationBehaviorStrategy =
            Validate.notNull(annotationBehaviorMergeStrategy, "annotationBehaviorMergeStrategy");
        this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
    }

    public  Collector, ?, MetadataBuilder.ForBean> compose() {
        return Collectors.collectingAndThen(Collectors.toList(),
            delegates -> delegates.isEmpty() ? EmptyBuilder.instance().forBean()
                : delegates.size() == 1 ? delegates.get(0) : new CompositeBuilder.ForBean<>(delegates));
    }

    public  Collector, ?, MetadataBuilder.ForContainer> composeContainer() {
        return Collectors.collectingAndThen(Collectors.toList(),
            delegates -> delegates.isEmpty() ? EmptyBuilder.instance().forContainer()
                : delegates.size() == 1 ? delegates.get(0) : new CompositeBuilder.ForContainer<>(delegates));
    }

    protected final  List> getMetaParameters(Meta meta,
        BiFunction> getParameterNames) {
        final Parameter[] parameters = meta.getHost().getParameters();
        final List parameterNames =
            getParameterNames.apply(validatorFactory.getParameterNameProvider(), meta.getHost());

        if (parameterNames.size() != parameters.length) {
            Exceptions.raise(IllegalStateException::new, "%s returned wrong number of parameter names",
                validatorFactory.getParameterNameProvider());
        }
        return IntStream.range(0, parameters.length)
            .mapToObj(n -> new Meta.ForParameter(parameters[n], parameterNames.get(n))).collect(Collectors.toList());
    }

    protected  Map, Annotation[]> getConstraintDeclarationMap(
        CompositeBuilder.ForElement, E> composite, Meta meta) {
        return Collections.singletonMap(meta, composite.getDeclaredConstraints(meta));
    }

    protected  List> getGroupSequence(CompositeBuilder.ForClass composite, Meta> meta) {
        final List>> 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);
    }

    protected , E extends AnnotatedElement> MetadataBuilder.ForContainer forContainer(
        List delegates, Meta meta, ElementKind elementKind) {
        return new CompositeBuilder.ForContainer<>(delegates);
    }

    protected , E extends Executable> MetadataBuilder.ForElement forCrossParameter(
        List delegates, Meta meta) {
        return new CompositeBuilder.ForElement<>(delegates);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy