org.easyrec.plugin.configuration.ConfigurationHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.easyrec.plugin.configuration.ConfigurationHelper.java

Source

/*
 * Copyright 2010 Research Studios Austria Forschungsgesellschaft mBH
 *
 * This file is part of easyrec.
 *
 * easyrec is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * easyrec is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with easyrec.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.easyrec.plugin.configuration;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Validator;

import java.beans.PropertyEditor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

/**
 * Provides utilities for setting and querying configuration parameters and their associated metadata as defined in the
 * {@link PluginParameter} annotations.
 * <p/>
 * Getting, setting and validation of parameter values is done by spring's {@link BeanWrapper} and {@link DataBinder}
 * classes. As the <code>DataBinder</code> maintains state over multiple calls to setValues(), we provide a reset()
 * method that re-initializes the <code>ConfigurationHelper</code>'s resources.
 *
 * @author fkleedorfer
 */
public class ConfigurationHelper {
    protected final Log logger = LogFactory.getLog(getClass());

    protected Validator validator;

    protected Configuration configuration;

    protected BeanWrapper configurationWrapper;

    protected Map<String, Field> parameterFields = new HashMap<String, Field>();
    private DataBinder dataBinder;

    public ConfigurationHelper(Configuration configuration) {
        this.configuration = configuration;
        init();
    }

