org.soybeanMilk.core.bean.DefaultGenericConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.soybeanMilk.core.bean.DefaultGenericConverter.java

Source

/**
 * 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.soybeanMilk.core.bean;

import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.soybeanMilk.SbmUtils;
import org.soybeanMilk.core.Constants;
import org.soybeanMilk.core.bean.converters.BigDecimalConverter;
import org.soybeanMilk.core.bean.converters.BigIntegerConverter;
import org.soybeanMilk.core.bean.converters.BooleanConverter;
import org.soybeanMilk.core.bean.converters.ByteConverter;
import org.soybeanMilk.core.bean.converters.CharacterConverter;
import org.soybeanMilk.core.bean.converters.DateConverter;
import org.soybeanMilk.core.bean.converters.DoubleConverter;
import org.soybeanMilk.core.bean.converters.FloatConverter;
import org.soybeanMilk.core.bean.converters.IntegerConverter;
import org.soybeanMilk.core.bean.converters.LongConverter;
import org.soybeanMilk.core.bean.converters.ShortConverter;
import org.soybeanMilk.core.bean.converters.SqlDateConverter;
import org.soybeanMilk.core.bean.converters.SqlTimeConverter;
import org.soybeanMilk.core.bean.converters.SqlTimestampConverter;

/**
 * ?
 * <p>
 * ??<br>
 * <table border="1" cellspacing="1" cellpadding="3">
 *   <tr><td>?</td><td></td></tr>
 *   <tr>
 *      <td>String</td>
 *      <td>
 *         boolean, Boolean; byte, Byte; char, Character; double, Double; float, Float;<br>
 *         int, Integer; long, Long; short, Short;<br>
 *         java.math.BigDecimal; java.math.BigInteger; java.util.Date; java.sql.Date; java.sql.Time; java.sql.Timestamp;<br>
 *         enum
 *      </td>
 *   </tr><tr>
 *      <td>String[]</td>
 *      <td>
 *         ??java.util.List?java.util.Set<br>
 *         int[]??boolean[]??Short[]??List&lt;Integer&gt;?List&lt;Date&gt;?Set&lt;Integer&gt;?Set&lt;Date&gt; <br>
 *   </td></tr>
 *   <tr>
 *      <td>
 *         Map&lt;String, ?&gt;
 *      </td><td>
 *         JavaBean; List&lt;JavaBean&gt;; Set&lt;JavaBean&gt;; Map&lt;SomeKeyType, JavaBean&gt;
 *      </td>
 *   </tr>
 * </table>
 * </p>
 * <p>
 * Map&lt;String, ?&gt;???<i>?</i>?
 * </p>
 * <p>
 *       <i>accessor</i>[.<i>accessor</i> ...]
 * </p>
 * <p>
 * <i>accessor</i>???
 * </p>
 * <ul>
 *    <li>
 *     JavaBean??<br>
 *     JavaBean<i>accessor</i>
 *  </li>
 *  <li>
 *     List?Set?<br>
 *     ??<i>accessor</i>
 *  </li>
 *  <li>
 *     Map<br>
 *     ?<i>accessor</i>
 *  </li>
 *  <li>
 *     "class"?<br>
 *     ????????<br>
 *     "class" -&gt; "org.somePkg.SomeJavaBean"<br>
 *     &nbsp;&nbsp;&nbsp;&nbsp;?org.somePkg.SomeJavaBean?<br>
 *     "2.class" -&gt; "org.somePkg.SomeJavaBeanSub"<br>
 *     &nbsp;&nbsp;&nbsp;&nbsp;??org.somePkg.SomeJavaBeanSub?<br>
 *     "someKey.class" -&gt; "org.somePkg.SomeJavaBean"<br>
 *     &nbsp;&nbsp;&nbsp;&nbsp;?"someKey"org.somePkg.SomeJavaBean?<br>
 *  </li>
 *  <li>
 *     "classes"?<br>
 *     ???List?Set<br>
 *     "classes" -&gt; ["org.somePkg.SomeJava", "org.somePkg.SomeJavaSub"]<br>
 *     "2.class" -&gt; "org.somePkg.SomeJavaBean"<br>
 *     &nbsp;&nbsp;&nbsp;&nbsp;?org.somePkg.SomeJava?org.somePkg.SomeJavaSub?
 *     org.somePkg.SomeJavaBean?
 *  </li>
 * </ul>
 * <p>
 * ??
 * <pre>
 * "id"                    -&gt;  "1"
 * "name"                  -&gt;  "jack"
 * "listChildren.id"       -&gt;  ["11", "12"]
 * "listChildren.name"     -&gt;  ["tom", "mary"]
 * "setChildren.id"        -&gt;  ["11", "12"]
 * "setChildren.name"      -&gt;  ["tom", "mary"]
 * "arrayChildren.id"      -&gt;  ["11", "12"]
 * "arrayChildren.name"    -&gt;  ["tom", "mary"]
 * "mapChildren.key0.id"   -&gt;  "11"
 * "mapChildren.key0.name" -&gt;  "tom"
 * "mapChildren.key1.id"   -&gt;  "22"
 * "mapChildren.key1.name" -&gt;  "mary"
 * </pre>
 * ?
 * <pre>
 * package org.somePkg;
 * 
 * class User{
 *    private Integer id;
 *    private String name;
 *    private List&lt;User&gt; listChildren;
 *    private Set&lt;User&gt; setChildren;
 *    private User[] arrayChildren;
 *    private Map&lt;String, User&gt; mapChildren;
 *    ...
 * }
 * </pre>
 * 
 * <pre>
 * "id"                             -&gt;  ["1","2","3"]
 * "name"                           -&gt;  ["jack","tom","cherry"]
 * "0.listChildren.id"              -&gt;  ["10","11"]
 * "0.listChildren.name"            -&gt;  ["jack10","jack11"]
 * "1.setChildren.0.id"             -&gt;  "20"
 * "1.setChildren.0.name"           -&gt;  "tom20"
 * "1.setChildren.1.id"             -&gt;  "21"
 * "1.setChildren.1.name"           -&gt;  "tom21"
 * "2.arrayChildren.0.id"           -&gt;  "30"
 * "2.arrayChildren.0.name"         -&gt;  "cherry30"
 * "2.arrayChildren.1.id"           -&gt;  "31"
 * "2.arrayChildren.1.name"         -&gt;  "cherry31"
 * "2.mapChildren.key0.id"          -&gt;  "30"
 * "2.mapChildren.key0.name"        -&gt;  "cherry30"
 * "2.mapChildren.key1.id"          -&gt;  "31"
 * "2.mapChildren.key1.name"        -&gt;  "cherry31"
 * </pre>
 * ?
 * <pre>
 * List&lt;User&gt;
 * Set&lt;User&gt;
 * User[]
 * </pre>
 * 
 * </p>
 * <p>
 * ?{@linkplain #addConverter(Type, Type, Converter)}???<br>
 * ?<code>String</code>?<code>String</code>?<code>toString()</code>
 * </p>
 * 
 * @author earthangry@gmail.com
 * @date 2010-10-6
 */
