All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.lightlink.dao.mapping.BeanMapper Maven / Gradle / Ivy

There is a newer version: 1.2.4
Show newest version
package io.lightlink.dao.mapping;

/*
 * #%L
 * lightlink-core
 * %%
 * Copyright (C) 2015 Vitaliy Shevchuk
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */


import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.map.CaseInsensitiveMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BeanMapper {

    public static final Logger LOG = LoggerFactory.getLogger(BeanMapper.class);

    Class paramClass;

    List ownFields = new ArrayList();

    Map childMappers = new CaseInsensitiveMap();
    Map childListMappers = new CaseInsensitiveMap();

    Map pool = new HashMap();

    private final Map descriptorMap;


    public BeanMapper(Class paramClass, List fieldsOfLine) {

        PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(paramClass);
        descriptorMap = new CaseInsensitiveMap();
        for (PropertyDescriptor descriptor : descriptors) {
            descriptorMap.put(normalizePropertyName(descriptor.getName()), descriptor);
        }

        List ownFields = new ArrayList();
        Map> fieldsByChild = new CaseInsensitiveMap();

        groupFields(fieldsOfLine, ownFields, fieldsByChild);

        this.ownFields = ownFields;
        for (Map.Entry> entry : fieldsByChild.entrySet()) {
            String childProperty = normalizePropertyName(entry.getKey());
            PropertyDescriptor descriptor = descriptorMap.get(childProperty);
            if (descriptor != null) {
                List properties = entry.getValue();
                if (descriptor.getPropertyType().isAssignableFrom(ArrayList.class)) {
                    Type[] typeArguments = ((ParameterizedType) descriptor.getReadMethod().getGenericReturnType()).getActualTypeArguments();
                    if (typeArguments.length == 0)
                        throw new RuntimeException("Cannot define Generic list type of " + entry.getKey() + " property of " + paramClass);
                    Type firstType = typeArguments[0];
                    if (firstType instanceof Class) {
                        childListMappers.put(childProperty, new BeanMapper((Class) firstType, properties));
                    }
                } else {
                    childMappers.put(childProperty, new BeanMapper(descriptor.getPropertyType(), properties));
                }
            } else {
                throw new RuntimeException("cannot define mapping for class:" + paramClass.getName() + " property:" + childProperty);
            }
        }

        this.paramClass = paramClass;
    }

    public static void groupFields(List allFieldsOfLine, List ownFields, Map> fieldsByChild) {
        for (String f : allFieldsOfLine) {
            if (f.contains(".")) {
                int dotPos = f.indexOf('.');
                String childName = f.substring(0, dotPos);
                List list = fieldsByChild.get(childName);
                if (list == null)
                    fieldsByChild.put(childName, list = new ArrayList());
                list.add(f.substring(dotPos + 1));
            } else {
                ownFields.add(f);
            }
        }
    }

    public List convert(List> maps) throws IllegalAccessException, InstantiationException,
            InvocationTargetException {
        if (maps == null) {
            return new ArrayList();
        }
        List beanList = new ArrayList(maps.size());
        for (Map map : maps) {
            Object object = convertObject(map, true);
            if (object != null) // object is already present il the result as a parent containing multiple child objects
                beanList.add(object);
        }
        return beanList;
    }

    public Object convertObject(Map map, boolean returnNullIfAlreadyPresent) throws IllegalAccessException, InstantiationException,
            InvocationTargetException {

        Object bean = paramClass.newInstance();

        populate(bean, map);

        List fieldsToCompare = new ArrayList(ownFields);
        fieldsToCompare.addAll(childMappers.keySet());

        MappingEntry mappingEntry = new MappingEntry(paramClass, fieldsToCompare, bean);

        Object pooledEntry = pool.get(mappingEntry);
        boolean fromPool;
        if (pooledEntry != null) {
            bean = pooledEntry;
            fromPool = true;
        } else {
            pool.put(mappingEntry, bean);
            fromPool = false;
        }

        boolean lineAdded = populateListObjects(bean, map);

        if (fromPool && lineAdded && returnNullIfAlreadyPresent)
            return null;
        else
            return bean;

    }

    private void populate(Object bean, Map map) throws InvocationTargetException, IllegalAccessException {

        Map data = new CaseInsensitiveMap(map.size());
        for (Map.Entry entry : map.entrySet()) {
            data.put(normalizePropertyName(entry.getKey()), entry.getValue());
        }

        populateOwnFields(bean, data);

        populateChildObjects(bean, map);

    }

    private boolean populateListObjects(Object bean, Map map) {
        boolean lineAdded = false;

        for (Map.Entry entry : childListMappers.entrySet()) {
            String originalKey = entry.getKey();
            String key = normalizePropertyName(entry.getKey());
            PropertyDescriptor propertyDescriptor = descriptorMap.get(key);
            if (propertyDescriptor == null) {

                LOG.info("Cannot find property for " + entry.getKey() + " in class " + paramClass.getCanonicalName());

            } else {

                Map childData = prepareChildData(originalKey, map);
                try {
                    Object childObject = entry.getValue().convertObject(childData, true);

                    List list = (List) propertyDescriptor.getReadMethod().invoke(bean);

                    if (list == null)
                        propertyDescriptor.getWriteMethod().invoke(bean, list = new ArrayList());

                    if (childObject != null)
                        list.add(childObject);

                    lineAdded = true;

                } catch (Exception e) {
                    LOG.error(e.getMessage(), e);
                }
            }

        }
        return lineAdded;
    }

    private Map prepareChildData(String originalKey, Map map) {

        Map childData = new CaseInsensitiveMap(map.size());

        for (Map.Entry childEntry : map.entrySet()) {
            String childEntryKey = childEntry.getKey();
            if (childEntryKey.startsWith(originalKey + ".")) {
                childData.put(childEntryKey.substring(originalKey.length() + 1), childEntry.getValue());
            }
        }

        return childData;

    }

    private void populateChildObjects(Object bean, Map map) {
        for (Map.Entry entry : childMappers.entrySet()) {
            String originalKey = entry.getKey();
            String key = normalizePropertyName(originalKey);
            PropertyDescriptor propertyDescriptor = descriptorMap.get(key);
            if (propertyDescriptor == null) {
                LOG.info("Cannot find property for " + key + " in class " + paramClass.getCanonicalName());
            } else {
                Map childData = prepareChildData(originalKey, map);
                try {
                    Object childObject = entry.getValue().convertObject(childData, false);
                    propertyDescriptor.getWriteMethod().invoke(bean, childObject);
                } catch (Exception e) {
                    LOG.error(e.getMessage(), e);
                }
            }

        }
    }

    private void populateOwnFields(Object bean, Map data) {
        for (String field : ownFields) {
            String key = normalizePropertyName(field);
            PropertyDescriptor propertyDescriptor = descriptorMap.get(key);
            if (propertyDescriptor == null) {
                LOG.info("Cannot find property for " + field + " in class " + paramClass.getCanonicalName());
            } else {
                try {
                    propertyDescriptor.getWriteMethod().invoke(bean,
                            MappingUtils.convert(propertyDescriptor.getPropertyType(), data.get(key), propertyDescriptor.getName()));
                } catch (Exception e) {
                    LOG.error(e.getMessage(), e);
                }
            }
        }
    }

    private String normalizePropertyName(String key) {
        return key.replaceAll("_", "");
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy