Modified: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONPopulator.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONPopulator.java?rev=799110&r1=799109&r2=799110&view=diff ============================================================================== --- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONPopulator.java (original) +++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONPopulator.java Wed Jul 29 22:44:26 2009 @@ -1,424 +1,444 @@ -package org.apache.struts2.json; - -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import org.apache.struts2.json.annotations.JSON; - -import com.opensymphony.xwork2.util.logging.Logger; -import com.opensymphony.xwork2.util.logging.LoggerFactory; - -/** - * Isolate the process of populating JSON objects from the Interceptor class - * itself. - */ -public class JSONPopulator { - - private static final Logger LOG = LoggerFactory.getLogger(JSONPopulator.class); - - private String dateFormat = JSONUtil.RFC3339_FORMAT; - - public JSONPopulator() { - } - - public JSONPopulator(String dateFormat) { - this.dateFormat = dateFormat; - } - - public String getDateFormat() { - return dateFormat; - } - - public void setDateFormat(String dateFormat) { - this.dateFormat = dateFormat; - } - - @SuppressWarnings("unchecked") - public void populateObject(Object object, final Map elements) throws IllegalAccessException, - InvocationTargetException, NoSuchMethodException, IntrospectionException, - IllegalArgumentException, JSONException, InstantiationException { - Class clazz = object.getClass(); - - BeanInfo info = Introspector.getBeanInfo(clazz); - PropertyDescriptor[] props = info.getPropertyDescriptors(); - - // iterate over class fields - for (int i = 0; i < props.length; ++i) { - PropertyDescriptor prop = props[i]; - String name = prop.getName(); - - if (elements.containsKey(name)) { - Object value = elements.get(name); - Method method = prop.getWriteMethod(); - - if (method != null) { - JSON json = method.getAnnotation(JSON.class); - if ((json != null) && !json.deserialize()) { - continue; - } - - // use only public setters - if (Modifier.isPublic(method.getModifiers())) { - Class[] paramTypes = method.getParameterTypes(); - Type[] genericTypes = method.getGenericParameterTypes(); - - if (paramTypes.length == 1) { - Object convertedValue = this.convert(paramTypes[0], genericTypes[0], value, - method); - method.invoke(object, new Object[] { convertedValue }); - } - } - } - } - } - } - - @SuppressWarnings("unchecked") - public Object convert(Class clazz, Type type, Object value, Method method) - throws IllegalArgumentException, JSONException, IllegalAccessException, - InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException { - - if (value == null) { - // if it is a java primitive then get a default value, otherwise - // leave it as null - return clazz.isPrimitive() ? convertPrimitive(clazz, value, method) : null; - } else if (isJSONPrimitive(clazz)) - return convertPrimitive(clazz, value, method); - else if (Collection.class.isAssignableFrom(clazz)) - return convertToCollection(clazz, type, value, method); - else if (Map.class.isAssignableFrom(clazz)) - return convertToMap(clazz, type, value, method); - else if (clazz.isArray()) - return convertToArray(clazz, type, value, method); - else if (value instanceof Map) { - // nested bean - Object convertedValue = clazz.newInstance(); - this.populateObject(convertedValue, (Map) value); - return convertedValue; - } else if (BigDecimal.class.equals(clazz)) { - return new BigDecimal(value != null ? value.toString() : "0"); - } else if (BigInteger.class.equals(clazz)) { - return new BigInteger(value != null ? value.toString() : "0"); - } else - throw new JSONException("Incompatible types for property " + method.getName()); - } - - private static boolean isJSONPrimitive(Class clazz) { - return clazz.isPrimitive() || clazz.equals(String.class) || clazz.equals(Date.class) - || clazz.equals(Boolean.class) || clazz.equals(Byte.class) || clazz.equals(Character.class) - || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Integer.class) - || clazz.equals(Long.class) || clazz.equals(Short.class) || clazz.equals(Locale.class) - || clazz.isEnum(); - } - - @SuppressWarnings("unchecked") - private Object convertToArray(Class clazz, Type type, Object value, Method accessor) - throws JSONException, IllegalArgumentException, IllegalAccessException, - InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException { - if (value == null) - return null; - else if (value instanceof List) { - Class arrayType = clazz.getComponentType(); - List values = (List) value; - Object newArray = Array.newInstance(arrayType, values.size()); - - // create an object for each element - for (int j = 0; j < values.size(); j++) { - Object listValue = values.get(j); - - if (arrayType.equals(Object.class)) { - // Object[] - Array.set(newArray, j, listValue); - } else if (isJSONPrimitive(arrayType)) { - // primitive array - Array.set(newArray, j, this.convertPrimitive(arrayType, listValue, accessor)); - } else if (listValue instanceof Map) { - // array of other class - Object newObject = null; - if (Map.class.isAssignableFrom(arrayType)) { - newObject = convertToMap(arrayType, type, listValue, accessor); - } else if (List.class.isAssignableFrom(arrayType)) { - newObject = convertToCollection(arrayType, type, listValue, accessor); - } else { - newObject = arrayType.newInstance(); - this.populateObject(newObject, (Map) listValue); - } - - Array.set(newArray, j, newObject); - } else - throw new JSONException("Incompatible types for property " + accessor.getName()); - } - - return newArray; - } else - throw new JSONException("Incompatible types for property " + accessor.getName()); - } - - @SuppressWarnings("unchecked") - private Object convertToCollection(Class clazz, Type type, Object value, Method accessor) - throws JSONException, IllegalArgumentException, IllegalAccessException, - InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException { - if (value == null) - return null; - else if (value instanceof List) { - Class itemClass = Object.class; - Type itemType = null; - if ((type != null) && (type instanceof ParameterizedType)) { - ParameterizedType ptype = (ParameterizedType) type; - itemType = ptype.getActualTypeArguments()[0]; - if (itemType.getClass().equals(Class.class)) { - itemClass = (Class) itemType; - } else { - itemClass = (Class) ((ParameterizedType) itemType).getRawType(); - } - } - List values = (List) value; - - Collection newCollection = null; - try { - newCollection = (Collection) clazz.newInstance(); - } catch (InstantiationException ex) { - // fallback if clazz represents an interface or abstract class - if (Set.class.isAssignableFrom(clazz)) { - newCollection = new HashSet(); - } else { - newCollection = new ArrayList(); - } - } - - // create an object for each element - for (int j = 0; j < values.size(); j++) { - Object listValue = values.get(j); - - if (itemClass.equals(Object.class)) { - // Object[] - newCollection.add(listValue); - } else if (isJSONPrimitive(itemClass)) { - // primitive array - newCollection.add(this.convertPrimitive(itemClass, listValue, accessor)); - } else if (Map.class.isAssignableFrom(itemClass)) { - Object newObject = convertToMap(itemClass, itemType, listValue, accessor); - newCollection.add(newObject); - } else if (List.class.isAssignableFrom(itemClass)) { - Object newObject = convertToCollection(itemClass, itemType, listValue, accessor); - newCollection.add(newObject); - } else if (listValue instanceof Map) { - // array of beans - Object newObject = itemClass.newInstance(); - this.populateObject(newObject, (Map) listValue); - newCollection.add(newObject); - } else - throw new JSONException("Incompatible types for property " + accessor.getName()); - } - - return newCollection; - } else - throw new JSONException("Incompatible types for property " + accessor.getName()); - } - - @SuppressWarnings("unchecked") - private Object convertToMap(Class clazz, Type type, Object value, Method accessor) throws JSONException, - IllegalArgumentException, IllegalAccessException, InvocationTargetException, - InstantiationException, NoSuchMethodException, IntrospectionException { - if (value == null) - return null; - else if (value instanceof Map) { - Class itemClass = Object.class; - Type itemType = null; - if ((type != null) && (type instanceof ParameterizedType)) { - ParameterizedType ptype = (ParameterizedType) type; - itemType = ptype.getActualTypeArguments()[1]; - if (itemType.getClass().equals(Class.class)) { - itemClass = (Class) itemType; - } else { - itemClass = (Class) ((ParameterizedType) itemType).getRawType(); - } - } - Map values = (Map) value; - - Map newMap = null; - try { - newMap = (Map) clazz.newInstance(); - } catch (InstantiationException ex) { - // fallback if clazz represents an interface or abstract class - newMap = new HashMap(); - } - - // create an object for each element - Iterator iter = values.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = (Map.Entry) iter.next(); - String key = (String) entry.getKey(); - Object v = entry.getValue(); - - if (itemClass.equals(Object.class)) { - // String, Object - newMap.put(key, v); - } else if (isJSONPrimitive(itemClass)) { - // primitive map - newMap.put(key, this.convertPrimitive(itemClass, v, accessor)); - } else if (Map.class.isAssignableFrom(itemClass)) { - Object newObject = convertToMap(itemClass, itemType, v, accessor); - newMap.put(key, newObject); - } else if (List.class.isAssignableFrom(itemClass)) { - Object newObject = convertToCollection(itemClass, itemType, v, accessor); - newMap.put(key, newObject); - } else if (v instanceof Map) { - // map of beans - Object newObject = itemClass.newInstance(); - this.populateObject(newObject, (Map) v); - newMap.put(key, newObject); - } else - throw new JSONException("Incompatible types for property " + accessor.getName()); - } - - return newMap; - } else - throw new JSONException("Incompatible types for property " + accessor.getName()); - } - - /** - * Converts numbers to the desired class, if possible - * - * @throws JSONException - */ - @SuppressWarnings("unchecked") - private Object convertPrimitive(Class clazz, Object value, Method method) throws JSONException { - if (value == null) { - if (Short.TYPE.equals(clazz) || Short.class.equals(clazz)) - return (short) 0; - else if (Byte.TYPE.equals(clazz) || Byte.class.equals(clazz)) - return (byte) 0; - else if (Integer.TYPE.equals(clazz) || Integer.class.equals(clazz)) - return 0; - else if (Long.TYPE.equals(clazz) || Long.class.equals(clazz)) - return 0L; - else if (Float.TYPE.equals(clazz) || Float.class.equals(clazz)) - return 0f; - else if (Double.TYPE.equals(clazz) || Double.class.equals(clazz)) - return 0d; - else if (Boolean.TYPE.equals(clazz) || Boolean.class.equals(clazz)) - return Boolean.FALSE; - else - return null; - } else if (value instanceof Number) { - Number number = (Number) value; - - if (Short.TYPE.equals(clazz)) - return number.shortValue(); - else if (Short.class.equals(clazz)) - return new Short(number.shortValue()); - else if (Byte.TYPE.equals(clazz)) - return number.byteValue(); - else if (Byte.class.equals(clazz)) - return new Byte(number.byteValue()); - else if (Integer.TYPE.equals(clazz)) - return number.intValue(); - else if (Integer.class.equals(clazz)) - return new Integer(number.intValue()); - else if (Long.TYPE.equals(clazz)) - return number.longValue(); - else if (Long.class.equals(clazz)) - return new Long(number.longValue()); - else if (Float.TYPE.equals(clazz)) - return number.floatValue(); - else if (Float.class.equals(clazz)) - return new Float(number.floatValue()); - else if (Double.TYPE.equals(clazz)) - return number.doubleValue(); - else if (Double.class.equals(clazz)) - return new Double(number.doubleValue()); - else if (String.class.equals(clazz)) - return value.toString(); - } else if (clazz.equals(Date.class)) { - try { - JSON json = method.getAnnotation(JSON.class); - - DateFormat formatter = new SimpleDateFormat( - (json != null) && (json.format().length() > 0) ? json.format() : this.dateFormat); - return formatter.parse((String) value); - } catch (ParseException e) { - LOG.error(e.getMessage(), e); - throw new JSONException("Unable to parse date from: " + value); - } - } else if (clazz.isEnum()) { - String sValue = (String) value; - return Enum.valueOf(clazz, sValue); - } else if (value instanceof String) { - String sValue = (String) value; - if (Boolean.TYPE.equals(clazz)) - return Boolean.parseBoolean(sValue); - else if (Boolean.class.equals(clazz)) - return Boolean.valueOf(sValue); - else if (Short.TYPE.equals(clazz)) - return Short.parseShort(sValue); - else if (Short.class.equals(clazz)) - return Short.valueOf(sValue); - else if (Byte.TYPE.equals(clazz)) - return Byte.parseByte(sValue); - else if (Byte.class.equals(clazz)) - return Byte.valueOf(sValue); - else if (Integer.TYPE.equals(clazz)) - return Integer.parseInt(sValue); - else if (Integer.class.equals(clazz)) - return Integer.valueOf(sValue); - else if (Long.TYPE.equals(clazz)) - return Long.parseLong(sValue); - else if (Long.class.equals(clazz)) - return Long.valueOf(sValue); - else if (Float.TYPE.equals(clazz)) - return Float.parseFloat(sValue); - else if (Float.class.equals(clazz)) - return Float.valueOf(sValue); - else if (Double.TYPE.equals(clazz)) - return Double.parseDouble(sValue); - else if (Double.class.equals(clazz)) - return Double.valueOf(sValue); - else if (Character.TYPE.equals(clazz) || Character.class.equals(clazz)) { - char charValue = 0; - if (sValue.length() > 0) { - charValue = sValue.charAt(0); - } - if (Character.TYPE.equals(clazz)) - return charValue; - else - return new Character(charValue); - } else if (clazz.equals(Locale.class)) { - String[] components = sValue.split("_", 2); - if (components.length == 2) { - return new Locale(components[0], components[1]); - } else { - return new Locale(sValue); - } - } else if (Enum.class.isAssignableFrom(clazz)) { - return Enum.valueOf(clazz, sValue); - } - } - - return value; - } - -} +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.struts2.json; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.struts2.json.annotations.JSON; + +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; + +/** + * Isolate the process of populating JSON objects from the Interceptor class + * itself. + */ +public class JSONPopulator { + + private static final Logger LOG = LoggerFactory.getLogger(JSONPopulator.class); + + private String dateFormat = JSONUtil.RFC3339_FORMAT; + + public JSONPopulator() { + } + + public JSONPopulator(String dateFormat) { + this.dateFormat = dateFormat; + } + + public String getDateFormat() { + return dateFormat; + } + + public void setDateFormat(String dateFormat) { + this.dateFormat = dateFormat; + } + + @SuppressWarnings("unchecked") + public void populateObject(Object object, final Map elements) throws IllegalAccessException, + InvocationTargetException, NoSuchMethodException, IntrospectionException, + IllegalArgumentException, JSONException, InstantiationException { + Class clazz = object.getClass(); + + BeanInfo info = Introspector.getBeanInfo(clazz); + PropertyDescriptor[] props = info.getPropertyDescriptors(); + + // iterate over class fields + for (int i = 0; i < props.length; ++i) { + PropertyDescriptor prop = props[i]; + String name = prop.getName(); + + if (elements.containsKey(name)) { + Object value = elements.get(name); + Method method = prop.getWriteMethod(); + + if (method != null) { + JSON json = method.getAnnotation(JSON.class); + if ((json != null) && !json.deserialize()) { + continue; + } + + // use only public setters + if (Modifier.isPublic(method.getModifiers())) { + Class[] paramTypes = method.getParameterTypes(); + Type[] genericTypes = method.getGenericParameterTypes(); + + if (paramTypes.length == 1) { + Object convertedValue = this.convert(paramTypes[0], genericTypes[0], value, + method); + method.invoke(object, new Object[] { convertedValue }); + } + } + } + } + } + } + + @SuppressWarnings("unchecked") + public Object convert(Class clazz, Type type, Object value, Method method) + throws IllegalArgumentException, JSONException, IllegalAccessException, + InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException { + + if (value == null) { + // if it is a java primitive then get a default value, otherwise + // leave it as null + return clazz.isPrimitive() ? convertPrimitive(clazz, value, method) : null; + } else if (isJSONPrimitive(clazz)) + return convertPrimitive(clazz, value, method); + else if (Collection.class.isAssignableFrom(clazz)) + return convertToCollection(clazz, type, value, method); + else if (Map.class.isAssignableFrom(clazz)) + return convertToMap(clazz, type, value, method); + else if (clazz.isArray()) + return convertToArray(clazz, type, value, method); + else if (value instanceof Map) { + // nested bean + Object convertedValue = clazz.newInstance(); + this.populateObject(convertedValue, (Map) value); + return convertedValue; + } else if (BigDecimal.class.equals(clazz)) { + return new BigDecimal(value != null ? value.toString() : "0"); + } else if (BigInteger.class.equals(clazz)) { + return new BigInteger(value != null ? value.toString() : "0"); + } else + throw new JSONException("Incompatible types for property " + method.getName()); + } + + private static boolean isJSONPrimitive(Class clazz) { + return clazz.isPrimitive() || clazz.equals(String.class) || clazz.equals(Date.class) + || clazz.equals(Boolean.class) || clazz.equals(Byte.class) || clazz.equals(Character.class) + || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Integer.class) + || clazz.equals(Long.class) || clazz.equals(Short.class) || clazz.equals(Locale.class) + || clazz.isEnum(); + } + + @SuppressWarnings("unchecked") + private Object convertToArray(Class clazz, Type type, Object value, Method accessor) + throws JSONException, IllegalArgumentException, IllegalAccessException, + InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException { + if (value == null) + return null; + else if (value instanceof List) { + Class arrayType = clazz.getComponentType(); + List values = (List) value; + Object newArray = Array.newInstance(arrayType, values.size()); + + // create an object for each element + for (int j = 0; j < values.size(); j++) { + Object listValue = values.get(j); + + if (arrayType.equals(Object.class)) { + // Object[] + Array.set(newArray, j, listValue); + } else if (isJSONPrimitive(arrayType)) { + // primitive array + Array.set(newArray, j, this.convertPrimitive(arrayType, listValue, accessor)); + } else if (listValue instanceof Map) { + // array of other class + Object newObject = null; + if (Map.class.isAssignableFrom(arrayType)) { + newObject = convertToMap(arrayType, type, listValue, accessor); + } else if (List.class.isAssignableFrom(arrayType)) { + newObject = convertToCollection(arrayType, type, listValue, accessor); + } else { + newObject = arrayType.newInstance(); + this.populateObject(newObject, (Map) listValue); + } + + Array.set(newArray, j, newObject); + } else + throw new JSONException("Incompatible types for property " + accessor.getName()); + } + + return newArray; + } else + throw new JSONException("Incompatible types for property " + accessor.getName()); + } + + @SuppressWarnings("unchecked") + private Object convertToCollection(Class clazz, Type type, Object value, Method accessor) + throws JSONException, IllegalArgumentException, IllegalAccessException, + InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException { + if (value == null) + return null; + else if (value instanceof List) { + Class itemClass = Object.class; + Type itemType = null; + if ((type != null) && (type instanceof ParameterizedType)) { + ParameterizedType ptype = (ParameterizedType) type; + itemType = ptype.getActualTypeArguments()[0]; + if (itemType.getClass().equals(Class.class)) { + itemClass = (Class) itemType; + } else { + itemClass = (Class) ((ParameterizedType) itemType).getRawType(); + } + } + List values = (List) value; + + Collection newCollection = null; + try { + newCollection = (Collection) clazz.newInstance(); + } catch (InstantiationException ex) { + // fallback if clazz represents an interface or abstract class + if (Set.class.isAssignableFrom(clazz)) { + newCollection = new HashSet(); + } else { + newCollection = new ArrayList(); + } + } + + // create an object for each element + for (int j = 0; j < values.size(); j++) { + Object listValue = values.get(j); + + if (itemClass.equals(Object.class)) { + // Object[] + newCollection.add(listValue); + } else if (isJSONPrimitive(itemClass)) { + // primitive array + newCollection.add(this.convertPrimitive(itemClass, listValue, accessor)); + } else if (Map.class.isAssignableFrom(itemClass)) { + Object newObject = convertToMap(itemClass, itemType, listValue, accessor); + newCollection.add(newObject); + } else if (List.class.isAssignableFrom(itemClass)) { + Object newObject = convertToCollection(itemClass, itemType, listValue, accessor); + newCollection.add(newObject); + } else if (listValue instanceof Map) { + // array of beans + Object newObject = itemClass.newInstance(); + this.populateObject(newObject, (Map) listValue); + newCollection.add(newObject); + } else + throw new JSONException("Incompatible types for property " + accessor.getName()); + } + + return newCollection; + } else + throw new JSONException("Incompatible types for property " + accessor.getName()); + } + + @SuppressWarnings("unchecked") + private Object convertToMap(Class clazz, Type type, Object value, Method accessor) throws JSONException, + IllegalArgumentException, IllegalAccessException, InvocationTargetException, + InstantiationException, NoSuchMethodException, IntrospectionException { + if (value == null) + return null; + else if (value instanceof Map) { + Class itemClass = Object.class; + Type itemType = null; + if ((type != null) && (type instanceof ParameterizedType)) { + ParameterizedType ptype = (ParameterizedType) type; + itemType = ptype.getActualTypeArguments()[1]; + if (itemType.getClass().equals(Class.class)) { + itemClass = (Class) itemType; + } else { + itemClass = (Class) ((ParameterizedType) itemType).getRawType(); + } + } + Map values = (Map) value; + + Map newMap = null; + try { + newMap = (Map) clazz.newInstance(); + } catch (InstantiationException ex) { + // fallback if clazz represents an interface or abstract class + newMap = new HashMap(); + } + + // create an object for each element + Iterator iter = values.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry) iter.next(); + String key = (String) entry.getKey(); + Object v = entry.getValue(); + + if (itemClass.equals(Object.class)) { + // String, Object + newMap.put(key, v); + } else if (isJSONPrimitive(itemClass)) { + // primitive map + newMap.put(key, this.convertPrimitive(itemClass, v, accessor)); + } else if (Map.class.isAssignableFrom(itemClass)) { + Object newObject = convertToMap(itemClass, itemType, v, accessor); + newMap.put(key, newObject); + } else if (List.class.isAssignableFrom(itemClass)) { + Object newObject = convertToCollection(itemClass, itemType, v, accessor); + newMap.put(key, newObject); + } else if (v instanceof Map) { + // map of beans + Object newObject = itemClass.newInstance(); + this.populateObject(newObject, (Map) v); + newMap.put(key, newObject); + } else + throw new JSONException("Incompatible types for property " + accessor.getName()); + } + + return newMap; + } else + throw new JSONException("Incompatible types for property " + accessor.getName()); + } + + /** + * Converts numbers to the desired class, if possible + * + * @throws JSONException + */ + @SuppressWarnings("unchecked") + private Object convertPrimitive(Class clazz, Object value, Method method) throws JSONException { + if (value == null) { + if (Short.TYPE.equals(clazz) || Short.class.equals(clazz)) + return (short) 0; + else if (Byte.TYPE.equals(clazz) || Byte.class.equals(clazz)) + return (byte) 0; + else if (Integer.TYPE.equals(clazz) || Integer.class.equals(clazz)) + return 0; + else if (Long.TYPE.equals(clazz) || Long.class.equals(clazz)) + return 0L; + else if (Float.TYPE.equals(clazz) || Float.class.equals(clazz)) + return 0f; + else if (Double.TYPE.equals(clazz) || Double.class.equals(clazz)) + return 0d; + else if (Boolean.TYPE.equals(clazz) || Boolean.class.equals(clazz)) + return Boolean.FALSE; + else + return null; + } else if (value instanceof Number) { + Number number = (Number) value; + + if (Short.TYPE.equals(clazz)) + return number.shortValue(); + else if (Short.class.equals(clazz)) + return new Short(number.shortValue()); + else if (Byte.TYPE.equals(clazz)) + return number.byteValue(); + else if (Byte.class.equals(clazz)) + return new Byte(number.byteValue()); + else if (Integer.TYPE.equals(clazz)) + return number.intValue(); + else if (Integer.class.equals(clazz)) + return new Integer(number.intValue()); + else if (Long.TYPE.equals(clazz)) + return number.longValue(); + else if (Long.class.equals(clazz)) + return new Long(number.longValue()); + else if (Float.TYPE.equals(clazz)) + return number.floatValue(); + else if (Float.class.equals(clazz)) + return new Float(number.floatValue()); + else if (Double.TYPE.equals(clazz)) + return number.doubleValue(); + else if (Double.class.equals(clazz)) + return new Double(number.doubleValue()); + else if (String.class.equals(clazz)) + return value.toString(); + } else if (clazz.equals(Date.class)) { + try { + JSON json = method.getAnnotation(JSON.class); + + DateFormat formatter = new SimpleDateFormat( + (json != null) && (json.format().length() > 0) ? json.format() : this.dateFormat); + return formatter.parse((String) value); + } catch (ParseException e) { + LOG.error(e.getMessage(), e); + throw new JSONException("Unable to parse date from: " + value); + } + } else if (clazz.isEnum()) { + String sValue = (String) value; + return Enum.valueOf(clazz, sValue); + } else if (value instanceof String) { + String sValue = (String) value; + if (Boolean.TYPE.equals(clazz)) + return Boolean.parseBoolean(sValue); + else if (Boolean.class.equals(clazz)) + return Boolean.valueOf(sValue); + else if (Short.TYPE.equals(clazz)) + return Short.parseShort(sValue); + else if (Short.class.equals(clazz)) + return Short.valueOf(sValue); + else if (Byte.TYPE.equals(clazz)) + return Byte.parseByte(sValue); + else if (Byte.class.equals(clazz)) + return Byte.valueOf(sValue); + else if (Integer.TYPE.equals(clazz)) + return Integer.parseInt(sValue); + else if (Integer.class.equals(clazz)) + return Integer.valueOf(sValue); + else if (Long.TYPE.equals(clazz)) + return Long.parseLong(sValue); + else if (Long.class.equals(clazz)) + return Long.valueOf(sValue); + else if (Float.TYPE.equals(clazz)) + return Float.parseFloat(sValue); + else if (Float.class.equals(clazz)) + return Float.valueOf(sValue); + else if (Double.TYPE.equals(clazz)) + return Double.parseDouble(sValue); + else if (Double.class.equals(clazz)) + return Double.valueOf(sValue); + else if (Character.TYPE.equals(clazz) || Character.class.equals(clazz)) { + char charValue = 0; + if (sValue.length() > 0) { + charValue = sValue.charAt(0); + } + if (Character.TYPE.equals(clazz)) + return charValue; + else + return new Character(charValue); + } else if (clazz.equals(Locale.class)) { + String[] components = sValue.split("_", 2); + if (components.length == 2) { + return new Locale(components[0], components[1]); + } else { + return new Locale(sValue); + } + } else if (Enum.class.isAssignableFrom(clazz)) { + return Enum.valueOf(clazz, sValue); + } + } + + return value; + } + +}
Propchange: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONPopulator.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONPopulator.java ------------------------------------------------------------------------------ svn:keywords = Id Modified: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONReader.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONReader.java?rev=799110&r1=799109&r2=799110&view=diff ============================================================================== --- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONReader.java (original) +++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONReader.java Wed Jul 29 22:44:26 2009 @@ -1,289 +1,289 @@ -/* - * $Id$ - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.struts2.json; - -import java.text.CharacterIterator; -import java.text.StringCharacterIterator; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * <p> - * Deserializes and object from a JSON string - * </p> - */ -class JSONReader { - private static final Object OBJECT_END = new Object(); - private static final Object ARRAY_END = new Object(); - private static final Object COLON = new Object(); - private static final Object COMMA = new Object(); - private static Map<Character, Character> escapes = new HashMap<Character, Character>(); - - static { - escapes.put(new Character('"'), new Character('"')); - escapes.put(new Character('\\'), new Character('\\')); - escapes.put(new Character('/'), new Character('/')); - escapes.put(new Character('b'), new Character('\b')); - escapes.put(new Character('f'), new Character('\f')); - escapes.put(new Character('n'), new Character('\n')); - escapes.put(new Character('r'), new Character('\r')); - escapes.put(new Character('t'), new Character('\t')); - } - - private CharacterIterator it; - private char c; - private Object token; - private StringBuilder buf = new StringBuilder(); - - private char next() { - this.c = this.it.next(); - - return this.c; - } - - private void skipWhiteSpace() { - while (Character.isWhitespace(this.c)) { - this.next(); - } - } - - public Object read(String string) throws JSONException { - this.it = new StringCharacterIterator(string); - this.c = this.it.first(); - - return this.read(); - } - - private Object read() throws JSONException { - Object ret = null; - - this.skipWhiteSpace(); - - if (this.c == '"') { - this.next(); - ret = this.string('"'); - } else if (this.c == '\'') { - this.next(); - ret = this.string('\''); - } else if (this.c == '[') { - this.next(); - ret = this.array(); - } else if (this.c == ']') { - ret = ARRAY_END; - this.next(); - } else if (this.c == ',') { - ret = COMMA; - this.next(); - } else if (this.c == '{') { - this.next(); - ret = this.object(); - } else if (this.c == '}') { - ret = OBJECT_END; - this.next(); - } else if (this.c == ':') { - ret = COLON; - this.next(); - } else if ((this.c == 't') && (this.next() == 'r') && (this.next() == 'u') && (this.next() == 'e')) { - ret = Boolean.TRUE; - this.next(); - } else if ((this.c == 'f') && (this.next() == 'a') && (this.next() == 'l') && (this.next() == 's') - && (this.next() == 'e')) { - ret = Boolean.FALSE; - this.next(); - } else if ((this.c == 'n') && (this.next() == 'u') && (this.next() == 'l') && (this.next() == 'l')) { - ret = null; - this.next(); - } else if (Character.isDigit(this.c) || (this.c == '-')) { - ret = this.number(); - } else { - throw buildInvalidInputException(); - } - - this.token = ret; - - return ret; - } - - @SuppressWarnings("unchecked") - private Map object() throws JSONException { - Map ret = new HashMap(); - Object next = this.read(); - if (next != OBJECT_END) { - String key = (String) next; - while (this.token != OBJECT_END) { - this.read(); // should be a colon - - if (this.token != OBJECT_END) { - ret.put(key, this.read()); - - if (this.read() == COMMA) { - Object name = this.read(); - - if (name instanceof String) { - key = (String) name; - } else - throw buildInvalidInputException(); - } - } - } - } - - return ret; - } - - private JSONException buildInvalidInputException() { - return new JSONException("Input string is not well formed JSON (invalid char " + this.c + ")"); - } - - @SuppressWarnings("unchecked") - private List array() throws JSONException { - List ret = new ArrayList(); - Object value = this.read(); - - while (this.token != ARRAY_END) { - ret.add(value); - - Object read = this.read(); - if (read == COMMA) { - value = this.read(); - } else if (read != ARRAY_END) { - throw buildInvalidInputException(); - } - } - - return ret; - } - - private Object number() { - this.buf.setLength(0); - - if (this.c == '-') { - this.add(); - } - - this.addDigits(); - - if (this.c == '.') { - this.add(); - this.addDigits(); - } - - if ((this.c == 'e') || (this.c == 'E')) { - this.add(); - - if ((this.c == '+') || (this.c == '-')) { - this.add(); - } - - this.addDigits(); - } - - return (this.buf.indexOf(".") >= 0) ? (Object) Double.parseDouble(this.buf.toString()) - : (Object) Long.parseLong(this.buf.toString()); - } - - private Object string(char quote) { - this.buf.setLength(0); - - while ((this.c != quote) && (this.c != CharacterIterator.DONE)) { - if (this.c == '\\') { - this.next(); - - if (this.c == 'u') { - this.add(this.unicode()); - } else { - Object value = escapes.get(new Character(this.c)); - - if (value != null) { - this.add(((Character) value).charValue()); - } - } - } else { - this.add(); - } - } - - this.next(); - - return this.buf.toString(); - } - - private void add(char cc) { - this.buf.append(cc); - this.next(); - } - - private void add() { - this.add(this.c); - } - - private void addDigits() { - while (Character.isDigit(this.c)) { - this.add(); - } - } - - private char unicode() { - int value = 0; - - for (int i = 0; i < 4; ++i) { - switch (this.next()) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - value = (value << 4) + (this.c - '0'); - - break; - - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - value = (value << 4) + (this.c - 'W'); - - break; - - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - value = (value << 4) + (this.c - '7'); - - break; - } - } - - return (char) value; - } -} +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.struts2.json; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * <p> + * Deserializes and object from a JSON string + * </p> + */ +class JSONReader { + private static final Object OBJECT_END = new Object(); + private static final Object ARRAY_END = new Object(); + private static final Object COLON = new Object(); + private static final Object COMMA = new Object(); + private static Map<Character, Character> escapes = new HashMap<Character, Character>(); + + static { + escapes.put(new Character('"'), new Character('"')); + escapes.put(new Character('\\'), new Character('\\')); + escapes.put(new Character('/'), new Character('/')); + escapes.put(new Character('b'), new Character('\b')); + escapes.put(new Character('f'), new Character('\f')); + escapes.put(new Character('n'), new Character('\n')); + escapes.put(new Character('r'), new Character('\r')); + escapes.put(new Character('t'), new Character('\t')); + } + + private CharacterIterator it; + private char c; + private Object token; + private StringBuilder buf = new StringBuilder(); + + private char next() { + this.c = this.it.next(); + + return this.c; + } + + private void skipWhiteSpace() { + while (Character.isWhitespace(this.c)) { + this.next(); + } + } + + public Object read(String string) throws JSONException { + this.it = new StringCharacterIterator(string); + this.c = this.it.first(); + + return this.read(); + } + + private Object read() throws JSONException { + Object ret = null; + + this.skipWhiteSpace(); + + if (this.c == '"') { + this.next(); + ret = this.string('"'); + } else if (this.c == '\'') { + this.next(); + ret = this.string('\''); + } else if (this.c == '[') { + this.next(); + ret = this.array(); + } else if (this.c == ']') { + ret = ARRAY_END; + this.next(); + } else if (this.c == ',') { + ret = COMMA; + this.next(); + } else if (this.c == '{') { + this.next(); + ret = this.object(); + } else if (this.c == '}') { + ret = OBJECT_END; + this.next(); + } else if (this.c == ':') { + ret = COLON; + this.next(); + } else if ((this.c == 't') && (this.next() == 'r') && (this.next() == 'u') && (this.next() == 'e')) { + ret = Boolean.TRUE; + this.next(); + } else if ((this.c == 'f') && (this.next() == 'a') && (this.next() == 'l') && (this.next() == 's') + && (this.next() == 'e')) { + ret = Boolean.FALSE; + this.next(); + } else if ((this.c == 'n') && (this.next() == 'u') && (this.next() == 'l') && (this.next() == 'l')) { + ret = null; + this.next(); + } else if (Character.isDigit(this.c) || (this.c == '-')) { + ret = this.number(); + } else { + throw buildInvalidInputException(); + } + + this.token = ret; + + return ret; + } + + @SuppressWarnings("unchecked") + private Map object() throws JSONException { + Map ret = new HashMap(); + Object next = this.read(); + if (next != OBJECT_END) { + String key = (String) next; + while (this.token != OBJECT_END) { + this.read(); // should be a colon + + if (this.token != OBJECT_END) { + ret.put(key, this.read()); + + if (this.read() == COMMA) { + Object name = this.read(); + + if (name instanceof String) { + key = (String) name; + } else + throw buildInvalidInputException(); + } + } + } + } + + return ret; + } + + private JSONException buildInvalidInputException() { + return new JSONException("Input string is not well formed JSON (invalid char " + this.c + ")"); + } + + @SuppressWarnings("unchecked") + private List array() throws JSONException { + List ret = new ArrayList(); + Object value = this.read(); + + while (this.token != ARRAY_END) { + ret.add(value); + + Object read = this.read(); + if (read == COMMA) { + value = this.read(); + } else if (read != ARRAY_END) { + throw buildInvalidInputException(); + } + } + + return ret; + } + + private Object number() { + this.buf.setLength(0); + + if (this.c == '-') { + this.add(); + } + + this.addDigits(); + + if (this.c == '.') { + this.add(); + this.addDigits(); + } + + if ((this.c == 'e') || (this.c == 'E')) { + this.add(); + + if ((this.c == '+') || (this.c == '-')) { + this.add(); + } + + this.addDigits(); + } + + return (this.buf.indexOf(".") >= 0) ? (Object) Double.parseDouble(this.buf.toString()) + : (Object) Long.parseLong(this.buf.toString()); + } + + private Object string(char quote) { + this.buf.setLength(0); + + while ((this.c != quote) && (this.c != CharacterIterator.DONE)) { + if (this.c == '\\') { + this.next(); + + if (this.c == 'u') { + this.add(this.unicode()); + } else { + Object value = escapes.get(new Character(this.c)); + + if (value != null) { + this.add(((Character) value).charValue()); + } + } + } else { + this.add(); + } + } + + this.next(); + + return this.buf.toString(); + } + + private void add(char cc) { + this.buf.append(cc); + this.next(); + } + + private void add() { + this.add(this.c); + } + + private void addDigits() { + while (Character.isDigit(this.c)) { + this.add(); + } + } + + private char unicode() { + int value = 0; + + for (int i = 0; i < 4; ++i) { + switch (this.next()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + value = (value << 4) + (this.c - '0'); + + break; + + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + value = (value << 4) + (this.c - 'W'); + + break; + + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + value = (value << 4) + (this.c - '7'); + + break; + } + } + + return (char) value; + } +} Propchange: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONReader.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONReader.java ------------------------------------------------------------------------------ svn:keywords = Id