public class DefaultGenericConverter implements GenericConverter {
    private static Log log = LogFactory.getLog(DefaultGenericConverter.class);

    /**?*/
    public static final String KEY_CUSTOM_CLASS = "class";

    /**??*/
    public static final String KEY_CUSTOM_ELEMENT_CLASSES = "classes";

    private Map<ConverterKey, Converter> converters;

    /**
     * ??
     */
    public DefaultGenericConverter() {
        this(true);
    }

    /**
     * ?
     * @param initDefaultSupportConverter ??
     */
    public DefaultGenericConverter(boolean initDefaultSupportConverter) {
        if (initDefaultSupportConverter)
            addStringSourceConverters();
    }

    /**
     * ??
     * @return
     * @date 2010-12-29
     */
    public Collection<Converter> getSupportConverters() {
        return converters == null ? null : converters.values();
    }

    //@Override
    public void addConverter(Type sourceType, Type targetType, Converter converter) {
        if (getConverters() == null)
            setConverters(new HashMap<ConverterKey, Converter>());

        getConverters().put(generateConverterKey(sourceType, targetType), converter);

        if (log.isDebugEnabled())
            log.debug("add a support Converter " + SbmUtils.toString(converter.getClass()) + " for converting "
                    + SbmUtils.toString(sourceType) + " to " + SbmUtils.toString(targetType));
    }

    //@Override
    public Converter getConverter(Type sourceType, Type targetType) {
        Converter re = null;
        Map<ConverterKey, Converter> converters = getConverters();

        if (converters != null) {
            re = converters.get(generateConverterKey(sourceType, targetType));
            if (re == null && SbmUtils.isPrimitive(targetType))
                re = converters.get(generateConverterKey(sourceType, wrapType(targetType)));
        }

        return re;
    }

    //@Override
    @SuppressWarnings("unchecked")
    public <T> T convert(Object sourceObj, Type targetType) throws ConvertException {
        return (T) convertObjectToType(sourceObj, targetType);
    }

    /**
     * ?
     * @param obj
     * @param type
     * @return
     * @throws ConvertException
     * @date 2012-5-12
     */
    protected Object convertObjectToType(Object obj, Type type) throws ConvertException {
        if (log.isDebugEnabled())
            log.debug("start converting " + SbmUtils.toString(obj) + " to type " + SbmUtils.toString(type));

        Object result = null;

        if (obj == null) {
            if (SbmUtils.isPrimitive(type))
                throw new GenericConvertException("can not convert " + SbmUtils.toString(obj)
                        + " to primitive type " + SbmUtils.toString(type));
            else
                result = null;
        } else if (type == null || isInstanceOf(obj, wrapType(type))) {
            //Map????
            if (obj instanceof Map<?, ?>) {
                result = convertMapToType((Map<?, ?>) obj, type);
            } else {
                result = obj;
            }
        } else {
            Converter converter = getConverter(obj.getClass(), type);

            if (converter != null)
                result = doConvert(converter, obj, type);
            else
                result = convertWhenNoSupportConverter(obj, type);
        }

        if (log.isDebugEnabled())
            log.debug("finish converting " + SbmUtils.toString(obj) + " to type " + SbmUtils.toString(type));

        return result;
    }

    /**
     * ??
     * @param obj
     * @param type
     * @return
     * @date 2011-1-5
     */
    protected Object convertWhenNoSupportConverter(Object obj, Type type) throws ConvertException {
        if (obj == null)
            return null;

        Object result = null;

        if (obj instanceof String) {
            result = convertStringToType((String) obj, type);
        } else if (obj.getClass().isArray()) {
            result = convertArrayToType(obj, type);
        } else if (obj instanceof Map<?, ?>) {
            result = convertMapToType((Map<?, ?>) obj, type);
        } else if (String.class.equals(type)) {
            result = obj.toString();
        } else
            result = converterNotFoundThrow(obj.getClass(), type);

        return result;
    }

    @SuppressWarnings("unchecked")
    protected Object convertStringToType(String str, Type type) throws ConvertException {
        Object result = null;

        if (str == null || str.length() == 0) {
            if (SbmUtils.isPrimitive(type))
                throw new GenericConvertException("can not convert " + SbmUtils.toString(str)
                        + " to primitive type " + SbmUtils.toString(type));
            else
                return null;
        } else if (SbmUtils.isClassType(type)) {
            @SuppressWarnings("rawtypes")
            Class clazz = SbmUtils.narrowToClass(type);

            if (clazz.isEnum())
                result = Enum.valueOf(clazz, str);
            else
                result = converterNotFoundThrow(str.getClass(), type);
        } else if (type instanceof TypeVariable<?>) {
            result = convertObjectToType(str, reify(type));
        } else if (type instanceof WildcardType) {
            result = convertObjectToType(str, reify(type));
        } else if (type instanceof ParameterizedType) {
            result = converterNotFoundThrow(str.getClass(), type);
        } else if (type instanceof GenericArrayType) {
            result = converterNotFoundThrow(str.getClass(), type);
        } else
            result = converterNotFoundThrow(str.getClass(), type);

        return result;
    }

    /**
     * ?
     * @param array
     * @param type
     * @return
     * @throws ConvertException
     * @date 2012-5-14
     */
    protected Object convertArrayToType(Object array, Type type) throws ConvertException {
        if (array == null)
            return null;

        Object result = null;

        if (type == null || isInstanceOf(array, type)) {
            result = array;
        } else if (SbmUtils.isClassType(type)) {
            result = convertArrayToClass(array, SbmUtils.narrowToClass(type));
        } else if (type instanceof ParameterizedType) {
            result = convertArrayToParameterrizedType(array, (ParameterizedType) type);
        } else if (type instanceof GenericArrayType) {
            result = convertArrayToGenericArrayType(array, (GenericArrayType) type);
        } else if (type instanceof TypeVariable<?>) {
            result = convertObjectToType(array, reify(type));
        } else if (type instanceof WildcardType) {
            result = convertObjectToType(array, reify(type));
        } else
            result = converterNotFoundThrow(array.getClass(), type);

        return result;
    }

    /**
     * ?{@linkplain Class}
     * @param array
     * @param clazz
     * @return
     * @throws ConvertException
     * @date 2012-5-14
     */
    protected Object convertArrayToClass(Object array, Class<?> clazz) throws ConvertException {
        Object result = null;

        if (clazz.isArray()) {
            result = convertArrayToArray(array, clazz.getComponentType());
        } else
            result = converterNotFoundThrow(array.getClass(), clazz);

        return result;
    }

    /**
     * ?{@linkplain ParameterizedType}
     * @param array
     * @param type
     * @return
     * @throws ConvertException
     * @date 2012-5-14
     */
    protected Object convertArrayToParameterrizedType(Object array, ParameterizedType type)
            throws ConvertException {
        Object result = null;

        Type rt = type.getRawType();
        Type[] atas = type.getActualTypeArguments();

        if (SbmUtils.isClassType(rt)) {
            Class<?> actualType = SbmUtils.narrowToClass(rt);

            //List<T>
            if (isAncestorType(List.class, actualType)) {
                result = convertArrayToList(array, actualType, atas[0]);
            }
            //Set<T>
            else if (isAncestorType(Set.class, actualType)) {
                List<?> list = convertArrayToList(array, List.class, atas[0]);
                result = listToSet(list, actualType);
            } else
                result = converterNotFoundThrow(array.getClass(), type);
        } else
            result = converterNotFoundThrow(array.getClass(), type);

        return result;
    }

    /**
     * ?{@linkplain GenericArrayType}
     * @param array
     * @param type
     * @return
     * @throws ConvertException
     * @date 2012-5-14
     */
    protected Object convertArrayToGenericArrayType(Object array, GenericArrayType type) throws ConvertException {
        Object result = null;

        Type ct = type.getGenericComponentType();

        result = convertArrayToArray(array, ct);

        return result;
    }

    /**
     * ??
     * @param array
     * @param elementType
     * @return
     * @date 2012-2-20
     */
    protected Object convertArrayToArray(Object array, Type elementType) throws ConvertException {
        Object result = null;

        int len = Array.getLength(array);

        result = instance(elementType, len);

        for (int i = 0; i < len; i++) {
            Object v = convertObjectToType(Array.get(array, i), elementType);
            Array.set(result, i, v);
        }

        return result;
    }

    /**
     * ?{@linkplain java.util.List List}
     * @param array
     * @param listClass
     * @param elementType
     * @return
     * @date 2011-1-5
     */
    @SuppressWarnings("unchecked")
    protected List<?> convertArrayToList(Object array, Class<?> listClass, Type elementType)
            throws ConvertException {
        List<Object> result = null;

        result = (List<Object>) instance(listClass, -1);

        for (int i = 0, len = Array.getLength(array); i < len; i++)
            result.add(convertObjectToType(Array.get(array, i), elementType));

        return result;
    }

