Java tutorial
/** * 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<Integer>?List<Date>?Set<Integer>?Set<Date> <br> * </td></tr> * <tr> * <td> * Map<String, ?> * </td><td> * JavaBean; List<JavaBean>; Set<JavaBean>; Map<SomeKeyType, JavaBean> * </td> * </tr> * </table> * </p> * <p> * Map<String, ?>???<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" -> "org.somePkg.SomeJavaBean"<br> * ?org.somePkg.SomeJavaBean?<br> * "2.class" -> "org.somePkg.SomeJavaBeanSub"<br> * ??org.somePkg.SomeJavaBeanSub?<br> * "someKey.class" -> "org.somePkg.SomeJavaBean"<br> * ?"someKey"org.somePkg.SomeJavaBean?<br> * </li> * <li> * "classes"?<br> * ???List?Set<br> * "classes" -> ["org.somePkg.SomeJava", "org.somePkg.SomeJavaSub"]<br> * "2.class" -> "org.somePkg.SomeJavaBean"<br> * ?org.somePkg.SomeJava?org.somePkg.SomeJavaSub? * org.somePkg.SomeJavaBean? * </li> * </ul> * <p> * ?? * <pre> * "id" -> "1" * "name" -> "jack" * "listChildren.id" -> ["11", "12"] * "listChildren.name" -> ["tom", "mary"] * "setChildren.id" -> ["11", "12"] * "setChildren.name" -> ["tom", "mary"] * "arrayChildren.id" -> ["11", "12"] * "arrayChildren.name" -> ["tom", "mary"] * "mapChildren.key0.id" -> "11" * "mapChildren.key0.name" -> "tom" * "mapChildren.key1.id" -> "22" * "mapChildren.key1.name" -> "mary" * </pre> * ? * <pre> * package org.somePkg; * * class User{ * private Integer id; * private String name; * private List<User> listChildren; * private Set<User> setChildren; * private User[] arrayChildren; * private Map<String, User> mapChildren; * ... * } * </pre> * * <pre> * "id" -> ["1","2","3"] * "name" -> ["jack","tom","cherry"] * "0.listChildren.id" -> ["10","11"] * "0.listChildren.name" -> ["jack10","jack11"] * "1.setChildren.0.id" -> "20" * "1.setChildren.0.name" -> "tom20" * "1.setChildren.1.id" -> "21" * "1.setChildren.1.name" -> "tom21" * "2.arrayChildren.0.id" -> "30" * "2.arrayChildren.0.name" -> "cherry30" * "2.arrayChildren.1.id" -> "31" * "2.arrayChildren.1.name" -> "cherry31" * "2.mapChildren.key0.id" -> "30" * "2.mapChildren.key0.name" -> "cherry30" * "2.mapChildren.key1.id" -> "31" * "2.mapChildren.key1.name" -> "cherry31" * </pre> * ? * <pre> * List<User> * Set<User> * 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<?></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() + "]"; } } }