/* * Copyright (C) 2013-2017 NTT DATA Corporation * * 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.terasoluna.gfw.common.validator.constraintvalidators; import static org.terasoluna.gfw.common.validator.constraintvalidators.ConstraintValidatorsUtils.getPropertyValue; import static org.terasoluna.gfw.common.validator.constraintvalidators.ConstraintValidatorsUtils.reportUnexpectedType; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import org.terasoluna.gfw.common.validator.constraints.Compare; import org.terasoluna.gfw.common.validator.constraints.Compare.Node; import org.terasoluna.gfw.common.validator.constraints.Compare.Operator; /** * Constraint validator class of {@link Compare} annotation. * <p> * Validate the result of comparing the two properties specified, must match the specified operator. * </p> * @since 5.1.0 * @see ConstraintValidator * @see Compare */ public class CompareValidator implements ConstraintValidator<Compare, Object> { /** * Name of property to become left side of comparison. */ private String left; /** * Name of property to become right side of comparison. */ private String right; /** * Operator used in the comparison. */ private Operator operator; /** * Configure how to treat {@code null} value in given parameters. If true, it is regarded as valid only when both left and * right are {@code null}. It is regarded as invalid when either is {@code null}. If false, both cases are regarded as * valid. */ private boolean requireBoth; /** * Node of bind validation message */ private Node node; /** * Validation message. */ private String message; /** * Initialize validator. * @param constraintAnnotation annotation instance for a given constraint declaration * @see javax.validation.ConstraintValidator#initialize(java.lang.annotation.Annotation) */ @Override public void initialize(Compare constraintAnnotation) { left = constraintAnnotation.left(); right = constraintAnnotation.right(); operator = constraintAnnotation.operator(); requireBoth = constraintAnnotation.requireBoth(); node = constraintAnnotation.node(); message = constraintAnnotation.message(); } /** * Validate execute. * @param bean bean to validate * @param context context in which the constraint is evaluated * @return {@code true} if result to comparing {@code left} and {@code right} is expected {@code operator}, or is match * conditions that are described in the {@link CompareValidator#requireBoth} . otherwise {@code false}. * @see javax.validation.ConstraintValidator#isValid(java.lang.Object, javax.validation.ConstraintValidatorContext) */ @Override public boolean isValid(Object bean, ConstraintValidatorContext context) { Object leftValue = getPropertyValue(bean, left); Object rightValue = getPropertyValue(bean, right); if (leftValue == null || rightValue == null) { if (requireBoth && !(leftValue == null && rightValue == null)) { constructValidationMessage(context); return false; } return true; } if (!assertComparable(leftValue, rightValue)) { constructValidationMessage(context); return false; } if (!isCompareValid(leftValue, rightValue)) { constructValidationMessage(context); return false; } return true; } /** * Assert left value and right value are able to {@code Comparable#compareTo()}. * @param leftValue value to become left side of comparison * @param rightValue value to become right side of comparison * @return {@code true} if left value is {@code Comparable}, and right value is able to cast to left value. otherwise * {@code false}. * @throws IllegalArgumentException type of {@code leftValue} is not {@code Comparable}. */ private boolean assertComparable(Object leftValue, Object rightValue) { if (!(leftValue instanceof Comparable)) { throw reportUnexpectedType(leftValue); } return leftValue.getClass().isAssignableFrom(rightValue.getClass()); } /** * Compare objects by {@code Comparable#compareTo()}. * @param leftValue value to become left side of comparison * @param rightValue value to become right side of comparison * @return {@code true} if comparison result as the expected to specified {@code operator}, otherwise {@code false}. */ private boolean isCompareValid(Object leftValue, Object rightValue) { @SuppressWarnings({ "rawtypes", "unchecked" }) int result = ((Comparable) leftValue).compareTo(rightValue); return operator.isExpected(result); } /** * Construct validation message when selected {@link Node#PROPERTY}. * @param context constraint validation context */ private void constructValidationMessage(ConstraintValidatorContext context) { if (node == Node.ROOT_BEAN) { return; } context.buildConstraintViolationWithTemplate(message).addPropertyNode( left).addConstraintViolation() .disableDefaultConstraintViolation(); } }