    /**
     * ?
     * @param map
     * @param type
     * @return
     * @throws ConvertException
     * @date 2012-5-14
     */
    @SuppressWarnings("unchecked")
    protected Object convertMapToType(Map<?, ?> map, Type type) throws ConvertException {
        Object result = null;

        //
        Type customType = getMapCustomTargetType(map, null);
        if (customType != null)
            type = customType;

        if (type == null) {
            result = map;
        } else if (SbmUtils.isClassType(type)) {
            Class<?> clazz = SbmUtils.narrowToClass(type);

            //Map???
            if (customType == null && isAncestorType(Map.class, clazz)) {
                result = map;
            } else {
                result = convertPropertyValueMapToClass(toPropertyValueMap((Map<String, ?>) map), clazz);
            }
        } else if (type instanceof ParameterizedType) {
            boolean convert = true;

            ParameterizedType pt = (ParameterizedType) type;
            Type rt = pt.getRawType();

            //Map<?, ?>Map<Object, Object>???
            if (isAncestorType(rt, map.getClass())) {
                Type[] at = pt.getActualTypeArguments();
                if (at != null && at.length == 2 && ((Object.class.equals(at[0]) && Object.class.equals(at[1]))
                        || (isSimpleWildcardType(at[0]) && isSimpleWildcardType(at[1])))) {
                    convert = false;
                }
            }

            if (convert)
                result = convertPropertyValueMapToParameterrizedType(toPropertyValueMap((Map<String, ?>) map), pt);
            else
                result = map;
        } else if (type instanceof GenericArrayType) {
            result = convertPropertyValueMapToGenericArrayType(toPropertyValueMap((Map<String, ?>) map),
                    (GenericArrayType) type);
        } else if (type instanceof TypeVariable<?>) {
            result = convertObjectToType(map, reify(type));
        } else if (type instanceof WildcardType) {
            result = convertObjectToType(map, reify(type));
        } else
            result = converterNotFoundThrow(map.getClass(), type);

        return result;
    }

    /**
     * ?<code>Class&lt?&gt;</code>
     * ?JavaBean??List?Set?Map
     * @param pvm 
     * @param type
     * @return
     */
    protected Object convertPropertyValueMapToClass(PropertyValueMap pvm, Class<?> type) throws ConvertException {
        Object result = null;

        //
        if (type.isArray()) {
            Class<?> eleClass = type.getComponentType();

            List<?> tmpRe = convertPropertyValueMapToList(pvm, List.class, eleClass);

            result = listToArray(tmpRe, eleClass);
        }
        //List
        else if (isAncestorType(List.class, type)) {
            result = convertPropertyValueMapToList(pvm, type, null);
        }
        //Set
        else if (isAncestorType(Set.class, type)) {
            List<?> tmpRe = convertPropertyValueMapToList(pvm, List.class, null);
            result = listToSet(tmpRe, type);
        }
        //Map
        else if (isAncestorType(Map.class, type)) {
            result = convertPropertyValueMapToMap(pvm, type, null, null);
        }
        //JavaBean
        else {
            result = convertPropertyValueMapToJavaBeanClass(pvm, type);
        }

        return result;
    }

    /**
     * ?JavaBean
     * @param pvm
     * @param javaBeanClass
     * @return
     * @throws ConvertException
     * @date 2012-5-14
     */
    protected Object convertPropertyValueMapToJavaBeanClass(PropertyValueMap pvm, Class<?> javaBeanClass)
            throws ConvertException {
        Object result = null;

        PropertyInfo beanInfo = PropertyInfo.getPropertyInfo(javaBeanClass);

        if (!beanInfo.hasSubPropertyInfo())
            throw new GenericConvertException("the target javaBean Class " + SbmUtils.toString(javaBeanClass)
                    + " is not valid, it has no javaBean property");

        Set<String> propertyKeys = pvm.keySet();
        for (String property : propertyKeys) {
            //
            if (KEY_CUSTOM_CLASS.equals(property))
                continue;

            PropertyInfo propInfo = null;
            if (!pvm.isCleaned()) {
                propInfo = beanInfo.getSubPropertyInfo(property);

                //
                if (propInfo == null)
                    continue;
            } else
                propInfo = getSubPropertyInfoNotNull(beanInfo, property);

            //?
            if (result == null)
                result = instance(beanInfo.getPropType(), -1);

            try {
                setJavaBeanProperty(result, propInfo, pvm.get(property));
            } catch (ConvertException e) {
                handlePropertyValueMapConvertException(pvm, property, e);
            }
        }

        return result;
    }

    /**
     * ?{@linkplain ParameterizedType}
     * @param pvm
     * @param type
     * @return
     * @throws ConvertException
     * @date 2012-5-14
     */
    protected Object convertPropertyValueMapToParameterrizedType(PropertyValueMap pvm, ParameterizedType type)
            throws ConvertException {
        Object result = null;

        Type rt = type.getRawType();
        Type[] atas = type.getActualTypeArguments();

        if (SbmUtils.isClassType(rt)) {
            Class<?> actualType = SbmUtils.narrowToClass(rt);

            //List<T>
            if (isAncestorType(List.class, actualType)) {
                result = convertPropertyValueMapToList(pvm, actualType, atas[0]);
            }
            //Set<T>
            else if (isAncestorType(Set.class, actualType)) {
                List<?> tmpRe = convertPropertyValueMapToList(pvm, List.class, atas[0]);
                result = listToSet(tmpRe, actualType);
            }
            //Map<K, V>
            else if (isAncestorType(Map.class, actualType)) {
                result = convertPropertyValueMapToMap(pvm, actualType, atas[0], atas[1]);
            } else
                result = converterNotFoundThrow(pvm.getClass(), type);
        } else
            result = converterNotFoundThrow(pvm.getClass(), type);

        return result;
    }

    /**
     * ?{@linkplain GenericArrayType}
     * @param pvm
     * @param type
     * @return
     * @throws ConvertException
     * @date 2012-5-14
     */
    protected Object convertPropertyValueMapToGenericArrayType(PropertyValueMap pvm, GenericArrayType type)
            throws ConvertException {
        Object result = null;

        Type ct = type.getGenericComponentType();

        result = convertPropertyValueMapToList(pvm, List.class, ct);
        result = listToArray((List<?>) result, ct);

        return result;
    }

