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

com.blackbuild.groovy.configdsl.transform.Validate Maven / Gradle / Ivy

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015-2024 Stephan Pauxberger
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.blackbuild.groovy.configdsl.transform;

import com.blackbuild.klum.cast.KlumCastValidated;
import com.blackbuild.klum.cast.KlumCastValidator;
import com.blackbuild.klum.cast.checks.NotOn;
import com.blackbuild.klum.cast.checks.NumberOfParameters;
import groovy.lang.Closure;

import java.lang.annotation.*;

/**
 * 

Activates validation for the given field or marks the annotated method as validation method.

* *

On a class

*

If set on a class, the class behaves as if the annotation was set on all fields of the class not already annotated. * In this usage, fields can be exempted by annotating them with Validate(Ignore).

* *

On a field

* *

If this annotation is set on a field, this field is validated as part of the object validation, During the * validation phase. The actual validation can be one of the following:

* * * * * * * *
Valid options to use Validate annotation on a field
emptyValidates the content of the field according to groovy truth
empty (for Boolean fields)Validates that the content of the field is not null
{@link Validate.Ignore}Don't validate this field. This only makes sense if the class itself is * annotated with Validate to exclude the annotated field from validation.
a closureThe given closure is evaluated called with the field value as parameter. If the result * of the call satisfies Groovy Truth, the field is assumed valid.
* *

It is illegal to place the annotation on a primitive boolean field.

* *

 * {@literal @DSL}
 * class Foo {
 *   {@literal @Validate}
 *   String notEmpty
 *   {@literal @Validate}({ {@literal it.length > 3} })
 *   String minLength
 * }
 * 
* *

{@link #message()} can be used to provide a custom message for failed validations.

* *

On a method

* *

On a method, this annotation is used to designate a validation method which is called as part of the validation process. * If the method returns without throwing an exception / an {@link AssertionError}, the method is considered to be passed.

* *

Validation methods are commonly used for interdependent fields (field a must have a value matching field b) or * to perform a validation that would be to long to comfortably include in a closure

* *

 * given:
 * {@literal @DSL}
 * class Foo {
 *   String value1
 *   String value2
 *
 *   {@literal @Validate}
 *   private def stringLength() {
 *     {@literal assert value1.length() < value2.length()}
 *   }
 * }
 *
 * when:
 * clazz.Create.With {
 *   value1 "abc"
 *   value2 "bl"
 * }
 *
 * then:
 * thrown(IllegalStateException)
 * 
*

Validation methods should not change the state of an object, use {@link PostApply} or {@link PostCreate} for that.

*

When using validation on a method or a class, neither a {@link #message()} nor a {@link #value()} must be given.

* *

Order of validation

* When validating an object, the following order is executed. *
    *
  • Validation of the superclass, if the superclass is also a model class
  • *
  • validation of all fields
  • *
  • custom validation methods
  • *
* *

if the validation fails for any validation field or method, an {@link IllegalStateException} is thrown.

*/ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @KlumCastValidated @NumberOfParameters(0) @KlumCastValidator("com.blackbuild.klum.ast.validation.CheckForPrimitiveBoolean") @Documented public @interface Validate { /** * A closure to be executed to validate the annotated field. If empty, Groovy Truth is used to validate the field. * Illegal when annotating a method. */ @NotOn({ElementType.METHOD, ElementType.TYPE}) Class value() default GroovyTruth.class; /** * A message to be returned when validation fails. * Illegal when annotating a method. */ @NotOn({ElementType.METHOD, ElementType.TYPE}) String message() default ""; /** * Default value for {@link Validate#value()}. Designates the field to be validated against Groovy Truth. */ class GroovyTruth extends NamedAnnotationMemberClosure { public GroovyTruth(Object owner, Object thisObject) { super(owner, thisObject); } } /** * If used as value for {@link Validate#value()}, configures validation to ignore this field. Makes only sense * in combination with {@link Validation.Option#VALIDATE_UNMARKED}. */ class Ignore extends NamedAnnotationMemberClosure { public Ignore(Object owner, Object thisObject) { super(owner, thisObject); } } }