org.hibernate.validator.internal.xml.mapping.ConstraintDefinitionStaxBuilder Maven / Gradle / Ivy
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or .
*/
package org.hibernate.validator.internal.xml.mapping;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.validation.ConstraintValidator;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.internal.xml.AbstractStaxBuilder;
/**
* Builder for a constraint definition.
*
* @author Marko Bekhta
*/
class ConstraintDefinitionStaxBuilder extends AbstractStaxBuilder {
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
private static final String CONSTRAINT_DEFINITION_QNAME_LOCAL_PART = "constraint-definition";
private static final QName ANNOTATION_QNAME = new QName( "annotation" );
private final ClassLoadingHelper classLoadingHelper;
private final ConstraintHelper constraintHelper;
private final DefaultPackageStaxBuilder defaultPackageStaxBuilder;
private String annotation;
private ValidatedByStaxBuilder validatedByStaxBuilder;
ConstraintDefinitionStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) {
this.classLoadingHelper = classLoadingHelper;
this.constraintHelper = constraintHelper;
this.defaultPackageStaxBuilder = defaultPackageStaxBuilder;
this.validatedByStaxBuilder = new ValidatedByStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder );
}
@Override
protected String getAcceptableQName() {
return CONSTRAINT_DEFINITION_QNAME_LOCAL_PART;
}
@Override
protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException {
annotation = readAttribute( xmlEvent.asStartElement(), ANNOTATION_QNAME ).get();
while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) {
validatedByStaxBuilder.process( xmlEventReader, xmlEvent );
xmlEvent = xmlEventReader.nextEvent();
}
}
@SuppressWarnings("unchecked")
void build(Set alreadyProcessedConstraintDefinitions) {
checkProcessedAnnotations( alreadyProcessedConstraintDefinitions );
String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" );
Class clazz = classLoadingHelper.loadClass( annotation, defaultPackage );
if ( !clazz.isAnnotation() ) {
throw LOG.getIsNotAnAnnotationException( clazz );
}
Class annotationClass = (Class) clazz;
addValidatorDefinitions( annotationClass );
}
private void checkProcessedAnnotations(Set alreadyProcessedConstraintDefinitions) {
if ( alreadyProcessedConstraintDefinitions.contains( annotation ) ) {
throw LOG.getOverridingConstraintDefinitionsInMultipleMappingFilesException( annotation );
}
else {
alreadyProcessedConstraintDefinitions.add( annotation );
}
}
private void addValidatorDefinitions(Class annotationClass) {
constraintHelper.putValidatorDescriptors(
annotationClass,
validatedByStaxBuilder.build( annotationClass ),
validatedByStaxBuilder.isIncludeExistingValidators()
);
}
private static class ValidatedByStaxBuilder extends AbstractStaxBuilder {
private static final String VALIDATED_BY_QNAME_LOCAL_PART = "validated-by";
private static final String VALUE_QNAME_LOCAL_PART = "value";
private static final QName INCLUDE_EXISTING_VALIDATORS_QNAME = new QName( "include-existing-validators" );
private final ClassLoadingHelper classLoadingHelper;
private final DefaultPackageStaxBuilder defaultPackageStaxBuilder;
private boolean includeExistingValidators;
private final List values;
protected ValidatedByStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) {
this.classLoadingHelper = classLoadingHelper;
this.defaultPackageStaxBuilder = defaultPackageStaxBuilder;
this.values = new ArrayList<>();
}
@Override
protected String getAcceptableQName() {
return VALIDATED_BY_QNAME_LOCAL_PART;
}
@Override
protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException {
includeExistingValidators = readAttribute( xmlEvent.asStartElement(), INCLUDE_EXISTING_VALIDATORS_QNAME )
.map( Boolean::parseBoolean ).orElse( true );
while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) {
if ( xmlEvent.isStartElement() && xmlEvent.asStartElement().getName().getLocalPart().equals( VALUE_QNAME_LOCAL_PART ) ) {
values.add( readSingleElement( xmlEventReader ) );
}
xmlEvent = xmlEventReader.nextEvent();
}
}
@SuppressWarnings("unchecked")
List> build(Class annotation) {
String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" );
return values.stream()
.map( value -> classLoadingHelper.loadClass( value, defaultPackage ) )
.peek( this::checkValidatorAssignability )
.map( clazz -> (Class>) clazz )
.map( validatorClass -> ConstraintValidatorDescriptor.forClass( validatorClass, annotation ) )
.collect( Collectors.toList() );
}
public boolean isIncludeExistingValidators() {
return includeExistingValidators;
}
private void checkValidatorAssignability(Class validatorClass) {
if ( !ConstraintValidator.class.isAssignableFrom( validatorClass ) ) {
throw LOG.getIsNotAConstraintValidatorClassException( validatorClass );
}
}
}
}