    /**
     * ?JavaBean
     * @param pvm
     * @param listClass
     * @param elementType
     * @return
     */
    protected List<?> convertPropertyValueMapToList(PropertyValueMap pvm, Class<?> listClass, Type elementType)
            throws ConvertException {
        if (pvm == null || pvm.isEmpty())
            return null;

        @SuppressWarnings("unchecked")
        List<Object> result = (List<Object>) instance(listClass, -1);

        Type[] customTypes = getMapTargetListElementCustomTypes(pvm);

        //??????
        PropertyInfo commonEleBeanInfo = null;
        Type commonEleType = null;
        if (elementType != null)
            commonEleType = reify(elementType);
        else {
            Type cet = getMapTargetListElementCustomType(pvm, 0, customTypes, elementType);

            //?
            if (cet != null)
                commonEleType = reify(cet);
        }

        if (commonEleType != null && SbmUtils.isClassType(commonEleType))
            commonEleBeanInfo = PropertyInfo.getPropertyInfo(SbmUtils.narrowToClass(commonEleType));

        Set<String> propertyKeyes = pvm.keySet();
        for (String property : propertyKeyes) {
            //
            if (KEY_CUSTOM_ELEMENT_CLASSES.equals(property) || KEY_CUSTOM_CLASS.equals(property))
                continue;

            Object value = pvm.get(property);

            //?
            if (isIndexAccessor(property)) {
                int idx = -1;
                try {
                    idx = Integer.parseInt(property);
                } catch (Exception e) {
                    throw new GenericConvertException("illegal index value " + SbmUtils.toString(property)
                            + " of property " + SbmUtils.toString(pvm.getPropertyNamePath(property)), e);
                }

                while (result.size() < idx + 1)
                    result.add(null);

                Object element = result.get(idx);

                //?
                if (element != null && (value instanceof PropertyValueMap)) {
                    PropertyValueMap subPropMap = (PropertyValueMap) value;

                    PropertyInfo subBeanInfo = PropertyInfo.getPropertyInfo(element.getClass());
                    Set<String> subPropKeys = subPropMap.keySet();
                    for (String subProp : subPropKeys) {
                        //
                        if (KEY_CUSTOM_CLASS.equals(subProp))
                            continue;

                        PropertyInfo subPropInfo = getSubPropertyInfoNotNull(subBeanInfo, subProp);

                        try {
                            setJavaBeanProperty(element, subPropInfo, subPropMap.get(subProp));
                        } catch (ConvertException e) {
                            handlePropertyValueMapConvertException(pvm, property, e);
                        }
                    }
                }
                //?
                else {
                    try {
                        element = convertObjectToType(value,
                                getMapTargetListElementCustomType(pvm, idx, customTypes, elementType));
                    } catch (ConvertException e) {
                        handlePropertyValueMapConvertException(pvm, property, e);
                    }

                    result.set(idx, element);
                }
            }
            //???JavaBeancommonEleBeanInfo?
            else {
                if (value == null)
                    continue;

                PropertyInfo propInfo = null;
                //
                if (!pvm.isCleaned() && commonEleBeanInfo != null)
                    propInfo = commonEleBeanInfo.getSubPropertyInfo(property);
                else {
                    if (commonEleBeanInfo == null)
                        throw new GenericConvertException("illegal key "
                                + SbmUtils.toString(pvm.getPropertyNamePath(property)) + " in Map "
                                + SbmUtils.toString(pvm)
                                + ", you must specify the target collection index but not JavaBean property because its element type "
                                + SbmUtils.toString(commonEleType) + " is not JavaBean class");

                    propInfo = getSubPropertyInfoNotNull(commonEleBeanInfo, property);
                }

                if (propInfo == null)
                    continue;

                //?
                if (value.getClass().isArray()) {
                    int len = Array.getLength(value);

                    while (result.size() < len)
                        result.add(null);

                    for (int i = 0; i < len; i++) {
                        Object element = result.get(i);
                        if (element == null) {
                            element = instance(getMapTargetListElementCustomType(pvm, i, customTypes, elementType),
                                    -1);
                            result.set(i, element);
                        }

                        try {
                            setJavaBeanProperty(element, propInfo, Array.get(value, i));
                        } catch (ConvertException e) {
                            handlePropertyValueMapConvertException(pvm, property, e);
                        }
                    }
                }
                //??????
                else if (value instanceof PropertyValueMap) {
                    PropertyValueMap pppm = (PropertyValueMap) value;

                    List<?> propList = convertPropertyValueMapToList(pppm, List.class, propInfo.getPropType());

                    while (result.size() < propList.size())
                        result.add(null);

                    for (int i = 0; i < propList.size(); i++) {
                        Object element = result.get(i);
                        if (element == null) {
                            element = instance(getMapTargetListElementCustomType(pvm, i, customTypes, elementType),
                                    -1);
                            result.set(i, element);
                        }

                        try {
                            setJavaBeanProperty(element, propInfo, propList.get(i));
                        } catch (ConvertException e) {
                            handlePropertyValueMapConvertException(pvm, property, e);
                        }
                    }
                }
                //
                else {
                    while (result.size() < 1)
                        result.add(null);

                    Object element = result.get(0);
                    if (element == null) {
                        element = instance(getMapTargetListElementCustomType(pvm, 0, customTypes, elementType), -1);
                        result.set(0, element);
                    }

                    try {
                        setJavaBeanProperty(element, propInfo, value);
                    } catch (ConvertException e) {
                        handlePropertyValueMapConvertException(pvm, property, e);
                    }
                }
            }
        }

        return result;
    }

