Skip to content

dev-mlo/validation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Validation

Maven Central

The validation framework supports you to organize the validation process of your data. You can add, group, chain and execute multiple validation statements and aggregate the result

Getting started

For Maven

<dependency>
    <groupId>de.mlo-dev</groupId>
    <artifactId>validation</artifactId>
    <version>0.5.0</version>
</dependency>

For Gradle

implementation group: 'de.mlo-dev', name: 'validation', version: '0.5.0'

If you are using Java modules:

requires de.mlo.dev.validation

Usage

Basic

It is recommended that you separate validation statements into functions. This makes the code clearer and makes it more testable. To execute the statements use the Validator. The Validator will execute the added statement and aggregates the result. Check the returned ValidationResult if process was successful or not.

public class PersonValidator {
    public ValidationResult validate() {
        return new Validator()
                .add(this::validateName)
                .add(this::validateAge)
                .validate();
    }

    ValidationInfo validateName() {
        String name = unbindName();
        if (name.isBlank()) {
            return ValidationInfo.invalid("Name is empty");
        }
        return ValidationInfo.valid();
    }

    ValidationInfo validateAge() {
        int age = unbindAge();
        if (age <= 0) {
            return ValdationInfo.invalid("Age must be positive value");
        }
        return ValidationInfo.valid();
    }
}

Value dependencies

Validate data that depends on each other using setValidateAndStopOnFirstFail.

public class DependingValueValidation {
    public ValidationResult validateContact(Person person) {
        return new Validator()
                .add(() -> validateNull(person))
                .add(() -> validateName(person.getName())) // Won't be executed if 'person' was null
                .add(() -> validateAddress(person.getAddress())) // Won't be executed if 'person' was null or 'name' was empty
                .setValidateAndStopOnFirstFail()
                .validate();
    }
}

Groups

Group multiple validation processes to one and define separate validation strategies.

In the following example all groups will be executed but if one statement within a group fails, no more statements of the group will be executed.

public class Groups {

    public ValidationResult validateContact() {
        return new Validator()
                .groupBuilder()
                .add(this::validateName)
                .add(this::validateAge)
                .setValidateAndStopOnFirstFail()
                .build()
                .groupBuilder()
                .add(this::validateStreet)
                .add(this::validateZipAndTown)
                .setValidateAndStopOnFirstFail()
                .build()
                .validate();
    }
}

You can also define separate


Customization

The validator comes with two modes: validate all or stop on first fail. You can add your own execution logic by setting your own ValidationRunner using Validator.setValidationRunner(...).

class CustomRunnerValidation {
    public ValidationResult validate() {
        return new Validator()
                .setValidationRunner(this::execute)
                .validate();
    }

    /** This runner fails if there is no instruction to execute */
    ValidationResult execute(List<ValidationInstruction> instructionList) {
        if (instructionList.isEmpty()) {
            return ValidationResult.invalid("No instructions found");
        }
        return ValidationRunners.VALIDATE_ALL.validate(instructionList);
    }
}

Type specific validator

Use the ValueValidator to validate a specific type. This allows you to build reusable Validators.

public class PersonValidator {
    public static ValueValidator<Person> PERSON_VALIDATOR = createValidator();

    static ValueValidationResult<Person> validate(Person person) {
        return PERSON_VALIDATOR.validate(person);
    }

    static ValueValidator<Person> createValidator() {
        return ValueValidator.create(Person.class)
                .add(PersonValidator::validateName)
                .add(PersonValidator::validateAge);
    }

    static ValidationInfo validateName(Person person) {
        if (person.getName().isBlank()) {
            return ValidationInfo.invalid("Name is empty");
        }
        return ValidationInfo.valid();
    }

    static ValidationInfo validateAge(Person person) {
        if (person.getAge() <= 0) {
            return ValdationInfo.invalid("Age must be positive value");
        }
        return ValidationInfo.valid(person);
    }
}

Switch

Use the switchValue function to alter the type of the validator which allows you to validate more complex beans.

public class Switches {
    public ValueValidationResult<Person> validateContact() {
        return new ValueValidator<Person>()
                .add(this::validateName)
                .add(this::validateAge)
                .switchValue(person::getAddress) // <-- Switch to type 'Address'
                .add(this::validateStreet)
                .add(this::validateZip)
                .add(this::validateTown)
                .<Person>switchBack()
                .validate(person);
    }
}

Conditional validation

Use conditions to ex- or include validation branches. Start a new condition block by using the conditionBuilder(<condition>) function and finish it with build().

The example below will execute different validation branches depending on the country of a given address object. If the address is a german address, we will make sure that a valid 'Bundesland' was set. If the address is as US one, we make sure the address contains a valid 'State'.

public class Conditions {
    public ValueValidationResult<Address> validateAddress() {
        return new ValueValidator<Address>()
                .add(this::validateStreet)
                .add(this::validateTown)
                .conditionBuilder(address::isGermanAddress) // <-- Validate German specific values
                .add(this::validateBundesland)
                .build()
                .conditionBuilder(address::isUsAddress) // <-- Validate US specific values
                .add(this::validateState)
                .build()
                .validate(address);
    }
}

About

Mini framework for simple validation

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages