org.jaxygen.converters.properties.PropertiesToBeanConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.jaxygen.converters.properties.PropertiesToBeanConverter.java

Source

/*
 * Copyright 2012 Artur Keska.
 *
 * 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.
 */
package org.jaxygen.converters.properties;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.converters.BooleanConverter;
import org.apache.commons.beanutils.converters.ByteConverter;
import org.apache.commons.beanutils.converters.CharacterConverter;
import org.apache.commons.beanutils.converters.DoubleConverter;
import org.apache.commons.beanutils.converters.FloatConverter;
import org.apache.commons.beanutils.converters.IntegerConverter;
import org.apache.commons.beanutils.converters.LongConverter;
import org.apache.commons.beanutils.converters.ShortConverter;
import org.apache.commons.beanutils.converters.StringConverter;
import org.jaxygen.converters.exceptions.DeserialisationError;
import org.jaxygen.converters.RequestConverter;
import org.jaxygen.dto.Uploadable;
import org.jaxygen.network.UploadedFile;
import org.jaxygen.exceptions.WrongProperyIndex;
import org.jaxygen.http.HttpRequestParams;

/**
 *
 * @author Artur Keska
 */
public class PropertiesToBeanConverter implements RequestConverter {

    static final Map<Class<?>, Converter> converters = new HashMap<Class<?>, Converter>();

    static {
        converters.put(Boolean.class, new BooleanConverter());
        converters.put(Boolean.TYPE, new BooleanConverter());
        converters.put(Byte.class, new ByteConverter());
        converters.put(Byte.TYPE, new ByteConverter());
        converters.put(Character.class, new CharacterConverter());
        converters.put(Character.TYPE, new CharacterConverter());
        converters.put(Float.class, new FloatConverter());
        converters.put(Float.TYPE, new FloatConverter());
        converters.put(Double.class, new DoubleConverter());
        converters.put(Double.TYPE, new DoubleConverter());
        converters.put(double.class, new DoubleConverter());
        converters.put(Integer.class, new IntegerConverter());
        converters.put(Integer.TYPE, new IntegerConverter());
        converters.put(Long.class, new LongConverter());
        converters.put(Long.TYPE, new LongConverter());
        converters.put(Short.class, new ShortConverter());
        converters.put(Short.TYPE, new ShortConverter());
        converters.put(Enum.class, new EnumConverter());
        converters.put(String.class, new StringConverter());
        for (Class<?> c : converters.keySet()) {
            ConvertUtils.register(converters.get(c), c);
        }
    }

    static public boolean isCovertable(Class<?> c) {
        return converters.containsKey(c);
    }

    public static final String NAME = "PROPERTIES";

    @Override
    public Object deserialise(HttpRequestParams params, Class<?> beanClass) throws DeserialisationError {
        try {
            return convertPropertiesToBean(params.getParameters(), params.getFiles(), beanClass);
        } catch (Exception ex) {
            throw new DeserialisationError("Could not parse input parameters for beed class " + beanClass, ex);
        }
    }

    /**
     * Applies a collection of properties to a JavaBean. Converts String and String[] values to correct property types
     *
     * @param properties A map of the properties to set on the JavaBean
     * @param files List of files.
     * @param beanClass Bean class to be converted.
     * @return A new object of beanClass.
     * @throws InstantiationException .
     * @throws InvocationTargetException .
     * @throws IllegalAccessException .
     * @throws IntrospectionException .
     * @throws IllegalArgumentException .
     * @throws WrongProperyIndex Exception thrown on property validation errors.
     */
    public static Object convertPropertiesToBean(Map<String, String> properties, Map<String, Uploadable> files,
            Class<?> beanClass) throws IllegalArgumentException, IntrospectionException, IllegalAccessException,
            InvocationTargetException, InstantiationException, WrongProperyIndex {
        Object bean = beanClass.newInstance();
        for (final String key : properties.keySet()) {
            final String value = properties.get(key);
            bean = fillBeanValueByName(key, value, beanClass, bean);
        }

        for (final String key : files.keySet()) {
            final Uploadable value = files.get(key);
            bean = fillBeanValueByName(key, value, beanClass, bean);
        }

        return bean;
    }

    public static Object convertPropertiesToBean(Map<String, String> properties, Map<String, Uploadable> files,
            Object bean) throws IllegalArgumentException, IntrospectionException, IllegalAccessException,
            InvocationTargetException, InstantiationException, WrongProperyIndex {
        Object pojo = bean;
        for (final String key : properties.keySet()) {
            final String value = properties.get(key);
            pojo = fillBeanValueByName(key, value, bean.getClass(), pojo);
        }
        for (final String key : files.keySet()) {
            final Uploadable value = files.get(key);
            pojo = fillBeanValueByName(key, value, bean.getClass(), pojo);
        }
        return bean;
    }

