org.apache.bval.jsr.xml.ValidationMappingParser 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.xml;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.Payload;
import javax.validation.ValidationException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import org.apache.bval.jsr.ApacheValidatorFactory;
import org.apache.bval.jsr.ConstraintAnnotationAttributes;
import org.apache.bval.jsr.util.IOs;
import org.apache.bval.util.FieldAccess;
import org.apache.bval.util.MethodAccess;
import org.apache.bval.util.ObjectUtils;
import org.apache.bval.util.StringUtils;
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;
/**
* Uses JAXB to parse constraints.xml based on validation-mapping-1.0.xsd.
*/
@Privilizing(@CallTo(Reflection.class))
public class ValidationMappingParser {
private static final String VALIDATION_MAPPING_XSD = "META-INF/validation-mapping-1.1.xsd";
private static final Set RESERVED_PARAMS = Collections
.unmodifiableSet(EnumSet.of(ConstraintAnnotationAttributes.GROUPS, ConstraintAnnotationAttributes.MESSAGE,
ConstraintAnnotationAttributes.PAYLOAD, ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO));
private final Set> processedClasses;
private final ApacheValidatorFactory factory;
public ValidationMappingParser(ApacheValidatorFactory factory) {
this.factory = factory;
this.processedClasses = new HashSet>();
}
/**
* Parse files with constraint mappings and collect information in the factory.
*
* @param xmlStreams - one or more contraints.xml file streams to parse
*/
public void processMappingConfig(Set xmlStreams) throws ValidationException {
for (final InputStream xmlStream : xmlStreams) {
ConstraintMappingsType mapping = parseXmlMappings(xmlStream);
final String defaultPackage = mapping.getDefaultPackage();
processConstraintDefinitions(mapping.getConstraintDefinition(), defaultPackage);
for (final BeanType bean : mapping.getBean()) {
Class> beanClass = loadClass(bean.getClazz(), defaultPackage);
if (!processedClasses.add(beanClass)) {
// spec: A given class must not be described more than once amongst all
// the XML mapping descriptors.
throw new ValidationException(beanClass.getName() + " has already be configured in xml.");
}
boolean ignoreAnnotations = bean.getIgnoreAnnotations() == null ? true : bean.getIgnoreAnnotations();
factory.getAnnotationIgnores().setDefaultIgnoreAnnotation(beanClass, ignoreAnnotations);
processClassLevel(bean.getClassType(), beanClass, defaultPackage);
processConstructorLevel(bean.getConstructor(), beanClass, defaultPackage, ignoreAnnotations);
processFieldLevel(bean.getField(), beanClass, defaultPackage, ignoreAnnotations);
final Collection potentialMethodName =
processPropertyLevel(bean.getGetter(), beanClass, defaultPackage, ignoreAnnotations);
processMethodLevel(bean.getMethod(), beanClass, defaultPackage, ignoreAnnotations, potentialMethodName);
processedClasses.add(beanClass);
}
}
}
/** @param in XML stream to parse using the validation-mapping-1.0.xsd */
private ConstraintMappingsType parseXmlMappings(final InputStream in) {
ConstraintMappingsType mappings;
try {
final JAXBContext jc = JAXBContext.newInstance(ConstraintMappingsType.class);
final Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setSchema(getSchema());
final StreamSource stream = new StreamSource(in);
final JAXBElement root =
unmarshaller.unmarshal(stream, ConstraintMappingsType.class);
mappings = root.getValue();
} catch (final JAXBException e) {
throw new ValidationException("Failed to parse XML deployment descriptor file.", e);
} finally {
IOs.closeQuietly(in);
try {
in.reset(); // can be read several times + we ensured it was re-readable in addMapping()
} catch (final IOException e) {
// no-op
}
}
return mappings;
}
/** @return validation-mapping-1.0.xsd based schema */
private Schema getSchema() {
return ValidationParser.getSchema(VALIDATION_MAPPING_XSD);
}
private void processClassLevel(ClassType classType, Class> beanClass, String defaultPackage) {
if (classType == null) {
return;
}
// ignore annotation
if (classType.getIgnoreAnnotations() != null) {
factory.getAnnotationIgnores().setIgnoreAnnotationsOnClass(beanClass, classType.getIgnoreAnnotations());
}
// group sequence
Class>[] groupSequence = createGroupSequence(classType.getGroupSequence(), defaultPackage);
if (groupSequence != null) {
factory.addDefaultSequence(beanClass, groupSequence);
}
// constraints
for (ConstraintType constraint : classType.getConstraint()) {
MetaConstraint, ?> metaConstraint = createConstraint(constraint, beanClass, null, defaultPackage);
factory.addMetaConstraint(beanClass, metaConstraint);
}
}
@SuppressWarnings("unchecked")
private MetaConstraint, ?> createConstraint(final ConstraintType constraint,
final Class beanClass, final Member member, final String defaultPackage) {
final Class annotationClass = (Class) loadClass(constraint.getAnnotation(), defaultPackage);
final AnnotationProxyBuilder annoBuilder = new AnnotationProxyBuilder(annotationClass);
if (constraint.getMessage() != null) {
annoBuilder.setMessage(constraint.getMessage());
}
annoBuilder.setGroups(getGroups(constraint.getGroups(), defaultPackage));
annoBuilder.setPayload(getPayload(constraint.getPayload(), defaultPackage));
for (final ElementType elementType : constraint.getElement()) {
final String name = elementType.getName();
checkValidName(name);
final Class> returnType = getAnnotationParameterType(annotationClass, name);
final Object elementValue = getElementValue(elementType, returnType, defaultPackage);
annoBuilder.putValue(name, elementValue);
}
return new MetaConstraint(beanClass, member, annoBuilder.createAnnotation());
}
private void checkValidName(String name) {
for (ConstraintAnnotationAttributes attr : RESERVED_PARAMS) {
if (attr.getAttributeName().equals(name)) {
throw new ValidationException(name + " is a reserved parameter name.");
}
}
}
private Class> getAnnotationParameterType(final Class annotationClass,
final String name) {
final Method m = Reflection.getPublicMethod(annotationClass, name);
if (m == null) {
throw new ValidationException(
"Annotation of type " + annotationClass.getName() + " does not contain a parameter " + name + ".");
}
return m.getReturnType();
}
private Object getElementValue(ElementType elementType, Class> returnType, String defaultPackage) {
removeEmptyContentElements(elementType);
boolean isArray = returnType.isArray();
if (!isArray) {
if (elementType.getContent().size() != 1) {
throw new ValidationException("Attempt to specify an array where single value is expected.");
}
return getSingleValue(elementType.getContent().get(0), returnType, defaultPackage);
}
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy