Skip to content

Thor will use his powerful Mjölnir to validate instances of objects protecting the developer from bugs.

License

Notifications You must be signed in to change notification settings

paulmarcelinbejan/ValidaThor

Repository files navigation

ValidaThor

Thor will use his powerful Mjölnir to validate instances of objects protecting the developer from bugs.

Maven Central GitHub license

ValidaThor is composed of 4 modules:

  • validathor-base maven central
    Contains base classes and configuration
  • validathor-bfs maven central
    implements validathor-base using Breadth-first search algorithm
    Breadth-first search
  • validathor-dfs maven central
    implements validathor-base using Depth-first search algorithm
    Depth-first search
  • validathor-impl maven central
    Contains some Validathors ready to use for:
    • Object
    • String
    • List
    • Map

How to...

How to create a custom validation for specific type?

There are three types of Validathor:

  1. Validathor for simple type, will extends io.github.paulmarcelinbejan.toolbox.validathor.Validathor

Example:

import io.github.paulmarcelinbejan.toolbox.validathor.Validathor;

public class StringValidathor extends Validathor<String> {

	// The constructor must call the super constructor passing the class of the type we want to validate
	public StringValidathor() {
		super(String.class);
	}

	// The isValid method must be implemented choosing how we will want to validate that type of object.
	@Override
	public boolean isValid(String toValidate) {
		return toValidate != null && !toValidate.isBlank();
	}

}
  1. Validathor for parameterized type, will extends io.github.paulmarcelinbejan.toolbox.validathor.ValidathorParameterizedType

ValidathorParameterizedType extends Validathor

Example:

import java.util.Collection;
import java.util.Map;
import java.util.function.Function;

@SuppressWarnings("rawtypes")
public class MapValidathor extends ValidathorParameterizedType<Map> {

	// The constructor must call the super constructor passing the class of the type we want to validate 
	// and a boolean indicating if elements must be validated
	public MapValidathor(boolean toValidateParameterizedTypeElements) {
		super(Map.class, toValidateParameterizedTypeElements);
	}

	// The isValid method must be implemented choosing how we will want to validate that type of object.
	@Override
	public boolean isValid(Map toValidate) {
		return toValidate != null && !toValidate.isEmpty();
	}

	// parameterizedTypeElementsToValidate return a Function that will return a Collection of elements to be validated
	// for Map we can decide for example if we want to validate only keys, only values, or both.
	@Override
	public Function<Map, Collection<?>> parameterizedTypeElementsToValidate() {
		return (map) -> map.values();
	}

}
  1. A default Validathor for all the objects that doesn't have a specific Validathor, will extends io.github.paulmarcelinbejan.toolbox.validathor.AbstractObjectValidathor

Example:

import io.github.paulmarcelinbejan.toolbox.validathor.AbstractObjectValidathor;

/**
 * A concrete implementation of ObjectValidathor
 * object to validate is valid if not null
 * it will lookup into inner fields
 */
public class ObjectValidathor extends AbstractObjectValidathor {

	public ObjectValidathor() {
		super(true);
	}
	
	public ObjectValidathorImpl(boolean validateInnerFields) {
		super(validateInnerFields);
	}

	@Override
	public boolean isValid(Object toValidate) {
		return toValidate != null;
	}

}

How to customize the validation flow ?

The class ValidathorRegistry is responsible for holding all the configuration of the validation flow. The easyest way to create a ValidathorRegistry is using the builder pattern with Fluid API.

Let's see an example:

ValidathorRegistry validathorRegistry = ValidathorRegistry.builder()
	.registerObjectValidathor(new ObjectValidathor())
	.registerValidathor(new StringValidathor()) // register single validathor, it can be called multiple times
	.registerValidathors(List.of(new StringValidathor(), new PersonValidathor())) // register multiple validathors 
	.registerValidathorParameterizedType(new ListValidathor()) // register single validathor parameterized type, it can be called multiple times
	.registerValidathorsParameterizedType(List.of(new ListValidathor(), new MapValidathor())) // register multiple validathors of parameterized type
	.useCompatibleValidathorIfSpecificNotPresent(false) // if true, an object can be validated by a compatible Validathor if the specific is not registered.
	.registerSkipBeforeValidationProcessor(skipBeforeValidationProcessor)
	.registerSkipAfterValidationProcessor(skipAfterValidationProcessor)
	.build();

Let me provide an example of why 'useCompatibleValidathorIfSpecificNotPresent' is useful. Suppose you have a field of type 'ArrayList', and you haven't registered a 'ValidathorParameterizedType' for that type. However, you have registered a 'ValidathorParameterizedType' for a compatible class/interface, like 'Collection'. If 'useCompatibleValidathorIfSpecificNotPresent' is set to true, the 'CollectionValidathor' can be used to validate the 'ArrayList'.

It's also possible to configure two 'SkipProcessor' instances:

  1. SkipBeforeValidationProcessor.
  2. SkipAfterValidationProcessor.

Both have two sets as fields: a set of classes and a set of strings (package names). Before validating an object, if the type of this object is present in the set of classes of 'SkipBeforeValidationProcessor', or the class is in a package that starts with one of the values in the set of strings, then it will be skipped, and no validation will be executed on this object. However, if the type of object is present in the set of classes of 'SkipAfterValidationProcessor', or the class is in a package that starts with one of the values in the set of strings, then it will be validated, but it will not look up internally to validate the fields.

This is the reason why 'SkipAfterValidationProcessor' contains, by default, the "java" package.

How to start the validation ?

You need to choose between BFS and DFS algorithms, then create an instance accordingly. The constructor parameters are the validathorRegistry and a boolean indicating if you want to collect all the errors, or if you want to trigger the exception as soon as one occurs.

BFS:

ValidathorBFS validathorBFS = new ValidathorBFS(validathorRegistry, false);
validathorBFS.validate(anObjectToValidate)

DFS

ValidathorDFS validathorDFS = new ValidathorDFS(validathorRegistry, false);
validathorDFS.validate(anObjectToValidate)

About

Thor will use his powerful Mjölnir to validate instances of objects protecting the developer from bugs.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages