Skip to main content

Overview

The Validator library provides two approaches to handle validation errors:
  1. Event-based - Using the onInvalidEvaluation callback
  2. Exception-based - Using validOrFail() and compareOrFail() methods
Choose the approach that best fits your application architecture and error handling strategy.

Event-Based Error Handling

Event-based handling uses callbacks to respond to validation failures without throwing exceptions.

Basic Event Handling

import io.github.ApamateSoft.validator.Validator;

Validator validator = new Validator.Builder()
    .required()
    .minLength(6)
    .email()
    .build();

// Register error handler
validator.onInvalidEvaluation(message -> {
    System.err.println("Validation error: " + message);
});

// Validate - returns boolean
boolean isValid = validator.isValid("test");
if (isValid) {
    // Proceed with valid input
}

How Events Work

1

Register the event handler

Call onInvalidEvaluation() with a callback function:
validator.onInvalidEvaluation(message -> {
    // Handle the error message
});
2

Call validation method

Use isValid() or isMatch() to validate:
boolean valid = validator.isValid(userInput);
3

Event triggers on failure

If validation fails, your callback is invoked with the error message:
// If minLength fails:
// message = "6 or more characters are required"
Only the first failing rule triggers the event. Rules are evaluated in the order they were added, and validation stops at the first failure.

Multiple Field Validation with Events

import io.github.ApamateSoft.validator.Validator;

public class LoginForm {

    private final Validator emailValidator = new Validator.Builder()
        .required()
        .email()
        .build();

    private final Validator passwordValidator = new Validator.Builder()
        .required()
        .minLength(8)
        .build();

    private String emailError = null;
    private String passwordError = null;

    public LoginForm() {
        emailValidator.onInvalidEvaluation(message -> {
            emailError = message;
            displayError("email", message);
        });

        passwordValidator.onInvalidEvaluation(message -> {
            passwordError = message;
            displayError("password", message);
        });
    }

    public boolean submit(String email, String password) {
        // Clear previous errors
        emailError = null;
        passwordError = null;

        boolean emailValid = emailValidator.isValid(email);
        boolean passwordValid = passwordValidator.isValid(password);

        return emailValid && passwordValid;
    }

    private void displayError(String field, String message) {
        System.err.println(field + ": " + message);
    }
}

Password Confirmation with Events

import io.github.ApamateSoft.validator.Validator;

Validator passwordValidator = new Validator.Builder()
    .required()
    .minLength(8)
    .setNotMatchMessage("Passwords do not match")
    .build();

passwordValidator.onInvalidEvaluation(message -> {
    System.err.println("Password error: " + message);
});

// Validates password AND checks if both match
boolean valid = passwordValidator.isMatch(password, confirmPassword);
Use .setNotMatchMessage() to customize the error message when passwords don’t match.

Exception-Based Error Handling

Exception-based handling throws InvalidEvaluationException on validation failure, providing structured error information.

Basic Exception Handling

import io.github.ApamateSoft.validator.Validator;
import io.github.ApamateSoft.validator.exceptions.InvalidEvaluationException;

Validator validator = new Validator.Builder()
    .required()
    .minLength(6)
    .email()
    .build();

try {
    validator.validOrFail("email", userInput);
    // Validation passed - continue
} catch (InvalidEvaluationException e) {
    System.err.println("Field: " + e.getKey());
    System.err.println("Value: " + e.getValue());
    System.err.println("Error: " + e.getMessage());
}

InvalidEvaluationException Details

The exception provides three key pieces of information:
MethodReturnsDescription
getKey()StringField identifier passed to validOrFail()
getValue()StringThe actual value that failed validation
getMessage()StringThe error message from the failing rule
try {
    validator.validOrFail("username", "ab");
} catch (InvalidEvaluationException e) {
    e.getKey();      // "username"
    e.getValue();    // "ab"
    e.getMessage();  // "6 or more characters are required"
}

Multiple Field Validation with Exceptions

import io.github.ApamateSoft.validator.Validator;
import io.github.ApamateSoft.validator.exceptions.InvalidEvaluationException;

public class RegistrationForm {

    private final Validator emailValidator = new Validator.Builder()
        .required()
        .email()
        .build();

    private final Validator usernameValidator = new Validator.Builder()
        .required()
        .minLength(3)
        .maxLength(20)
        .onlyAlphanumeric()
        .build();

    private final Validator passwordValidator = new Validator.Builder()
        .required()
        .minLength(8)
        .build();

    public void submit(String email, String username, String password, String confirmPassword) {
        try {
            emailValidator.validOrFail("email", email);
            usernameValidator.validOrFail("username", username);
            passwordValidator.compareOrFail("password", password, confirmPassword);
            
            // All validations passed
            createAccount(email, username, password);
            
        } catch (InvalidEvaluationException e) {
            switch (e.getKey()) {
                case "email":
                    displayError("email", e.getMessage());
                    break;
                case "username":
                    displayError("username", e.getMessage());
                    break;
                case "password":
                    displayError("password", e.getMessage());
                    break;
            }
        }
    }