    /**
     * Re-initializes the internally used resources of this configuration helper. If this method is not called between
     * multiple calls of setValues(), the <code>BindingResult</code> accumulates errors.
     */
    public void reset() {
        init();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.easyrec.plugin.container.ConfigurationHelper#getParameterDescription (java.lang.String)
     */
    public String getParameterDescription(String parameterName) {
        PluginParameter parameterAnnotation = getParameterAnnotation(parameterName);
        return parameterAnnotation.description();
    }

    public String getParameterDisplayName(String parameterName) {
        PluginParameter parameterAnnotation = getParameterAnnotation(parameterName);
        return parameterAnnotation.displayName();
    }

    public Set<String> getParameterNames() {
        return Collections.unmodifiableSet(this.parameterFields.keySet());
    }

    public boolean getParameterOptional(String parameterName) {
        PluginParameter parameterAnnotation = getParameterAnnotation(parameterName);
        return parameterAnnotation.optional();
    }

    public String getParameterShortDescription(String parameterName) {
        PluginParameter parameterAnnotation = getParameterAnnotation(parameterName);
        return parameterAnnotation.shortDescription();
    }

    public int getParameterDisplayOrder(String parameterName) {
        PluginParameter parameterAnnotation = getParameterAnnotation(parameterName);
        return parameterAnnotation.displayOrder();
    }

    public boolean getParameterAsTextArea(String parameterName) {
        PluginParameter parameterAnnotation = getParameterAnnotation(parameterName);
        return parameterAnnotation.asTextArea();
    }

    /**
     * Converts the parameter value to a string. If a custom {@link PropertyEditor} is configured for the parameter (via
     * the {@link PluginParameterPropertyEditor} Annotation), it is used for conversion, otherwise the value's
     * <code>toString()</code> method is called. If the value is <code>null</code>, <code>null</code> is returned.
     *
     * @param parameterName
     *
     * @throws IllegalArgumentException if no parameter of given name exists.
     */
    public String getParameterStringValue(String parameterName) {
        Field field = getParameterField(parameterName);
        PropertyEditor editor = this.configurationWrapper.findCustomEditor(field.getType(), field.getName());
        Object value = getParameterValue(parameterName);
        if (editor != null) {
            editor.setValue(value);
            return editor.getAsText();
        } else {
            if (value == null) {
                return null;
            }
            return value.toString();
        }
    }

    /**
     * Returns the value for the specified parameter name.
     *
     * @param parameterName
     *
     */
    public Object getParameterValue(String parameterName) {
        return this.configurationWrapper.getPropertyValue(parameterName);
    }

    /**
     * Returns all values as a {@link PropertyValues} object, using no property key prefix.
     *
     *
     */
    public PropertyValues getValues() {
        return getValues(null);
    }

    /**
     * Same as getValues(), but property keys are prefixed with the specified prefix.
     *
     * @param propertyKeyPrefix
     *
     */
    public PropertyValues getValues(String propertyKeyPrefix) {
        if (propertyKeyPrefix == null) {
            propertyKeyPrefix = "";
        } else {
            propertyKeyPrefix += ".";
        }
        MutablePropertyValues props = new MutablePropertyValues();
        for (String propertyName : getParameterNames()) {
            String strVal = getParameterStringValue(propertyName);
            if (strVal != null) {
                props.addPropertyValue(propertyKeyPrefix + propertyName, strVal);
            }
        }
        return props;
    }

    /**
     * Delegates the call to <code>getValuesAsProperties(null,null)</code>.
     *
     *
     */
    public Properties getValuesAsProperties() {
        return getValuesAsProperties(null, null);
    }

    /**
     * Delegates the call to <code>getValuesAsProperties(props,null)</code>
     *
     * @param props
     *
     */
    public Properties getValuesAsProperties(Properties props) {
        return getValuesAsProperties(props, null);
    }

    /**
     * Populate the specified properties object with the configuration values, using the specified prefix for the
     * property keys. If a parameter value is null, no property is created for it.
     *
     * @param props             properties to populate. New Properties are created if null.
     * @param propertyKeyPrefix prefix for property keys. Use null to omit prefix.
     *
     */
    public Properties getValuesAsProperties(Properties props, String propertyKeyPrefix) {
        if (props == null) {
            props = new Properties();
        }
        if (propertyKeyPrefix == null) {
            propertyKeyPrefix = "";
        } else {
            propertyKeyPrefix += ".";
        }
        for (String propertyName : getParameterNames()) {
            String strVal = getParameterStringValue(propertyName);
            if (strVal != null) {
                props.setProperty(propertyKeyPrefix + propertyName, strVal);
            }
        }
        return props;
    }

    /**
     * Delegates the call to <code>getValuesAsProperties(null,propertyKeyPrefix)</code>
     *
     * @param propertyKeyPrefix
     *
     */
    public Properties getValuesAsProperties(String propertyKeyPrefix) {
        return getValuesAsProperties(null, propertyKeyPrefix);
    }

    /**
     * Sets the parameter values from the specified PropertyValues object. A parameter value is set to <code>null</code>
     * if the respective property is not present.
     *
     * @param values
     *
     */
    public BindingResult setValues(PropertyValues values) {
        this.dataBinder.bind(values);

        try {
            this.dataBinder.validate();
        } catch (RuntimeException ex) {
            logger.warn("Plugin configuration validator threw an exception!", ex);
        }

        return this.dataBinder.getBindingResult();
    }

    /**
     * Sets the parameter values from the specified Properties object. A parameter value is set to <code>null</code> if
     * the respective property is not present.
     *
     * @param properties
     * @param propertyKeyPrefix
     *
     */
    public BindingResult setValues(Properties properties, String propertyKeyPrefix) {
        if (propertyKeyPrefix == null) {
            propertyKeyPrefix = "";
        } else {
            propertyKeyPrefix += ".";
        }
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        for (String propertyName : getParameterNames()) {
            propertyValues.addPropertyValue(propertyName, properties.getProperty(propertyKeyPrefix + propertyName));
        }
        return setValues(propertyValues);
    }

    /**
     * Returns the PluginParameter annotation for the specified name.
     *
     * @param parameterName
     *
     */
    protected PluginParameter getParameterAnnotation(String parameterName) {
        Field field = getParameterField(parameterName);
        return field.getAnnotation(PluginParameter.class);
    }

    /**
     * Retrieves the {@link Field} with the given name. If no Field is found in the <code>Configuration</code> class of
     * this <code>ConfigurationWrapper</code> that is annotated with the {@link PluginParameter} annotation, an
     * {@link IllegalArgumentException} is raised.
     *
     * @param parameterName
     *
     * @throws IllegalArgumentException if no parameter of the specified name is found.
     */
    protected Field getParameterField(String parameterName) {
        Field field = this.parameterFields.get(parameterName);
        if (field == null) {
            throw new IllegalArgumentException("No field named '" + parameterName
                    + "' with the field-level annotation 'PluginParameter' in class '"
                    + this.configuration.getClass().getName() + "'");
        }
        return field;
    }

    protected void init() {
        this.configurationWrapper = new BeanWrapperImpl(this.configuration);
        this.parameterFields = getParameterFields(this.configuration);
        this.dataBinder = new DataBinder(this.configuration, "Configuration");
        allowFieldsForDataBinding();
        registerPropertyEditors();
        checkForValidator();
    }

    private void allowFieldsForDataBinding() {
        Set<String> propertyNames = getParameterNames();
        this.dataBinder.setAllowedFields(propertyNames.toArray(new String[propertyNames.size()]));
    }

    private void checkForValidator() {
        PluginConfigurationValidator validatorAnnotation = this.configuration.getClass()
                .getAnnotation(PluginConfigurationValidator.class);
        if (validatorAnnotation != null) {
            Validator validator;
            try {
                validator = validatorAnnotation.validatorClass().getConstructor().newInstance();
            } catch (Exception e) {
                throw new IllegalArgumentException("could not create validator for configuration", e);
            }
            this.dataBinder.setValidator(validator);
            this.validator = validator;
        }
    }

    private Set<Field> collectFields(Class<?> clazz) {
        return collectFields(clazz, null);
    }

    private Set<Field> collectFields(Class<?> clazz, Set<Field> fields) {
        if (fields == null) {
            fields = new HashSet<Field>();
        }
        Field[] curFields = clazz.getDeclaredFields();
        for (int i = 0; i < curFields.length; i++) {
            fields.add(curFields[i]);
        }
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            return collectFields(superClass, fields);
        }
        return fields;
    }

    private Map<String, Field> getParameterFields(Configuration config) {
        Set<Field> fields = collectFields(config.getClass());
        Map<String, Field> parameterFields = new HashMap<String, Field>();
        for (Field field : fields) {
            Annotation pluginParameterAnnotation = field.getAnnotation(PluginParameter.class);
            if (pluginParameterAnnotation != null) {
                parameterFields.put(field.getName(), field);
            }
        }
        return parameterFields;
    }

    private void registerPropertyEditors() {
        for (String fieldName : this.parameterFields.keySet()) {
            Field field = this.parameterFields.get(fieldName);
            PluginParameterPropertyEditor propertyEditorAnnotation = field
                    .getAnnotation(PluginParameterPropertyEditor.class);
            if (propertyEditorAnnotation != null) {
                PropertyEditor editor;
                try {
                    editor = propertyEditorAnnotation.propertyEditorClass().getConstructor().newInstance();
                } catch (Exception e) {
                    throw new IllegalArgumentException(
                            "could not create property editor for field '" + field.getName() + "'", e);
                }
                this.dataBinder.registerCustomEditor(field.getType(), field.getName(), editor);
                this.configurationWrapper.registerCustomEditor(field.getType(), field.getName(), editor);
            }
        }
    }

}