    /**
     * ?, <code>sourceMap</code>??
     * @param pvm
     * @param mapClass
     * @param keyType
     * @param valueType
     * @return
     */
    protected Map<?, ?> convertPropertyValueMapToMap(PropertyValueMap pvm, Class<?> mapClass, Type keyType,
            Type valueType) throws ConvertException {
        if (pvm == null)
            return null;

        @SuppressWarnings("unchecked")
        Map<Object, Object> result = (Map<Object, Object>) instance(mapClass, -1);

        Set<String> keys = pvm.keySet();
        for (String key : keys) {
            Object tk = null;
            Object tv = null;

            try {
                tk = convertObjectToType(key, keyType);
            } catch (ConvertException e) {
                throw new GenericConvertException("convert " + SbmUtils.toString(key) + " in key "
                        + SbmUtils.toString(pvm.getPropertyNamePath(key)) + " to Map key of type "
                        + SbmUtils.toString(keyType) + " failed", e);
            }

            try {
                Object value = pvm.get(key);
                tv = convertObjectToType(value, valueType);
            } catch (ConvertException e) {
                handlePropertyValueMapConvertException(pvm, key, e);
            }

            result.put(tk, tv);
        }

        return result;
    }

    /**
     * ??class?{@linkplain Class}
     * @param map
     * @return
     * @date 2012-5-20
     */
    protected Type getMapCustomTargetType(Map<?, ?> map, Type defaultType) {
        if (map.isEmpty())
            return defaultType;

        Object typeObj = map.get(KEY_CUSTOM_CLASS);

        if (typeObj == null)
            return defaultType;
        else if (typeObj instanceof Type)
            return (Type) typeObj;
        else if (typeObj instanceof String) {
            Type re = nameToType((String) typeObj);
            return (re == null ? defaultType : re);
        } else
            throw new GenericConvertException(
                    "illegal custom target type " + SbmUtils.toString(typeObj) + " with key "
                            + SbmUtils.toString(KEY_CUSTOM_ELEMENT_CLASSES) + " in Map " + SbmUtils.toString(map));
    }

    /**
     * ??
     * @param map
     * @return
     * @date 2012-5-21
     */
    protected Type[] getMapTargetListElementCustomTypes(Map<?, ?> map) {
        if (map.isEmpty())
            return null;

        Object typeObj = map.get(KEY_CUSTOM_ELEMENT_CLASSES);

        if (typeObj == null)
            return null;
        else {
            Type[] re = null;

            if (typeObj instanceof String[]) {
                String[] strTypes = (String[]) typeObj;
                re = new Type[strTypes.length];

                for (int i = 0; i < strTypes.length; i++)
                    re[i] = nameToType(strTypes[i]);
            } else if (typeObj instanceof Type[]) {
                re = (Type[]) typeObj;
            } else
                throw new GenericConvertException("illegal custom target type " + SbmUtils.toString(typeObj)
                        + " with key " + SbmUtils.toString(KEY_CUSTOM_ELEMENT_CLASSES) + " in Map "
                        + SbmUtils.toString(map));

            return re;
        }
    }

    /**
     * ??
     * @param map
     * @param idx ??
     * @param customListEleTypes
     * @param defaultType
     * @return
     * @date 2012-5-21
     */
    protected Type getMapTargetListElementCustomType(Map<?, ?> map, int idx, Type[] customListEleTypes,
            Type defaultType) {
        Type re = null;

        Object cv = map.get(String.valueOf(idx));

        //"0.class"
        if (cv != null && (cv instanceof Map<?, ?>)) {
            re = getMapCustomTargetType((Map<?, ?>) cv, null);
        }

        if (re == null && customListEleTypes != null && customListEleTypes.length > 0) {
            if (idx >= customListEleTypes.length) {
                //
                if (defaultType == null)
                    re = customListEleTypes[customListEleTypes.length - 1];
            } else
                re = customListEleTypes[idx];
        }

        return (re == null ? defaultType : re);
    }

    /**
     * ?WildcardType???
     * @param type
     * @return
     * @date 2012-5-20
     */
    protected boolean isSimpleWildcardType(Type type) {
        if (!(type instanceof WildcardType))
            return false;

        WildcardType wt = (WildcardType) type;

        Type[] lb = wt.getLowerBounds();
        Type[] ub = wt.getUpperBounds();

        if ((lb == null || lb.length == 0) && (ub == null || ub.length == 0))
            return true;
        else
            return false;
    }

    /**
     * ?????
     * @param str
     * @return
     */
    protected boolean isIndexAccessor(String str) {
        if (str == null || str.length() == 0)
            return false;

        boolean digit = true;

        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c < '0' || c > '9') {
                digit = false;
                break;
            }
        }

        return digit;
    }

    /**
     * ?{@linkplain Converter ?}?<code>null</code>
     * @param sourceType
     * @param targetType
     * @return
     */
    protected Converter getConverterNotNull(Type sourceType, Type targetType) {
        Converter cvt = getConverter(sourceType, targetType);

        if (cvt == null)
            converterNotFoundThrow(sourceType, targetType);

        return cvt;
    }

    /**
     * ??
     * @param paramPropertyMap
     * @param key
     * @param e
     */
    protected void handlePropertyValueMapConvertException(PropertyValueMap paramPropertyMap, String key,
            ConvertException e) throws ConvertException {
        if (e instanceof MapConvertException)
            throw e;
        else
            throw new MapConvertException(paramPropertyMap.getPropertyNamePath(key), e.getSourceObject(),
                    e.getTargetType(), e.getCause());
    }

    /**
     * ?{@linkplain java.util.Set Set}??
     * @param list
     * @param setClass
     * @return
     * @date 2012-2-19
     */
    @SuppressWarnings("unchecked")
    protected Set<?> listToSet(List<?> list, Class<?> setClass) {
        Set<Object> result = null;

        if (list != null) {
            result = (Set<Object>) instance(setClass, -1);

            for (int i = 0, len = list.size(); i < len; i++)
                result.add(list.get(i));
        }

        return result;
    }

    /**
     * ???
     * @param list 
     * @param elementType 
     * @return
     * @date 2012-2-19
     */
    protected Object listToArray(List<?> list, Type elementType) {
        Object result = null;

        if (list != null) {
            int size = list.size();

            result = instance(elementType, size);

            for (int i = 0; i < size; i++)
                Array.set(result, i, list.get(i));
        }

        return result;
    }

    /**
     * ????
     * @param obj ?
     * @param propertyInfo ??
     * @param value 
     * @date 2012-2-20
     */
    protected void setJavaBeanProperty(Object obj, PropertyInfo propertyInfo, Object value)
            throws ConvertException {
        Type targetType = propertyInfo.getPropGenericType();
        if (!SbmUtils.isClassType(targetType))
            targetType = reify(targetType, propertyInfo.getOwnerClass());

        Object destValue = convertObjectToType(value, targetType);
        try {
            propertyInfo.getWriteMethod().invoke(obj, new Object[] { destValue });
        } catch (Exception e) {
            throw new GenericConvertException("exception occur while calling write method "
                    + SbmUtils.toString(propertyInfo.getWriteMethod()), e);
        }
    }

    /**
     * ???
     * @param parent
     * @param property
     * @return
     * @date 2012-2-26
     */
    protected PropertyInfo getSubPropertyInfoNotNull(PropertyInfo parent, String property) {
        PropertyInfo re = parent.getSubPropertyInfo(property);
        if (re == null)
            throw new GenericConvertException("can not find property " + SbmUtils.toString(property) + " in class "
                    + SbmUtils.toString(parent.getPropType()));

        return re;
    }

    /**
     * ??
     * @param converter
     * @param sourceObj
     * @param targetType
     * @return
     * @date 2011-4-10
     */
    protected Object doConvert(Converter converter, Object sourceObj, Type targetType) throws ConvertException {
        return converter.convert(sourceObj, targetType);
    }

    /**
     * ??
     * @param sourceType
     * @param targetType
     * @return
     * @date 2012-4-1
     */
    protected Object converterNotFoundThrow(Type sourceType, Type targetType) {
        throw new GenericConvertException("can not find Converter for converting " + SbmUtils.toString(sourceType)
                + " to " + SbmUtils.toString(targetType));
    }

    /**
     * ?
     * @param map
     * @return
     * @date 2012-4-1
     */
    protected PropertyValueMap toPropertyValueMap(Map<String, ?> map) {
        return ((map instanceof PropertyValueMap) ? (PropertyValueMap) map : new PropertyValueMap(map));
    }

    /**
     * ??
     * @param sourceClass
     * @param targetClass
     * @return
     */
    protected ConverterKey generateConverterKey(Type sourceType, Type targetType) {
        return new ConverterKey(sourceType, targetType);
    }

    /**
     * <br>
     * <code>type</code>{@linkplain List}?{@linkplain Set}?{@linkplain Map}?
     * <code>arrayLength</code>?<code>type</code>?0?0?
     * @param type 
     * @param arrayLength ?
     * @return
     * @date 2010-12-29
     */
    protected Object instance(Type type, int arrayLength) {
        Class<?> clazz = null;

        if (SbmUtils.isClassType(type)) {
            if (java.util.List.class.equals(type))
                clazz = ArrayList.class;
            else if (java.util.Set.class.equals(type))
                clazz = HashSet.class;
            else if (java.util.Map.class.equals(type))
                clazz = HashMap.class;
            else
                clazz = SbmUtils.narrowToClass(type);
        } else
            clazz = null;

        try {
            if (clazz == null) {
                String fqn = SbmUtils.getFullQualifiedClassName(type);
                clazz = SbmUtils.narrowToClass(nameToType(fqn));
            }

            if (arrayLength < 0)
                return clazz.newInstance();
            else
                return Array.newInstance(clazz, arrayLength);
        } catch (Exception e) {
            throw new GenericConvertException(
                    "exception occur while creating instance of type " + SbmUtils.toString(type), e);
        }
    }

    /**
     * ??
     * @param ancestor
     * @param descendant
     * @return
     * @date 2012-5-24
     */
    protected boolean isAncestorType(Type ancestor, Type descendant) {
        return SbmUtils.isAncestorType(ancestor, descendant);
    }

    /**
     * ??
     * @param obj
     * @param type
     * @return
     * @date 2012-5-24
     */
    protected boolean isInstanceOf(Object obj, Type type) {
        return SbmUtils.isInstanceOf(obj, type);
    }

    /**
     * ?<code>type</code>?
     * @param type
     * @return
     * @date 2012-5-24
     */
    protected Type wrapType(Type type) {
        return SbmUtils.wrapType(type);
    }

    /**
     * ?
     * @param typeObj
     * @return
     * @date 2012-5-21
     */
    protected Type nameToType(String str) {
        try {
            return SbmUtils.nameToType(str);
        } catch (ClassNotFoundException e) {
            throw new GenericConvertException("type named " + SbmUtils.toString(str) + " not found", e);
        }
    }

    /**
     * ?{@linkplain TypeVariable}{@linkplain WildcardType}
     * @param type
     * @return
     * @date 2012-5-15
     */
    protected Type reify(Type type) {
        return SbmUtils.reify(type, null);
    }

    /**
     * ?{@linkplain TypeVariable}{@linkplain WildcardType}<code>ownerClass</code>
     * @param type
     * @param ownerClass
     * @return
     * @date 2012-5-15
     */
    protected Type reify(Type type, Class<?> ownerClass) {
        return SbmUtils.reify(type, ownerClass);
    }

    protected Map<ConverterKey, Converter> getConverters() {
        return converters;
    }

    protected void setConverters(Map<ConverterKey, Converter> converters) {
        this.converters = converters;
    }

    /**
     * ????
     */
    protected void addStringSourceConverters() {
        //?
        addConverter(String.class, Boolean.class, new BooleanConverter());
        addConverter(String.class, Byte.class, new ByteConverter());
        addConverter(String.class, Character.class, new CharacterConverter());
        addConverter(String.class, Double.class, new DoubleConverter());
        addConverter(String.class, Float.class, new FloatConverter());
        addConverter(String.class, Integer.class, new IntegerConverter());
        addConverter(String.class, Long.class, new LongConverter());
        addConverter(String.class, Short.class, new ShortConverter());

        //
        addConverter(String.class, java.math.BigDecimal.class, new BigDecimalConverter());
        addConverter(String.class, java.math.BigInteger.class, new BigIntegerConverter());
        addConverter(String.class, java.util.Date.class, new DateConverter());
        addConverter(String.class, java.sql.Date.class, new SqlDateConverter());
        addConverter(String.class, java.sql.Time.class, new SqlTimeConverter());
        addConverter(String.class, java.sql.Timestamp.class, new SqlTimestampConverter());
    }

    /**
     * ?
     * @author earthangry@gmail.com
     * @date 2011-10-8
     */
    protected static class ConverterKey {
        private Type sourceType;
        private Type targetType;

        public ConverterKey(Type sourceType, Type targetType) {
            this.sourceType = sourceType;
            this.targetType = targetType;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((sourceType == null) ? 0 : sourceType.hashCode());
            result = prime * result + ((targetType == null) ? 0 : targetType.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            ConverterKey other = (ConverterKey) obj;
            if (sourceType == null) {
                if (other.sourceType != null)
                    return false;
            } else if (!sourceType.equals(other.sourceType))
                return false;
            if (targetType == null) {
                if (other.targetType != null)
                    return false;
            } else if (!targetType.equals(other.targetType))
                return false;
            return true;
        }
    }

    /**
     * <i>?</i><br>
     * ???????
     * {@linkplain #clean}?????
     * ??????????
     * 
     * @author earthangry@gmail.com
     * @date 2012-3-27
     */
    protected static class PropertyValueMap extends HashMap<String, Object> {
        private static final long serialVersionUID = 1L;

        /**?-??*/
        public static final String RESERVE_KEY_CUSTOM_CLASS = "class";

        /**??*/
        private String propertyName;

        /***/
        private PropertyValueMap parent;

        /**??*/
        private boolean cleaned;

        /**
         * ?
         * @param map ?<i>?</i>
         */
        public PropertyValueMap(Map<String, ?> map) {
            this(map, true);
        }

        /**
         * ?
         * @param map ?<i>?</i>
         * @param cleaned ???
         */
        public PropertyValueMap(Map<String, ?> map, boolean cleaned) {
            super();

            this.cleaned = cleaned;
            this.resolve(map);
        }

        /**
         * 
         */
        private PropertyValueMap(String propertyName, PropertyValueMap parent) {
            super();
            this.propertyName = propertyName;
            this.parent = parent;
            this.cleaned = true;
        }

        /**
         * ????
         * @param propertyName
         * @return
         */
        public String getPropertyNamePath(String propertyName) {
            String result = null;

            if (this.parent != null)
                result = this.parent.getPropertyNamePath(this.propertyName);
            else
                result = this.propertyName;

            if (result == null || result.length() == 0)
                return propertyName;
            else
                return result + Constants.ACCESSOR + propertyName;
        }

        public boolean isCleaned() {
            return cleaned;
        }

        public void setCleaned(boolean cleaned) {
            this.cleaned = cleaned;
        }

        public String getPropertyName() {
            return propertyName;
        }

        public void setPropertyName(String propertyName) {
            this.propertyName = propertyName;
        }

        public PropertyValueMap getParent() {
            return parent;
        }

        public void setParent(PropertyValueMap parent) {
            this.parent = parent;
        }

        /**
         * <i>?</i>
         * @param map <i>?</i>
         */
        protected void resolve(Map<String, ?> map) {
            Set<String> keys = map.keySet();

            for (String key : keys) {
                String[] propKeys = SbmUtils.splitAccessExpression(key);

                PropertyValueMap parent = this;

                for (int i = 0; i < propKeys.length; i++) {
                    if (i == propKeys.length - 1) {
                        parent.put(propKeys[i], map.get(key));
                    } else {
                        PropertyValueMap tmp = (PropertyValueMap) parent.get(propKeys[i]);
                        if (tmp == null) {
                            tmp = new PropertyValueMap(propKeys[i], parent);
                            parent.put(propKeys[i], tmp);
                        }

                        parent = tmp;
                    }
                }
            }
        }

        @Override
        public String toString() {
            return getClass().getSimpleName() + " [propertyName=" + getPropertyName() + ", cleaned=" + cleaned
                    + ", " + super.toString() + "]";
        }
    }
}