    private void displayError(String field, String message) {
        System.err.println(field + ": " + message);
    }
}
Exception-based validation stops at the first failure. If you need to collect all validation errors, you must call each validator separately and handle exceptions individually.

Password Confirmation with Exceptions

import io.github.ApamateSoft.validator.Validator;
import io.github.ApamateSoft.validator.exceptions.InvalidEvaluationException;

Validator passwordValidator = new Validator.Builder()
    .required()
    .minLength(8)
    .build();

try {
    // Validates password AND checks if both match
    passwordValidator.compareOrFail("password", password, confirmPassword);
    updatePassword(password);
} catch (InvalidEvaluationException e) {
    System.err.println("Password error: " + e.getMessage());
}

Annotation-Based Validation with Exceptions

When using annotations, always use the exception-based approach:
import io.github.ApamateSoft.validator.Validator;
import io.github.ApamateSoft.validator.annotations.*;
import io.github.ApamateSoft.validator.exceptions.InvalidEvaluationException;

public class User {
    @Required
    @Email
    private String email;

    @Required
    @MinLength(min = 3)
    private String username;

    @Required
    @MinLength(min = 8)
    @Compare(compareWith = "passwordConfirm")
    private String password;
    
    private String passwordConfirm;

    public void validate() {
        try {
            Validator.validOrFail(this);
            // All validations passed
        } catch (InvalidEvaluationException e) {
            System.err.println(e.getKey() + ": " + e.getMessage());
        }
    }
}
When using Validator.validOrFail(Object), the key in the exception corresponds to the field name in your class.

Collecting All Validation Errors

By default, validation stops at the first error. To collect all errors, validate each field individually:
import io.github.ApamateSoft.validator.Validator;
import java.util.ArrayList;
import java.util.List;

public class FormValidator {
    
    private final List<String> errors = new ArrayList<>();
    
    public boolean validateForm(String email, String username, String password) {
        errors.clear();
        
        Validator emailValidator = new Validator.Builder()
            .required()
            .email()
            .build();
        emailValidator.onInvalidEvaluation(msg -> 
            errors.add("Email: " + msg));
        
        Validator usernameValidator = new Validator.Builder()
            .required()
            .minLength(3)
            .build();
        usernameValidator.onInvalidEvaluation(msg -> 
            errors.add("Username: " + msg));
        
        Validator passwordValidator = new Validator.Builder()
            .required()
            .minLength(8)
            .build();
        passwordValidator.onInvalidEvaluation(msg -> 
            errors.add("Password: " + msg));
        
        // Validate all fields
        emailValidator.isValid(email);
        usernameValidator.isValid(username);
        passwordValidator.isValid(password);
        
        if (!errors.isEmpty()) {
            errors.forEach(System.err::println);
            return false;
        }
        
        return true;
    }
}

Checking Exception Type

When catching multiple exception types, verify the exception is InvalidEvaluationException:
import io.github.ApamateSoft.validator.Validator;
import io.github.ApamateSoft.validator.exceptions.InvalidEvaluationException;

try {
    validator.validOrFail("field", value);
    // Other operations that might throw exceptions
} catch (Exception e) {
    if (e instanceof InvalidEvaluationException) {
        InvalidEvaluationException validationError = (InvalidEvaluationException) e;
        System.err.println("Validation failed: " + validationError.getMessage());
    } else {
        // Handle other exceptions
        throw e;
    }
}

Choosing the Right Approach

Use Events When

  • Building UI forms with inline validation
  • You prefer reactive, callback-based code
  • You want to avoid try-catch blocks
  • Working with event-driven architectures

Use Exceptions When

  • You need structured error information (key, value, message)
  • Building APIs or services
  • You prefer traditional exception handling
  • Using annotation-based validation

Comparison Table

FeatureEvent-BasedException-Based
Validation methodisValid(), isMatch()validOrFail(), compareOrFail()
Return valuebooleanvoid (throws on failure)
Error infoMessage onlyKey, value, and message
Setup requiredYes (onInvalidEvaluation)No
Annotation supportNoYes
Code styleCallback/reactiveTraditional try-catch
Stops on first errorYesYes

Best Practices

The InvalidEvaluationException.getValue() method returns the actual input value. Never log this for passwords or other sensitive fields:
catch (InvalidEvaluationException e) {
    // DON'T: log(e.getValue());
    // DO: log(e.getKey() + ": " + e.getMessage());
}
When re-validating, clear previous error states to avoid showing stale errors:
public void submit() {
    emailError = null;  // Clear previous error
    if (!emailValidator.isValid(email)) {
        return;
    }
}
Choose descriptive keys that match your field names:
// Good
validator.validOrFail("emailAddress", email);
validator.validOrFail("currentPassword", password);

// Avoid
validator.validOrFail("field1", email);
validator.validOrFail("input", password);
Always set up onInvalidEvaluation before calling validation methods:
// Good
validator.onInvalidEvaluation(msg -> handleError(msg));
validator.isValid(input);

// Bad - event not triggered
validator.isValid(input);
validator.onInvalidEvaluation(msg -> handleError(msg));

Next Steps

Form Validation

Apply error handling to complete form validation

Localization

Customize error messages and support multiple languages

Build docs developers (and LLMs) love