org.hibernate.validator.internal.metadata.aggregated.MetaDataBuilder Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and/or its affiliates, 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.validator.internal.metadata.aggregated;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.core.ConstraintOrigin;
import org.hibernate.validator.internal.metadata.core.MetaConstraint;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
/**
* Builds {@link ConstraintMetaData} instances for the
* {@link ConstrainedElement} objects representing one method or property in a
* type's inheritance hierarchy.
*
* @author Gunnar Morling
*/
public abstract class MetaDataBuilder {
private static final Log log = LoggerFactory.make();
protected final ConstraintHelper constraintHelper;
private final Class beanClass;
private final Set> constraints = newHashSet();
private final Map, Class> groupConversions = newHashMap();
private boolean isCascading = false;
protected MetaDataBuilder(Class beanClass, ConstraintHelper constraintHelper) {
this.beanClass = beanClass;
this.constraintHelper = constraintHelper;
}
/**
* Whether this builder allows to add the given element or not. This is the
* case if the specified element relates to the same property or method with
* which this builder was instantiated.
*
* @param constrainedElement The element to check.
*
* @return true
if the given element can be added to this
* builder, false
otherwise.
*/
public abstract boolean accepts(ConstrainedElement constrainedElement);
/**
* Adds the given element to this builder. It must be checked with
* {@link #accepts(ConstrainedElement)} before, whether this is allowed or
* not.
*
* @param constrainedElement The element to add.
*/
public void add(ConstrainedElement constrainedElement) {
constraints.addAll( constrainedElement.getConstraints() );
isCascading = isCascading || constrainedElement.isCascading();
addGroupConversions( constrainedElement.getGroupConversions() );
}
/**
* Creates a new, read-only {@link ConstraintMetaData} object with all
* constraint information related to the method or property represented by
* this builder.
*
* @return A {@link ConstraintMetaData} object.
*/
public abstract ConstraintMetaData build();
private void addGroupConversions(Map, Class> groupConversions) {
for ( Entry, Class> oneConversion : groupConversions.entrySet() ) {
if ( this.groupConversions.containsKey( oneConversion.getKey() ) ) {
throw log.getMultipleGroupConversionsForSameSourceException(
oneConversion.getKey(),
CollectionHelper.>asSet(
groupConversions.get( oneConversion.getKey() ),
oneConversion.getValue()
)
);
}
else {
this.groupConversions.put( oneConversion.getKey(), oneConversion.getValue() );
}
}
}
protected Map, Class> getGroupConversions() {
return groupConversions;
}
protected Set> getConstraints() {
return constraints;
}
protected boolean isCascading() {
return isCascading;
}
/**
* Adapts the given constraints to the given bean type. In case a constraint
* is defined locally at the bean class the original constraint will be
* returned without any modifications. If a constraint is defined in the
* hierarchy (interface or super class) a new constraint will be returned
* with an origin of {@link org.hibernate.validator.internal.metadata.core.ConstraintOrigin#DEFINED_IN_HIERARCHY}. If a
* constraint is defined on an interface, the interface type will
* additionally be part of the constraint's groups (implicit grouping).
*
* @param constraints The constraints that shall be adapted. The constraints themselves
* will not be altered.
*
* @return A constraint adapted to the given bean type.
*/
protected Set> adaptOriginsAndImplicitGroups(Set> constraints) {
Set> adaptedConstraints = newHashSet();
for ( MetaConstraint oneConstraint : constraints ) {
adaptedConstraints.add( adaptOriginAndImplicitGroup( oneConstraint ) );
}
return adaptedConstraints;
}
private MetaConstraint adaptOriginAndImplicitGroup(MetaConstraint constraint) {
ConstraintOrigin definedIn = definedIn( beanClass, constraint.getLocation().getBeanClass() );
if ( definedIn == ConstraintOrigin.DEFINED_LOCALLY ) {
return constraint;
}
Class constraintClass = constraint.getLocation().getBeanClass();
ConstraintDescriptorImpl descriptor = new ConstraintDescriptorImpl(
constraint.getDescriptor().getAnnotation(),
constraintHelper,
constraintClass.isInterface() ? constraintClass : null,
constraint.getElementType(),
definedIn,
constraint.getLocation().getMember()
);
return new MetaConstraint(
descriptor,
constraint.getLocation()
);
}
/**
* @param rootClass The root class. That is the class for which we currently
* create a {@code BeanMetaData}
* @param hierarchyClass The class on which the current constraint is defined on
*
* @return Returns {@code ConstraintOrigin.DEFINED_LOCALLY} if the
* constraint was defined on the root bean,
* {@code ConstraintOrigin.DEFINED_IN_HIERARCHY} otherwise.
*/
private ConstraintOrigin definedIn(Class rootClass, Class hierarchyClass) {
if ( hierarchyClass.equals( rootClass ) ) {
return ConstraintOrigin.DEFINED_LOCALLY;
}
else {
return ConstraintOrigin.DEFINED_IN_HIERARCHY;
}
}
}