You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jg...@apache.org on 2021/04/30 20:37:06 UTC
[tomee-jakarta] branch master updated: Add patch and new XSDs for
bval-jsr
This is an automated email from the ASF dual-hosted git repository.
jgallimore pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git
The following commit(s) were added to refs/heads/master by this push:
new dbb0c0f Add patch and new XSDs for bval-jsr
dbb0c0f is described below
commit dbb0c0f3f9ef2075eae58ebc92c7c07002d934fa
Author: Jonathan Gallimore <jo...@jrg.me.uk>
AuthorDate: Fri Apr 30 21:35:41 2021 +0100
Add patch and new XSDs for bval-jsr
---
.../org/apache/bval/jsr/metadata/XmlBuilder.java | 708 +++++++++++++++++++++
.../bval/jsr/xml/ValidationMappingParser.java | 167 +++++
.../org/apache/bval/jsr/xml/ValidationParser.java | 154 +++++
.../META-INF/validation-configuration-3.0.xsd | 81 +++
.../resources/META-INF/validation-mapping-3.0.xsd | 295 +++++++++
5 files changed, 1405 insertions(+)
diff --git a/transform/src/patch/java/org/apache/bval/jsr/metadata/XmlBuilder.java b/transform/src/patch/java/org/apache/bval/jsr/metadata/XmlBuilder.java
new file mode 100644
index 0000000..5a03087
--- /dev/null
+++ b/transform/src/patch/java/org/apache/bval/jsr/metadata/XmlBuilder.java
@@ -0,0 +1,708 @@
+/*
+ * 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"), v30("3.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<T> implements MetadataBuilder.ForBean<T> {
+
+ private final BeanType descriptor;
+
+ ForBean(BeanType descriptor) {
+ super();
+ this.descriptor = Validate.notNull(descriptor, "descriptor");
+ }
+
+ Class<?> getBeanClass() {
+ return resolveClass(descriptor.getClazz());
+ }
+
+ @Override
+ public MetadataBuilder.ForClass<T> getClass(Meta<Class<T>> meta) {
+ final ClassType classType = descriptor.getClassType();
+ return classType == null ? EmptyBuilder.instance().<T> forBean().getClass(meta)
+ : new XmlBuilder.ForClass<T>(classType);
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Meta<Class<T>> meta) {
+ return descriptor.getField().stream()
+ .collect(ToUnmodifiable.map(FieldType::getName, XmlBuilder.ForField::new));
+ }
+
+ @Override
+ public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Meta<Class<T>> meta) {
+ return descriptor.getGetter().stream()
+ .collect(ToUnmodifiable.map(GetterType::getName, XmlBuilder.ForGetter::new));
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Constructor<? extends T>>> getConstructors(Meta<Class<T>> meta) {
+ if (!atLeast(Version.v11)) {
+ return Collections.emptyMap();
+ }
+ final Function<ConstructorType, Class<?>[]> params = ct -> ct.getParameter().stream()
+ .map(ParameterType::getType).map(XmlBuilder.this::resolveClass).toArray(Class[]::new);
+
+ final Function<ConstructorType, Signature> signature =
+ ct -> new Signature(meta.getHost().getName(), params.apply(ct));
+
+ return descriptor.getConstructor().stream()
+ .collect(Collectors.toMap(signature, XmlBuilder.ForConstructor::new));
+ }
+
+ @Override
+ public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Meta<Class<T>> meta) {
+ if (!atLeast(Version.v11)) {
+ return Collections.emptyMap();
+ }
+ final Function<MethodType, Class<?>[]> params = mt -> mt.getParameter().stream().map(ParameterType::getType)
+ .map(XmlBuilder.this::resolveClass).toArray(Class[]::new);
+
+ final Function<MethodType, Signature> 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<SELF extends NonRootLevel<SELF, D>, D> implements HasAnnotationBehavior {
+ protected final D descriptor;
+ private Lazy<Boolean> 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<D, Boolean> getIgnoreAnnotations) {
+ Validate.notNull(getIgnoreAnnotations);
+ this.getIgnoreAnnotations = new Lazy<>(() -> getIgnoreAnnotations.apply(descriptor));
+ return (SELF) this;
+ }
+ }
+
+ private class ForElement<SELF extends XmlBuilder.ForElement<SELF, E, D>, E extends AnnotatedElement, D>
+ extends NonRootLevel<SELF, D> implements MetadataBuilder.ForElement<E> {
+
+ private Lazy<Annotation[]> getDeclaredConstraints;
+
+ ForElement(D descriptor) {
+ super(descriptor);
+ }
+
+ @Override
+ public final Annotation[] getDeclaredConstraints(Meta<E> meta) {
+ return lazy(getDeclaredConstraints, "getDeclaredConstraints");
+ }
+
+ final SELF withGetConstraintTypes(Function<D, List<ConstraintType>> getConstraintTypes) {
+ return withGetDeclaredConstraints(getConstraintTypes
+ .andThen(l -> l.stream().map(XmlBuilder.this::createConstraint).toArray(Annotation[]::new)));
+ }
+
+ @SuppressWarnings("unchecked")
+ final SELF withGetDeclaredConstraints(Function<D, Annotation[]> getDeclaredConstraints) {
+ this.getDeclaredConstraints = new Lazy<>(() -> getDeclaredConstraints.apply(descriptor));
+ return (SELF) this;
+ }
+ }
+
+ private class ForClass<T> extends ForElement<ForClass<T>, Class<T>, ClassType> implements MetadataBuilder.ForClass<T> {
+
+ ForClass(ClassType descriptor) {
+ super(descriptor);
+ this.withGetConstraintTypes(ClassType::getConstraint)
+ .withGetIgnoreAnnotations(ClassType::getIgnoreAnnotations);
+ }
+
+ @Override
+ public List<Class<?>> getGroupSequence(Meta<Class<T>> meta) {
+ final GroupSequenceType groupSequence = descriptor.getGroupSequence();
+ return groupSequence == null ? null
+ : groupSequence.getValue().stream().map(XmlBuilder.this::resolveClass).collect(ToUnmodifiable.list());
+ }
+ }
+
+ private class ForContainer<SELF extends XmlBuilder.ForContainer<SELF, E, D>, E extends AnnotatedElement, D>
+ extends XmlBuilder.ForElement<SELF, E, D> implements MetadataBuilder.ForContainer<E> {
+
+ private Lazy<Boolean> isCascade;
+ private Lazy<Set<GroupConversion>> getGroupConversions;
+ private Lazy<List<ContainerElementTypeType>> getContainerElementTypes;
+
+ ForContainer(D descriptor) {
+ super(descriptor);
+ }
+
+ @Override
+ public boolean isCascade(Meta<E> meta) {
+ return Boolean.TRUE.equals(lazy(isCascade, "isCascade"));
+ }
+
+ @Override
+ public Set<GroupConversion> getGroupConversions(Meta<E> meta) {
+ return lazy(getGroupConversions, "getGroupConversions");
+ }
+
+ @Override
+ public Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+ Meta<E> meta) {
+ if (!atLeast(Version.v20)) {
+ return Collections.emptyMap();
+ }
+ final List<ContainerElementTypeType> 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<D, String> getValid) {
+ Validate.notNull(getValid);
+ this.isCascade = new Lazy<>(() -> getValid.apply(descriptor) != null);
+ return (SELF) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ SELF withGetGroupConversions(Function<D, List<GroupConversionType>> 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<D, List<ContainerElementTypeType>> getContainerElementTypes) {
+ Validate.notNull(getContainerElementTypes);
+ this.getContainerElementTypes = new Lazy<>(() -> getContainerElementTypes.apply(descriptor));
+ return (SELF) this;
+ }
+ }
+
+ private class ForContainerElementType
+ extends ForContainer<ForContainerElementType, AnnotatedType, ContainerElementTypeType> {
+
+ 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, Field, FieldType> {
+
+ 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, Method, GetterType> {
+
+ 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<SELF extends ForExecutable<SELF, E, D>, E extends Executable, D>
+ extends NonRootLevel<SELF, D> implements MetadataBuilder.ForExecutable<E> {
+
+ Lazy<ReturnValueType> getReturnValue;
+ Lazy<CrossParameterType> getCrossParameter;
+ Lazy<List<ParameterType>> getParameters;
+
+ ForExecutable(D descriptor) {
+ super(descriptor);
+ }
+
+ @Override
+ public MetadataBuilder.ForElement<E> getCrossParameter(Meta<E> meta) {
+ final CrossParameterType cp = lazy(getCrossParameter, "getCrossParameter");
+ if (cp == null) {
+ return EmptyBuilder.instance().<E> forExecutable().getCrossParameter(meta);
+ }
+ return new XmlBuilder.ForCrossParameter<>(cp);
+ }
+
+ @Override
+ public MetadataBuilder.ForContainer<E> getReturnValue(Meta<E> meta) {
+ final ReturnValueType rv = lazy(getReturnValue, "getReturnValue");
+ if (rv == null) {
+ return EmptyBuilder.instance().<E> forExecutable().getReturnValue(meta);
+ }
+ return new XmlBuilder.ForReturnValue<>(rv);
+ }
+
+ @Override
+ public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Meta<E> meta) {
+ return lazy(getParameters, "getParameters").stream().map(XmlBuilder.ForParameter::new)
+ .collect(Collectors.toList());
+ }
+
+ @SuppressWarnings("unchecked")
+ SELF withGetReturnValue(Function<D, ReturnValueType> getReturnValue) {
+ Validate.notNull(getReturnValue);
+ this.getReturnValue = new Lazy<>(() -> getReturnValue.apply(descriptor));
+ return (SELF) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ SELF withGetCrossParameter(Function<D, CrossParameterType> getCrossParameter) {
+ Validate.notNull(getCrossParameter);
+ this.getCrossParameter = new Lazy<>(() -> getCrossParameter.apply(descriptor));
+ return (SELF) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ SELF withGetParameters(Function<D, List<ParameterType>> getParameters) {
+ Validate.notNull(getParameters);
+ this.getParameters = new Lazy<>(() -> getParameters.apply(descriptor));
+ return (SELF) this;
+ }
+ }
+
+ private class ForConstructor<T> extends ForExecutable<ForConstructor<T>, Constructor<? extends T>, 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, Method, MethodType> {
+
+ ForMethod(MethodType descriptor) {
+ super(descriptor);
+ this.withGetIgnoreAnnotations(MethodType::getIgnoreAnnotations)
+ .withGetReturnValue(MethodType::getReturnValue).withGetCrossParameter(MethodType::getCrossParameter)
+ .withGetParameters(MethodType::getParameter);
+ }
+ }
+
+ private class ForParameter extends ForContainer<ForParameter, Parameter, ParameterType> {
+
+ ForParameter(ParameterType descriptor) {
+ super(descriptor);
+ this.withGetIgnoreAnnotations(ParameterType::getIgnoreAnnotations)
+ .withGetConstraintTypes(ParameterType::getConstraint).withGetValid(ParameterType::getValid)
+ .withGetGroupConversions(ParameterType::getConvertGroup)
+ .withGetContainerElementTypes(ParameterType::getContainerElementType);
+ }
+ }
+
+ private class ForCrossParameter<E extends Executable>
+ extends ForElement<ForCrossParameter<E>, 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<E extends Executable> extends ForContainer<ForReturnValue<E>, 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> T lazy(Lazy<T> 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<Class<?>, 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;
+ }
+
+ <T> Class<T> 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 <T> Class<T> loadClass(final String fqn) {
+ ClassLoader loader = Reflection.loaderFromThreadOrClass(XmlBuilder.class);
+ if (loader == null) {
+ loader = getClass().getClassLoader();
+ }
+ try {
+ return (Class<T>) Class.forName(fqn, true, loader);
+ } catch (ClassNotFoundException ex) {
+ throw Exceptions.create(ValidationException::new, ex, "Unable to load class: %d", fqn);
+ }
+ }
+
+ private Class<?>[] loadClasses(Supplier<Stream<String>> classNames) {
+ return streamClasses(classNames).toArray(Class[]::new);
+ }
+
+ private Stream<Class<?>> streamClasses(Supplier<Stream<String>> classNames) {
+ return classNames.get().map(this::loadClass);
+ }
+
+ private <A extends Annotation, T> A createConstraint(final ConstraintType constraint) {
+ return createConstraint(constraint, ConstraintTarget.IMPLICIT);
+ }
+
+ private <A extends Annotation, T> A createConstraint(final ConstraintType constraint, ConstraintTarget target) {
+ final Class<A> annotationClass = this.<A> loadClass(toQualifiedClassName(constraint.getAnnotation()));
+ final AnnotationProxyBuilder<A> 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 <A extends Annotation> Class<?> getAnnotationParameterType(final Class<A> 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<Serializable> 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<Serializable> 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<? extends Annotation>) 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 <A extends Annotation> Annotation createAnnotation(AnnotationType annotationType, Class<A> returnType) {
+ final AnnotationProxyBuilder<A> 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<? extends Payload>[] getPayload(PayloadType payloadType) {
+ if (payloadType == null) {
+ return (Class<? extends Payload>[]) 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());
+ }).<Class<? extends Payload>> map(pc -> pc.asSubclass(Payload.class)).toArray(Class[]::new);
+ }
+}
diff --git a/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationMappingParser.java b/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
new file mode 100644
index 0000000..33076b4
--- /dev/null
+++ b/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
@@ -0,0 +1,167 @@
+/*
+ * 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.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import javax.validation.ValidationException;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ConfigurationState;
+
+import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.metadata.MetadataBuilder;
+import org.apache.bval.jsr.metadata.MetadataBuilder.ForBean;
+import org.apache.bval.jsr.metadata.MetadataSource;
+import org.apache.bval.jsr.metadata.ValidatorMappingProvider;
+import org.apache.bval.jsr.metadata.XmlBuilder;
+import org.apache.bval.jsr.metadata.XmlValidationMappingProvider;
+import org.apache.bval.util.Exceptions;
+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;
+import org.xml.sax.InputSource;
+
+/**
+ * Uses JAXB to parse constraints.xml based on the validation-mapping XML schema.
+ */
+@Privilizing(@CallTo(Reflection.class))
+public class ValidationMappingParser implements MetadataSource {
+ private static final SchemaManager SCHEMA_MANAGER = new SchemaManager.Builder()
+ .add(XmlBuilder.Version.v10.getId(), "http://jboss.org/xml/ns/javax/validation/mapping",
+ "META-INF/validation-mapping-1.0.xsd")
+ .add(XmlBuilder.Version.v11.getId(), "http://jboss.org/xml/ns/javax/validation/mapping",
+ "META-INF/validation-mapping-1.1.xsd")
+ .add(XmlBuilder.Version.v20.getId(), "http://xmlns.jcp.org/xml/ns/validation/mapping",
+ "META-INF/validation-mapping-2.0.xsd")
+ .add(XmlBuilder.Version.v30.getId(), "https://jakarta.ee/xml/ns/validation/mapping",
+ "META-INF/validation-mapping-2.0.xsd")
+ .build();
+
+ private ApacheValidatorFactory validatorFactory;
+
+ @Override
+ public void initialize(ApacheValidatorFactory validatorFactory) {
+ this.validatorFactory = Validate.notNull(validatorFactory);
+ }
+
+ @Override
+ public void process(ConfigurationState configurationState,
+ Consumer<ValidatorMappingProvider> addValidatorMappingProvider, BiConsumer<Class<?>, ForBean<?>> addBuilder) {
+ Validate.validState(validatorFactory != null, "validatorFactory unknown");
+
+ if (configurationState.isIgnoreXmlConfiguration()) {
+ return;
+ }
+ final Set<Class<?>> beanTypes = new HashSet<>();
+ for (final InputStream xmlStream : configurationState.getMappingStreams()) {
+ final ConstraintMappingsType mapping = parseXmlMappings(xmlStream);
+
+ Optional.of(mapping).map(this::toMappingProvider).ifPresent(addValidatorMappingProvider);
+
+ final Map<Class<?>, MetadataBuilder.ForBean<?>> builders =
+ new XmlBuilder(validatorFactory, mapping).forBeans();
+ if (Collections.disjoint(beanTypes, builders.keySet())) {
+ builders.forEach(addBuilder::accept);
+ beanTypes.addAll(builders.keySet());
+ } else {
+ Exceptions.raise(ValidationException::new,
+ builders.keySet().stream().filter(beanTypes::contains).map(Class::getName).collect(Collectors
+ .joining("bean classes specified multiple times for XML validation mapping: [", "; ", "]")));
+ }
+ }
+ }
+
+ /**
+ * @param in
+ * XML stream to parse using the validation-mapping-1.0.xsd
+ */
+ private ConstraintMappingsType parseXmlMappings(final InputStream in) {
+ try {
+ return SCHEMA_MANAGER.unmarshal(new InputSource(in), ConstraintMappingsType.class);
+ } catch (Exception e) {
+ throw new ValidationException("Failed to parse XML deployment descriptor file.", e);
+ } finally {
+ try {
+ in.reset(); // can be read several times + we ensured it was
+ // re-readable in addMapping()
+ } catch (final IOException e) {
+ // no-op
+ }
+ }
+ }
+
+ private ValidatorMappingProvider toMappingProvider(ConstraintMappingsType mapping) {
+ if (mapping.getConstraintDefinition().isEmpty()) {
+ return null;
+ }
+ final Map<Class<? extends Annotation>, ValidatedByType> validatorMappings = new HashMap<>();
+
+ for (ConstraintDefinitionType constraintDefinition : mapping.getConstraintDefinition()) {
+ final String annotationClassName = constraintDefinition.getAnnotation();
+
+ final Class<?> clazz = loadClass(annotationClassName, mapping.getDefaultPackage());
+
+ Exceptions.raiseUnless(clazz.isAnnotation(), ValidationException::new, "%s is not an annotation",
+ annotationClassName);
+
+ final Class<? extends Annotation> annotationClass = clazz.asSubclass(Annotation.class);
+
+ Exceptions.raiseIf(validatorMappings.containsKey(annotationClass), ValidationException::new,
+ "XML constraint validator(s) for %s already configured.", annotationClass);
+
+ validatorMappings.put(annotationClass, constraintDefinition.getValidatedBy());
+ }
+ return new XmlValidationMappingProvider(validatorMappings,
+ cn -> toQualifiedClassName(cn, mapping.getDefaultPackage()));
+ }
+
+ private Class<?> loadClass(String className, String defaultPackage) {
+ final String fqn = toQualifiedClassName(className, defaultPackage);
+ try {
+ return Reflection.toClass(fqn, Reflection.loaderFromThreadOrClass(ValidationMappingParser.class));
+ } catch (ClassNotFoundException ex) {
+ throw Exceptions.create(ValidationException::new, ex, "Unable to load class: %s", fqn);
+ }
+ }
+
+ private String toQualifiedClassName(String className, String defaultPackage) {
+ if (!isQualifiedClass(className)) {
+ if (className.startsWith("[L") && className.endsWith(";")) {
+ className = "[L" + defaultPackage + '.' + className.substring(2);
+ } else {
+ className = defaultPackage + '.' + className;
+ }
+ }
+ return className;
+ }
+
+ private boolean isQualifiedClass(String clazz) {
+ return clazz.indexOf('.') >= 0;
+ }
+}
diff --git a/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationParser.java b/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationParser.java
new file mode 100644
index 0000000..f0dfaa4
--- /dev/null
+++ b/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationParser.java
@@ -0,0 +1,154 @@
+/*
+ * 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.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import javax.validation.BootstrapConfiguration;
+import javax.validation.ValidationException;
+import javax.validation.executable.ExecutableType;
+
+import org.apache.bval.jsr.BootstrapConfigurationImpl;
+import org.apache.bval.jsr.ConfigurationImpl;
+import org.apache.bval.jsr.metadata.XmlBuilder;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privileged;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+import org.xml.sax.InputSource;
+
+/**
+ * Description: uses jaxb to parse validation.xml<br/>
+ */
+@Privilizing(@CallTo(Reflection.class))
+public class ValidationParser {
+
+ private static final String DEFAULT_VALIDATION_XML_FILE = "META-INF/validation.xml";
+ private static final Logger log = Logger.getLogger(ValidationParser.class.getName());
+
+ private static final SchemaManager SCHEMA_MANAGER = new SchemaManager.Builder()
+ .add(XmlBuilder.Version.v10.getId(), "http://jboss.org/xml/ns/javax/validation/configuration",
+ "META-INF/validation-configuration-1.0.xsd")
+ .add(XmlBuilder.Version.v11.getId(), "http://jboss.org/xml/ns/javax/validation/configuration",
+ "META-INF/validation-configuration-1.1.xsd")
+ .add(XmlBuilder.Version.v20.getId(), "http://xmlns.jcp.org/xml/ns/validation/configuration",
+ "META-INF/validation-configuration-2.0.xsd")
+ .add(XmlBuilder.Version.v30.getId(), "https://jakarta.ee/xml/ns/validation/configuration",
+ "META-INF/validation-configuration-2.0.xsd")
+ .build();
+
+ private static String getValidationXmlFile(String file) {
+ return file == null ? DEFAULT_VALIDATION_XML_FILE : file;
+ }
+
+ private static Map<String, String> toMap(final List<PropertyType> property) {
+ return property == null || property.isEmpty() ? Collections.emptyMap()
+ : property.stream().collect(Collectors.toMap(PropertyType::getName, PropertyType::getValue));
+ }
+
+ private final ClassLoader loader;
+
+ public ValidationParser(ClassLoader loader) {
+ this.loader = Validate.notNull(loader, null);
+ }
+
+ public BootstrapConfiguration processValidationConfig(final String file,
+ final ConfigurationImpl targetConfig) {
+ final ValidationConfigType xmlConfig = parseXmlConfig(file);
+ if (xmlConfig == null) {
+ return null;
+ }
+ final boolean executableValidationEnabled;
+ final Set<ExecutableType> defaultValidatedExecutableTypes;
+
+ if (xmlConfig.getExecutableValidation() == null) {
+ defaultValidatedExecutableTypes = EnumSet.of(ExecutableType.IMPLICIT);
+ executableValidationEnabled = true;
+ } else {
+ final Optional<ExecutableValidationType> executableValidation =
+ Optional.of(xmlConfig).map(ValidationConfigType::getExecutableValidation);
+ executableValidationEnabled = executableValidation.map(ExecutableValidationType::getEnabled)
+ .filter(Predicate.isEqual(Boolean.TRUE)).isPresent();
+
+ defaultValidatedExecutableTypes = executableValidation.filter(x -> executableValidationEnabled)
+ .map(ExecutableValidationType::getDefaultValidatedExecutableTypes)
+ .map(DefaultValidatedExecutableTypesType::getExecutableType).map(EnumSet::copyOf)
+ .orElse(EnumSet.noneOf(ExecutableType.class));
+ }
+ return new BootstrapConfigurationImpl(xmlConfig.getDefaultProvider(), xmlConfig.getConstraintValidatorFactory(),
+ xmlConfig.getMessageInterpolator(), xmlConfig.getTraversableResolver(),
+ xmlConfig.getParameterNameProvider(), new HashSet<>(xmlConfig.getConstraintMapping()),
+ executableValidationEnabled, defaultValidatedExecutableTypes, toMap(xmlConfig.getProperty()),
+ xmlConfig.getClockProvider(), new HashSet<>(xmlConfig.getValueExtractor()));
+ }
+
+ public InputStream open(String mappingFileName) {
+ if (mappingFileName.charAt(0) == '/') {
+ // Classloader needs a path without a starting /
+ mappingFileName = mappingFileName.substring(1);
+ }
+ try {
+ final InputStream in = getInputStream(mappingFileName);
+ Exceptions.raiseIf(in == null, ValidationException::new,
+ "Unable to open input stream for mapping file %s", mappingFileName);
+ return(in);
+ } catch (IOException e) {
+ throw Exceptions.create(ValidationException::new, e, "Unable to open input stream for mapping file %s",
+ mappingFileName);
+ }
+ }
+
+ InputStream getInputStream(final String path) throws IOException {
+ final List<URL> urls = Collections.list(loader.getResources(path));
+ Exceptions.raiseIf(urls.stream().distinct().count() > 1, ValidationException::new,
+ "More than one %s is found in the classpath", path);
+ return urls.isEmpty() ? null : urls.get(0).openStream();
+ }
+
+ @Privileged
+ private ValidationConfigType parseXmlConfig(final String validationXmlFile) {
+ try (InputStream inputStream = getInputStream(getValidationXmlFile(validationXmlFile))) {
+ if (inputStream == null) {
+ log.log(Level.FINEST,
+ String.format("No %s found. Using annotation based configuration only.", validationXmlFile));
+ return null;
+ }
+ log.log(Level.FINEST, String.format("%s found.", validationXmlFile));
+
+ return SCHEMA_MANAGER.unmarshal(new InputSource(inputStream), ValidationConfigType.class);
+ } catch (Exception e) {
+ throw Exceptions.create(ValidationException::new, e, "Unable to parse %s", validationXmlFile);
+ }
+ }
+}
diff --git a/transform/src/patch/resources/META-INF/validation-configuration-3.0.xsd b/transform/src/patch/resources/META-INF/validation-configuration-3.0.xsd
new file mode 100644
index 0000000..abce03e
--- /dev/null
+++ b/transform/src/patch/resources/META-INF/validation-configuration-3.0.xsd
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+-->
+<xs:schema attributeFormDefault="unqualified"
+ elementFormDefault="qualified"
+ targetNamespace="https://jakarta.ee/xml/ns/validation/configuration"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:config="https://jakarta.ee/xml/ns/validation/configuration"
+ version="3.0">
+
+ <xs:element name="validation-config" type="config:validation-configType"/>
+ <xs:complexType name="validation-configType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="default-provider" minOccurs="0"/>
+ <xs:element type="xs:string" name="message-interpolator" minOccurs="0"/>
+ <xs:element type="xs:string" name="traversable-resolver" minOccurs="0"/>
+ <xs:element type="xs:string" name="constraint-validator-factory" minOccurs="0"/>
+ <xs:element type="xs:string" name="parameter-name-provider" minOccurs="0"/>
+ <xs:element type="xs:string" name="clock-provider" minOccurs="0"/>
+ <xs:element type="xs:string" name="value-extractor" maxOccurs="unbounded"
+ minOccurs="0"/>
+ <xs:element type="config:executable-validationType" name="executable-validation"
+ minOccurs="0"/>
+ <xs:element type="xs:string" name="constraint-mapping" maxOccurs="unbounded"
+ minOccurs="0"/>
+ <xs:element type="config:propertyType" name="property" maxOccurs="unbounded"
+ minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="version" type="config:versionType" fixed="3.0" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="executable-validationType">
+ <xs:sequence>
+ <xs:element type="config:default-validated-executable-typesType"
+ name="default-validated-executable-types" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="enabled" use="optional" type="xs:boolean" default="true"/>
+ </xs:complexType>
+ <xs:complexType name="default-validated-executable-typesType">
+ <xs:sequence>
+ <xs:element name="executable-type" maxOccurs="unbounded" minOccurs="1">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="NONE"/>
+ <xs:enumeration value="CONSTRUCTORS"/>
+ <xs:enumeration value="NON_GETTER_METHODS"/>
+ <xs:enumeration value="GETTER_METHODS"/>
+ <xs:enumeration value="ALL"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="propertyType">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="name" use="required" type="xs:string"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <xs:simpleType name="versionType">
+ <xs:restriction base="xs:token">
+ <xs:pattern value="[0-9]+(\.[0-9]+)*"/>
+ </xs:restriction>
+ </xs:simpleType>
+</xs:schema>
\ No newline at end of file
diff --git a/transform/src/patch/resources/META-INF/validation-mapping-3.0.xsd b/transform/src/patch/resources/META-INF/validation-mapping-3.0.xsd
new file mode 100644
index 0000000..38991ec
--- /dev/null
+++ b/transform/src/patch/resources/META-INF/validation-mapping-3.0.xsd
@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+-->
+<xs:schema attributeFormDefault="unqualified"
+ elementFormDefault="qualified"
+ targetNamespace="https://jakarta.ee/xml/ns/validation/mapping"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:map="https://jakarta.ee/xml/ns/validation/mapping"
+ version="3.0">
+
+ <xs:element name="constraint-mappings" type="map:constraint-mappingsType"/>
+
+ <xs:complexType name="payloadType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="groupsType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="groupSequenceType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="groupConversionType">
+ <xs:attribute type="xs:string" name="from" use="optional"/>
+ <xs:attribute type="xs:string" name="to" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="constraint-mappingsType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="default-package" minOccurs="0"/>
+ <xs:element type="map:beanType"
+ name="bean"
+ maxOccurs="unbounded"
+ minOccurs="0"/>
+ <xs:element type="map:constraint-definitionType"
+ name="constraint-definition"
+ maxOccurs="unbounded"
+ minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="version" type="map:versionType" fixed="3.0" use="required"/>
+ </xs:complexType>
+ <xs:simpleType name="versionType">
+ <xs:restriction base="xs:token">
+ <xs:pattern value="[0-9]+(\.[0-9]+)*"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="validated-byType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute type="xs:boolean" name="include-existing-validators" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="constraintType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="message" minOccurs="0"/>
+ <xs:element type="map:groupsType"
+ name="groups"
+ minOccurs="0"/>
+ <xs:element type="map:payloadType"
+ name="payload"
+ minOccurs="0"/>
+ <xs:element type="map:elementType"
+ name="element"
+ maxOccurs="unbounded"
+ minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="annotation" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="elementType" mixed="true">
+ <xs:sequence>
+ <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+ <xs:element type="map:annotationType"
+ name="annotation"
+ maxOccurs="unbounded"
+ minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="name" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="containerElementTypeType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+ <xs:element type="map:groupConversionType"
+ name="convert-group"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:containerElementTypeType"
+ name="container-element-type"
+ maxOccurs="unbounded"
+ minOccurs="0"/>
+ <xs:element type="map:constraintType"
+ name="constraint"
+ maxOccurs="unbounded"
+ minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="type-argument-index" use="optional">
+ <xs:simpleType>
+ <xs:restriction base="xs:int">
+ <xs:minInclusive value="0" />
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:complexType>
+ <xs:complexType name="classType">
+ <xs:sequence>
+ <xs:element type="map:groupSequenceType"
+ name="group-sequence"
+ minOccurs="0"/>
+ <xs:element type="map:constraintType"
+ name="constraint"
+ maxOccurs="unbounded"
+ minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="beanType">
+ <xs:sequence>
+ <xs:element type="map:classType"
+ name="class"
+ minOccurs="0">
+ </xs:element>
+ <xs:element type="map:fieldType"
+ name="field"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:getterType"
+ name="getter"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:constructorType"
+ name="constructor"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:methodType"
+ name="method"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="class" use="required"/>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"
+ default="true"/>
+ </xs:complexType>
+ <xs:complexType name="annotationType">
+ <xs:sequence>
+ <xs:element type="map:elementType"
+ name="element"
+ maxOccurs="unbounded"
+ minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="getterType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+ <xs:element type="map:groupConversionType"
+ name="convert-group"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:containerElementTypeType"
+ name="container-element-type"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:constraintType"
+ name="constraint"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="name" use="required"/>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="methodType">
+ <xs:sequence>
+ <xs:element type="map:parameterType"
+ name="parameter"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:crossParameterType"
+ name="cross-parameter"
+ minOccurs="0"
+ maxOccurs="1"/>
+ <xs:element type="map:returnValueType"
+ name="return-value"
+ minOccurs="0"
+ maxOccurs="1"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="name" use="required"/>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="constructorType">
+ <xs:sequence>
+ <xs:element type="map:parameterType"
+ name="parameter"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:crossParameterType"
+ name="cross-parameter"
+ minOccurs="0"
+ maxOccurs="1"/>
+ <xs:element type="map:returnValueType"
+ name="return-value"
+ minOccurs="0"
+ maxOccurs="1"/>
+ </xs:sequence>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="parameterType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+ <xs:element type="map:groupConversionType"
+ name="convert-group"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:containerElementTypeType"
+ name="container-element-type"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:constraintType"
+ name="constraint"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="type" use="required"/>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="returnValueType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+ <xs:element type="map:groupConversionType"
+ name="convert-group"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:containerElementTypeType"
+ name="container-element-type"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:constraintType"
+ name="constraint"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="crossParameterType">
+ <xs:sequence>
+ <xs:element type="map:constraintType"
+ name="constraint"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="constraint-definitionType">
+ <xs:sequence>
+ <xs:element type="map:validated-byType"
+ name="validated-by"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="annotation" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="fieldType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+ <xs:element type="map:groupConversionType"
+ name="convert-group"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:containerElementTypeType"
+ name="container-element-type"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xs:element type="map:constraintType"
+ name="constraint"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="name" use="required"/>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+</xs:schema>
\ No newline at end of file