Package org.apache.bval.jsr303

Source Code of org.apache.bval.jsr303.ConstraintValidation

/*
* 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 org.apache.commons.lang3.ArrayUtils;

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)<br/>
* this instance is immutable!<br/>
*/
public class ConstraintValidation<T extends Annotation> implements Validation, ConstraintDescriptor<T> {
    private final ConstraintValidator<T, ?> validator;
    private T annotation; // for metadata request API
    private final AccessStrategy access;
    private final boolean reportFromComposite;
    private final Map<String, Object> attributes;

    private Set<ConstraintValidation<?>> composedConstraints;

    /**
     * the owner is the type where the validation comes from. it is used to
     * support implicit grouping.
     */
    private final Class<?> owner;
    private Set<Class<?>> groups;
    private Set<Class<? extends Payload>> payload;
    private Class<? extends ConstraintValidator<T, ?>>[] 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<? extends ConstraintValidator<T, ?>>[] validatorClasses,
        ConstraintValidator<T, ?> validator, T annotation, Class<?> owner, AccessStrategy access,
        boolean reportFromComposite) {
        this.attributes = new HashMap<String, Object>();
        this.validatorClasses = ArrayUtils.clone(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<T> asSerializableDescriptor() {
        return new ConstraintDescriptorImpl<T>(this);
    }

    /**
     * Set the applicable validation groups.
     *
     * @param groups
     */
    void setGroups(Set<Class<?>> groups) {
        this.groups = groups;
        ConstraintAnnotationAttributes.GROUPS.put(attributes, groups.toArray(new Class[groups.size()]));
    }

    /**
     * Set the payload.
     *
     * @param payload
     */
    void setPayload(Set<Class<? extends Payload>> payload) {
        this.payload = payload;
        ConstraintAnnotationAttributes.PAYLOAD.put(attributes, 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<ConstraintValidation<?>>();
        }
        composedConstraints.add(aConstraintValidation);
    }

    /**
     * {@inheritDoc}
     */
    public <L extends ValidationListener> void validate(ValidationContext<L> 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<ConstraintValidation<?>> 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<T, Object> objectValidator = (ConstraintValidator<T, Object>) validator;
            if (!objectValidator.isValid(context.getValidatedValue(), jsrContext)) {
                addErrors(context, jsrContext);
            }
        }
    }

    /**
     * Initialize the validator (if not <code>null</code>) 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 ConstraintAnnotationAttributes.MESSAGE.get(attributes);
    }

    /**
     * Get the {@link ConstraintValidator} invoked by this
     * {@link ConstraintValidation}.
     *
     * @return
     */
    public ConstraintValidator<T, ?> 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<String, Object> getAttributes() {
        return attributes;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public Set<ConstraintDescriptor<?>> 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<ConstraintValidation<?>> getComposingValidations() {
        return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
    }

    /**
     * {@inheritDoc}
     */
    public Set<Class<?>> getGroups() {
        return groups;
    }

    /**
     * {@inheritDoc}
     */
    public Set<Class<? extends Payload>> getPayload() {
        return payload;
    }

    /**
     * {@inheritDoc}
     */
    public List<Class<? extends ConstraintValidator<T, ?>>> getConstraintValidatorClasses() {
        if (validatorClasses == null) {
            return Collections.emptyList();
        }
        return Arrays.asList(validatorClasses);
    }

}
TOP

Related Classes of org.apache.bval.jsr303.ConstraintValidation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.