package simpleworkflow.engine.util;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DataCopyUtil {
    private final static Map<String, Map<String, PropertyDescriptor>> _cache = new ConcurrentHashMap<String, Map<String, PropertyDescriptor>>();

    /**
     *
     * @param srcData could be Map<String, Object>
     * @param destData could be Map<String, Object>
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IntrospectionException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    public static void copyData(Object srcData, Object destData) throws InstantiationException, IllegalAccessException, IntrospectionException, IllegalArgumentException, InvocationTargetException {
        if(Map.class.isAssignableFrom(srcData.getClass())) {
            if(Map.class.isAssignableFrom(destData.getClass())) {
                //map to map
                ((Map<String, Object>) destData).putAll(((Map<String, Object>) srcData));
            } else {
                //map to bean
                copyDataWithMapToBean((Map<String, Object>) srcData, destData);
            }
        } else {
            if(Map.class.isAssignableFrom(destData.getClass())) {
                //bean to map
                copyDataWithBeanToMap(srcData, (Map<String, Object>) destData);
            } else {
                //bean to bean
                copyDataWithBeanToBean(srcData, destData);
            }
        }
    }

    public static Map<String, Object> beanToMap(Object data) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        if(data == null) return null;

        Map<String, Object> kvMap = new HashMap<String, Object>();

        copyDataWithBeanToMap(data, kvMap);

        return kvMap;
    }

    private static void copyDataWithBeanToBean(Object srcData, Object destData) throws InstantiationException, IllegalAccessException, IntrospectionException, IllegalArgumentException, InvocationTargetException {
        Map<String, PropertyDescriptor> srcPropMap = getPropertyDescriptorMap(srcData.getClass());
        Map<String, PropertyDescriptor> destPropMap = getPropertyDescriptorMap(destData.getClass());
        for(Map.Entry<String, PropertyDescriptor> srcPdEntry : srcPropMap.entrySet()) {
            final String propName = srcPdEntry.getKey();
            final PropertyDescriptor srcPd = srcPdEntry.getValue();
            final Method srcReadMethod = srcPd.getReadMethod();

            if(srcReadMethod == null) {
                continue;
            }

            final PropertyDescriptor destPd = destPropMap.get(propName);
            if(destPd == null) {
                continue;
            }

            final Method destWriteMethod = destPd.getWriteMethod();
            if(destWriteMethod == null) {
                continue;
            }

            destWriteMethod.invoke(
                    destData,
                    srcReadMethod.invoke(srcData, (Object[]) null)
            );
        }
    }

    private static void copyDataWithMapToBean(Map<String, Object> srcData, Object destData) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        Map<String, PropertyDescriptor> destPropMap = getPropertyDescriptorMap(destData.getClass());
        for(Map.Entry<String, Object> entry : srcData.entrySet()) {
            final String key = entry.getKey();
            final Object val = entry.getValue();

            final PropertyDescriptor destPd = destPropMap.get(key);
            if(destPd == null) {
                continue;
            }

            final Method destWriteMethod = destPd.getWriteMethod();
            if(destWriteMethod == null) {
                continue;
            }

            destWriteMethod.invoke(destData, val);
        }
    }

    private static void copyDataWithBeanToMap(Object srcData, Map<String, Object> destData) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        Class<?> dataCls = srcData.getClass();

        if(Map.class.isAssignableFrom(dataCls)) {
            destData.putAll((Map<String, Object>) srcData);
            return;
        }

        Map<String, PropertyDescriptor> srcPropMap = getPropertyDescriptorMap(dataCls);
        for(Map.Entry<String, PropertyDescriptor> srcPdEntry : srcPropMap.entrySet()) {
            final String propName = srcPdEntry.getKey();
            final PropertyDescriptor srcPd = srcPdEntry.getValue();
            final Method srcReadMethod = srcPd.getReadMethod();

            if(srcReadMethod == null) {
                continue;
            }

            final Object val = srcReadMethod.invoke(srcData, (Object[]) null);
            if(val != null) {
                destData.put(propName, val);
            }
        }
    }

    private static Map<String, PropertyDescriptor> getPropertyDescriptorMap(Class<?> cls) throws IntrospectionException {
        final String key = cls.getName();
        Map<String, PropertyDescriptor> pdMap = _cache.get(key);

        if(pdMap == null) {
            pdMap = new HashMap<String, PropertyDescriptor>();

            PropertyDescriptor[] pdArray = Introspector.getBeanInfo(cls).getPropertyDescriptors();
            for(PropertyDescriptor pd : pdArray) {
                pdMap.put(pd.getName(), pd);
            }

            _cache.put(key, pdMap);
        }

        return pdMap;
    }

}

@@ -244,23 +244,74 @@
 va.util.
-HashMap
+ArrayList;%0Aimport java.util.HashMap;%0Aimport java.util.List
 ;%0Aimport
@@ -1801,71 +1801,432 @@
     
-public static Map%3CString, Object%3E beanToMap(Object data) throws
+/**%0A     *%0A     * @param srcData could be Map%3CString, Object%3E%0A     * @param destDataClass could be HashMap.class%0A     * @param %3CT%3E%0A     * @return%0A     * @throws InvocationTargetException%0A     * @throws IntrospectionException%0A     * @throws InstantiationException%0A     * @throws IllegalAccessException%0A     */%0A    public static %3CT%3E T createAndCopyData(Object srcData, Class%3CT%3E destDataClass) throws InvocationTargetException,
  Int
@@ -2244,38 +2244,35 @@
 xception, In
-vocationTarget
+stantiation
 Exception, I
@@ -2307,172 +2307,586 @@
     
-if(d
+T destD
 ata =
-= null) return null;%0A%0A        Map%3CString, Object%3E kvMap = new HashMap%3CString, Object%3E();%0A%0A        copyDataWithBeanToMap(data, kvMap);%0A%0A        return kvMap
+ destDataClass.newInstance();%0A%0A        copyData(srcData, destData);%0A%0A        return destData;%0A    %7D%0A%0A    public static %3CT%3E List%3CT%3E createAndCopyDataList(List%3C?%3E srcDataList, Class%3CT%3E destDataClass) throws IntrospectionException, InstantiationException, IllegalAccessException, InvocationTargetException %7B%0A        List%3CT%3E destDataList = srcDataList.getClass().newInstance();%0A%0A        for(Object srcData : srcDataList) %7B%0A            T destData = createAndCopyData(srcData, destDataClass);%0A%0A            destDataList.add(destData);%0A        %7D%0A%0A        return destDataList
 ;%0A  

add createAndCopyDataList