Package org.crank.validation.readers

Source Code of org.crank.validation.readers.PropertiesFileValidatorMetaDataReader

package org.crank.validation.readers;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.crank.annotations.design.NeedsRefactoring;
import org.crank.validation.ValidatorMetaData;
import org.crank.validation.ValidatorMetaDataReader;

/**
*
* <p>
* <b>PropertiesFileValidatorMetaDataReader</b> reads validation meta-data from
* a properties files.
* </p>
*
* <p>
* This class reads a properties file as follows: <br />
* <br />
* If the class name is com.foo.Foo, then the resource name is
* com.foo.Foo.properties.
* </p>
*
* <p>
* The properties file will contain validation meta-data as follows: <br />
* <br />
*
* <pre>
*  firstName=required; length min=10, max=100
*  age=required; range min=10, max=100
* </pre>
*
* The <b>firstName</b> corresponds to a property of the Foo class. The
* <b>firstName</b> is associated with the validation rules <b>required</b>
* and <b>length</b>. The <b>length</b> validation rule states the minimum and
* maximum allowed number of characters with the <b>min</b> and <b>max</b>
* parameters.
* </p>
*
* <p>
* Two different frameworks read this meta-data (curently). Our validation
* framework, which is mostly geared towards server-side validation and our
* client-side JavaScript framework, which is geared towards producing
* client-side JavaScript.
* </p>
*
* @author Rick Hightower
*
*/
public class PropertiesFileValidatorMetaDataReader implements ValidatorMetaDataReader {

    /** Holds a cache of Properties file contents to reduce IO. */
    private static Map<String, Properties> metaDataPropsCache =
        Collections.synchronizedMap(new HashMap<String, Properties>());

    /** Holds a cache of meta-data to reduce parsing with regex. */
    private static Map<String, List<ValidatorMetaData>> metaDataCache =
        new HashMap<String, List<ValidatorMetaData>>();

    /**
     * Read the meta-data from a properties file.
     */
    public List<ValidatorMetaData> readMetaData(Class<?> clazz, String propertyName) {

        /* Load the properties file. */
        Properties props = loadMetaDataPropsFile(clazz);
        /* Get the raw validation data for the given property. */
        String unparsedString = props.getProperty(propertyName);
        /* Parse the string into a list of ValidationMetaData. */
        return extractMetaDataFromString(clazz, propertyName, unparsedString);
    }

    /**
     * This method loads the MetaData properties file. The Properties are cached
     * in <b>metaDataPropsCache</b> and will not be reloaded twice.
     *
     * @param clazzWhoseValidationMetaDataWeAreReading
     *            The class whose property meta-data we are retrieving.
     * @return
     */
    private Properties loadMetaDataPropsFile(
            Class<?> clazzWhoseValidationMetaDataWeAreReading) {
        String className = clazzWhoseValidationMetaDataWeAreReading.getName();

        /*
         * If the class is proxied there will be a $CGLIB on the end of it.
         * Remove this.
         */
        className = className.split("[$]")[0];

        /*
         * The resourceName is as follows: If the class name is com.foo.Foo Then
         * the resource name is com.foo.Foo.properties.
         */
        String resourceName =
            (("/" + className).replace('.', '/')) + ".properties";

        /* Check to see if this properties file was already loaded. */
        Properties validationMetaDataProps = metaDataPropsCache.get(resourceName);

        /* If the properties file was not loaded, then load it. */
        if (validationMetaDataProps == null) {
            validationMetaDataProps = new Properties();
            try {
                /*
                 * Try to load the properties file that contains the validation
                 * meta-data.
                 */
                validationMetaDataProps.load(this.getClass().getResourceAsStream(
                        resourceName));
            } catch (IOException ioex) {
                /*
                 * This can happen and is not an error. It just means there is
                 * no validation for this guy. Maybe we should log this.
                 * Note self... add logging capability to this project!.
                 */
            }
            /*
             * Put the properties file into the cache so we don't have to read
             * it again.
             */
            metaDataPropsCache.put(resourceName, validationMetaDataProps);
        }
        assert validationMetaDataProps != null :
            "Properties for validation meta-data were loaded";
        return validationMetaDataProps;
    }

    /**
     * This method extracts meta-data from a string.
     * @param clazz
     * @param propertyName
     * @param unparsedString
     * @return
     */
    @NeedsRefactoring("Parsing a string into validation data seems like" +
            "something that ValidatorMetaData should handle internally. " +
            "Then we could reuse this ability with other meta-data readers, " +
            " i.e., one that reads the metadata from a Spring config file. ")
    private List<ValidatorMetaData> extractMetaDataFromString(Class<?> clazz,
            String propertyName, String unparsedString) {
        String propertyKey = clazz.getName() + "." + propertyName;

        /* See if we parsed this bad boy already. */
        List<ValidatorMetaData> validatorMetaDataList =
            metaDataCache.get(propertyKey);

       
        /* If we did not find the list, then we have some work to do.*/
        if (validatorMetaDataList == null) {
            /* Initialize a new list. */
            validatorMetaDataList = new ArrayList<ValidatorMetaData>();
           
            /* Remember we have a string that looks like this:
             * required; length min=10, max=100
             * So we need to split on semi-colon.
             */
            String[] validatorsParts = unparsedString.split("[;]");
           
            /* Now we have the two strings as follows:
             *  ["required",
             *  ["length min=10, max=100"]
             *
             */
            for (String validatorString : validatorsParts) {
                ValidatorMetaData validatorMetaData = new ValidatorMetaData();
                validatorMetaDataList.add(validatorMetaData);
               
                /* Now we split one of the string (we will use length)
                 * as follows:
                 * parts=["length", "min=10", "max=100"]
                 * */
                String[] parts = validatorString.trim().split("[ ,]");
               
                /* The first part is the name of the validation,
                 * e.g., "length".
                 *
                 */
                validatorMetaData.setName(parts[0]);

                /* If the string has more than one part, then there must
                 * be arguments as in: ["min=10", "max=100"]
                 *
                 * Parse the arguments and add them to the list as well.
                 */
                if (parts.length > 1) {

                    /* This line converts:
                     *
                     * ["length", "min=10", "max=100"]
                     *
                     * into:
                     *
                     * ["min=10", "max=100"]
                     */
                    List<String> values =
                        Arrays.asList(parts).subList(1, parts.length);
                   
                    /* For each value convert it into name value pairs. */
                    for (String value : values) {
                       
                        if (value.indexOf("=") != -1) {
                            /* Split "min=10" into ["min", "10"] */
                            String[] valueParts = value.split("[=]");
                            /* Stick this value into validatorMetaData's
                             * list of properties.
                             */
                            validatorMetaData.getProperties().put(
                                    valueParts[0], valueParts[1]);
                        }
                    }
                }
            }
            metaDataCache.put(propertyKey, validatorMetaDataList);
        }
        return validatorMetaDataList;
    }

}
TOP

Related Classes of org.crank.validation.readers.PropertiesFileValidatorMetaDataReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.