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

org.apache.bval.jsr.metadata.XmlBuilder Maven / Gradle / Ivy

There is a newer version: 10.0.0-M3
Show newest version
/*
 *  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.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Array;
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.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.validation.ConstraintDeclarationException;
import javax.validation.ConstraintTarget;
import javax.validation.Payload;
import javax.validation.ValidationException;
import javax.validation.groups.Default;
import javax.xml.bind.JAXBElement;

import org.apache.bval.jsr.ApacheValidatorFactory;
import org.apache.bval.jsr.ConstraintAnnotationAttributes;
import org.apache.bval.jsr.groups.GroupConversion;
import org.apache.bval.jsr.util.AnnotationProxyBuilder;
import org.apache.bval.jsr.util.ToUnmodifiable;
import org.apache.bval.jsr.xml.AnnotationType;
import org.apache.bval.jsr.xml.BeanType;
import org.apache.bval.jsr.xml.ClassType;
import org.apache.bval.jsr.xml.ConstraintMappingsType;
import org.apache.bval.jsr.xml.ConstraintType;
import org.apache.bval.jsr.xml.ConstructorType;
import org.apache.bval.jsr.xml.ContainerElementTypeType;
import org.apache.bval.jsr.xml.CrossParameterType;
import org.apache.bval.jsr.xml.ElementType;
import org.apache.bval.jsr.xml.FieldType;
import org.apache.bval.jsr.xml.GetterType;
import org.apache.bval.jsr.xml.GroupConversionType;
import org.apache.bval.jsr.xml.GroupSequenceType;
import org.apache.bval.jsr.xml.GroupsType;
import org.apache.bval.jsr.xml.MappingValidator;
import org.apache.bval.jsr.xml.MethodType;
import org.apache.bval.jsr.xml.ParameterType;
import org.apache.bval.jsr.xml.PayloadType;
import org.apache.bval.jsr.xml.ReturnValueType;
import org.apache.bval.util.Exceptions;
import org.apache.bval.util.Lazy;
import org.apache.bval.util.ObjectUtils;
import org.apache.bval.util.StringUtils;
import org.apache.bval.util.Validate;
import org.apache.bval.util.reflection.Reflection;
import org.apache.commons.weaver.privilizer.Privilizing;
import org.apache.commons.weaver.privilizer.Privilizing.CallTo;

@Privilizing(@CallTo(Reflection.class))
public class XmlBuilder {
    //@formatter:off
    public enum Version {
        v10("1.0"), v11("1.1"), v20("2.0");
        //@formatter:on

        static Version of(ConstraintMappingsType constraintMappings) {
            Validate.notNull(constraintMappings);
            String version = constraintMappings.getVersion();
            if (StringUtils.isBlank(version)) {
                return v10;
            }
            version = version.trim();
            for (Version candidate : values()) {
                if (candidate.id.equals(version)) {
                    return candidate;
                }
            }
            throw new ValidationException("Unknown schema version: " + version);
        }

        private final String id;

        private Version(String number) {
            this.id = number;
        }

        public String getId() {
            return id;
        }
    }

    private class ForBean implements MetadataBuilder.ForBean {

        private final BeanType descriptor;

        ForBean(BeanType descriptor) {
            super();
            this.descriptor = Validate.notNull(descriptor, "descriptor");
        }

        Class getBeanClass() {
            return resolveClass(descriptor.getClazz());
        }

        @Override
        public MetadataBuilder.ForClass getClass(Meta> meta) {
            final ClassType classType = descriptor.getClassType();
            return classType == null ? EmptyBuilder.instance(). forBean().getClass(meta)
                : new XmlBuilder.ForClass(classType);
        }

        @Override
        public Map> getFields(Meta> meta) {
            return descriptor.getField().stream()
                .collect(ToUnmodifiable.map(FieldType::getName, XmlBuilder.ForField::new));
        }

        @Override
        public Map> getGetters(Meta> meta) {
            return descriptor.getGetter().stream()
                .collect(ToUnmodifiable.map(GetterType::getName, XmlBuilder.ForGetter::new));
        }

        @Override
        public Map>> getConstructors(Meta> meta) {
            if (!atLeast(Version.v11)) {
                return Collections.emptyMap();
            }
            final Function[]> params = ct -> ct.getParameter().stream()
                .map(ParameterType::getType).map(XmlBuilder.this::resolveClass).toArray(Class[]::new);

            final Function signature =
                ct -> new Signature(meta.getHost().getName(), params.apply(ct));

            return descriptor.getConstructor().stream()
                .collect(Collectors.toMap(signature, XmlBuilder.ForConstructor::new));
        }

        @Override
        public Map> getMethods(Meta> meta) {
            if (!atLeast(Version.v11)) {
                return Collections.emptyMap();
            }
            final Function[]> params = mt -> mt.getParameter().stream().map(ParameterType::getType)
                .map(XmlBuilder.this::resolveClass).toArray(Class[]::new);

            final Function signature = mt -> new Signature(mt.getName(), params.apply(mt));

            return descriptor.getMethod().stream().collect(Collectors.toMap(signature, XmlBuilder.ForMethod::new));
        }

        @Override
        public final AnnotationBehavior getAnnotationBehavior() {
            return descriptor.getIgnoreAnnotations() ? AnnotationBehavior.EXCLUDE : AnnotationBehavior.INCLUDE;
        }
    }

    private class NonRootLevel, D> implements HasAnnotationBehavior {
        protected final D descriptor;
        private Lazy getIgnoreAnnotations;

        public NonRootLevel(D descriptor) {
            super();
            this.descriptor = Validate.notNull(descriptor, "descriptor");
        }

        @Override
        public final AnnotationBehavior getAnnotationBehavior() {
            return Optional.ofNullable(getIgnoreAnnotations).map(Lazy::get)
                .map(b -> b.booleanValue() ? AnnotationBehavior.EXCLUDE : AnnotationBehavior.INCLUDE)
                .orElse(AnnotationBehavior.ABSTAIN);
        }

        @SuppressWarnings("unchecked")
        final SELF withGetIgnoreAnnotations(Function getIgnoreAnnotations) {
            Validate.notNull(getIgnoreAnnotations);
            this.getIgnoreAnnotations = new Lazy<>(() -> getIgnoreAnnotations.apply(descriptor));
            return (SELF) this;
        }
    }

    private class ForElement, E extends AnnotatedElement, D>
        extends NonRootLevel implements MetadataBuilder.ForElement {

        private Lazy getDeclaredConstraints;

        ForElement(D descriptor) {
            super(descriptor);
        }

        @Override
        public final Annotation[] getDeclaredConstraints(Meta meta) {
            return lazy(getDeclaredConstraints, "getDeclaredConstraints");
        }

        final SELF withGetConstraintTypes(Function> getConstraintTypes) {
            return withGetDeclaredConstraints(getConstraintTypes
                .andThen(l -> l.stream().map(XmlBuilder.this::createConstraint).toArray(Annotation[]::new)));
        }

        @SuppressWarnings("unchecked")
        final SELF withGetDeclaredConstraints(Function getDeclaredConstraints) {
            this.getDeclaredConstraints = new Lazy<>(() -> getDeclaredConstraints.apply(descriptor));
            return (SELF) this;
        }
    }

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

        ForClass(ClassType descriptor) {
            super(descriptor);
            this.withGetConstraintTypes(ClassType::getConstraint)
                .withGetIgnoreAnnotations(ClassType::getIgnoreAnnotations);
        }

        @Override
        public List> getGroupSequence(Meta> meta) {
            final GroupSequenceType groupSequence = descriptor.getGroupSequence();
            return groupSequence == null ? null
                : groupSequence.getValue().stream().map(XmlBuilder.this::resolveClass).collect(ToUnmodifiable.list());
        }
    }

    private class ForContainer, E extends AnnotatedElement, D>
        extends XmlBuilder.ForElement implements MetadataBuilder.ForContainer {

        private Lazy isCascade;
        private Lazy> getGroupConversions;
        private Lazy> getContainerElementTypes;

        ForContainer(D descriptor) {
            super(descriptor);
        }

        @Override
        public boolean isCascade(Meta meta) {
            return Boolean.TRUE.equals(lazy(isCascade, "isCascade"));
        }

        @Override
        public Set getGroupConversions(Meta meta) {
            return lazy(getGroupConversions, "getGroupConversions");
        }

        @Override
        public Map> getContainerElementTypes(
            Meta meta) {
            if (!atLeast(Version.v20)) {
                return Collections.emptyMap();
            }
            final List elements = lazy(getContainerElementTypes, "getContainerElementTypes");
            final AnnotatedType annotatedType = meta.getAnnotatedType();
            final E host = meta.getHost();

            if (annotatedType instanceof AnnotatedParameterizedType) {
                final AnnotatedType[] actualTypeArguments =
                    ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();

                return elements.stream().collect(ToUnmodifiable.map(cet -> {
                    Integer typeArgumentIndex = cet.getTypeArgumentIndex();
                    if (typeArgumentIndex == null) {
                        Exceptions.raiseIf(actualTypeArguments.length > 1, ValidationException::new,
                            "Missing required type argument index for %s", host);
                        typeArgumentIndex = Integer.valueOf(0);
                    }
                    return new ContainerElementKey(annotatedType, typeArgumentIndex);
                }, XmlBuilder.ForContainerElementType::new));
            }
            if (!elements.isEmpty()) {
                Exceptions.raise(ValidationException::new, "Illegally specified %d container element type(s) for %s",
                    elements.size(), host);
            }
            return Collections.emptyMap();
        }

        @SuppressWarnings("unchecked")
        SELF withGetValid(Function getValid) {
            Validate.notNull(getValid);
            this.isCascade = new Lazy<>(() -> getValid.apply(descriptor) != null);
            return (SELF) this;
        }

        @SuppressWarnings("unchecked")
        SELF withGetGroupConversions(Function> getGroupConversions) {
            Validate.notNull(getGroupConversions);

            this.getGroupConversions = new Lazy<>(() -> {
                return getGroupConversions.apply(descriptor).stream().map(gc -> {
                    final String from = gc.getFrom();
                    final Class source = from == null ? Default.class : resolveClass(from);
                    final Class target = resolveClass(gc.getTo());
                    return GroupConversion.from(source).to(target);
                }).collect(ToUnmodifiable.set());
            });
            return (SELF) this;
        }

        @SuppressWarnings("unchecked")
        SELF withGetContainerElementTypes(Function> getContainerElementTypes) {
            Validate.notNull(getContainerElementTypes);
            this.getContainerElementTypes = new Lazy<>(() -> getContainerElementTypes.apply(descriptor));
            return (SELF) this;
        }
    }

    private class ForContainerElementType
        extends ForContainer {

        ForContainerElementType(ContainerElementTypeType descriptor) {
            super(descriptor);
            this.withGetConstraintTypes(ContainerElementTypeType::getConstraint)
                .withGetValid(ContainerElementTypeType::getValid)
                .withGetGroupConversions(ContainerElementTypeType::getConvertGroup)
                .withGetContainerElementTypes(ContainerElementTypeType::getContainerElementType);
        }
    }

    private class ForField extends XmlBuilder.ForContainer {

        ForField(FieldType descriptor) {
            super(descriptor);
            this.withGetIgnoreAnnotations(FieldType::getIgnoreAnnotations)
                .withGetConstraintTypes(FieldType::getConstraint).withGetValid(FieldType::getValid)
                .withGetGroupConversions(FieldType::getConvertGroup)
                .withGetContainerElementTypes(FieldType::getContainerElementType);
        }
    }

    private class ForGetter extends XmlBuilder.ForContainer {

        ForGetter(GetterType descriptor) {
            super(descriptor);
            this.withGetIgnoreAnnotations(GetterType::getIgnoreAnnotations)
                .withGetConstraintTypes(GetterType::getConstraint).withGetValid(GetterType::getValid)
                .withGetGroupConversions(GetterType::getConvertGroup)
                .withGetContainerElementTypes(GetterType::getContainerElementType);
        }
    }

    private abstract class ForExecutable, E extends Executable, D>
        extends NonRootLevel implements MetadataBuilder.ForExecutable {

        Lazy getReturnValue;
        Lazy getCrossParameter;
        Lazy> getParameters;

        ForExecutable(D descriptor) {
            super(descriptor);
        }

        @Override
        public MetadataBuilder.ForElement getCrossParameter(Meta meta) {
            final CrossParameterType cp = lazy(getCrossParameter, "getCrossParameter");
            if (cp == null) {
                return EmptyBuilder.instance(). forExecutable().getCrossParameter(meta);
            }
            return new XmlBuilder.ForCrossParameter<>(cp);
        }

        @Override
        public MetadataBuilder.ForContainer getReturnValue(Meta meta) {
            final ReturnValueType rv = lazy(getReturnValue, "getReturnValue");
            if (rv == null) {
                return EmptyBuilder.instance(). forExecutable().getReturnValue(meta);
            }
            return new XmlBuilder.ForReturnValue<>(rv);
        }

        @Override
        public List> getParameters(Meta meta) {
            return lazy(getParameters, "getParameters").stream().map(XmlBuilder.ForParameter::new)
                .collect(Collectors.toList());
        }

        @SuppressWarnings("unchecked")
        SELF withGetReturnValue(Function getReturnValue) {
            Validate.notNull(getReturnValue);
            this.getReturnValue = new Lazy<>(() -> getReturnValue.apply(descriptor));
            return (SELF) this;
        }

        @SuppressWarnings("unchecked")
        SELF withGetCrossParameter(Function getCrossParameter) {
            Validate.notNull(getCrossParameter);
            this.getCrossParameter = new Lazy<>(() -> getCrossParameter.apply(descriptor));
            return (SELF) this;
        }

        @SuppressWarnings("unchecked")
        SELF withGetParameters(Function> getParameters) {
            Validate.notNull(getParameters);
            this.getParameters = new Lazy<>(() -> getParameters.apply(descriptor));
            return (SELF) this;
        }
    }

    private class ForConstructor extends ForExecutable, Constructor, ConstructorType> {

        ForConstructor(ConstructorType descriptor) {
            super(descriptor);
            this.withGetIgnoreAnnotations(ConstructorType::getIgnoreAnnotations)
                .withGetReturnValue(ConstructorType::getReturnValue)
                .withGetCrossParameter(ConstructorType::getCrossParameter)
                .withGetParameters(ConstructorType::getParameter);
        }
    }

    private class ForMethod extends ForExecutable {

        ForMethod(MethodType descriptor) {
            super(descriptor);
            this.withGetIgnoreAnnotations(MethodType::getIgnoreAnnotations)
                .withGetReturnValue(MethodType::getReturnValue).withGetCrossParameter(MethodType::getCrossParameter)
                .withGetParameters(MethodType::getParameter);
        }
    }

    private class ForParameter extends ForContainer {

        ForParameter(ParameterType descriptor) {
            super(descriptor);
            this.withGetIgnoreAnnotations(ParameterType::getIgnoreAnnotations)
                .withGetConstraintTypes(ParameterType::getConstraint).withGetValid(ParameterType::getValid)
                .withGetGroupConversions(ParameterType::getConvertGroup)
                .withGetContainerElementTypes(ParameterType::getContainerElementType);
        }
    }

    private class ForCrossParameter
        extends ForElement, E, CrossParameterType> {

        ForCrossParameter(CrossParameterType descriptor) {
            super(descriptor);
            this.withGetIgnoreAnnotations(CrossParameterType::getIgnoreAnnotations)
                .withGetDeclaredConstraints(d -> d.getConstraint().stream()
                    .map(ct -> createConstraint(ct, ConstraintTarget.PARAMETERS)).toArray(Annotation[]::new));
        }
    }

    private class ForReturnValue extends ForContainer, E, ReturnValueType> {

        ForReturnValue(ReturnValueType descriptor) {
            super(descriptor);
            this.withGetDeclaredConstraints(d -> d.getConstraint().stream()
                .map(ct -> createConstraint(ct, ConstraintTarget.RETURN_VALUE)).toArray(Annotation[]::new))
                .withGetIgnoreAnnotations(ReturnValueType::getIgnoreAnnotations).withGetValid(ReturnValueType::getValid)
                .withGetGroupConversions(ReturnValueType::getConvertGroup)
                .withGetContainerElementTypes(ReturnValueType::getContainerElementType);
        }
    }

    private static final  T lazy(Lazy lazy, String name) {
        Validate.validState(lazy != null, "%s not set", name);
        return lazy.get();
    }

    private final ApacheValidatorFactory validatorFactory;
    private final ConstraintMappingsType constraintMappings;
    private final Version version;

    public XmlBuilder(ApacheValidatorFactory validatorFactory, ConstraintMappingsType constraintMappings) {
        super();
        this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
        this.constraintMappings = Validate.notNull(constraintMappings, "constraintMappings");
        this.version = Version.of(constraintMappings);
        new MappingValidator(constraintMappings, this::resolveClass).validateMappings();
    }

    public Map, MetadataBuilder.ForBean> forBeans() {
        return constraintMappings.getBean().stream().map(XmlBuilder.ForBean::new)
            .collect(ToUnmodifiable.map(XmlBuilder.ForBean::getBeanClass, Function.identity()));
    }

    public String getDefaultPackage() {
        return constraintMappings.getDefaultPackage();
    }

    boolean atLeast(Version v) {
        return version.compareTo(v) >= 0;
    }

     Class resolveClass(String className) {
        return loadClass(toQualifiedClassName(className));
    }

    private String toQualifiedClassName(String className) {
        if (isQualifiedClass(className)) {
            return className;
        }
        if (className.startsWith("[L") && className.endsWith(";")) {
            return "[L" + getDefaultPackage() + "." + className.substring(2);
        }
        return getDefaultPackage() + "." + className;
    }

    private boolean isQualifiedClass(String clazz) {
        return clazz.indexOf('.') >= 0;
    }

    @SuppressWarnings("unchecked")
    private  Class loadClass(final String fqn) {
        ClassLoader loader = Reflection.loaderFromThreadOrClass(XmlBuilder.class);
        if (loader == null) {
            loader = getClass().getClassLoader();
        }
        try {
            return (Class) Class.forName(fqn, true, loader);
        } catch (ClassNotFoundException ex) {
            throw Exceptions.create(ValidationException::new, ex, "Unable to load class: %d", fqn);
        }
    }

    private Class[] loadClasses(Supplier> classNames) {
        return streamClasses(classNames).toArray(Class[]::new);
    }

    private Stream> streamClasses(Supplier> classNames) {
        return classNames.get().map(this::loadClass);
    }

    private  A createConstraint(final ConstraintType constraint) {
        return createConstraint(constraint, ConstraintTarget.IMPLICIT);
    }

    private  A createConstraint(final ConstraintType constraint, ConstraintTarget target) {
        final Class annotationClass = this. loadClass(toQualifiedClassName(constraint.getAnnotation()));
        final AnnotationProxyBuilder annoBuilder =
            validatorFactory.getAnnotationsManager().buildProxyFor(annotationClass);

        if (constraint.getMessage() != null) {
            annoBuilder.setMessage(constraint.getMessage());
        }
        annoBuilder.setGroups(getGroups(constraint.getGroups()));
        annoBuilder.setPayload(getPayload(constraint.getPayload()));

        if (ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.analyze(annotationClass).isValid()) {
            annoBuilder.setValidationAppliesTo(target);
        }
        for (final ElementType elementType : constraint.getElement()) {
            final String name = elementType.getName();
            final Class returnType = getAnnotationParameterType(annotationClass, name);
            final Object elementValue = getElementValue(elementType, returnType);
            annoBuilder.setValue(name, elementValue);
        }
        return annoBuilder.createAnnotation();
    }

    private  Class getAnnotationParameterType(final Class annotationClass,
        final String name) {
        final Method m = Reflection.getPublicMethod(annotationClass, name);
        Exceptions.raiseIf(m == null, ValidationException::new,
            "Annotation of type %s does not contain a parameter %s.", annotationClass.getName(), name);
        return m.getReturnType();
    }

    private Object getElementValue(ElementType elementType, Class returnType) {
        removeEmptyContentElements(elementType);

        final List content = elementType.getContent();
        final int sz = content.size();
        if (returnType.isArray()) {
            final Object result = Array.newInstance(returnType.getComponentType(), sz);
            for (int i = 0; i < sz; i++) {
                Array.set(result, i, getSingleValue(content.get(i), returnType.getComponentType()));
            }
            return result;
        }
        Exceptions.raiseIf(sz != 1, ValidationException::new,
            "Attempt to specify an array where single value is expected.");

        return getSingleValue(content.get(0), returnType);
    }

    private void removeEmptyContentElements(ElementType elementType) {
        for (Iterator iter = elementType.getContent().iterator(); iter.hasNext();) {
            final Serializable content = iter.next();
            if (content instanceof String && ((String) content).matches("[\\n ].*")) {
                iter.remove();
            }
        }
    }

    @SuppressWarnings("unchecked")
    private Object getSingleValue(Serializable serializable, Class returnType) {
        if (serializable instanceof String) {
            return convertToResultType(returnType, (String) serializable);
        }
        if (serializable instanceof JAXBElement) {
            final JAXBElement elem = (JAXBElement) serializable;
            if (String.class.equals(elem.getDeclaredType())) {
                return convertToResultType(returnType, (String) elem.getValue());
            }
            if (AnnotationType.class.equals(elem.getDeclaredType())) {
                AnnotationType annotationType = (AnnotationType) elem.getValue();
                try {
                    return createAnnotation(annotationType, (Class) returnType);
                } catch (ClassCastException e) {
                    throw new ValidationException("Unexpected parameter value");
                }
            }
        }
        throw new ValidationException("Unexpected parameter value");
    }

    private Object convertToResultType(Class returnType, String value) {
        /**
         * Class is represented by the fully qualified class name of the class. spec: Note that if the raw string is
         * unqualified, default package is taken into account.
         */
        if (String.class.equals(returnType)) {
            return value;
        }
        if (Class.class.equals(returnType)) {
            return resolveClass(value);
        }
        if (returnType.isEnum()) {
            try {
                @SuppressWarnings({ "rawtypes", "unchecked" })
                final Enum e = Enum.valueOf(returnType.asSubclass(Enum.class), value);
                return e;
            } catch (IllegalArgumentException e) {
                throw new ConstraintDeclarationException(e);
            }
        }
        try {
            if (Byte.class.equals(returnType) || byte.class.equals(returnType)) {
                // spec mandates it:
                return Byte.parseByte(value);
            }
            if (Short.class.equals(returnType) || short.class.equals(returnType)) {
                return Short.parseShort(value);
            }
            if (Integer.class.equals(returnType) || int.class.equals(returnType)) {
                return Integer.parseInt(value);
            }
            if (Long.class.equals(returnType) || long.class.equals(returnType)) {
                return Long.parseLong(value);
            }
            if (Float.class.equals(returnType) || float.class.equals(returnType)) {
                return Float.parseFloat(value);
            }
            if (Double.class.equals(returnType) || double.class.equals(returnType)) {
                return Double.parseDouble(value);
            }
            if (Boolean.class.equals(returnType) || boolean.class.equals(returnType)) {
                return Boolean.parseBoolean(value);
            }
        } catch (Exception e) {
            Exceptions.raise(ValidationException::new, e, "Unable to coerce value '%s' to %s", value, returnType);
        }
        if (Character.class.equals(returnType) || char.class.equals(returnType)) {
            Exceptions.raiseIf(value.length() > 1, ConstraintDeclarationException::new,
                "a char must have a length of 1");
            return value.charAt(0);
        }
        return Exceptions.raise(ValidationException::new, "Unknown annotation value type %s", returnType.getName());
    }

    private  Annotation createAnnotation(AnnotationType annotationType, Class returnType) {
        final AnnotationProxyBuilder metaAnnotation =
            validatorFactory.getAnnotationsManager().buildProxyFor(returnType);
        for (ElementType elementType : annotationType.getElement()) {
            final String name = elementType.getName();
            metaAnnotation.setValue(name, getElementValue(elementType, getAnnotationParameterType(returnType, name)));
        }
        return metaAnnotation.createAnnotation();
    }

    private Class[] getGroups(GroupsType groupsType) {
        if (groupsType == null) {
            return ObjectUtils.EMPTY_CLASS_ARRAY;
        }
        return loadClasses(groupsType.getValue()::stream);
    }

    @SuppressWarnings("unchecked")
    private Class[] getPayload(PayloadType payloadType) {
        if (payloadType == null) {
            return (Class[]) ObjectUtils.EMPTY_CLASS_ARRAY;
        }
        return streamClasses(payloadType.getValue()::stream).peek(pc -> {
            Exceptions.raiseUnless(Payload.class.isAssignableFrom(pc), ConstraintDeclarationException::new,
                "Specified payload class %s does not implement %s", pc.getName(), Payload.class.getName());
        }).> map(pc -> pc.asSubclass(Payload.class)).toArray(Class[]::new);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy