com.erudika.para.validation.ValidationUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.erudika.para.validation.ValidationUtils.java

Source

/*
 * Copyright 2013-2015 Erudika. http://erudika.com
 *
 * 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
 *
 *      http://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.
 *
 * For issues and patches go to: https://github.com/erudika
 */
package com.erudika.para.validation;

import com.erudika.para.annotations.Email;
import com.erudika.para.core.App;
import com.erudika.para.core.ParaObject;
import com.erudika.para.core.ParaObjectUtils;
import com.erudika.para.core.Sysprop;
import com.erudika.para.utils.Utils;
import static com.erudika.para.validation.Constraint.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.validator.constraints.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Helper methods for validating objects and generating JSON schemas.
 *
 * @author Alex Bogdanovski [alex@erudika.com]
 */
public final class ValidationUtils {

    private static final Logger logger = LoggerFactory.getLogger(ValidationUtils.class);
    private static final Map<String, Map<String, Map<String, Map<String, ?>>>> coreValidationConstraints = new HashMap<String, Map<String, Map<String, Map<String, ?>>>>();
    private static Validator validator;

    private ValidationUtils() {
    }

    /**
     * A Hibernate Validator.
     * @return a validator object
     */
    public static Validator getValidator() {
        if (validator == null) {
            validator = Validation.buildDefaultValidatorFactory().getValidator();
        }
        return validator;
    }

    /**
     * Validates objects using Hibernate Validator. Used for basic validation.
     * @param obj an object to be validated
     * @return true if the object is valid (all fields are populated properly)
     */
    public static boolean isValidObject(ParaObject obj) {
        return validateObject(obj).length == 0;
    }

    /**
     * Validates objects using Hibernate Validator. Used for full object validation.
     * @param app the current app
     * @param obj an object to be validated
     * @return true if the object is valid (all fields are populated properly)
     */
    public static boolean isValidObject(App app, ParaObject obj) {
        return validateObject(app, obj).length == 0;
    }

    /**
     * Validates objects using Hibernate Validator.
     * @param content an object to be validated
     * @return a list of error messages or empty if object is valid
     */
    public static String[] validateObject(ParaObject content) {
        if (content == null) {
            return new String[] { "Object cannot be null." };
        }
        LinkedList<String> list = new LinkedList<String>();
        try {
            for (ConstraintViolation<ParaObject> constraintViolation : getValidator().validate(content)) {
                String prop = "'".concat(constraintViolation.getPropertyPath().toString()).concat("'");
                list.add(prop.concat(" ").concat(constraintViolation.getMessage()));
            }
        } catch (Exception e) {
            logger.error(null, e);
        }
        return list.toArray(new String[] {});
    }

