All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.bval.jsr303.ConstraintValidation 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.jsr303;

import org.apache.bval.jsr303.util.NodeImpl;
import org.apache.bval.jsr303.util.PathImpl;
import org.apache.bval.model.Validation;
import org.apache.bval.model.ValidationContext;
import org.apache.bval.model.ValidationListener;
import org.apache.bval.util.AccessStrategy;

import javax.validation.ConstraintDefinitionException;
import javax.validation.ConstraintValidator;
import javax.validation.Payload;
import javax.validation.ValidationException;
import javax.validation.metadata.ConstraintDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.*;

/**
 * Description: Adapter between Constraint (JSR303) and Validation (Core)
* this instance is immutable!
*/ public class ConstraintValidation implements Validation, ConstraintDescriptor { private final ConstraintValidator validator; private T annotation; // for metadata request API private final AccessStrategy access; private final boolean reportFromComposite; private final Map attributes; private Set> composedConstraints; /** * the owner is the type where the validation comes from. it is used to * support implicit grouping. */ private final Class owner; private Set> groups; private Set> payload; private Class>[] validatorClasses; /** * Create a new ConstraintValidation instance. * * @param validatorClasses * @param validator * - the constraint validator * @param annotation * - the annotation of the constraint * @param owner * - the type where the annotated element is placed (class, * interface, annotation type) * @param access * - how to access the value * @param reportFromComposite */ public ConstraintValidation(Class>[] validatorClasses, ConstraintValidator validator, T annotation, Class owner, AccessStrategy access, boolean reportFromComposite) { this.attributes = new HashMap(); this.validatorClasses = validatorClasses; this.validator = validator; this.annotation = annotation; this.owner = owner; this.access = access; this.reportFromComposite = reportFromComposite; } /** * Return a {@link Serializable} {@link ConstraintDescriptor} capturing a * snapshot of current state. * * @return {@link ConstraintDescriptor} */ public ConstraintDescriptor asSerializableDescriptor() { return new ConstraintDescriptorImpl(this); } /** * Set the applicable validation groups. * * @param groups */ void setGroups(Set> groups) { this.groups = groups; this.attributes.put(Jsr303MetaBeanFactory.ANNOTATION_GROUPS, groups.toArray(new Class[groups.size()])); } /** * Set the payload. * * @param payload */ void setPayload(Set> payload) { this.payload = payload; this.attributes.put(Jsr303MetaBeanFactory.ANNOTATION_PAYLOAD, payload.toArray(new Class[payload.size()])); } /** * {@inheritDoc} */ public boolean isReportAsSingleViolation() { return reportFromComposite; } /** * Add a composing constraint. * * @param aConstraintValidation * to add */ public void addComposed(ConstraintValidation aConstraintValidation) { if (composedConstraints == null) { composedConstraints = new HashSet>(); } composedConstraints.add(aConstraintValidation); } /** * {@inheritDoc} */ public void validate(ValidationContext context) { validate((GroupValidationContext) context); } /** * Validate a {@link GroupValidationContext}. * * @param context * root */ public void validate(GroupValidationContext context) { context.setConstraintValidation(this); /** * execute unless the given validation constraint has already been * processed during this validation routine (as part of a previous group * match) */ if (!isMemberOf(context.getCurrentGroup().getGroup())) { return; // do not validate in the current group } if (context.getCurrentOwner() != null && this.owner != context.getCurrentOwner()) { return; } if (validator != null && !context.collectValidated(validator)) return; // already done if (context.getMetaProperty() != null && !isReachable(context)) { return; } // process composed constraints if (isReportAsSingleViolation()) { ConstraintValidationListener listener = context.getListener(); listener.beginReportAsSingle(); boolean failed = listener.hasViolations(); try { // stop validating when already failed and // ReportAsSingleInvalidConstraint = true ? for (Iterator> composed = getComposingValidations().iterator(); !failed && composed.hasNext();) { composed.next().validate(context); failed = listener.hasViolations(); } } finally { listener.endReportAsSingle(); // Restore current constraint validation context.setConstraintValidation(this); } if (failed) { // TODO RSt - how should the composed constraint error report // look like? ConstraintValidatorContextImpl jsrContext = new ConstraintValidatorContextImpl(context, this); addErrors(context, jsrContext); // add defaultErrorMessage only return; } } else { for (ConstraintValidation composed : getComposingValidations()) { composed.validate(context); } // Restore current constraint validation context.setConstraintValidation(this); } if (validator != null) { ConstraintValidatorContextImpl jsrContext = new ConstraintValidatorContextImpl(context, this); @SuppressWarnings("unchecked") final ConstraintValidator objectValidator = (ConstraintValidator) validator; if (!objectValidator.isValid(context.getValidatedValue(), jsrContext)) { addErrors(context, jsrContext); } } } /** * Initialize the validator (if not null) with the stored * annotation. */ public void initialize() { if (null != validator) { try { validator.initialize(annotation); } catch (RuntimeException e) { // Either a "legit" problem initializing the validator or a // ClassCastException if the validator associated annotation is // not a supertype of the validated annotation. throw new ConstraintDefinitionException("Incorrect validator [" + validator.getClass().getCanonicalName() + "] for annotation " + annotation.annotationType().getCanonicalName(), e); } } } private boolean isReachable(GroupValidationContext context) { PathImpl path = context.getPropertyPath(); NodeImpl node = path.getLeafNode(); PathImpl beanPath = path.getPathWithoutLeafNode(); if (beanPath == null) { beanPath = PathImpl.create(null); } try { if (!context.getTraversableResolver().isReachable(context.getBean(), node, context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType())) return false; } catch (RuntimeException e) { throw new ValidationException("Error in TraversableResolver.isReachable() for " + context.getBean(), e); } return true; } private void addErrors(GroupValidationContext context, ConstraintValidatorContextImpl jsrContext) { for (ValidationListener.Error each : jsrContext.getErrorMessages()) { context.getListener().addError(each, context); } } /** * {@inheritDoc} */ public String toString() { return "ConstraintValidation{" + validator + '}'; } /** * Get the message template used by this constraint. * * @return String */ public String getMessageTemplate() { return (String) attributes.get(Jsr303MetaBeanFactory.ANNOTATION_MESSAGE); } /** * Get the {@link ConstraintValidator} invoked by this * {@link ConstraintValidation}. * * @return */ public ConstraintValidator getValidator() { return validator; } /** * Learn whether this {@link ConstraintValidation} belongs to the specified * group. * * @param reqGroup * @return boolean */ protected boolean isMemberOf(Class reqGroup) { return groups.contains(reqGroup); } /** * Get the owning class of this {@link ConstraintValidation}. * * @return Class */ public Class getOwner() { return owner; } /** * {@inheritDoc} */ public T getAnnotation() { return annotation; } /** * Get the {@link AccessStrategy} used by this {@link ConstraintValidation}. * * @return {@link AccessStrategy} */ public AccessStrategy getAccess() { return access; } /** * Override the Annotation set at construction. * * @param annotation */ public void setAnnotation(T annotation) { this.annotation = annotation; } // ///////////////////////// ConstraintDescriptor implementation /** * {@inheritDoc} */ public Map getAttributes() { return attributes; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public Set> getComposingConstraints() { return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints; } /** * Get the composing {@link ConstraintValidation} objects. This is * effectively an implementation-specific analogue to * {@link #getComposingConstraints()}. * * @return {@link Set} of {@link ConstraintValidation} */ @SuppressWarnings("unchecked") Set> getComposingValidations() { return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints; } /** * {@inheritDoc} */ public Set> getGroups() { return groups; } /** * {@inheritDoc} */ public Set> getPayload() { return payload; } /** * {@inheritDoc} */ public List>> getConstraintValidatorClasses() { if (validatorClasses == null) { return Collections.emptyList(); } return Arrays.asList(validatorClasses); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy