com.sap.cloud.yaas.servicesdk.auditbase.validation.DefaultValidator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of service-sdk-audit-base Show documentation
Show all versions of service-sdk-audit-base Show documentation
Contains base utility classes for audit event logging
The newest version!
/*
* © 2017 SAP SE or an SAP affiliate company.
* All rights reserved.
* Please see http://www.sap.com/corporate-en/legal/copyright/index.epx for additional trademark information and
* notices.
*/
package com.sap.cloud.yaas.servicesdk.auditbase.validation;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Function;
/**
* Default validator that evaluates the annotations on the fields and types ({@link Validated}, {@link Mandatory} and
* {@link CustomValidated}). It checks:
*
* - those fields
* - nested Collections
*
* recursively. Checking of nested Objects is not implemented.
*
* @param the type of object that will be validated
*/
public class DefaultValidator implements Validator
{
// If true, will treat every class as if it was annotated with @Validated. Needed only for test, because of
// https://bugs.openjdk.java.net/browse/JDK-8059531.
private boolean ignoreValidated;
/**
* Validates the object according to the {@link Validated}, {@link Mandatory} and {@link CustomValidated}
* annotations.
* Only classes marked with {@link Validated} will be validated.
*
* @param object the object to validate
* @return the collection of possible validation violations or empty collection
*/
public Collection validate(final Object object)
{
return validateInternal(object, object.getClass(), null);
}
/**
* Validates the object in the context of the given class (the fields from superclass are not visible in subclass),
* passing the information about the propertyPath to keep track of the property path within the object.
*
* @param object the object to validate
* @param clazzContext the class or superclass of the object which to investigate
* @param propertyPath the String representation of the property path
* @return the collection of possible validation violations or empty collection
*/
protected Collection validateInternal(
final Object object,
final Class clazzContext,
final String propertyPath)
{
final Collection result = new ArrayList<>();
checkSuperClass(result, clazzContext, object);
if (ignoreValidated || clazzContext.isAnnotationPresent(Validated.class))
{
for (final Field field : clazzContext.getDeclaredFields())
{
final boolean accessible = field.isAccessible();
field.setAccessible(true);
try
{
checkMandatoryFields(result, field, object, propertyPath);
checkRegexValidatedFields(result, field, object, propertyPath);
checkCustomValidations(result, field, object);
checkNestedCollections(result, field, object, propertyPath);
}
catch (final InstantiationException | IllegalAccessException e)
{
throw new IllegalStateException("Unexpected error: " + e.getMessage(), e);
}
finally
{
field.setAccessible(accessible);
}
}
}
return result;
}
/**
* Resolves the path of current field. Either it had some parents and they should be prepended with a ".", or maybe
* it is even a part of a collection, then the index should be printed before.
*
* @param fieldName the field name
* @param propertyPath the field property path up to now, or null
* @param indexInOwner if a member of collection, the index in that collection; if not then null
* @return the field's path
*/
private String resolvePath(final String fieldName, final String propertyPath, final Integer indexInOwner)
{
if (indexInOwner == null && propertyPath != null)
{
return String.format("%s.%s", propertyPath, fieldName);
}
else if (indexInOwner != null)
{
return String.format("%s[%s]", fieldName, indexInOwner);
}
else
{
return fieldName;
}
}
/**
* Validated fields of the superclass, unless it is the Object class.
*
* @param validationViolations the collection to fill in
* @param clazzContext the class or superclass of the object
* @param object the object to validate
*/
private void checkSuperClass(
final Collection validationViolations,
final Class clazzContext,
final Object object)
{
if (!Object.class.equals(clazzContext))
{
validationViolations.addAll(validateInternal(object, clazzContext.getSuperclass(), null));
}
}
/**
* Checks a field if it is a collection (aka ParameterizedType) and if yes, validates all its members.
*
* @param validationViolations the collection to fill in
* @param field the field to check if it contains the collection and if yes validate its members
* @param object the object who owns the field
* @param propertyPath the field property path up to now, or null
* @throws IllegalAccessException if a field is not accessible by reflection
*/
private void checkNestedCollections(
final Collection validationViolations,
final Field field,
final Object object,
final String propertyPath)
throws IllegalAccessException
{
if (field.getGenericType() instanceof ParameterizedType)
{
final ParameterizedType collectionType = (ParameterizedType) field.getGenericType();
final Class> clazz = (Class>) collectionType.getActualTypeArguments()[0];
if (field.get(object) instanceof Collection)
{
final Collection collection = (Collection) field.get(object);
int idx = 0;
for (final Object ob : collection)
{
validationViolations.addAll(validateInternal(ob, clazz, resolvePath(field.getName(), propertyPath, idx)));
idx++;
}
}
}
}
/**
* Checks for {@link CustomValidated} annotation on the field and evaluates it.
*
* @param validationViolations the collection to fill in
* @param field the field to check
* @param object the object that contains the field
* @throws InstantiationException if the custom validator cannot be instantiated
* @throws IllegalAccessException if a field is not accessible by reflection
*/
private void checkCustomValidations(
final Collection validationViolations,
final Field field,
final Object object)
throws InstantiationException, IllegalAccessException
{
if (field.isAnnotationPresent(CustomValidated.class))
{
final CustomValidated annotation = field.getAnnotation(CustomValidated.class);
final Class extends Function
© 2015 - 2024 Weber Informatics LLC | Privacy Policy