    /**
     * Fill the field in bean by the value pointed by the name. Name format name=<(KEY([N])?)+> where KEY bean property name, N index in table (if bean field is
     * List of java array).
     *
     * @param name
     * @param value
     * @param beanClass
     * @param baseBean
     * @param conversionReport
     * @return
     * @throws IntrospectionException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws InstantiationException
     * @throws WrongProperyIndex
     */
    private static Object fillBeanValueByName(final String name, Object value, Class<?> beanClass, Object baseBean)
            throws IntrospectionException, IllegalArgumentException, IllegalAccessException,
            InvocationTargetException, InstantiationException, WrongProperyIndex {
        // parse name x.y[i].z[n].v
        Object bean = baseBean;
        if (bean == null) {
            bean = beanClass.newInstance();
        }
        Class<?> c = beanClass;
        BeanInfo beanInfo = Introspector.getBeanInfo(c, Object.class);
        final String childName = name.substring(name.indexOf(".") + 1);
        String path[] = name.split("\\.");

        final String fieldName = path[0];
        // parse arrays [n]
        if (fieldName.endsWith("]")) {
            int bracketStart = fieldName.indexOf("[");
            int len = fieldName.length();
            if (bracketStart > 0) {
                fillBeanArrayField(name, value, bean, beanInfo, path, fieldName, bracketStart, len);
            } else {
                throw new WrongProperyIndex(name);
            }
        } else {
            // parse non arrays
            for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
                if (pd.getName().equals(fieldName)) {
                    Method writter = pd.getWriteMethod();
                    Method reader = pd.getReadMethod();
                    if (writter != null && reader != null) {
                        Class<?> valueType = reader.getReturnType();
                        if (path.length == 1) {
                            Object valueObject = parsePropertyToValue(value, valueType);
                            writter.invoke(bean, valueObject);
                        } else {
                            Object childBean = reader.invoke(bean);
                            Object valueObject = fillBeanValueByName(childName, value, valueType, childBean);
                            writter.invoke(bean, valueObject);
                        }
                    }
                }
            }
        }

        // Object bean = c.newInstance();
        return bean;
    }

    private static void fillBeanArrayField(final String name, Object value, Object bean, BeanInfo beanInfo,
            String[] path, final String fieldName, int bracketStart, int len)
            throws IllegalAccessException, InvocationTargetException, IntrospectionException,
            InstantiationException, IllegalArgumentException, WrongProperyIndex {
        final String indexStr = fieldName.substring(bracketStart + 1, len - 1);
        final String propertyName = fieldName.substring(0, bracketStart);
        int index = Integer.parseInt(indexStr);
        String childName = "";
        int firstDot = name.indexOf(".");
        if (firstDot > 0) {
            childName = name.substring(firstDot + 1);
        }

        for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
            if (pd.getName().equals(propertyName)) {
                Method writter = pd.getWriteMethod();
                Method reader = pd.getReadMethod();
                if (writter != null && reader != null) {
                    Object array = reader.invoke(bean);
                    if (pd.getPropertyType().isArray()) {
                        if (array == null) {
                            array = Array.newInstance(pd.getPropertyType().getComponentType(), index + 1);
                            writter.invoke(bean, array);
                        }
                        if (Array.getLength(array) < (index + 1)) {
                            array = resizeArray(array, index + 1);
                            writter.invoke(bean, array);
                        }
                        if (path.length == 1) {
                            Object valueObject = parsePropertyToValue(value, array.getClass().getComponentType());
                            Array.set(array, index, valueObject);
                        } else {
                            Object valueObject = fillBeanValueByName(childName, value,
                                    array.getClass().getComponentType(), Array.get(array, index));
                            Array.set(array, index, valueObject);
                        }
                    } else if (pd.getPropertyType().equals(List.class)) {
                        if (array == null) {
                            array = pd.getPropertyType().newInstance();
                            writter.invoke(bean, array);
                        }
                        Class<?> genericClass = array.getClass().getTypeParameters()[0].getClass();
                        if (path.length == 1) {
                            Object valueObject = parsePropertyToValue(value, genericClass);
                            Array.set(array, index, valueObject);
                        } else {
                            Object valueObject = fillBeanValueByName(childName, value, genericClass, null);
                            Array.set(array, index, valueObject);
                        }
                    }
                }
            }
        }
    }

    private static Object parsePropertyToValue(Object valueObject, Class<?> propertyType) {
        Object value = null;

        //TODO: add cache of enum converters
        boolean isEnum = propertyType.isEnum();
        if (isEnum) {
            ConvertUtils.register(new EnumConverter(), propertyType);
        }

        if (valueObject != null && valueObject.getClass().equals(String.class)) {
            value = ConvertUtils.convert((String) valueObject, propertyType);
        } else {
            value = valueObject;
        }

        return value;
    }

    private static Object resizeArray(Object array, int size) {
        Object newArray = Array.newInstance(array.getClass().getComponentType(), size);
        for (int i = 0; i < Array.getLength(array); i++) {
            Object value = Array.get(array, i);
            Array.set(newArray, i, value);
        }
        return newArray;
    }

    public String getName() {
        return NAME;
    }
}