/* * 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.constraints; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; import javax.validation.ValidationException; import org.terasoluna.gfw.common.validator.constraintvalidators.CompareValidator; /** * The annotated element, the result of comparing the two properties specified, must match the specified operator. Operator can * be specified to {@link Operator} * <p> * Supported specified property types are: * </p> * <ul> * <li>{@code Comparable}</li> * </ul> * <p> * {@code null} elements are considered valid. If any of the specified two properties is {@code null}, are considered valid. If * specify two properties of different types, are considered invalid. If specify a property not {@link Comparable}, it is thrown * {@link IllegalArgumentException}(wrapped in {@link ValidationException}). * </p> * @since 5.1.0 * @see CompareValidator * @see Operator */ @Documented @Constraint(validatedBy = { CompareValidator.class }) @Target({ TYPE, ANNOTATION_TYPE }) @Retention(RUNTIME) public @interface Compare { /** * Error message or message key * @return error message or message key */ String message() default "{org.terasoluna.gfw.common.validator.constraints.Compare.message}"; /** * Constraint groups * @return constraint groups */ Class<?>[] groups() default {}; /** * Payload * @return payload */ Class<? extends Payload>[] payload() default {}; /** * @return name of property to become left side of comparison */ String left(); /** * @return name of property to become right side of comparison */ String right(); /** * @return operator used in the comparison */ Operator operator(); /** * @return 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. */ boolean requireBoth() default false; /** * @return node of bind validation message */ Node node() default Node.PROPERTY; /** * Defines several {@link Compare} annotations on the same element. * @see Compare * @since 5.1.0 */ @Documented @Target({ METHOD, FIELD, TYPE, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) @interface List { /** * <code>@Compare</code> annotations * @return annotations */ Compare[] value(); } /** * The operator used in the {@link Compare} comparison. * @since 5.1.0 */ enum Operator { /** * Left side object must be less than or equal Right side object. */ LESS_THAN_OR_EQUAL(CompareStrategy.EQ, CompareStrategy.LT), /** * Left side object must be less than Right side object. */ LESS_THAN(CompareStrategy.LT), /** * Left side object must be equal Right side object. */ EQUAL(CompareStrategy.EQ), /** * Left side object must be greater than Right side object. */ GREATER_THAN(CompareStrategy.GT), /** * Left side object must be greater than or equal Right side object. */ GREATER_THAN_OR_EQUAL(CompareStrategy.EQ, CompareStrategy.GT); /** * strategies to assert result of {@code Comparable#compareTo(Object)} as the expected. */ private final CompareStrategy[] strategies; /** * Constructor. * @param strategies to assert result of {@code Comparable#compareTo(Object)} as the expected */ Operator(CompareStrategy... strategies) { this.strategies = strategies; } /** * Assert result of {@code Comparable#compareTo(Object)} as the expected. * @param comparisonResult result of {@code Comparable#compareTo(Object)} * @return {@code true} if result of {@code Comparable#compareTo(Object)} as the expected in any of the * {@code CompareStrategy#isExpected(int)}, otherwise {@code false}. */ public boolean isExpected(int comparisonResult) { for (CompareStrategy strategy : strategies) { if (strategy.isExpected(comparisonResult)) { return true; } } return false; } /** * Strategy to assert result of {@code Comparable#compareTo(Object)} as the expected. * @since 5.1.0 */ private enum CompareStrategy { /** * Expected equals ZERO. */ EQ { /** * {@inheritDoc} */ @Override protected boolean isExpected(int comparisonResult) { return comparisonResult == 0; } }, /** * Expected greater than ZERO. */ GT { /** * {@inheritDoc} */ @Override protected boolean isExpected(int comparisonResult) { return comparisonResult > 0; } }, /** * Expected less than ZERO. */ LT { /** * {@inheritDoc} */ @Override protected boolean isExpected(int comparisonResult) { return comparisonResult < 0; } }; /** * Assert result of {@code Comparable#compareTo(Object)} as the expected. * @param comparisonResult result of {@code Comparable#compareTo(Object)} * @return {@code true} if result of {@code Comparable#compareTo(Object)} as the expected, otherwise {@code false}. */ protected abstract boolean isExpected(int comparisonResult); } } /** * The node of bind validation message. * @since 5.1.0 */ enum Node { /** * Bind validation message to property specified {@code Compare#left()}. */ PROPERTY, /** * Bind validation message to root bean {@code Compare} annotated. */ ROOT_BEAN } }