    /**
     * Validates objects.
     * @param content an object to be validated
     * @param app the current app
     * @return a list of error messages or empty if object is valid
     */
    public static String[] validateObject(App app, ParaObject content) {
        if (content == null || app == null) {
            return new String[] { "Object cannot be null." };
        }
        try {
            String type = content.getType();
            boolean isCustomType = (content instanceof Sysprop) && !type.equals(Utils.type(Sysprop.class));
            // Validate custom types and user-defined properties
            if (!app.getValidationConstraints().isEmpty() && isCustomType) {
                Map<String, Map<String, Map<String, ?>>> fieldsMap = app.getValidationConstraints().get(type);
                if (fieldsMap != null && !fieldsMap.isEmpty()) {
                    LinkedList<String> errors = new LinkedList<String>();
                    for (Map.Entry<String, Map<String, Map<String, ?>>> e : fieldsMap.entrySet()) {
                        String field = e.getKey();
                        Object actualValue = ((Sysprop) content).getProperty(field);
                        // overriding core property validation rules is allowed
                        if (actualValue == null && PropertyUtils.isReadable(content, field)) {
                            actualValue = PropertyUtils.getProperty(content, field);
                        }
                        Map<String, Map<String, ?>> consMap = e.getValue();
                        for (Map.Entry<String, Map<String, ?>> constraint : consMap.entrySet()) {
                            String consName = constraint.getKey();
                            Map<String, ?> vals = constraint.getValue();
                            if (vals == null) {
                                vals = Collections.emptyMap();
                            }

                            Object val = vals.get("value");
                            Object min = vals.get("min");
                            Object max = vals.get("max");
                            Object in = vals.get("integer");
                            Object fr = vals.get("fraction");

                            if ("required".equals(consName) && !required().isValid(actualValue)) {
                                errors.add(Utils.formatMessage("{0} is required.", field));
                            } else if (matches(Min.class, consName) && !min(val).isValid(actualValue)) {
                                errors.add(
                                        Utils.formatMessage("{0} must be a number larger than {1}.", field, val));
                            } else if (matches(Max.class, consName) && !max(val).isValid(actualValue)) {
                                errors.add(
                                        Utils.formatMessage("{0} must be a number smaller than {1}.", field, val));
                            } else if (matches(Size.class, consName) && !size(min, max).isValid(actualValue)) {
                                errors.add(
                                        Utils.formatMessage("{0} must be between {1} and {2}.", field, min, max));
                            } else if (matches(Email.class, consName) && !email().isValid(actualValue)) {
                                errors.add(Utils.formatMessage("{0} is not a valid email.", field));
                            } else if (matches(Digits.class, consName) && !digits(in, fr).isValid(actualValue)) {
                                errors.add(
                                        Utils.formatMessage("{0} is not a valid number or within range.", field));
                            } else if (matches(Pattern.class, consName) && !pattern(val).isValid(actualValue)) {
                                errors.add(Utils.formatMessage("{0} doesn't match the pattern {1}.", field, val));
                            } else if (matches(AssertFalse.class, consName) && !falsy().isValid(actualValue)) {
                                errors.add(Utils.formatMessage("{0} must be false.", field));
                            } else if (matches(AssertTrue.class, consName) && !truthy().isValid(actualValue)) {
                                errors.add(Utils.formatMessage("{0} must be true.", field));
                            } else if (matches(Future.class, consName) && !future().isValid(actualValue)) {
                                errors.add(Utils.formatMessage("{0} must be in the future.", field));
                            } else if (matches(Past.class, consName) && !past().isValid(actualValue)) {
                                errors.add(Utils.formatMessage("{0} must be in the past.", field));
                            } else if (matches(URL.class, consName) && !url().isValid(actualValue)) {
                                errors.add(Utils.formatMessage("{0} is not a valid URL.", field));
                            }
                        }
                    }
                    if (!errors.isEmpty()) {
                        return errors.toArray(new String[0]);
                    }
                }
            }
        } catch (Exception ex) {
            logger.error(null, ex);
        }
        return validateObject(content);
    }

    /**
     * Returns all validation constraints that are defined by Java annotation in the core classes.
     *
     * @return a map of all core types to all core annotated constraints. See JSR-303.
     */
    public static Map<String, Map<String, Map<String, Map<String, ?>>>> getCoreValidationConstraints() {
        if (coreValidationConstraints.isEmpty()) {
            for (Map.Entry<String, Class<? extends ParaObject>> e : ParaObjectUtils.getCoreClassesMap()
                    .entrySet()) {
                String type = e.getKey();
                List<Field> fieldlist = Utils.getAllDeclaredFields(e.getValue());
                for (Field field : fieldlist) {
                    Annotation[] annos = field.getAnnotations();
                    if (annos.length > 1) {
                        Map<String, Map<String, ?>> constrMap = new HashMap<String, Map<String, ?>>();
                        for (Annotation anno : annos) {
                            if (isValidConstraintType(anno.annotationType())) {
                                Constraint c = fromAnnotation(anno);
                                if (c != null) {
                                    constrMap.put(c.getName(), c.getPayload());
                                }
                            }
                        }
                        if (!constrMap.isEmpty()) {
                            if (!coreValidationConstraints.containsKey(type)) {
                                coreValidationConstraints.put(type,
                                        new HashMap<String, Map<String, Map<String, ?>>>());
                            }
                            coreValidationConstraints.get(type).put(field.getName(), constrMap);
                        }
                    }
                }
            }
        }
        return Collections.unmodifiableMap(coreValidationConstraints);
    }
}