diff --git a/pom.xml b/pom.xml index 36603f2..8311ee6 100644 --- a/pom.xml +++ b/pom.xml @@ -246,46 +246,10 @@ true - - - org.codehaus.mojo - aspectj-maven-plugin - ${aspectj-maven-plugin.version} - - ignore - ${java.version} - false - false - ${java.version} - ${java.version} - - - - org.aspectj - aspectjtools - ${aspectj.version} - - - - - process-sources - - compile - test-compile - - - - - - org.aspectj - aspectjrt - ${aspectj.version} - - org.junit.jupiter @@ -373,8 +337,6 @@ 8 - 1.9.7 - 1.14.0 @@ -385,8 +347,6 @@ 11 - 1.9.7 - 1.14.0 @@ -397,8 +357,6 @@ 17 - 1.9.25.1 - 1.16.0 @@ -409,8 +367,6 @@ 21 - 1.9.25.1 - 1.16.0 diff --git a/src/main/java/br/com/fluentvalidator/AbstractValidator.java b/src/main/java/br/com/fluentvalidator/AbstractValidator.java index 897bca7..697db58 100644 --- a/src/main/java/br/com/fluentvalidator/AbstractValidator.java +++ b/src/main/java/br/com/fluentvalidator/AbstractValidator.java @@ -36,31 +36,97 @@ import br.com.fluentvalidator.rule.RuleProcessorStrategy; import br.com.fluentvalidator.transform.ValidationResultTransform; +/** + * Abstract base class for implementing fluent validators. + *

+ * This class provides the core functionality for building and executing validation rules + * in a fluent, chainable manner. It supports both single object and collection validation, + * with configurable processing strategies including fail-fast behavior. + *

+ *

+ * Subclasses should implement the {@link Validator#rules()} method to define + * validation rules using the fluent API provided by {@link #ruleFor(Function)} and + * {@link #ruleForEach(Function)} methods. + *

+ *

+ * Thread Safety: This class is thread-safe for validation operations. Rule initialization + * is performed using double-checked locking with atomic references to prevent race conditions. + *

+ * + * @param the type of object being validated + */ public abstract class AbstractValidator implements Validator { + /** + * List of validation rules to be applied to instances of type T. + * Rules are executed in the order they were added. + */ private final List> rules = new LinkedList<>(); + /** + * Initializer responsible for thread-safe rule initialization. + */ private final Initializer initialize; + /** + * Property name to be set in the validation context during validation. + */ private String property; + /** + * Strategy for processing validation rules. Defaults to standard processing, + * but can be changed to fail-fast mode. + */ private RuleProcessorStrategy ruleProcessor = RuleProcessorStrategy.getDefault(); + /** + * Thread-safe initializer for validation rules. + *

+ * This inner class ensures that validation rules are initialized exactly once + * per validator instance, even in multi-threaded environments. It uses + * Compare-And-Swap (CAS) operations with double-checked locking to prevent + * race conditions during initialization. + *

+ * + * @param the type of object being validated + */ private static class Initializer { - + /** + * Atomic reference to track initialization state. + * FALSE indicates not initialized, TRUE indicates initialized. + */ private final AtomicReference atomicReference = new AtomicReference<>(Boolean.FALSE); + /** + * Reference to the validator instance being initialized. + */ private final Validator validator; + /** + * Constructs a new initializer for the given validator. + * + * @param validator the validator instance to initialize + */ Initializer(final Validator validator) { this.validator = validator; } /** - * This method cause Race Condition. We are using Compare And Swap (CAS) + * Initializes the validator rules in a thread-safe manner. + *

+ * This method uses double-checked locking with atomic operations to ensure + * that the validator's rules are initialized exactly once, even when called + * concurrently from multiple threads. This prevents race conditions that + * could occur during rule initialization. + *

*

- * {@link https://en.wikipedia.org/wiki/Race_condition} - * {@link https://en.wikipedia.org/wiki/Compare-and-swap} + * The implementation follows the Compare-And-Swap (CAS) pattern for + * lock-free programming where possible, falling back to synchronized + * blocks only when necessary. + *

+ * + * @see Race Condition + * @see Compare-and-swap */ public void init() { if (isNotInitialized()) { @@ -75,18 +141,37 @@ public void init() { } } + /** + * Checks if the validator has not been initialized yet. + * + * @return true if the validator is not initialized, false otherwise + */ private boolean isNotInitialized() { return Boolean.FALSE.equals(atomicReference.get()); } } + /** + * Constructs a new AbstractValidator instance. + *

+ * Initializes the validator with default settings and creates + * the thread-safe initializer for rule setup. + *

+ */ protected AbstractValidator() { this.initialize = new Initializer<>(this); } /** - * {@link #failFastRule() AbstractValidator} + * Configures the validator to use fail-fast rule processing. + *

+ * When fail-fast mode is enabled, validation will stop at the first + * rule failure instead of continuing to evaluate all rules. This can + * improve performance when early validation failure is acceptable. + *

+ * + * @see RuleProcessorStrategy#getFailFast() */ @Override public void failFastRule() { @@ -94,7 +179,14 @@ public void failFastRule() { } /** - * {@link #getCounter() AbstractValidator} + * Gets the current validation counter from the processor context. + *

+ * The counter tracks the number of validation operations or rules + * that have been processed. This can be useful for debugging, + * monitoring, or performance analysis. + *

+ * + * @return the current counter value, or null if no counter is available */ @Override public Integer getCounter() { @@ -102,7 +194,14 @@ public Integer getCounter() { } /** - * {@link #setPropertyOnContext(String) AbstractValidator } + * Sets a property name to be used in the validation context. + *

+ * This property name will be associated with the validated object + * in the validation context, allowing rules to access contextual + * information during validation. + *

+ * + * @param property the property name to set in the validation context */ @Override public void setPropertyOnContext(final String property) { @@ -110,7 +209,17 @@ public void setPropertyOnContext(final String property) { } /** - * {@link #getPropertyOnContext(String, Class) AbstractValidator } + * Retrieves a property value from the validation context. + *

+ * This method allows access to contextual information that was + * previously stored in the validation context, enabling rules + * to make decisions based on broader validation state. + *

+ * + * @param

the type of the property value + * @param property the name of the property to retrieve + * @param clazz the class type of the property value + * @return the property value cast to the specified type, or null if not found */ @Override public

P getPropertyOnContext(final String property, final Class

clazz) { @@ -118,7 +227,16 @@ public

P getPropertyOnContext(final String property, final Class

clazz) { } /** - * {@link #validate(Object) AbstractValidator } + * Validates a single instance and returns the validation result. + *

+ * This method processes the given instance through all configured + * validation rules and returns a comprehensive result containing + * any validation errors or success indicators. + *

+ * + * @param instance the object instance to validate + * @return a ValidationResult containing the outcome of validation + * @throws IllegalArgumentException if the instance is null and null values are not supported */ @Override public ValidationResult validate(final T instance) { @@ -129,7 +247,18 @@ public ValidationResult validate(final T instance) { } /** - * {@link #validate(Object, ValidationResultTransform) AbstractValidator} + * Validates a single instance and transforms the result using the provided transformer. + *

+ * This method combines validation with result transformation in a single operation, + * allowing for custom result formats or processing without intermediate objects. + *

+ * + * @param the type of the transformed result + * @param instance the object instance to validate + * @param resultTransform the transformer to apply to the validation result + * @return the transformed validation result + * @throws IllegalArgumentException if the instance is null and null values are not supported + * @throws NullPointerException if resultTransform is null */ @Override public E validate(final T instance, final ValidationResultTransform resultTransform) { @@ -137,7 +266,16 @@ public E validate(final T instance, final ValidationResultTransform resul } /** - * {@link #validate(Collection) AbstractValidator} + * Validates a collection of instances and returns a list of validation results. + *

+ * Each instance in the collection is validated independently, and the results + * are collected into an unmodifiable list. The order of results corresponds + * to the order of instances in the input collection. + *

+ * + * @param instances the collection of instances to validate + * @return an unmodifiable list of ValidationResult objects, one for each input instance + * @throws NullPointerException if the instances collection is null */ @Override public List validate(final Collection instances) { @@ -145,7 +283,17 @@ public List validate(final Collection instances) { } /** - * {@link #validate(Collection, ValidationResultTransform) AbstractValidator} + * Validates a collection of instances and transforms each result using the provided transformer. + *

+ * This method combines collection validation with result transformation, + * applying the transformer to each individual validation result. + *

+ * + * @param the type of the transformed results + * @param instances the collection of instances to validate + * @param resultTransform the transformer to apply to each validation result + * @return an unmodifiable list of transformed validation results + * @throws NullPointerException if instances or resultTransform is null */ @Override public List validate(final Collection instances, final ValidationResultTransform resultTransform) { @@ -153,7 +301,15 @@ public List validate(final Collection instances, final ValidationResul } /** - * {@link #apply(Object) AbstractValidator} + * Applies validation rules to an instance and returns a boolean result. + *

+ * This method is typically called internally during the validation process. + * It ensures rules are initialized, sets up the validation context, and + * processes all rules against the given instance. + *

+ * + * @param instance the object instance to validate + * @return true if all validation rules pass, false if any rule fails */ @Override public boolean apply(final T instance) { @@ -163,7 +319,17 @@ public boolean apply(final T instance) { } /** - * {@link #ruleFor(Function) AbstractValidator} + * Creates a validation rule for a specific property of the validated object. + *

+ * This method starts a fluent chain for defining validation rules that apply + * to a property extracted from the validated object using the provided function. + * The property name will be automatically derived from the function if possible. + *

+ * + * @param

the type of the property being validated + * @param function a function that extracts the property value from the validated object + * @return a RuleBuilderProperty for chaining additional validation constraints + * @throws NullPointerException if function is null */ @Override public

RuleBuilderProperty ruleFor(final Function function) { @@ -173,7 +339,19 @@ public

RuleBuilderProperty ruleFor(final Function function) { } /** - * {@link #ruleFor(String, Function) AbstractValidator} + * Creates a validation rule for a named property of the validated object. + *

+ * This method starts a fluent chain for defining validation rules that apply + * to a property extracted from the validated object. The field name is explicitly + * provided and will be used in error messages and validation context. + *

+ * + * @param

the type of the property being validated + * @param fieldName the name of the field being validated (used in error messages) + * @param function a function that extracts the property value from the validated object + * @return a RuleBuilderProperty for chaining additional validation constraints + * @throws NullPointerException if fieldName or function is null + * @throws IllegalArgumentException if fieldName is empty */ @Override public

RuleBuilderProperty ruleFor(final String fieldName, final Function function) { @@ -183,7 +361,19 @@ public

RuleBuilderProperty ruleFor(final String fieldName, final Funct } /** - * {@link #ruleForEach(String, Function) AbstractValidator} + * Creates validation rules for each element in a collection property. + *

+ * This method starts a fluent chain for defining validation rules that apply + * to each element of a collection extracted from the validated object. + * The field name is explicitly provided for error reporting. + *

+ * + * @param

the type of elements in the collection being validated + * @param fieldName the name of the collection field being validated + * @param function a function that extracts the collection from the validated object + * @return a RuleBuilderCollection for chaining additional validation constraints + * @throws NullPointerException if fieldName or function is null + * @throws IllegalArgumentException if fieldName is empty */ @Override public

RuleBuilderCollection ruleForEach(final String fieldName, final Function> function) { @@ -193,7 +383,17 @@ public

RuleBuilderCollection ruleForEach(final String fieldName, final } /** - * {@link #ruleForEach(Function) AbstractValidator} + * Creates validation rules for each element in a collection property. + *

+ * This method starts a fluent chain for defining validation rules that apply + * to each element of a collection extracted from the validated object. + * The field name will be automatically derived from the function if possible. + *

+ * + * @param

the type of elements in the collection being validated + * @param function a function that extracts the collection from the validated object + * @return a RuleBuilderCollection for chaining additional validation constraints + * @throws NullPointerException if function is null */ @Override public

RuleBuilderCollection ruleForEach(final Function> function) { diff --git a/src/main/java/br/com/fluentvalidator/annotation/CleanValidationContextException.java b/src/main/java/br/com/fluentvalidator/annotation/CleanValidationContextException.java deleted file mode 100644 index a620a8d..0000000 --- a/src/main/java/br/com/fluentvalidator/annotation/CleanValidationContextException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2019 the original author or authors. - * - * 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 - * - * https://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 br.com.fluentvalidator.annotation; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -@Inherited -@Target(METHOD) -@Retention(RUNTIME) -public @interface CleanValidationContextException { - -} diff --git a/src/main/java/br/com/fluentvalidator/aspect/ValidationExceptionAdvice.java b/src/main/java/br/com/fluentvalidator/aspect/ValidationExceptionAdvice.java deleted file mode 100644 index 0fd41e5..0000000 --- a/src/main/java/br/com/fluentvalidator/aspect/ValidationExceptionAdvice.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2019 the original author or authors. - * - * 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 - * - * https://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 br.com.fluentvalidator.aspect; - -import org.aspectj.lang.annotation.AfterThrowing; -import org.aspectj.lang.annotation.Aspect; - -import br.com.fluentvalidator.annotation.CleanValidationContextException; -import br.com.fluentvalidator.context.ValidationContext; - -@Aspect -public class ValidationExceptionAdvice { - - @AfterThrowing("execution(* *(..)) && @annotation(cleanValidationContextException)") - public void afterThrowing(final CleanValidationContextException cleanValidationContextException) { - ValidationContext.remove(); - } - -} diff --git a/src/main/java/br/com/fluentvalidator/context/Error.java b/src/main/java/br/com/fluentvalidator/context/Error.java index 89d4a4a..ca58ffa 100644 --- a/src/main/java/br/com/fluentvalidator/context/Error.java +++ b/src/main/java/br/com/fluentvalidator/context/Error.java @@ -16,6 +16,25 @@ package br.com.fluentvalidator.context; +/** + * Represents a validation error context, encapsulating details about a failed validation, + * including the field name, error message, error code, and the attempted value. + *

+ * Instances of {@code Error} are immutable and can be created using the static {@link #create(String, String, String, Object)} method. + *

+ * + *
    + *
  • {@code field}: The name of the field that failed validation.
  • + *
  • {@code message}: The error message describing the validation failure.
  • + *
  • {@code code}: The error code associated with the validation error.
  • + *
  • {@code attemptedValue}: The value that was attempted and caused the error.
  • + *
+ * + * Example usage: + *
+ *   Error error = Error.create("username", "Username must not be empty", "USR_001", null);
+ * 
+ */ public class Error { private final String message; @@ -27,17 +46,26 @@ public class Error { private final String code; /** + * Creates a new Error instance with the specified field, message, code, and attempted value. * - * @param field - * @param message - * @param code - * @param attemptedValue - * @return + * @param field the name of the field that failed validation + * @param message the validation error message + * @param code the error code associated with this validation error + * @param attemptedValue the value that was attempted and failed validation + * @return a new Error instance with the provided parameters */ public static Error create(final String field, final String message, final String code, final Object attemptedValue) { return new Error(field, message, code, attemptedValue); } + /** + * Constructs an {@code Error} instance with the specified field, message, code, and attempted value. + * + * @param field the name of the field where the error occurred + * @param message the error message describing the validation failure + * @param code the error code associated with the validation error + * @param attemptedValue the value that was attempted and caused the error + */ protected Error(final String field, final String message, final String code, final Object attemptedValue) { this.field = field; this.message = message; @@ -46,37 +74,47 @@ protected Error(final String field, final String message, final String code, fin } /** + * Returns the name of the field associated with this error. * - * @return + * @return the field name as a {@code String} */ public String getField() { return field; } /** + * Returns the error message associated with this error context. * - * @return + * @return the error message as a {@code String} */ public String getMessage() { return message; } /** + * Returns the error code associated with this error. * - * @return + * @return the error code as a {@code String} */ public String getCode() { return code; } /** + * Returns the value that was attempted during validation. * - * @return + * @return the attempted value, which may be null if not set */ public Object getAttemptedValue() { return attemptedValue; } + /** + * Returns a string representation of the Error object, including the message, + * field, attempted value, and code properties. + * + * @return a formatted string describing the Error instance + */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/br/com/fluentvalidator/context/ProcessorContext.java b/src/main/java/br/com/fluentvalidator/context/ProcessorContext.java index 243d25d..818f269 100644 --- a/src/main/java/br/com/fluentvalidator/context/ProcessorContext.java +++ b/src/main/java/br/com/fluentvalidator/context/ProcessorContext.java @@ -25,13 +25,19 @@ public final class ProcessorContext { private static final ThreadLocal threadLocal = new ThreadLocal<>(); + /** + * Private constructor to prevent instantiation of {@code ProcessorContext} from outside the class. + * Ensures that instances can only be created internally, typically for singleton or factory patterns. + */ private ProcessorContext() { super(); } /** + * Retrieves the current {@link Context} instance associated with the calling thread. + * If no {@link Context} exists for the thread, a new instance is created and set. * - * @return + * @return the {@link Context} instance for the current thread */ public static Context get() { if (Objects.isNull(threadLocal.get())) { @@ -41,35 +47,68 @@ public static Context get() { } /** - * + * Removes the current thread's value for the processor context from the ThreadLocal storage. + * This method should be called to clean up resources and avoid potential memory leaks + * when the context is no longer needed in the current thread. */ public static void remove() { threadLocal.remove(); } /** - * Context of processor + * Context is a utility class that manages a stack of counters using {@link AtomicInteger}. + * It provides methods to create, remove, increment, and retrieve the current counter value. + * The stack is thread-safe, allowing concurrent access. + * + *
    + *
  • {@link #create()} - Pushes a new counter onto the stack, initialized to zero.
  • + *
  • {@link #remove()} - Removes the top counter from the stack if it exists.
  • + *
  • {@link #inc()} - Increments the top counter if the stack is not empty.
  • + *
  • {@link #get()} - Retrieves the value of the top counter, or zero if the stack is empty.
  • + *
*/ public static final class Context implements AutoCloseable { private final Deque stackCounter = new ConcurrentLinkedDeque<>(); + /** + * Initializes a new processing context by pushing a fresh {@link AtomicInteger} with value 0 onto the stack counter. + * This method is typically used to start a new validation or processing scope. + */ public void create() { stackCounter.push(new AtomicInteger(0)); } + /** + * Removes the top element from the {@code stackCounter} if it is not empty. + * This method is typically used to manage the stack state within the processor context, + * ensuring that elements are only removed when available. + */ public void remove() { if (!stackCounter.isEmpty()) { stackCounter.pop(); } } + /** + * Increments the top value of the stack counter if the stack is not empty. + *

+ * This method checks if the {@code stackCounter} is not empty and, if so, + * increments the value at the top of the stack using {@code incrementAndGet()}. + *

+ */ public void inc() { if (!stackCounter.isEmpty()) { stackCounter.peek().incrementAndGet(); } } + /** + * Retrieves the current value from the stack counter. + * If the stack counter is empty, returns {@code 0}. + * + * @return the current value of the stack counter, or {@code 0} if empty + */ public Integer get() { return stackCounter.isEmpty() ? 0 : stackCounter.peek().get(); } diff --git a/src/main/java/br/com/fluentvalidator/context/ValidationContext.java b/src/main/java/br/com/fluentvalidator/context/ValidationContext.java index d3c84f7..60dc802 100644 --- a/src/main/java/br/com/fluentvalidator/context/ValidationContext.java +++ b/src/main/java/br/com/fluentvalidator/context/ValidationContext.java @@ -23,17 +23,38 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; +/** + * Utility class for managing validation context using thread-local storage. + *

+ * Provides access to a thread-local {@link Context} instance, which holds validation properties and errors. + *

+ * + *
    + *
  • {@link #get()} - Retrieves the current thread's validation context, creating one if necessary.
  • + *
  • {@link #remove()} - Removes the validation context from the current thread.
  • + *
+ * + *

+ * The nested {@link Context} class allows storing arbitrary properties and collecting validation errors. + *

+ */ public final class ValidationContext { private static final ThreadLocal threadLocal = new ThreadLocal<>(); + /** + * Private constructor to prevent instantiation of {@code ValidationContext}. + * This ensures that the class can only be used in a static context or through controlled access. + */ private ValidationContext() { super(); } /** + * Retrieves the current {@link Context} instance associated with the calling thread. + * If no instance exists, a new {@link Context} is created and associated with the thread. * - * @return + * @return the {@link Context} instance for the current thread */ public static Context get() { if (Objects.isNull(threadLocal.get())) { @@ -43,14 +64,31 @@ public static Context get() { } /** - * + * Removes the current {@link ValidationContext} instance from the thread-local storage. + * This method should be called to clean up resources and avoid memory leaks + * after validation operations are completed in the current thread. */ public static void remove() { threadLocal.remove(); } /** - * Context of validation + * Represents a validation context for storing properties and errors during validation. + *

+ * This class maintains a thread-safe map of properties and a queue of validation errors. + * It provides methods to add errors, set and retrieve properties, and obtain the validation result. + *

+ * + *
    + *
  • {@link #addErrors(Collection)} - Adds a collection of validation errors to the context.
  • + *
  • {@link #setProperty(String, Object)} - Sets a property in the context.
  • + *
  • {@link #getProperty(String, Class)} - Retrieves a property from the context, cast to the specified type.
  • + *
  • {@link #getValidationResult()} - Returns the validation result based on the collected errors.
  • + *
+ * + *

+ * This class is intended for internal use within the validation framework. + *

*/ public static final class Context implements AutoCloseable { @@ -59,20 +97,20 @@ public static final class Context implements AutoCloseable { private final Queue errors = new ConcurrentLinkedQueue<>(); /** + * Adds a collection of {@link Error} objects to the current list of errors. * - * @param field - * @param message - * @param code - * @param attemptedValue + * @param errs the collection of errors to be added */ public void addErrors(final Collection errs) { errs.stream().forEach(errors::add); } /** + * Sets a property in the validation context with the specified key and value. + * If the property key is not {@code null}, it will be added or updated in the context. * - * @param property - * @param value + * @param property the key of the property to set; must not be {@code null} + * @param value the value to associate with the property key */ public void setProperty(final String property, final Object value) { if (Objects.nonNull(property)) { @@ -81,8 +119,14 @@ public void setProperty(final String property, final Object value) { } /** + * Retrieves the validation result for the current context. + *

+ * This method clears the thread-local validation context and returns a {@link ValidationResult} + * indicating whether validation errors were found. If no errors are present, {@link ValidationResult#ok()} + * is returned; otherwise, {@link ValidationResult#fail(java.util.List)} is returned with the list of errors. + *

* - * @return + * @return the {@link ValidationResult} representing the outcome of the validation. */ public ValidationResult getValidationResult() { ValidationContext.remove(); @@ -90,10 +134,13 @@ public ValidationResult getValidationResult() { } /** + * Retrieves the value of a property by its name and casts it to the specified type. * - * @param property - * @param clazz - * @return + * @param property the name of the property to retrieve + * @param clazz the class object representing the desired return type + * @param

the type of the property value + * @return the property value cast to the specified type, or {@code null} if the property does not exist + * @throws ClassCastException if the property value cannot be cast to the specified type */ public

P getProperty(final String property, final Class

clazz) { return clazz.cast(properties.getOrDefault(property, null)); diff --git a/src/main/java/br/com/fluentvalidator/context/ValidationResult.java b/src/main/java/br/com/fluentvalidator/context/ValidationResult.java index 3345f80..311e3ac 100644 --- a/src/main/java/br/com/fluentvalidator/context/ValidationResult.java +++ b/src/main/java/br/com/fluentvalidator/context/ValidationResult.java @@ -20,8 +20,16 @@ import java.util.Collection; import java.util.Collections; import java.util.Optional; + import br.com.fluentvalidator.exception.ValidationException; +/** + * Represents the result of a validation operation, containing the validation status + * and any associated error messages. + * + *

This class is immutable and thread-safe. It provides factory methods to create + * instances representing successful or failed validation results.

+ */ public final class ValidationResult { private final boolean valid; @@ -29,30 +37,44 @@ public final class ValidationResult { private final Collection errors; /** + * Creates a successful validation result with no errors. * - * @return + * @return a ValidationResult instance representing a successful validation */ public static ValidationResult ok() { return new ValidationResult(true, new ArrayList<>()); } /** + * Creates a failed validation result with the specified error messages. * - * @param messages - * @return + * @param messages the collection of error messages; if null, an empty collection is used + * @return a ValidationResult instance representing a failed validation */ public static ValidationResult fail(final Collection messages) { return new ValidationResult(false, Optional.ofNullable(messages).orElse(new ArrayList<>())); } + /** + * Private constructor to create a ValidationResult instance. + * + * @param valid indicates whether the validation was successful + * @param messages the collection of error messages + */ private ValidationResult(final boolean valid, final Collection messages) { this.valid = valid; errors = Collections.unmodifiableCollection(messages); } /** + * Throws a validation exception if the validation result is invalid. * - * @param clazz + *

This method provides a convenient way to convert validation failures + * into exceptions for error handling purposes.

+ * + * @param the type of ValidationException to throw + * @param clazz the class of the ValidationException to instantiate and throw + * @throws ValidationException if the validation result is invalid */ public void isInvalidThrow(final Class clazz) { if (!isValid()) { @@ -61,21 +83,34 @@ public void isInvalidThrow(final Class clazz) } /** + * Checks whether the validation was successful. * - * @return + * @return {@code true} if the validation was successful, {@code false} otherwise */ public boolean isValid() { return valid; } /** + * Returns the collection of validation errors. + * + *

The returned collection is unmodifiable. If the validation was successful, + * this collection will be empty.

* - * @return + * @return an unmodifiable collection of validation errors */ public Collection getErrors() { return errors; } + /** + * Returns a string representation of this ValidationResult. + * + *

The string representation includes the validation status and + * the collection of errors.

+ * + * @return a string representation of this ValidationResult + */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/br/com/fluentvalidator/exception/ValidationException.java b/src/main/java/br/com/fluentvalidator/exception/ValidationException.java index 3f1e7b7..514e15a 100644 --- a/src/main/java/br/com/fluentvalidator/exception/ValidationException.java +++ b/src/main/java/br/com/fluentvalidator/exception/ValidationException.java @@ -21,39 +21,84 @@ import br.com.fluentvalidator.context.ValidationContext; import br.com.fluentvalidator.context.ValidationResult; +/** + * Abstract base class for validation exceptions in the FluentValidator framework. + *

+ * This class provides a foundation for creating custom validation exceptions that + * carry validation results. It extends {@link RuntimeException} and includes + * factory methods for creating instances of validation exception subclasses. + *

+ */ public abstract class ValidationException extends RuntimeException { private static final long serialVersionUID = 2274879814700248645L; private final transient ValidationResult validationResult; + /** + * Constructs a new ValidationException with the specified validation result. + *

+ * The exception message is automatically generated from the validation result's + * string representation. + *

+ * + * @param validationResult the validation result containing error details + * @throws NullPointerException if validationResult is null + */ protected ValidationException(final ValidationResult validationResult) { super(validationResult.toString()); this.validationResult = validationResult; } /** + * Returns the validation result associated with this exception. + *

+ * The validation result contains detailed information about validation + * errors that occurred during the validation process. + *

* - * @return + * @return the validation result, never null */ public ValidationResult getValidationResult() { return validationResult; } /** + * Creates a new instance of the specified validation exception class using + * the current validation context's result. + *

+ * This is a convenience method that retrieves the validation result from + * the current {@link ValidationContext} and creates an exception instance. + *

* - * @param exceptionClass - * @return + * @param the type of validation exception to create + * @param exceptionClass the class of the validation exception to instantiate + * @return a new RuntimeException instance of the specified type + * @throws RuntimeException if the exception class doesn't have the required constructor + * or if instantiation fails + * @see #create(Class, ValidationResult) */ public static RuntimeException create(final Class exceptionClass) { return create(exceptionClass, ValidationContext.get().getValidationResult()); } /** + * Creates a new instance of the specified validation exception class with + * the provided validation result. + *

+ * This method uses reflection to instantiate the exception class. The target + * class must have a constructor that accepts a single {@link ValidationResult} + * parameter. + *

* - * @param exceptionClass - * @param validationResult - * @return + * @param the type of validation exception to create + * @param exceptionClass the class of the validation exception to instantiate + * @param validationResult the validation result to associate with the exception + * @return a new RuntimeException instance of the specified type + * @throws RuntimeException if the exception class doesn't have a constructor + * accepting ValidationResult, or if instantiation fails + * due to security restrictions, illegal access, or + * invocation target exceptions */ public static RuntimeException create(final Class exceptionClass, final ValidationResult validationResult) { try { diff --git a/src/main/java/br/com/fluentvalidator/function/FunctionBuilder.java b/src/main/java/br/com/fluentvalidator/function/FunctionBuilder.java index 12bce84..f2c0b61 100644 --- a/src/main/java/br/com/fluentvalidator/function/FunctionBuilder.java +++ b/src/main/java/br/com/fluentvalidator/function/FunctionBuilder.java @@ -19,13 +19,39 @@ import java.util.Objects; import java.util.function.Function; +/** + * A builder wrapper for {@link Function} that provides null-safe function composition + * and execution. This class wraps a function and ensures that null inputs are handled + * gracefully by returning null instead of throwing exceptions. + * + *

The FunctionBuilder provides a fluent API for creating and composing functions + * while maintaining null safety throughout the chain of operations.

+ * + *

Example usage:

+ *
{@code
+ * Function stringToLength = FunctionBuilder.of(String::length);
+ * Integer result = stringToLength.apply(null); // Returns null instead of NPE
+ *
+ * Function composed = FunctionBuilder.of(String::toLowerCase)
+ *     .andThen(s -> s + "!");
+ * }
+ * + * @param the type of the input to the function + * @param the type of the result of the function + */ public final class FunctionBuilder implements Function { + /** + * The wrapped function that will be executed when apply is called. + */ private final Function function; /** + * Private constructor to create a new FunctionBuilder instance. + * Use {@link #of(Function)} to create instances. * - * @param function + * @param function the function to wrap, must not be null + * @throws NullPointerException if function is null */ private FunctionBuilder(final Function function) { this.function = function; @@ -35,16 +61,58 @@ public static Function of(final Function function) { return new FunctionBuilder<>(function); } + /** + * Applies this function to the given argument with null-safe behavior. + * + *

If the input value is null, this method returns null without + * calling the wrapped function. Otherwise, it applies the wrapped + * function to the input value.

+ * + * @param value the function argument + * @return the function result, or null if the input value is null + */ @Override public O apply(final I value) { return Objects.nonNull(value) ? function.apply(value) : null; } + /** + * Returns a composed function that first applies this function to its input, + * and then applies the {@code after} function to the result, maintaining + * null-safe behavior throughout the composition. + * + *

If either this function or the after function would receive a null input, + * the entire composition returns null without executing subsequent functions.

+ * + * @param the type of output of the after function, and of the composed function + * @param after the function to apply after this function is applied + * @return a composed function that first applies this function and then applies the + * {@code after} function + * @throws NullPointerException if after is null + * + * @see Function#andThen(Function) + */ @Override public Function andThen(final Function after) { return of(i -> of(after).apply(this.apply(i))); } + /** + * Returns a composed function that first applies the {@code before} function to + * its input, and then applies this function to the result, maintaining null-safe + * behavior throughout the composition. + * + *

If either the before function or this function would receive a null input, + * the entire composition returns null without executing subsequent functions.

+ * + * @param the type of input to the before function, and to the composed function + * @param before the function to apply before this function is applied + * @return a composed function that first applies the {@code before} function and + * then applies this function + * @throws NullPointerException if before is null + * + * @see Function#compose(Function) + */ @Override public Function compose(final Function before) { return of(v -> this.apply(of(before).apply(v))); diff --git a/src/main/java/br/com/fluentvalidator/handler/HandlerInvalidField.java b/src/main/java/br/com/fluentvalidator/handler/HandlerInvalidField.java index ac036eb..c040a5d 100644 --- a/src/main/java/br/com/fluentvalidator/handler/HandlerInvalidField.java +++ b/src/main/java/br/com/fluentvalidator/handler/HandlerInvalidField.java @@ -20,12 +20,64 @@ import java.util.Collections; import br.com.fluentvalidator.context.Error; +/** + * Interface for handling invalid field validation scenarios. + *

+ * This interface provides a contract for implementing custom handlers that process + * validation failures for specific field types. Implementations can define custom + * logic to generate appropriate error messages or perform additional processing + * when field validation fails. + *

+ *

+ * The interface supports two handling approaches: + *

    + *
  • Simple handling based only on the attempted value
  • + *
  • Context-aware handling that considers both the object instance and attempted value
  • + *
+ *

+ * + * @param

the type of the field value being validated + */ public interface HandlerInvalidField

{ + /** + * Handles validation failure for a field with the given attempted value. + *

+ * This method is called when field validation fails and provides an opportunity + * to generate custom error messages or perform additional processing based on + * the attempted value. + *

+ *

+ * The default implementation returns an empty collection, indicating no errors + * should be added beyond the standard validation failure. + *

+ * + * @param attemptedValue the value that failed validation, may be {@code null} + * @return a collection of {@link Error} objects representing validation errors, + * never {@code null} but may be empty + */ default Collection handle(final P attemptedValue) { return Collections.emptyList(); } + /** + * Handles validation failure for a field with context of the containing object instance. + *

+ * This method provides additional context by including the object instance that + * contains the field being validated. This allows for more sophisticated error + * handling that can consider the state of the entire object. + *

+ *

+ * The default implementation delegates to {@link #handle(Object)} with only + * the attempted value, ignoring the instance context. + *

+ * + * @param instance the object instance containing the field being validated, + * may be {@code null} + * @param attemptedValue the value that failed validation, may be {@code null} + * @return a collection of {@link Error} objects representing validation errors, + * never {@code null} but may be empty + */ default Collection handle(final Object instance, final P attemptedValue) { return handle(attemptedValue); } diff --git a/src/main/java/br/com/fluentvalidator/predicate/CollectionPredicate.java b/src/main/java/br/com/fluentvalidator/predicate/CollectionPredicate.java index c1362ef..9e06ad5 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/CollectionPredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/CollectionPredicate.java @@ -28,13 +28,19 @@ import java.util.function.Function; import java.util.function.Predicate; +/** + * Utility class providing predicate methods for validating collections. + * This class contains static methods that return predicates for common collection validation scenarios + * such as checking if a collection is empty, contains specific items, or has a certain size. + */ public final class CollectionPredicate { /** + * Creates a predicate that tests if a collection is empty (null or has no elements). * - * @param - * @param - * @return + * @param the type of elements in the collection + * @param the type of collection that extends Collection<E> + * @return a predicate that returns true if the collection is null or empty */ public static > Predicate empty() { return PredicateBuilder.from(is(nullValue())) @@ -42,11 +48,12 @@ public static > Predicate empty() { } /** + * Creates a predicate that tests if a collection extracted from an object is empty. * - * @param - * @param - * @param source - * @return + * @param the type of the object being tested + * @param the type of elements in the collection + * @param source a function that extracts a collection from the object + * @return a predicate that returns true if the extracted collection is null or empty */ public static Predicate empty(final Function> source) { return PredicateBuilder.from(is(nullValue())) @@ -55,11 +62,12 @@ public static Predicate empty(final Function> source) } /** + * Creates a predicate that tests if a collection contains any of the specified objects. * - * @param - * @param - * @param objects - * @return + * @param the type of elements in the collection + * @param the type of collection that extends Collection<E> + * @param objects the collection of objects to check for + * @return a predicate that returns true if the collection contains any of the specified objects */ public static > Predicate hasAny(final Collection objects) { return PredicateBuilder.from(not(nullValue())) @@ -68,11 +76,12 @@ public static > Predicate hasAny(final Collection< } /** + * Creates a predicate that tests if a collection contains any of the specified objects from an array. * - * @param - * @param - * @param objects - * @return + * @param the type of elements in the collection + * @param the type of collection that extends Collection<E> + * @param objects the array of objects to check for + * @return a predicate that returns true if the collection contains any of the specified objects */ public static > Predicate hasAny(final E[] objects) { return PredicateBuilder.from(not(nullValue())) @@ -81,12 +90,13 @@ public static > Predicate hasAny(final E[] objects } /** + * Creates a predicate that tests if a collection extracted from an object contains any of the specified objects. * - * @param - * @param - * @param source - * @param objects - * @return + * @param the type of the object being tested + * @param the type of elements in the collection + * @param source a function that extracts a collection from the object + * @param objects the collection of objects to check for + * @return a predicate that returns true if the extracted collection contains any of the specified objects */ public static Predicate hasAny(final Function> source, final Collection objects) { return PredicateBuilder.from(not(nullValue())) @@ -94,12 +104,13 @@ public static Predicate hasAny(final Function> source } /** + * Creates a predicate that tests if a collection extracted from an object contains any of the specified objects from an array. * - * @param - * @param - * @param source - * @param objects - * @return + * @param the type of the object being tested + * @param the type of elements in the collection + * @param source a function that extracts a collection from the object + * @param objects the array of objects to check for + * @return a predicate that returns true if the extracted collection contains any of the specified objects */ public static Predicate hasAny(final Function> source, final E[] objects) { return PredicateBuilder.from(not(nullValue())) @@ -121,7 +132,7 @@ public static Predicate hasAny(final Function> source * @param type of exam class * @param object the object to compare against the objects provided by the * examined {@link Collection} - * @return {@link Predicate} + * @return {@link Predicate} that returns true if the collection contains the specified object */ public static > Predicate hasItem(final E object) { return PredicateBuilder.from(not(nullValue())) @@ -129,12 +140,13 @@ public static > Predicate hasItem(final E object) } /** + * Creates a predicate that tests if a collection extracted from an object contains the specified item. * - * @param - * @param - * @param source - * @param object - * @return + * @param the type of the object being tested + * @param the type of elements in the collection + * @param source a function that extracts a collection from the object + * @param object the object to check for in the collection + * @return a predicate that returns true if the extracted collection contains the specified object */ public static Predicate hasItem(final Function> source, final E object) { return PredicateBuilder.from(not(nullValue())) @@ -142,11 +154,12 @@ public static Predicate hasItem(final Function> sourc } /** + * Creates a predicate that tests if a collection contains all of the specified objects. * - * @param - * @param - * @param objects - * @return + * @param the type of elements in the collection + * @param the type of collection that extends Collection<E> + * @param objects the collection of objects that must all be present + * @return a predicate that returns true if the collection contains all specified objects */ public static > Predicate hasItems(final Collection objects) { return PredicateBuilder.from(not(nullValue())) @@ -155,11 +168,12 @@ public static > Predicate hasItems(final Collectio } /** - * - * @param - * @param - * @param objects - * @return + * Creates a predicate that tests if a collection contains all of the specified objects from an array. + * + * @param the type of elements in the collection + * @param the type of collection that extends Collection<E> + * @param objects the array of objects that must all be present + * @return a predicate that returns true if the collection contains all specified objects */ public static > Predicate hasItems(final E[] objects) { return PredicateBuilder.from(not(nullValue())) @@ -168,12 +182,13 @@ public static > Predicate hasItems(final E[] objec } /** - * - * @param - * @param - * @param source - * @param objects - * @return + * Creates a predicate that tests if a collection extracted from an object contains all of the specified objects. + * + * @param the type of the object being tested + * @param the type of elements in the collection + * @param source a function that extracts a collection from the object + * @param objects the collection of objects that must all be present + * @return a predicate that returns true if the extracted collection contains all specified objects */ public static Predicate hasItems(final Function> source, final Collection objects) { return PredicateBuilder.from(not(nullValue())) @@ -181,12 +196,13 @@ public static Predicate hasItems(final Function> sour } /** + * Creates a predicate that tests if a collection extracted from an object contains all of the specified objects from an array. * - * @param - * @param - * @param source - * @param objects - * @return + * @param the type of the object being tested + * @param the type of elements in the collection + * @param source a function that extracts a collection from the object + * @param objects the array of objects that must all be present + * @return a predicate that returns true if the extracted collection contains all specified objects */ public static Predicate hasItems(final Function> source, final E[] objects) { return PredicateBuilder.from(not(nullValue())) @@ -194,12 +210,13 @@ public static Predicate hasItems(final Function> sour } /** + * Creates a predicate that tests if a collection extracted from an object has a size equal to a dynamically computed value. * - * @param - * @param - * @param source - * @param size - * @return + * @param the type of the object being tested + * @param the type of elements in the collection + * @param source a function that extracts a collection from the object + * @param size a function that computes the expected size from the object + * @return a predicate that returns true if the extracted collection has the computed size */ public static Predicate hasSize(final Function> source, final Function size) { return PredicateBuilder.from(not(nullValue())) @@ -209,12 +226,13 @@ public static Predicate hasSize(final Function> sourc } /** + * Creates a predicate that tests if a collection extracted from an object has a specific size. * - * @param - * @param - * @param source - * @param size - * @return + * @param the type of the object being tested + * @param the type of elements in the collection + * @param source a function that extracts a collection from the object + * @param size the expected size of the collection + * @return a predicate that returns true if the extracted collection has the specified size */ public static Predicate hasSize(final Function> source, final Integer size) { return PredicateBuilder.from(not(nullValue())) @@ -223,11 +241,12 @@ public static Predicate hasSize(final Function> sourc } /** + * Creates a predicate that tests if a collection has a specific size. * - * @param - * @param - * @param size - * @return + * @param the type of elements in the collection + * @param the type of collection that extends Collection<E> + * @param size the expected size of the collection + * @return a predicate that returns true if the collection has the specified size */ public static > Predicate hasSize(final Integer size) { return PredicateBuilder.from(not(nullValue())) @@ -235,13 +254,14 @@ public static > Predicate hasSize(final Integer si } /** - * - * @param - * @param - * @param source - * @param min - * @param max - * @return + * Creates a predicate that tests if a collection extracted from an object has a size between the specified bounds (exclusive). + * + * @param the type of the object being tested + * @param the type of elements in the collection + * @param source a function that extracts a collection from the object + * @param min the minimum size (exclusive) + * @param max the maximum size (exclusive) + * @return a predicate that returns true if the extracted collection size is between min and max (exclusive) */ public static Predicate hasSizeBetween(final Function> source, final Integer min, final Integer max) { return PredicateBuilder.from(not(nullValue())) @@ -250,12 +270,13 @@ public static Predicate hasSizeBetween(final Function } /** - * - * @param - * @param - * @param min - * @param max - * @return + * Creates a predicate that tests if a collection has a size between the specified bounds (exclusive). + * + * @param the type of elements in the collection + * @param the type of collection that extends Collection<E> + * @param min the minimum size (exclusive) + * @param max the maximum size (exclusive) + * @return a predicate that returns true if the collection size is between min and max (exclusive) */ public static > Predicate hasSizeBetween(final Integer min, final Integer max) { return PredicateBuilder.from(not(nullValue())) @@ -263,13 +284,14 @@ public static > Predicate hasSizeBetween(final Int } /** + * Creates a predicate that tests if a collection extracted from an object has a size between the specified bounds (inclusive). * - * @param - * @param - * @param source - * @param min - * @param max - * @return + * @param the type of the object being tested + * @param the type of elements in the collection + * @param source a function that extracts a collection from the object + * @param min the minimum size (inclusive) + * @param max the maximum size (inclusive) + * @return a predicate that returns true if the extracted collection size is between min and max (inclusive) */ public static Predicate hasSizeBetweenInclusive(final Function> source, final Integer min, final Integer max) { return PredicateBuilder.from(not(nullValue())) @@ -278,18 +300,22 @@ public static Predicate hasSizeBetweenInclusive(final Function - * @param - * @param min - * @param max - * @return + * @param the type of elements in the collection + * @param the type of collection that extends Collection<E> + * @param min the minimum size (inclusive) + * @param max the maximum size (inclusive) + * @return a predicate that returns true if the collection size is between min and max (inclusive) */ public static > Predicate hasSizeBetweenInclusive(final Integer min, final Integer max) { return PredicateBuilder.from(not(nullValue())) .and(betweenInclusive(Collection::size, min, max)); } + /** + * Private constructor to prevent instantiation of this utility class. + */ private CollectionPredicate() { super(); } diff --git a/src/main/java/br/com/fluentvalidator/predicate/ComparablePredicate.java b/src/main/java/br/com/fluentvalidator/predicate/ComparablePredicate.java index 7fa8e2b..e76a1fd 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/ComparablePredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/ComparablePredicate.java @@ -456,8 +456,13 @@ public static > Predicate lessThanOrEqual(final Fu .and(lessThan(source, target).or(equalTo(source, target))); } + /** + * Private constructor to prevent instantiation of this utility class. + */ private ComparablePredicate() { super(); } } + + diff --git a/src/main/java/br/com/fluentvalidator/predicate/DatePredicate.java b/src/main/java/br/com/fluentvalidator/predicate/DatePredicate.java index cfb4151..d3ff670 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/DatePredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/DatePredicate.java @@ -269,7 +269,9 @@ public static Predicate dateLessThanOrEqual(final String dateString, fin .from(dateLessThan(dateString, pattern).or(dateEqualTo(dateString, pattern))); } - + /** + * Private constructor to prevent instantiation of this utility class. + */ private DatePredicate() { super(); } diff --git a/src/main/java/br/com/fluentvalidator/predicate/DateTimePredicate.java b/src/main/java/br/com/fluentvalidator/predicate/DateTimePredicate.java index 187a928..29e8b50 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/DateTimePredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/DateTimePredicate.java @@ -269,6 +269,9 @@ public static Predicate dateTimeLessThanOrEqual(final String dateString, dateTimeLessThan(dateString, pattern).or(dateTimeEqualTo(dateString, pattern))); } + /** + * Private constructor to prevent instantiation of this utility class. + */ private DateTimePredicate() { super(); } diff --git a/src/main/java/br/com/fluentvalidator/predicate/LocalDatePredicate.java b/src/main/java/br/com/fluentvalidator/predicate/LocalDatePredicate.java index c13dc11..ea2082b 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/LocalDatePredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/LocalDatePredicate.java @@ -455,6 +455,9 @@ public static Predicate localDateIsToday(final Function sou .and(obj -> localDateIsToday().test(source.apply(obj))); } + /** + * Private constructor to prevent instantiation of this utility class. + */ private LocalDatePredicate() { super(); } diff --git a/src/main/java/br/com/fluentvalidator/predicate/LocalDateTimePredicate.java b/src/main/java/br/com/fluentvalidator/predicate/LocalDateTimePredicate.java index ecec05c..43bdfcb 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/LocalDateTimePredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/LocalDateTimePredicate.java @@ -506,7 +506,9 @@ public static Predicate localDateTimeBetweenOrEqual(final Function localDateTimeBetweenOrEqual(source, min.apply(obj), max.apply(obj)).test(obj)); } - + /** + * Private constructor to prevent instantiation of this utility class. + */ private LocalDateTimePredicate() { super(); } diff --git a/src/main/java/br/com/fluentvalidator/predicate/LocalTimePredicate.java b/src/main/java/br/com/fluentvalidator/predicate/LocalTimePredicate.java index ede9fb5..ef26107 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/LocalTimePredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/LocalTimePredicate.java @@ -390,7 +390,9 @@ public static Predicate localTimeBetweenOrEqual(final Function localTimeBetweenOrEqual(source, min.apply(obj), max.apply(obj)).test(obj)); } - + /** + * Private constructor to prevent instantiation of this utility class. + */ private LocalTimePredicate() { super(); } diff --git a/src/main/java/br/com/fluentvalidator/predicate/LogicalPredicate.java b/src/main/java/br/com/fluentvalidator/predicate/LogicalPredicate.java index 5e3dd2c..a5315b6 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/LogicalPredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/LogicalPredicate.java @@ -21,68 +21,100 @@ import java.util.function.Function; import java.util.function.Predicate; +/** + * Utility class providing logical predicate operations for fluent validation. + * This class contains static methods for creating and combining predicates + * with logical operations such as negation, boolean evaluation, and identity checks. + * + *

All methods in this class are static and the class cannot be instantiated.

+ */ public final class LogicalPredicate { - + /** - * - * @param - * @param predicate - * @return + * Creates an identity predicate that wraps the given predicate with an additional + * always-true condition. This method is primarily used for predicate composition + * and fluent API construction. + * + * @param the type of the input to the predicate + * @param predicate the predicate to wrap, must not be null + * @return a new predicate that combines the input predicate with an identity check + * @throws NullPointerException if predicate is null */ static Predicate is(final Predicate predicate) { return PredicateBuilder.from(predicate.and(is -> true)); } /** + * Creates a predicate that tests if a Boolean value is false. + * The predicate first checks that the input is not null, then verifies + * that the boolean value is false. * - * @return + * @return a predicate that returns true if the input Boolean is false, false otherwise */ public static Predicate isFalse() { return PredicateBuilder.from(not(nullValue())).and(not(isFalse -> isFalse)); } /** + * Creates a predicate that tests if the result of applying a function to an object is false. + * The predicate performs null checks on both the input object and the function result + * before evaluating the boolean value. * - * @param - * @param function - * @return + * @param the type of the input to the predicate + * @param function the function to apply to extract a Boolean value, must not be null + * @return a predicate that returns true if the function result is false, false otherwise + * @throws NullPointerException if function is null */ public static Predicate isFalse(final Function function) { return PredicateBuilder.from(not(nullValue())) .and(not(nullValue(function))) .and(not(function::apply)); } - + /** - * - * @return + * Creates a predicate that tests if a Boolean value is true. + * The predicate first checks that the input is not null, then verifies + * that the boolean value is true. + * + * @return a predicate that returns true if the input Boolean is true, false otherwise */ public static Predicate isTrue() { return PredicateBuilder.from(not(nullValue())).and(is(isTrue -> isTrue)); } /** - * - * @param - * @param function - * @return + * Creates a predicate that tests if the result of applying a function to an object is true. + * The predicate performs null checks on both the input object and the function result + * before evaluating the boolean value. + * + * @param the type of the input to the predicate + * @param function the function to apply to extract a Boolean value, must not be null + * @return a predicate that returns true if the function result is true, false otherwise + * @throws NullPointerException if function is null */ public static Predicate isTrue(final Function function) { return PredicateBuilder.from(not(nullValue())) .and(not(nullValue(function))) .and(function::apply); } - + /** + * Creates a predicate that represents the logical negation of the given predicate. + * This is a convenience method that wraps the standard {@link Predicate#negate()} method + * within the fluent validation framework. * - * @param - * @param predicate - * @return + * @param the type of the input to the predicate + * @param predicate the predicate to negate, must not be null + * @return a predicate that represents the logical negation of the input predicate + * @throws NullPointerException if predicate is null */ public static Predicate not(final Predicate predicate) { return PredicateBuilder.from(predicate.negate()); } + /** + * Private constructor to prevent instantiation of this utility class. + */ private LogicalPredicate() { super(); } diff --git a/src/main/java/br/com/fluentvalidator/predicate/MapPredicate.java b/src/main/java/br/com/fluentvalidator/predicate/MapPredicate.java index 76cd045..bfbdd67 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/MapPredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/MapPredicate.java @@ -24,11 +24,7 @@ import java.util.function.Predicate; public final class MapPredicate { - - private MapPredicate() { - super(); - } - + public static > Predicate mapGet(final K key, final Predicate predicate) { return PredicateBuilder.from(not(nullValue())) .and(obj -> not(nullValue()).test(key)) @@ -68,4 +64,11 @@ public static > Predicate containsValue(final Funct .and(obj -> obj.containsValue(value.apply(obj))); } + /** + * Private constructor to prevent instantiation of this utility class. + */ + private MapPredicate() { + super(); + } + } diff --git a/src/main/java/br/com/fluentvalidator/predicate/ObjectPredicate.java b/src/main/java/br/com/fluentvalidator/predicate/ObjectPredicate.java index cb5b5c3..a99f236 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/ObjectPredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/ObjectPredicate.java @@ -109,7 +109,10 @@ public static Predicate nullValue(final Function source) { .or(obj -> Objects.isNull(source)) .or(obj -> Objects.isNull(source.apply(obj))); } - + + /** + * Private constructor to prevent instantiation of this utility class. + */ private ObjectPredicate() { super(); } diff --git a/src/main/java/br/com/fluentvalidator/predicate/PredicateBuilder.java b/src/main/java/br/com/fluentvalidator/predicate/PredicateBuilder.java index 6e5c0c0..e1f4a55 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/PredicateBuilder.java +++ b/src/main/java/br/com/fluentvalidator/predicate/PredicateBuilder.java @@ -18,25 +18,61 @@ import java.util.function.Predicate; +/** + * A builder class that wraps a {@link Predicate} and provides a fluent interface + * for predicate operations. This class implements the {@link Predicate} interface + * and delegates the actual test logic to the wrapped predicate. + * + *

This class is designed to be used as part of a fluent validation framework, + * allowing for more readable and maintainable predicate compositions.

+ * + *

Example usage:

+ *
{@code
+ * Predicate notNull = PredicateBuilder.from(Objects::nonNull);
+ * boolean result = notNull.test("example");
+ * }
+ * + * @param the type of the input to the predicate + */ public final class PredicateBuilder implements Predicate { + /** + * The wrapped predicate that performs the actual test logic. + */ private final Predicate predicate; /** + * Creates a new PredicateBuilder instance wrapping the given predicate. + * This is the preferred way to create instances of this class. * - * @param - * @param predicate - * @return + * @param the type of the input to the predicate + * @param predicate the predicate to wrap; must not be null + * @return a new PredicateBuilder instance wrapping the given predicate + * @throws NullPointerException if the predicate is null */ public static Predicate from(final Predicate predicate) { return new PredicateBuilder<>(predicate); } - + /** + * Private constructor to create a new PredicateBuilder instance. + * Use the {@link #from(Predicate)} factory method instead. + * + * @param predicate the predicate to wrap; must not be null + * @throws NullPointerException if the predicate is null + */ private PredicateBuilder(final Predicate predicate) { this.predicate = predicate; } + /** + * Evaluates this predicate on the given argument by delegating to the + * wrapped predicate. + * + * @param value the input argument to test + * @return {@code true} if the input argument matches the predicate, + * otherwise {@code false} + */ @Override public boolean test(final T value) { return predicate.test(value); diff --git a/src/main/java/br/com/fluentvalidator/predicate/StringPredicate.java b/src/main/java/br/com/fluentvalidator/predicate/StringPredicate.java index ac7684b..c0b4138 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/StringPredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/StringPredicate.java @@ -591,6 +591,9 @@ public static Predicate stringInCollection(final Function sour .and(obj -> stringInCollection(target.apply(obj)).test(source.apply(obj))); } + /** + * Private constructor to prevent instantiation of this utility class. + */ private StringPredicate() { super(); } diff --git a/src/main/java/br/com/fluentvalidator/predicate/TimePredicate.java b/src/main/java/br/com/fluentvalidator/predicate/TimePredicate.java index c8b3af9..c7c76ba 100644 --- a/src/main/java/br/com/fluentvalidator/predicate/TimePredicate.java +++ b/src/main/java/br/com/fluentvalidator/predicate/TimePredicate.java @@ -268,6 +268,9 @@ public static Predicate timeLessThanOrEqual(final String timeString, fin .from(timeLessThan(timeString, pattern).or(timeEqualTo(timeString, pattern))); } + /** + * Private constructor to prevent instantiation of this utility class. + */ private TimePredicate() { super(); } diff --git a/src/main/java/br/com/fluentvalidator/rule/RuleBuilderCollectionImpl.java b/src/main/java/br/com/fluentvalidator/rule/RuleBuilderCollectionImpl.java index 9fd9d59..a2060c2 100644 --- a/src/main/java/br/com/fluentvalidator/rule/RuleBuilderCollectionImpl.java +++ b/src/main/java/br/com/fluentvalidator/rule/RuleBuilderCollectionImpl.java @@ -23,7 +23,6 @@ import java.util.function.Predicate; import br.com.fluentvalidator.Validator; -import br.com.fluentvalidator.annotation.CleanValidationContextException; import br.com.fluentvalidator.builder.AttemptedValue; import br.com.fluentvalidator.builder.Code; import br.com.fluentvalidator.builder.Critical; @@ -166,7 +165,6 @@ public boolean support(final Collection

instance) { } @Override - @CleanValidationContextException public boolean apply(final Object obj, final Collection

instance) { final boolean apply = getMust().test(instance); @@ -198,7 +196,6 @@ public boolean support(final Collection

instance) { } @Override - @CleanValidationContextException public boolean apply(final Object obj, final Collection

instance) { final boolean apply = ruleProcessor.process(obj, instance, getValidator()); diff --git a/src/main/java/br/com/fluentvalidator/rule/RuleBuilderPropertyImpl.java b/src/main/java/br/com/fluentvalidator/rule/RuleBuilderPropertyImpl.java index e92051c..ea34dd3 100644 --- a/src/main/java/br/com/fluentvalidator/rule/RuleBuilderPropertyImpl.java +++ b/src/main/java/br/com/fluentvalidator/rule/RuleBuilderPropertyImpl.java @@ -23,7 +23,6 @@ import java.util.function.Predicate; import br.com.fluentvalidator.Validator; -import br.com.fluentvalidator.annotation.CleanValidationContextException; import br.com.fluentvalidator.builder.AttemptedValue; import br.com.fluentvalidator.builder.Code; import br.com.fluentvalidator.builder.Critical; @@ -166,7 +165,6 @@ public boolean support(final P instance) { } @Override - @CleanValidationContextException public boolean apply(final Object obj, final P instance) { final boolean apply = getMust().test(instance); @@ -198,7 +196,6 @@ public boolean support(final P instance) { } @Override - @CleanValidationContextException public boolean apply(final Object obj, final P instance) { final boolean apply = ruleProcessor.process(obj, instance, getValidator()); diff --git a/src/test/java/br/com/fluentvalidator/aspect/ValidationExceptionAdviceTest.java b/src/test/java/br/com/fluentvalidator/aspect/ValidationExceptionAdviceTest.java index 4cb97a5..67fca3a 100644 --- a/src/test/java/br/com/fluentvalidator/aspect/ValidationExceptionAdviceTest.java +++ b/src/test/java/br/com/fluentvalidator/aspect/ValidationExceptionAdviceTest.java @@ -28,7 +28,6 @@ import java.util.Arrays; import java.util.List; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import br.com.fluentvalidator.AbstractValidator; @@ -42,7 +41,6 @@ class ValidationExceptionAdviceTest { @Test - @Disabled("fixing") void validationMustBeSuccess() { final Validator validatorParent = new ValidatorObjectFrom(); @@ -56,7 +54,6 @@ void validationMustBeSuccess() { } @Test - @Disabled("fixing") void validationMustBeFail() { final Validator validatorParent = new ValidatorObjectFrom();