All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.boon.validation.RecursiveDescentPropertyValidator Maven / Gradle / Ivy

/*
 * Copyright 2013-2014 Richard M. Hightower
 * 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.
 *
 * __________                              _____          __   .__
 * \______   \ ____   ____   ____   /\    /     \ _____  |  | _|__| ____    ____
 *  |    |  _//  _ \ /  _ \ /    \  \/   /  \ /  \\__  \ |  |/ /  |/    \  / ___\
 *  |    |   (  <_> |  <_> )   |  \ /\  /    Y    \/ __ \|    <|  |   |  \/ /_/  >
 *  |______  /\____/ \____/|___|  / \/  \____|__  (____  /__|_ \__|___|  /\___  /
 *         \/                   \/              \/     \/     \/       \//_____/
 *      ____.                     ___________   _____    ______________.___.
 *     |    |____ ___  _______    \_   _____/  /  _  \  /   _____/\__  |   |
 *     |    \__  \\  \/ /\__  \    |    __)_  /  /_\  \ \_____  \  /   |   |
 * /\__|    |/ __ \\   /  / __ \_  |        \/    |    \/        \ \____   |
 * \________(____  /\_/  (____  / /_______  /\____|__  /_______  / / ______|
 *               \/           \/          \/         \/        \/  \/
 */

package org.boon.validation;


import org.boon.Exceptions;
import org.boon.Maps;
import org.boon.core.Typ;
import org.boon.core.reflection.BeanUtils;
import org.boon.core.reflection.Reflection;
import org.boon.validation.readers.AnnotationValidatorMetaDataReader;
import org.boon.validation.validators.CompositeValidator;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.*;


public class RecursiveDescentPropertyValidator {
    protected ValidatorMetaDataReader validatorMetaDataReader = new AnnotationValidatorMetaDataReader();

    public class MessageHolder {
        public final String propertyPath;
        public final ValidatorMessageHolder holder;

        MessageHolder( String propertyPath, ValidatorMessageHolder holder ) {
            this.propertyPath = propertyPath;
            this.holder = holder;
        }
    }


    /**
     * Create the validator by looking it up in the ObjectRegistry and then
     * populating it with values from the meta-data list.
     *
     * @param validationMetaDataList Holds metadataInformation about validation.
     * @return composite validator with all of the validators for this property present.
     */
    protected CompositeValidator createValidator(
            List validationMetaDataList ) {

        /*
         * A field (property) can be associated with many validators so we use a
         * CompositeValidator to hold all of the validators associated with this
         * validator.
         */
        CompositeValidator compositeValidator = new CompositeValidator(); // hold
        // all
        // of
        // the
        // validators
        // associated
        // with
        // the
        // field.

        /*
         * Lookup the list of validators for the current field and initialize
         * them with validation meta-data properties.
         */
        List validatorsList =
                lookupTheListOfValidatorsAndInitializeThemWithMetaDataProperties( validationMetaDataList );

        compositeValidator.setValidatorList( validatorsList );

        return compositeValidator;
    }


    private List getFieldsToValidate( Object object ) {
        List properties;
        BeanInfo beanInfo;
        try {
            beanInfo = Introspector.getBeanInfo( object.getClass() );
        } catch ( IntrospectionException e ) {

            throw new RuntimeException( e );
        }
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        properties = new ArrayList<>( propertyDescriptors.length );
        for ( PropertyDescriptor pd : propertyDescriptors ) {
            if ( !pd.getName().equals( "class" ) ) {
                properties.add( pd );
            }
        }
        return properties;
    }

    protected List readMetaData( Class clazz,
                                                    String propertyName ) {
        return validatorMetaDataReader.readMetaData( clazz,
                propertyName );
    }

    private void validateProperty( final Object object, final Object objectProperty, final String property,
                                   List vMessageHolders ) {

        List metaDataList = readMetaData( object.getClass(),
                property );
        CompositeValidator cv = createValidator( metaDataList );
        ValidatorMessageHolder holder = cv.validate( objectProperty, property );
        vMessageHolders.add( new MessageHolder( ValidationContext.getBindingPath(), holder ) );
    }

    protected boolean shouldFieldBeValidated() {
        return true;
    }


