FormPopulator.java

// license-header java merge-point
// Generated by andromda-jsf cartridge (utils\FormPopulator.java.vsl) DO NOT EDIT!
package org.andromda.presentation.jsf;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.faces.context.FacesContext;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;

/**
 * Provides utilities for population of forms
 * when using JSF.
 *
 * @author Chad Brandon
 */
public class FormPopulator
    implements Serializable
{
    private static final long serialVersionUID = 1L;

    /**
     * Copies the properties from the <code>fromForm</code> to the <code>toForm</code>.  Only passes not-null values to the toForm.
     *
     * @param fromForm the form from which we're populating
     * @param toForm the form to which we're populating
     */
    public static final void populateForm(final Object fromForm, final Object toForm)
    {
        populateForm(fromForm, toForm, false);
    }

    /**
     * Copies the properties from the <code>fromForm</code> to the <code>toForm</code>.  Only passes not-null values to the toForm.
     *
     * @param fromForm the form from which we're populating
     * @param toForm the form to which we're populating
     * @param override whether or not properties that have already been copied, should be overridden.
     */
    @SuppressWarnings("unchecked") // apache commons-beanutils PropertyUtils has no generics
    public static final void populateForm(final Object fromForm, final Object toForm, boolean override)
    {
        if (fromForm != null && toForm != null)
        {
            try
            {
                final Map<String, Object> parameters = PropertyUtils.describe(fromForm);
                for (final Iterator<String> iterator = parameters.keySet().iterator(); iterator.hasNext();)
                {
                    final String name = iterator.next();
                    if (PropertyUtils.isWriteable(toForm, name))
                    {
                        // - the property name used for checking whether or not the property value has been set
                        final String isSetPropertyName = name + "Set";
                        Boolean isToFormPropertySet = null;
                        Boolean isFromFormPropertySet = null;
                        // - only if override isn't true do we care whether or not the to property has been populated
                        if (!override)
                        {
                            if (PropertyUtils.isReadable(toForm, isSetPropertyName))
                            {
                                isToFormPropertySet = (Boolean)PropertyUtils.getProperty(toForm, isSetPropertyName);
                            }
                        }
                        // - only if override is set to true, we check to see if the from form property has been set
                        if (override)
                        {
                            if (PropertyUtils.isReadable(fromForm, isSetPropertyName))
                            {
                                isFromFormPropertySet = (Boolean)PropertyUtils.getProperty(fromForm, isSetPropertyName);
                            }
                        }
                        if (!override || (override && isFromFormPropertySet != null && isFromFormPropertySet.booleanValue()))
                        {
                            if (override || (isToFormPropertySet == null || !isToFormPropertySet.booleanValue()))
                            {
                                final PropertyDescriptor toDescriptor =
                                    PropertyUtils.getPropertyDescriptor(toForm, name);
                                if (toDescriptor != null)
                                {
                                    Object fromValue = parameters.get(name);
                                    // - only populate if not null
                                    if (fromValue != null)
                                    {
                                        final PropertyDescriptor fromDescriptor =
                                            PropertyUtils.getPropertyDescriptor(fromForm, name);
                                        // - only attempt to set if the types match
                                        if (fromDescriptor.getPropertyType() == toDescriptor.getPropertyType())
                                        {
                                            PropertyUtils.setProperty(toForm, name, fromValue);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (final Throwable throwable)
            {
                throw new RuntimeException(throwable);
            }
        }
    }

    /**
     * Populates the form from the attributes contained in the request object.
     *
     * @param form the form to populate.
     * @param formatters any date or time formatters.
     * @param override whether or not to override properties already set on the given form.
     * @param assignableTypesOnly whether or not copying should be attempted only if the property types are assignable.
     */
    public static final void populateFormFromRequestAttributes(final Object form, final Map<String, DateFormat> formatters, boolean override, boolean assignableTypesOnly)
    {
        final FacesContext context = FacesContext.getCurrentInstance();
        final String[] names = JsfUtils.getAttributeNames(context.getExternalContext().getRequest());
        final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
        if (names != null)
        {
            for (final String name : names)
            {
                attributes.put(name, JsfUtils.getAttribute(context.getExternalContext().getRequest(), name));
            }
        }
        populateFormFromPropertyMap(form, formatters, attributes, null, override, assignableTypesOnly);
    }

    /**
     * Populates the form from the attributes contained in the request object.
     *
     * @param form the form to populate.
     * @param formatters any date or time formatters.
     * @param override whether or not to override properties already set on the given form.
     */
    public static final void populateFormFromRequestAttributes(final Object form, final Map<String, DateFormat> formatters, boolean override)
    {
        populateFormFromRequestAttributes(form, formatters, override, false);
    }

    /**
     * Populates the form from the given map of properties. If a matching property is null or an empty
     * string, then null is placed on the form.
     *
     * @param form the form to populate.
     * @param formatters any date or time formatters.
     * @param properties the properties to populate from.
     * @param override whether or not to override properties already set on the given form.
     */
    public static final void populateFormFromPropertyMap(final Object form, final Map<String, DateFormat> formatters, final Map<String, ?> properties, boolean override)
    {
        populateFormFromPropertyMap(form, formatters, properties, null, override, false);
    }

    /**
     * Populates the form from the given map of properties. If a matching property is null or an empty
     * string, then null is placed on the form.
     *
     * @param form the form to populate.
     * @param formatters any date or time formatters.
     * @param properties the properties to populate from.
     */
    public static final void populateFormFromPropertyMap(final Object form, final Map<String, DateFormat> formatters, final Map<String, ?> properties)
    {
        populateFormFromPropertyMap(form, formatters, properties, null, true, false);
    }

    /**
     * Populates the form from the given map of properties and populates only if the types
     * the properties are assignable. If a matching property is null or an empty
     * string, then null is placed on the form.
     *
     * @param form the form to populate.
     * @param formatters any date or time formatters.
     * @param properties the properties to populate from.
     */
    public static final void populateFormFromPropertyMapAssignableTypesOnly(final Object form, final Map<String, DateFormat> formatters, final Map<String, ?> properties)
    {
        populateFormFromPropertyMap(form, formatters, properties, null, true, true);
    }

    /**
     * Populates the form from the given map of properties. If a matching property is null or an empty
     * string, then null is placed on the form.
     *
     * @param form the form to populate.
     * @param formatters any date or time formatters.
     * @param properties the properties to populate from.
     * @param ignoreProperties names of any properties to ignore when it comes to populating on the form.
     */
    public static final void populateFormFromPropertyMap(final Object form, final Map<String, DateFormat> formatters,
        final Map<String, ?> properties, final String[] ignoreProperties)
    {
        populateFormFromPropertyMap(form, formatters, properties, ignoreProperties, true, false);
    }

    /**
     * Populates the form from the given map of properties. If a matching property is null or an empty
     * string, then null is placed on the form.
     *
     * @param form the form to populate.
     * @param formatters any date or time formatters.
     * @param properties the properties to populate from.
     * @param ignoreProperties names of any properties to ignore when it comes to populating on the form.
     * @param override whether or not to override properties already set on the given form.
     * @param assignableTypesOnly whether or not copying should be attempted only if the property types are assignable.
     */
    @SuppressWarnings("unchecked") // apache commons-beanutils PropertyUtils has no generics
    public static final void populateFormFromPropertyMap(final Object form, final Map<String, DateFormat> formatters,
        final Map<String, ?> properties, final String[] ignoreProperties, boolean override, boolean assignableTypesOnly)
    {
        if (properties != null)
        {
            try
            {
                final Collection<String> ignoredProperties = ignoreProperties != null ? Arrays.asList(ignoreProperties) : new ArrayList<String>();
                final Map<String, Object> formProperties = PropertyUtils.describe(form);
                for (final Iterator<String> iterator = formProperties.keySet().iterator(); iterator.hasNext();)
                {
                    final String name = iterator.next();
                    if (PropertyUtils.isWriteable(form, name) && !ignoredProperties.contains(name))
                    {
                        final PropertyDescriptor descriptor =
                            PropertyUtils.getPropertyDescriptor(form, name);
                        if (descriptor != null)
                        {
                            boolean populateProperty = true;
                            if (!override)
                            {
                                final String isSetPropertyName = name + "Set";
                                if (PropertyUtils.isReadable(form, isSetPropertyName))
                                {
                                    final Boolean isPropertySet = (Boolean)PropertyUtils.getProperty(form, isSetPropertyName);
                                    if (isPropertySet.booleanValue())
                                    {
                                        populateProperty = false;
                                    }
                                }
                            }
                            if (populateProperty)
                            {
                                final Object property = properties.get(name);

                                // - only convert if the string is not empty
                                if (property != null)
                                {
                                    Object value = null;
                                    if (property instanceof String)
                                    {
                                        final String propertyAsString = (String)property;
                                        if (propertyAsString.trim().length() > 0)
                                        {
                                            DateFormat formatter = formatters != null ? formatters.get(name) : null;
                                            // - if the formatter is available we use that, otherwise we attempt to convert
                                            if (formatter != null)
                                            {
                                                try
                                                {
                                                    value = formatter.parse(propertyAsString);
                                                }
                                                catch (ParseException parseException)
                                                {
                                                    // - try the default formatter (handles the default java.util.Date.toString() format)
                                                    formatter = formatters != null ? formatters.get(null) : null;
                                                    value = formatter.parse(propertyAsString);
                                                }
                                            }
                                            else
                                            {
                                                value = ConvertUtils.convert(propertyAsString, descriptor.getPropertyType());
                                            }
                                        }
                                        // - don't attempt to set null on primitive fields
                                        if (value != null || !descriptor.getPropertyType().isPrimitive())
                                        {
                                            PropertyUtils.setProperty(form, name, value);
                                        }
                                    }
                                    else
                                    {
                                        value = property;
                                        try
                                        {
                                            if (!assignableTypesOnly || descriptor.getPropertyType().isAssignableFrom(value.getClass()))
                                            {
                                                PropertyUtils.setProperty(form, name, value);
                                            }
                                        }
                                        catch (Exception exception)
                                        {
                                            final String valueTypeName = value.getClass().getName();
                                            final String propertyTypeName = descriptor.getPropertyType().getName();
                                            final StringBuilder message = new StringBuilder("Can not set form property '"
                                                + name + "' of type: " + propertyTypeName + " with value: "
                                                + value);
                                            if (!descriptor.getPropertyType().isAssignableFrom(value.getClass()))
                                            {
                                                message.append("; " + valueTypeName + " is not assignable to " + propertyTypeName);
                                            }
                                            throw new IllegalArgumentException(message + ": " + exception.toString());
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (final Throwable throwable)
            {
                throw new RuntimeException(throwable);
            }
        }
    }
}