ar.com.zauber.commons.mom.MapObjectMapper.java Source code

Java tutorial

Introduction

Here is the source code for ar.com.zauber.commons.mom.MapObjectMapper.java

Source

/**
 * Copyright (c) 2009-2011 Zauber S.A. <http://www.zaubersoftware.com/>
 *
 * 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 ar.com.zauber.commons.mom;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.beanutils.LazyDynaMap;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Transformer;
import org.apache.commons.lang.UnhandledException;
import org.apache.commons.lang.Validate;

/**
 * {@link MapObjectMapper}
 * 
 * @author flbulgarelli
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class MapObjectMapper {
    private final Transformer toMap = new ToMapTransformer();
    private final BeanUtilsBean beanUtils;
    private final MapToBeanConverter beanConverter = new MapToBeanConverter();
    private final ListToBeanArrayConverter listConverter = new ListToBeanArrayConverter();

    private final MappingPackage mappingPackage;
    private PropertyStyle propertyStyle = BeanStyle.STYLE;

    public MapObjectMapper(final String packageName) {
        this.mappingPackage = new MappingPackage(packageName);
    }

    {
        beanUtils = new BeanUtilsBean(new ConvertUtilsBean() {
            @Override
            public Converter lookup(final Class destinationType) {
                if (isMappeableArray(destinationType)) {
                    return listConverter;
                }
                if (isMappeable(destinationType)) {
                    return beanConverter;
                }
                return super.lookup(destinationType);
            }

            @Override
            public Object convert(Object value, Class targetType) {
                return super.convert(value, targetType);
            }
        }) {
            @Override
            protected Object convert(final Object value, final Class targetType) {
                if (!targetType.isPrimitive() && value == null) {
                    return null;
                }
                return super.convert(value, targetType);
            }
        };
    }

    public MapObjectMapper registerConverter(Converter converter, Class<?> clazz) {
        beanUtils.getConvertUtils().register(converter, clazz);
        return this;
    }

    public Map<String, Object> toMap(final Object object) {
        Validate.isTrue(mappingPackage.isMappeable(object.getClass()));
        return new AttributesMap(object, this);
    }

    public List<Map<String, Object>> toMap(final Object[] objects) {
        return (List<Map<String, Object>>) CollectionUtils.collect(Arrays.asList(objects), toMap);
    }

    public <T> T toObject(Class<T> targetType, Object input) {
        if (input == null) {
            return null;
        }
        if (targetType.isAssignableFrom(input.getClass()))
            return (T) input;
        return (T) beanUtils.getConvertUtils().convert(input, targetType);
    }

    public <T> T[] toArray(final Class<T> clazz, final List<Map<String, Object>> list) {
        return (T[]) beanUtils.getConvertUtils().convert(list, Array.newInstance(clazz, 0).getClass());
    }

    public boolean isMappeableArray(final Class<?> clazz) {
        return mappingPackage.isMappeableArray(clazz);
    }

    public boolean isMappeable(final Class<?> clazz) {
        return mappingPackage.isMappeable(clazz);
    }

    private class ToMapTransformer implements Transformer {
        public Object transform(final Object input) {
            return toMap(input);
        }
    }

    /**
     * {@link Converter} that transforms a list of maps into an array of
     * mappeable objects
     */
    private class ListToBeanArrayConverter implements Converter {
        public Object convert(final Class destinationType, final Object value) {
            try {
                List<Map<String, Object>> list = (List<Map<String, Object>>) value;
                Object[] array = (Object[]) Array.newInstance(destinationType.getComponentType(), list.size());
                Iterator<Map<String, Object>> iter = list.iterator();
                for (int i = 0; i < array.length; i++) {
                    array[i] = toObject(destinationType.getComponentType(), iter.next());
                }
                return array;

            } catch (Exception e) {
                throw soften(e);
            }
        }
    }

    private class MapToBeanConverter implements Converter {
        public Object convert(final Class type, final Object value) {
            if (type.isAssignableFrom(value.getClass()))
                return value;
            try {
                LazyDynaMap dynaMap = new LazyDynaMap((Map<String, Object>) value);
                Object bean = type.newInstance();
                copyProperties(dynaMap, bean);
                return bean;
            } catch (Exception e) {
                throw soften(e);
            }
        }

        /**
        * @param source
        * @param destination
        * @throws IllegalAccessException
        * @throws InvocationTargetException
        */
        public void copyProperties(LazyDynaMap source, Object destination)
                throws IllegalAccessException, InvocationTargetException {
            Validate.notNull(source);
            Validate.notNull(destination);
            DynaProperty[] origDescriptors = source.getDynaClass().getDynaProperties();
            for (int i = 0; i < origDescriptors.length; i++) {
                String name = origDescriptors[i].getName();
                copyProperty(source.get(name), destination, name);
            }
        }

        protected void copyProperty(Object property, Object destination, String name) {
            try {
                if (propertyStyle.isSeteable(beanUtils, property, destination, name)) {
                    if (List.class
                            .isAssignableFrom(beanUtils.getPropertyUtils().getPropertyType(destination, name))) {
                        final Class<?> componentType = getComponentType(destination, name);
                        property = CollectionUtils.collect(coalesceList((List) property), new Transformer() {
                            public Object transform(Object input) {
                                return toObject(componentType, input);
                            }
                        });
                    }
                    propertyStyle.setValue(beanUtils, property, destination, name);
                } else if (!name.equals("class")) {
                    throw new IllegalArgumentException(
                            "There is no settable property named " + name + " in class " + destination.getClass());
                }
            } catch (Exception e) {
                throw soften(e);
            }
        }

        protected List coalesceList(List list) {
            return list != null ? list : Collections.emptyList();
        }

    }

    public MapObjectMapper setPropertyStyle(PropertyStyle property) {
        this.propertyStyle = property;
        return this;
    }

    public RuntimeException soften(Exception e) {
        if (e instanceof RuntimeException) {
            return (RuntimeException) e;
        }
        return new UnhandledException(e);
    }

    protected Class<?> getComponentType(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        return (Class<?>) ((ParameterizedType) PropertyUtils.getPropertyDescriptor(bean, name).getReadMethod()
                .getGenericReturnType()).getActualTypeArguments()[0];
    }

}