    public List validateObject( final Object object ) {


        List list = Collections.EMPTY_LIST;
        try {
            ValidationContext.create();
            list = validateObjectWithMessages( object, null );
        } finally {
            ValidationContext.destroy();
        }
        return list;
    }

    public List validateObject( final Object object, Map registry ) {


        List list = Collections.EMPTY_LIST;
        try {
            ValidationContext.create();
            ValidationContext.get().setObjectRegistry( registry );
            list = validateObjectWithMessages( object, null );
        } finally {
            ValidationContext.destroy();
        }
        return list;
    }


    public List validateObjectWithMessages( final Object object, List validationMessages ) {
        List fieldsToValidate = getFieldsToValidate( object );
        Map objectPropertiesAsMap = Maps.toMap( object );
        if ( validationMessages == null ) {
            validationMessages = new ArrayList<>();
        }

        for ( PropertyDescriptor field : fieldsToValidate ) {

            /* Keep track of the field name and parentObject so the field validators can access them. */
            ValidationContext.get().pushProperty( field.getName() );
            ValidationContext.get().setParentObject( object );
            if ( shouldFieldBeValidated() ) {
                Object propertyObject = objectPropertiesAsMap.get( field.getName() );
                validateProperty( object, propertyObject, field.getName(), validationMessages );

                /* Don't validate if it is not a basic type. */
                if ( propertyObject != null && !Typ.isBasicType( propertyObject ) ) {
                    validateObjectWithMessages( propertyObject, validationMessages );
                }
            }
            ValidationContext.get().pop();
        }

        return validationMessages;

    }


    /**
     * Lookup the list of validators for the current field and initialize them
     * with validation meta-data properties.
     *
     * @param validationMetaDataList list of validation meta-data
     * @return list of field validators.
     */
    private List
    lookupTheListOfValidatorsAndInitializeThemWithMetaDataProperties(
            List validationMetaDataList ) {

        List validatorsList = new ArrayList<>();

        /*
         * Look up the crank validators and then apply the properties from the
         * validationMetaData to them.
         */
        for ( ValidatorMetaData validationMetaData : validationMetaDataList ) {
            /* Look up the FieldValidator. */
            FieldValidator validator = lookupValidatorInRegistry(
                    validationMetaData
                            .getName() );
            /*
             * Apply the properties from the validationMetaData to the
             * validator.
             */
            applyValidationMetaDataPropertiesToValidator( validationMetaData,
                    validator );
            validatorsList.add( validator );
        }
        return validatorsList;
    }

    /**
     * This method looks up the validator in the registry.
     *
     * @param validationMetaDataName The name of the validator that we are looking up.
     * @return field validator
     */
    private FieldValidator lookupValidatorInRegistry(
            String validationMetaDataName ) {


        Map applicationContext = ValidationContext.get().getObjectRegistry();

        Exceptions.requireNonNull( applicationContext );

        return ( FieldValidator ) applicationContext
                .get( "/org/boon/validator/" + validationMetaDataName );
    }

    /**
     * This method applies the properties from the validationMetaData to the
     * validator uses Spring's BeanWrapperImpl.
     *
     * @param metaData  validation meta data
     * @param validator field validator
     */
    private void applyValidationMetaDataPropertiesToValidator(
            ValidatorMetaData metaData, FieldValidator validator ) {
        Map properties = metaData.getProperties();
        ifPropertyBlankRemove( properties, "detailMessage" );
        ifPropertyBlankRemove( properties, "summaryMessage" );

        BeanUtils.copyProperties( validator,
                properties );
    }

    /**
     * Removes a property if it is null or an empty string.
     * This allows the property to have a null or emtpy string in the
     * meta-data but we don't copy it to the validator if the property
     * is not set.
     *
     * @param properties properties
     * @param property   property
     */
    private void ifPropertyBlankRemove( Map properties, String property ) {

        Object object = properties.get( property );
        if ( object == null ) {
            properties.remove( property );
        } else if ( object instanceof String ) {
            String string = ( String ) object;
            if ( "".equals( string.trim() ) ) {
                properties.remove( property );
            }
        }
    }

    public void setValidatorMetaDataReader(
            ValidatorMetaDataReader validatorMetaDataReader ) {
        this.validatorMetaDataReader = validatorMetaDataReader;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy