
org.hibernate.validation.xml.XmlMappingParser Maven / Gradle / Ivy
// $Id: XmlMappingParser.java 17265 2009-08-11 19:31:41Z epbernard $
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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.hibernate.validation.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.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.security.AccessController;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
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 javax.xml.validation.SchemaFactory;
import org.slf4j.Logger;
import org.xml.sax.SAXException;
import org.hibernate.validation.metadata.ConstraintDescriptorImpl;
import org.hibernate.validation.metadata.MetaConstraint;
import org.hibernate.validation.metadata.ConstraintHelper;
import org.hibernate.validation.metadata.AnnotationIgnores;
import org.hibernate.validation.util.LoggerFactory;
import org.hibernate.validation.util.ReflectionHelper;
import org.hibernate.validation.util.ContainsField;
import org.hibernate.validation.util.GetMethodFromPropertyName;
import org.hibernate.validation.util.ContainsMethod;
import org.hibernate.validation.util.GetMethod;
import org.hibernate.validation.util.GetDeclaredField;
import org.hibernate.validation.util.GetClassLoader;
import org.hibernate.validation.util.LoadClass;
import org.hibernate.validation.util.annotationfactory.AnnotationDescriptor;
import org.hibernate.validation.util.annotationfactory.AnnotationFactory;
import org.hibernate.validation.xml.AnnotationType;
import org.hibernate.validation.xml.BeanType;
import org.hibernate.validation.xml.ClassType;
import org.hibernate.validation.xml.ConstraintDefinitionType;
import org.hibernate.validation.xml.ConstraintMappingsType;
import org.hibernate.validation.xml.ConstraintType;
import org.hibernate.validation.xml.ElementType;
import org.hibernate.validation.xml.FieldType;
import org.hibernate.validation.xml.GetterType;
import org.hibernate.validation.xml.GroupSequenceType;
import org.hibernate.validation.xml.GroupsType;
import org.hibernate.validation.xml.ValidatedByType;
/**
* @author Hardy Ferentschik
*/
public class XmlMappingParser {
private static final Logger log = LoggerFactory.make();
private static final String VALIDATION_MAPPING_XSD = "META-INF/validation-mapping-1.0.xsd";
private static final String MESSAGE_PARAM = "message";
private static final String GROUPS_PARAM = "groups";
private static final String PACKAGE_SEPERATOR = ".";
private final Set> processedClasses = new HashSet>();
private final ConstraintHelper constraintHelper;
private final AnnotationIgnores annotationIgnores;
private final Map, List>> constraintMap;
private final Map, List> cascadedMembers;
private final Map, List>> defaultSequences;
public XmlMappingParser(ConstraintHelper constraintHelper) {
this.constraintHelper = constraintHelper;
this.annotationIgnores = new AnnotationIgnores();
this.constraintMap = new HashMap, List>>();
this.cascadedMembers = new HashMap, List>();
this.defaultSequences = new HashMap, List>>();
}
public void parse(Set mappingStreams) {
for ( InputStream in : mappingStreams ) {
try {
ConstraintMappingsType mapping = getValidationConfig( in );
parseConstraintDefinitions( mapping.getConstraintDefinition() );
String defaultPackage = mapping.getDefaultPackage();
for ( BeanType bean : mapping.getBean() ) {
Class> beanClass = getClass( bean.getClazz(), defaultPackage );
checkClassHasNotBeenProcessed( processedClasses, beanClass );
annotationIgnores.setDefaultIgnoreAnnotation( beanClass, bean.isIgnoreAnnotations() );
parseClassLevelOverrides( bean.getClassType(), beanClass, defaultPackage );
parseFieldLevelOverrides( bean.getField(), beanClass, defaultPackage );
parsePropertyLevelOverrides( bean.getGetter(), beanClass, defaultPackage );
processedClasses.add( beanClass );
}
}
finally {
try {
in.close();
}
catch ( IOException e ) {
log.warn( "Error closing input stream: {}", e.getMessage() );
}
}
}
}
public Set> getProcessedClasses() {
return processedClasses;
}
public AnnotationIgnores getAnnotationIgnores() {
return annotationIgnores;
}
public List> getConstraintsForClass(Class beanClass) {
List> list = new ArrayList>();
if ( constraintMap.containsKey( beanClass ) ) {
for ( MetaConstraint, ? extends Annotation> metaConstraint : constraintMap.get( beanClass ) ) {
@SuppressWarnings( "unchecked") // safe cast since the list of meta constraints is always specific to the bean type
MetaConstraint boundMetaConstraint = ( MetaConstraint ) metaConstraint;
list.add( boundMetaConstraint );
}
return list;
}
else {
return Collections.emptyList();
}
}
public List getCascadedMembersForClass(Class> beanClass) {
if ( cascadedMembers.containsKey( beanClass ) ) {
return cascadedMembers.get( beanClass );
}
else {
return Collections.emptyList();
}
}
public List> getDefaultSequenceForClass(Class> beanClass) {
if ( defaultSequences.containsKey( beanClass ) ) {
return defaultSequences.get( beanClass );
}
else {
return Collections.emptyList();
}
}
@SuppressWarnings("unchecked")
private void parseConstraintDefinitions(List constraintDefinitionList) {
for ( ConstraintDefinitionType constraintDefinition : constraintDefinitionList ) {
String annotationClassName = constraintDefinition.getAnnotation();
Class extends Annotation> annotationClass;
annotationClass = ( Class extends Annotation> ) loadClass(
annotationClassName, this.getClass()
);
if ( !annotationClass.isAnnotation() ) {
throw new ValidationException( annotationClassName + " is not an annotation" );
}
ValidatedByType validatedByType = constraintDefinition.getValidatedBy();
List>> constraintValidatorClasses = new ArrayList>>();
if ( validatedByType.isIncludeExistingValidators() != null && validatedByType.isIncludeExistingValidators() ) {
constraintValidatorClasses.addAll( findConstraintValidatorClasses( annotationClass ) );
}
for ( JAXBElement validatorClassName : validatedByType.getValue() ) {
Class extends ConstraintValidator, ?>> validatorClass;
validatorClass = ( Class extends ConstraintValidator, ?>> ) loadClass(
validatorClassName.getValue(),
this.getClass()
);
if ( !ConstraintValidator.class.isAssignableFrom( validatorClass ) ) {
throw new ValidationException( validatorClass + " is not a constraint validator class" );
}
constraintValidatorClasses.add( validatorClass );
}
constraintHelper.addConstraintValidatorDefinition(
annotationClass, constraintValidatorClasses
);
}
}
private Class> loadClass(String className, Class> caller) {
LoadClass action = LoadClass.action( className, caller );
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged( action );
}
else {
return action.run();
}
}
private List>> findConstraintValidatorClasses(Class extends Annotation> annotationType) {
List>> constraintValidatorDefinitonClasses = new ArrayList>>();
if ( constraintHelper.isBuiltinConstraint( annotationType ) ) {
constraintValidatorDefinitonClasses.addAll( constraintHelper.getBuiltInConstraints( annotationType ) );
}
else {
Class extends ConstraintValidator, ?>>[] validatedBy = annotationType
.getAnnotation( Constraint.class )
.validatedBy();
for ( Class extends ConstraintValidator, ?>> validator : validatedBy ) {
//FIXME does this create a CCE at runtime?
//FIXME if yes wrap into VE, if no we need to test the type here
//Once resolved,we can @SuppressWarning("unchecked") on the cast
Class extends ConstraintValidator extends Annotation, ?>> safeValidator = validator;
constraintValidatorDefinitonClasses.add( safeValidator );
}
}
return constraintValidatorDefinitonClasses;
}
private void checkClassHasNotBeenProcessed(Set> processedClasses, Class> beanClass) {
if ( processedClasses.contains( beanClass ) ) {
throw new ValidationException( beanClass.getName() + " has already be configured in xml." );
}
}
private void parseFieldLevelOverrides(List fields, Class> beanClass, String defaultPackage) {
for ( FieldType fieldType : fields ) {
String fieldName = fieldType.getName();
final boolean containsField;
ContainsField containsAction = ContainsField.action( beanClass, fieldName );
if ( System.getSecurityManager() != null ) {
containsField = AccessController.doPrivileged( containsAction );
}
else {
containsField = containsAction.run();
}
if ( !containsField ) {
throw new ValidationException( beanClass.getName() + " does not contain the fieldType " + fieldName );
}
GetDeclaredField action = GetDeclaredField.action( beanClass, fieldName );
final Field field;
if ( System.getSecurityManager() != null ) {
field = AccessController.doPrivileged( action );
}
else {
field = action.run();
}
// ignore annotations
boolean ignoreFieldAnnotation = fieldType.isIgnoreAnnotations() == null ? false : fieldType.isIgnoreAnnotations();
if ( ignoreFieldAnnotation ) {
annotationIgnores.setIgnoreAnnotationsOnMember( field );
}
// valid
if ( fieldType.getValid() != null ) {
addCascadedMember( beanClass, field );
}
// constraints
for ( ConstraintType constraint : fieldType.getConstraint() ) {
MetaConstraint, ?> metaConstraint = createMetaConstraint(
constraint, beanClass, field, defaultPackage
);
addMetaConstraint( beanClass, metaConstraint );
}
}
}
private void parsePropertyLevelOverrides(List getters, Class> beanClass, String defaultPackage) {
for ( GetterType getterType : getters ) {
String getterName = getterType.getName();
ContainsMethod cmAction = ContainsMethod.action( beanClass, getterName );
boolean containsMethod;
if ( System.getSecurityManager() != null ) {
containsMethod = AccessController.doPrivileged( cmAction );
}
else {
containsMethod = cmAction.run();
}
if ( !containsMethod ) {
throw new ValidationException( beanClass.getName() + " does not contain the property " + getterName );
}
final Method method;
GetMethodFromPropertyName action = GetMethodFromPropertyName.action( beanClass, getterName );
if ( System.getSecurityManager() != null ) {
method = AccessController.doPrivileged( action );
}
else {
method = action.run();
}
// ignore annotations
boolean ignoreGetterAnnotation = getterType.isIgnoreAnnotations() == null ? false : getterType.isIgnoreAnnotations();
if ( ignoreGetterAnnotation ) {
annotationIgnores.setIgnoreAnnotationsOnMember( method );
}
// valid
if ( getterType.getValid() != null ) {
addCascadedMember( beanClass, method );
}
// constraints
for ( ConstraintType constraint : getterType.getConstraint() ) {
MetaConstraint, ?> metaConstraint = createMetaConstraint(
constraint, beanClass, method, defaultPackage
);
addMetaConstraint( beanClass, metaConstraint );
}
}
}
private void parseClassLevelOverrides(ClassType classType, Class> beanClass, String defaultPackage) {
if ( classType == null ) {
return;
}
// ignore annotation
boolean ignoreClassAnnotation = classType.isIgnoreAnnotations() == null ? false : classType.isIgnoreAnnotations();
if ( ignoreClassAnnotation ) {
annotationIgnores.setIgnoreAnnotationsOnClass( beanClass );
}
// group sequence
List> groupSequence = createGroupSequence( classType.getGroupSequence(), defaultPackage );
if ( !groupSequence.isEmpty() ) {
defaultSequences.put( beanClass, groupSequence );
}
// constraints
for ( ConstraintType constraint : classType.getConstraint() ) {
MetaConstraint, ?> metaConstraint = createMetaConstraint( constraint, beanClass, null, defaultPackage );
addMetaConstraint( beanClass, metaConstraint );
}
}
private void addMetaConstraint(Class> beanClass, MetaConstraint, ?> metaConstraint) {
if ( constraintMap.containsKey( beanClass ) ) {
constraintMap.get( beanClass ).add( metaConstraint );
}
else {
List> constraintList = new ArrayList>();
constraintList.add( metaConstraint );
constraintMap.put( beanClass, constraintList );
}
}
private void addCascadedMember(Class> beanClass, Member member) {
if ( cascadedMembers.containsKey( beanClass ) ) {
cascadedMembers.get( beanClass ).add( member );
}
else {
List tmpList = new ArrayList();
tmpList.add( member );
cascadedMembers.put( beanClass, tmpList );
}
}
private List> createGroupSequence(GroupSequenceType groupSequenceType, String defaultPackage) {
List> groupSequence = new ArrayList>();
for ( JAXBElement groupName : groupSequenceType.getValue() ) {
Class> group = getClass( groupName.getValue(), defaultPackage );
groupSequence.add( group );
}
return groupSequence;
}
private MetaConstraint, ?> createMetaConstraint(ConstraintType constraint, Class beanClass, Member member, String defaultPackage) {
@SuppressWarnings("unchecked")
Class annotationClass = ( Class ) getClass( constraint.getAnnotation(), defaultPackage );
AnnotationDescriptor annotationDescriptor = new AnnotationDescriptor( annotationClass );
if ( constraint.getMessage() != null ) {
annotationDescriptor.setValue( MESSAGE_PARAM, constraint.getMessage() );
}
annotationDescriptor.setValue( GROUPS_PARAM, getGroups( constraint.getGroups(), defaultPackage ) );
for ( ElementType elementType : constraint.getElement() ) {
String name = elementType.getName();
checkNameIsValid( name );
Class> returnType = getAnnotationParamterType( annotationClass, name );
Object elementValue = getElementValue( elementType, returnType );
annotationDescriptor.setValue( name, elementValue );
}
A annotation = AnnotationFactory.create( annotationDescriptor );
ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl(
annotation, constraintHelper
);
MetaConstraint metaConstraint;
if ( member == null ) {
metaConstraint = new MetaConstraint( beanClass, constraintDescriptor );
}
else {
metaConstraint = new MetaConstraint( member, beanClass, constraintDescriptor );
}
return metaConstraint;
}
private Class> getAnnotationParamterType(Class annotationClass, String name) {
Method m;
GetMethod action = GetMethod.action( annotationClass, name );
if ( System.getSecurityManager() != null ) {
m = AccessController.doPrivileged( action );
}
else {
m = action.run();
}
if ( m == null ) {
throw new ValidationException( "Annotation of type " + annotationClass.getName() + " does not contain a paramter " + name + "." );
}
return m.getReturnType();
}
private Object getElementValue(ElementType elementType, Class> returnType) {
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 );
}
else {
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy