Author: etnu
Date: Tue Mar 17 22:08:11 2009
New Revision: 755421

URL: http://svn.apache.org/viewvc?rev=755421&view=rev
Log:
Patch for SHINDIG-912, enhancing parsing / conversion ability of 
BeanJsonConverter. After these changes, double / triple parsing of input json 
in JsonRpcServlet should no longer be necessary, and more classes can take 
advantage of BeanJsonConverter as a generic parsing facility.


Modified:
    
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanJsonConverter.java
    
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonAssert.java
    
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/JsonRpcServletTest.java
    
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/conversion/BeanJsonConverterTest.java
    
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AppDataHandler.java
    
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/AppDataHandlerTest.java

Modified: 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanJsonConverter.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanJsonConverter.java?rev=755421&r1=755420&r2=755421&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanJsonConverter.java
 (original)
+++ 
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanJsonConverter.java
 Tue Mar 17 22:08:11 2009
@@ -23,8 +23,6 @@
 import org.apache.shindig.protocol.model.Enum;
 import org.apache.shindig.protocol.model.EnumImpl;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
 import com.google.common.collect.MapMaker;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
@@ -37,17 +35,22 @@
 import java.io.IOException;
 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.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
-import java.util.Iterator;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
- * Converts pojos to json objects.
- * TODO: Replace with standard library
+ * Converts between JSON and java objects.
+ *
+ * TODO: Eliminate BeanConverter interface.
  */
 public class BeanJsonConverter implements BeanConverter {
 
@@ -61,50 +64,35 @@
     this.injector = injector;
   }
 
-  public String getContentType() {
-    return "application/json";
-  }
-
-  /**
-   * Convert the passed in object to a string.
-   *
-   * @param pojo The object to convert
-   * @return An object whos toString method will return json
-   */
-  public String convertToString(final Object pojo) {
-    return JsonSerializer.serialize(pojo);
-  }
-
   public void append(Appendable buf, Object pojo) throws IOException {
     JsonSerializer.append(buf, pojo);
   }
 
-  private static Map<String, Method> getSetters(Object pojo) {
-    Class<?> clazz = pojo.getClass();
+  private static Map<String, Method> getSetters(Class<?> type) {
+    Map<String, Method> methods = setters.get(type);
 
-    Map<String, Method> methods = setters.get(clazz);
     if (methods != null) {
       return methods;
     }
-    // Ensure consistent method ordering by using a linked hash map.
-    methods = Maps.newHashMap();
 
-    for (Method method : clazz.getMethods()) {
+    methods = new HashMap<String, Method>();
+
+    for (Method method : type.getMethods()) {
       String name = getPropertyName(method);
       if (name != null) {
         methods.put(name, method);
       }
     }
 
-    setters.put(clazz, methods);
+    setters.put(type, methods);
     return methods;
   }
 
-  private static String getPropertyName(Method method) {
-    JsonProperty property = method.getAnnotation(JsonProperty.class);
+  private static String getPropertyName(Method setter) {
+    JsonProperty property = setter.getAnnotation(JsonProperty.class);
     if (property == null) {
-      String name = method.getName();
-      if (name.startsWith("set")) {
+      String name = setter.getName();
+      if (name.startsWith("set") && !Modifier.isStatic(setter.getModifiers())) 
{
         return name.substring(3, 4).toLowerCase() + name.substring(4);
       }
       return null;
@@ -113,183 +101,158 @@
     }
   }
 
-  public <T> T convertToObject(String json, Class<T> className) {
-    String errorMessage = "Could not convert " + json + " to " + className;
+  public <T> T convertToObject(String string, Class<T> className) {
+    return convertToObject(string, (Type) className);
+  }
+
+  public String convertToString(Object pojo) {
+    return JsonSerializer.serialize(pojo);
+  }
+
+  public String getContentType() {
+    return "application/json";
+  }
 
+  @SuppressWarnings("unchecked")
+  public <T> T convertToObject(String json, Type type) {
     try {
-      T pojo = injector.getInstance(className);
-      return convertToObject(json, pojo);
+      return (T) convertToObject(new JSONObject(json), type);
     } catch (JSONException e) {
-      throw new RuntimeException(errorMessage, e);
-    } catch (InvocationTargetException e) {
-      throw new RuntimeException(errorMessage, e);
-    } catch (IllegalAccessException e) {
-      throw new RuntimeException(errorMessage, e);
-    } catch (NoSuchFieldException e) {
-      throw new RuntimeException(errorMessage, e);
+      throw new RuntimeException(e);
     }
   }
 
-  @SuppressWarnings("unchecked")
-  private <T> T convertToObject(String json, T pojo)
-      throws JSONException, InvocationTargetException, IllegalAccessException,
-      NoSuchFieldException {
-
-    if (pojo instanceof String) {
-      pojo = (T) json; // This is a weird cast...
-
-    } else if (pojo instanceof Map) {
-      // TODO: Figure out how to get the actual generic type for the
-      // second Map parameter. Right now we are hardcoding to String
-      Class<?> mapValueClass = String.class;
-
-      JSONObject jsonObject = new JSONObject(json);
-      Iterator<?> iterator = jsonObject.keys();
-      while (iterator.hasNext()) {
-        String key = (String) iterator.next();
-        Object value = convertToObject(jsonObject.getString(key), 
mapValueClass);
-        ((Map<String, Object>) pojo).put(key, value);
+  private Object convertToObject(Object value, Type type) {
+    if (type == null || type.equals(Object.class)) {
+      // Use the source type instead.
+      if (value instanceof JSONObject) {
+        return convertToMap((JSONObject) value, null);
+      } else if (value instanceof JSONArray) {
+        return convertToCollection((JSONArray) value, new ArrayList<Object>(), 
null);
+      }
+      return value;
+    } else if (type instanceof ParameterizedType) {
+      return convertGeneric(value, (ParameterizedType) type);
+    } else if (type.equals(String.class)) {
+      return String.valueOf(value);
+    } else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE)) {
+      return Boolean.TRUE.equals(value);
+    } else if (type.equals(Integer.class) || type.equals(Integer.TYPE)) {
+      return value instanceof String ? Integer.valueOf((String) value) : 
((Number) value).intValue();
+    } else if (type.equals(Long.class) || type.equals(Long.TYPE)) {
+      return value instanceof String ? Long.valueOf((String) value) : 
((Number) value).longValue();
+    } else if (type.equals(Double.class) || type.equals(Double.TYPE)) {
+      return value instanceof String ? Double.valueOf((String) value) : 
((Number) value).doubleValue();
+    } else if (type.equals(Float.class) || type.equals(Float.TYPE)) {
+      return value instanceof String ? Float.valueOf((String) value) : 
((Number) value).floatValue();
+    } else if (type.equals(Date.class)) {
+      return new DateTime(String.valueOf(value)).toDate();
+    } else if (type.equals(Uri.class)) {
+      return Uri.parse(String.valueOf(value));
+    } else if (type.equals(Map.class)) {
+      return convertToMap((JSONObject) value, null);
+    } else if (type.equals(List.class) || type.equals(Collection.class)) {
+      return convertToCollection((JSONArray) value, new ArrayList<Object>(), 
null);
+    } else if (type.equals(Set.class)) {
+      return convertToCollection((JSONArray) value, new HashSet<Object>(), 
null);
+    }
+
+    Class<?> clazz = (Class<?>) type;
+
+    if (clazz.isEnum()) {
+      return convertToEnum((String) value, clazz);
+    }
+
+    return convertToClass((JSONObject) value, clazz);
+  }
+
+  private Object convertGeneric(Object value, ParameterizedType type) {
+    Type[] typeArgs = type.getActualTypeArguments();
+    Class<?> clazz = (Class<?>) type.getRawType();
+
+    if (Set.class.isAssignableFrom(clazz)) {
+      return convertToCollection((JSONArray) value, new HashSet<Object>(), 
typeArgs[0]);
+    } else if (Collection.class.isAssignableFrom(clazz)) {
+      return convertToCollection((JSONArray) value, new ArrayList<Object>(), 
typeArgs[0]);
+    } else if (Map.class.isAssignableFrom(clazz)) {
+      return convertToMap((JSONObject) value, typeArgs[1]);
+    } else if 
(org.apache.shindig.protocol.model.Enum.class.isAssignableFrom(clazz)) {
+      // Special case for opensocial Enum objects. These really need to be 
refactored to not require
+      // this handling.
+      return convertToOsEnum((JSONObject) value, (Class<?>) typeArgs[0]);
+    }
+    return convertToClass((JSONObject) value, clazz);
+  }
+
+  private Enum<Enum.EnumKey> convertToOsEnum(JSONObject json, Class<?> 
enumKeyType) {
+    Enum<Enum.EnumKey> value;
+    String val = Enum.Field.VALUE.toString();
+    String display = Enum.Field.DISPLAY_VALUE.toString();
+    if (json.has(val)) {
+      Enum.EnumKey enumKey;
+      try {
+        enumKey = (Enum.EnumKey) 
enumKeyType.getField(json.optString(val)).get(null);
+      } catch (IllegalArgumentException e) {
+        throw new RuntimeException(e);
+      } catch (SecurityException e) {
+        throw new RuntimeException(e);
+      } catch (IllegalAccessException e) {
+        throw new RuntimeException(e);
+      } catch (NoSuchFieldException e) {
+        throw new RuntimeException(e);
       }
-
-    } else if (pojo instanceof Collection) {
-      JSONArray array = new JSONArray(json);
-      for (int i = 0; i < array.length(); i++) {
-        ((Collection<Object>) pojo).add(array.get(i));
+      String displayValue = null;
+      if (json.has(display)) {
+        displayValue = json.optString(display);
       }
+      value = new EnumImpl<Enum.EnumKey>(enumKey,displayValue);
     } else {
-      JSONObject jsonObject = new JSONObject(json);
-      for (Map.Entry<String, Method> setter : getSetters(pojo).entrySet()) {
-        if (jsonObject.has(setter.getKey())) {
-          callSetterWithValue(pojo, setter.getValue(), jsonObject, 
setter.getKey());
-        }
-      }
+      value = new EnumImpl<Enum.EnumKey>(null, json.optString(display));
     }
-    return pojo;
+    return value;
   }
 
-  @SuppressWarnings("boxing")
-  private <T> void callSetterWithValue(T pojo, Method method,
-      JSONObject jsonObject, String fieldName)
-      throws IllegalAccessException, InvocationTargetException, 
NoSuchFieldException,
-      JSONException {
-
-    Class<?> expectedType = method.getParameterTypes()[0];
-    Object value = null;
-
-    if (!jsonObject.has(fieldName)) {
-      // Skip
-    } else if (expectedType.equals(List.class)) {
-      ParameterizedType genericListType
-          = (ParameterizedType) method.getGenericParameterTypes()[0];
-      Type type = genericListType.getActualTypeArguments()[0];
-      Class<?> rawType;
-      Class<?> listElementClass;
-      if (type instanceof ParameterizedType) {
-        listElementClass = 
(Class<?>)((ParameterizedType)type).getActualTypeArguments()[0];
-        rawType = (Class<?>)((ParameterizedType)type).getRawType();
-      } else {
-        listElementClass = (Class<?>) type;
-        rawType = listElementClass;
-      }
-
-      List<Object> list = Lists.newArrayList();
-      JSONArray jsonArray = jsonObject.getJSONArray(fieldName);
-      for (int i = 0; i < jsonArray.length(); i++) {
-        if (org.apache.shindig.protocol.model.Enum.class
-            .isAssignableFrom(rawType)) {
-          list.add(convertEnum(listElementClass, jsonArray.getJSONObject(i)));
-        } else {
-          list.add(convertToObject(jsonArray.getString(i), listElementClass));
-        }
+  private Object convertToEnum(String value, Class<?> type) {
+    for (Object o : type.getEnumConstants()) {
+      if (o.toString().equals(value)) {
+        return o;
       }
+    }
+    throw new IllegalArgumentException("No enum value " + value + " in " + 
type.getName());
+  }
 
-      value = list;
-
-    } else if (expectedType.equals(Map.class)) {
-      ParameterizedType genericListType
-          = (ParameterizedType) method.getGenericParameterTypes()[0];
-      Type[] types = genericListType.getActualTypeArguments();
-      Class<?> valueClass = (Class<?>) types[1];
-
-      // We only support keys being typed as Strings.
-      // Nothing else really makes sense in json.
-      Map<String, Object> map = Maps.newHashMap();
-      JSONObject jsonMap = jsonObject.getJSONObject(fieldName);
-
-      Iterator<?> keys = jsonMap.keys();
-      while (keys.hasNext()) {
-        String keyName = (String) keys.next();
-        map.put(keyName, convertToObject(jsonMap.getString(keyName),
-            valueClass));
-      }
-
-      value = map;
-
-    } else if (org.apache.shindig.protocol.model.Enum.class
-        .isAssignableFrom(expectedType)) {
-      // TODO Need to stop using Enum as a class name :(
-      value = convertEnum(
-          (Class<?>)((ParameterizedType) method.getGenericParameterTypes()[0]).
-              getActualTypeArguments()[0],
-          jsonObject.getJSONObject(fieldName));
-    } else if (expectedType.isEnum()) {
-      if (jsonObject.has(fieldName)) {
-        for (Object v : expectedType.getEnumConstants()) {
-          if (v.toString().equals(jsonObject.getString(fieldName))) {
-            value = v;
-            break;
-          }
-        }
-        if (value == null) {
-          throw new IllegalArgumentException(
-              "No enum value  '" + jsonObject.getString(fieldName)
-                  + "' in " + expectedType.getName());
-        }
-      }
-    } else if (expectedType.equals(Uri.class)) {
-      value = Uri.parse(jsonObject.getString(fieldName));
-    } else if (expectedType.equals(String.class)) {
-      value = jsonObject.getString(fieldName);
-    } else if (expectedType.equals(Date.class)) {
-      // Use JODA ISO parsing for the conversion
-      value = new DateTime(jsonObject.getString(fieldName)).toDate();
-    } else if (expectedType.equals(Long.class) || 
expectedType.equals(Long.TYPE)) {
-      value = jsonObject.getLong(fieldName);
-    } else if (expectedType.equals(Integer.class) || 
expectedType.equals(Integer.TYPE)) {
-      value = jsonObject.getInt(fieldName);
-    } else if (expectedType.equals(Boolean.class) || 
expectedType.equals(Boolean.TYPE)) {
-      value = jsonObject.getBoolean(fieldName);
-    } else if (expectedType.equals(Float.class) || 
expectedType.equals(Float.TYPE)) {
-      value = ((Double) jsonObject.getDouble(fieldName)).floatValue();
-    } else if (expectedType.equals(Double.class) || 
expectedType.equals(Double.TYPE)) {
-      value = jsonObject.getDouble(fieldName);
-    } else {
-      // Assume its an injected type
-      value = convertToObject(jsonObject.getJSONObject(fieldName).toString(), 
expectedType);
+  private Map<String, Object> convertToMap(JSONObject in, Type type) {
+    Map<String, Object> out = new HashMap<String, Object>(in.length(), 1);
+    for (String name : JSONObject.getNames(in)) {
+      out.put(name, convertToObject(in.opt(name), type));
     }
+    return out;
+  }
 
-    if (value != null) {
-      method.invoke(pojo, value);
+  private Collection<Object> convertToCollection(JSONArray in, 
Collection<Object> out, Type type) {
+    for (int i = 0, j = in.length(); i < j; ++i) {
+      out.add(convertToObject(in.opt(i), type));
     }
+    return out;
   }
 
-  private Object convertEnum(Class<?> enumKeyType, JSONObject jsonEnum)
-      throws JSONException, IllegalAccessException, NoSuchFieldException {
-    // TODO This isnt injector friendly but perhaps implementors dont need it. 
If they do a
-    // refactoring of the Enum handling in general is needed.
-    Object value;
-    if (jsonEnum.has(Enum.Field.VALUE.toString())) {
-      Enum.EnumKey enumKey = (Enum.EnumKey) enumKeyType
-          .getField(jsonEnum.getString(Enum.Field.VALUE.toString())).get(null);
-      String displayValue = null;
-      if (jsonEnum.has(Enum.Field.DISPLAY_VALUE.toString())) {
-        displayValue = jsonEnum.getString(Enum.Field.DISPLAY_VALUE.toString());
+  private Object convertToClass(JSONObject in, Class<?> type) {
+    Object out = injector.getInstance(type);
+    for (Map.Entry<String, Method> entry : getSetters(type).entrySet()) {
+      Object value = in.opt(entry.getKey());
+      if (value != null) {
+        Method method = entry.getValue();
+        try {
+          method.invoke(out, convertToObject(value, 
method.getGenericParameterTypes()[0]));
+        } catch (IllegalArgumentException e) {
+          throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+          throw new RuntimeException(e);
+        } catch (InvocationTargetException e) {
+          throw new RuntimeException(e);
+        }
       }
-      value = new EnumImpl<Enum.EnumKey>(enumKey,displayValue);
-    } else {
-      value = new EnumImpl<Enum.EnumKey>(null,
-          jsonEnum.getString(Enum.Field.DISPLAY_VALUE.toString()));
     }
-    return value;
+    return out;
   }
 }

Modified: 
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonAssert.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonAssert.java?rev=755421&r1=755420&r2=755421&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonAssert.java
 (original)
+++ 
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/JsonAssert.java
 Tue Mar 17 22:08:11 2009
@@ -18,11 +18,12 @@
  */
 package org.apache.shindig.common;
 
-import org.json.JSONArray;
-import org.json.JSONObject;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
+import org.json.JSONArray;
+import org.json.JSONObject;
+
 public final class JsonAssert {
   private JsonAssert() {}
 
@@ -53,7 +54,7 @@
       assertEquals("Objects are not of equal size", left.toString(2), 
right.toString(2));
     }
 
-    // Both are emtpy so skip
+    // Both are empty so skip
     if (JSONObject.getNames(left) == null && JSONObject.getNames(right) == 
null) {
       return;
     }

Modified: 
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/JsonRpcServletTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/JsonRpcServletTest.java?rev=755421&r1=755420&r2=755421&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/JsonRpcServletTest.java
 (original)
+++ 
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/JsonRpcServletTest.java
 Tue Mar 17 22:08:11 2009
@@ -17,9 +17,13 @@
  */
 package org.apache.shindig.protocol;
 
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.classextension.EasyMock.reset;
+
 import org.apache.shindig.common.JsonAssert;
 import org.apache.shindig.common.testing.FakeGadgetToken;
-import org.apache.shindig.protocol.conversion.BeanConverter;
 import org.apache.shindig.protocol.conversion.BeanJsonConverter;
 import org.apache.shindig.protocol.multipart.FormDataItem;
 import org.apache.shindig.protocol.multipart.MultipartFormParser;
@@ -27,14 +31,10 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Guice;
 
-import junit.framework.TestCase;
-
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.isA;
 import org.easymock.IMocksControl;
 import org.easymock.classextension.EasyMock;
-import static org.easymock.classextension.EasyMock.reset;
+
+import junit.framework.TestCase;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -60,15 +60,11 @@
   private static final String IMAGE_FIELDNAME = "profile-photo";
   private static final byte[] IMAGE_DATA = "image data".getBytes();
   private static final String IMAGE_TYPE = "image/jpeg";
-  
+
   private HttpServletRequest req;
   private HttpServletResponse res;
   private JsonRpcServlet servlet;
   private MultipartFormParser multipartFormParser;
-  
-  private BeanJsonConverter jsonConverter;
-  private BeanConverter xmlConverter;
-  protected BeanConverter atomConverter;
 
   private final IMocksControl mockControl = EasyMock.createNiceControl();
 
@@ -80,20 +76,19 @@
     servlet = new JsonRpcServlet();
     req = mockControl.createMock(HttpServletRequest.class);
     res = mockControl.createMock(HttpServletResponse.class);
-    jsonConverter = new BeanJsonConverter(Guice.createInjector());
-    xmlConverter = mockControl.createMock(BeanConverter.class);
-    atomConverter = mockControl.createMock(BeanConverter.class);
 
     multipartFormParser = mockControl.createMock(MultipartFormParser.class);
     
EasyMock.expect(multipartFormParser.isMultipartContent(req)).andStubReturn(false);
     servlet.setMultipartFormParser(multipartFormParser);
-    
-    HandlerRegistry registry = new DefaultHandlerRegistry(null, jsonConverter,
+
+    BeanJsonConverter converter = new 
BeanJsonConverter(Guice.createInjector());
+
+    HandlerRegistry registry = new DefaultHandlerRegistry(null, null,
         new HandlerExecutionListener.NoOpHandlerExecutionListener());
     registry.addHandlers(Collections.<Object>singleton(handler));
 
     servlet.setHandlerRegistry(registry);
-    servlet.setBeanConverters(jsonConverter, xmlConverter, atomConverter);
+    servlet.setBeanConverters(converter, null, null);
     handler.setMock(new TestHandler() {
       @Override
       public Object get(RequestItem req) {
@@ -122,7 +117,7 @@
 
   public void testPostMultipartFormData() throws Exception {
     reset(multipartFormParser);
-    
+
     handler.setMock(new TestHandler() {
       @Override
       public Object get(RequestItem req) {
@@ -137,11 +132,11 @@
     expect(req.getCharacterEncoding()).andStubReturn("UTF-8");
     res.setCharacterEncoding("UTF-8");
     res.setContentType("application/json");
-    
+
     List<FormDataItem> formItems = new ArrayList<FormDataItem>();
     String request = "{method:test.get,id:id,params:" +
         "{userId:5,groupId:@self,image-ref:@" + IMAGE_FIELDNAME + "}}";
-    formItems.add(mockFormDataItem(JsonRpcServlet.REQUEST_PARAM, 
"application/json", 
+    formItems.add(mockFormDataItem(JsonRpcServlet.REQUEST_PARAM, 
"application/json",
         request.getBytes(), true));
     formItems.add(mockFormDataItem(IMAGE_FIELDNAME, IMAGE_TYPE, IMAGE_DATA, 
false));
     expect(multipartFormParser.isMultipartContent(req)).andReturn(true);
@@ -153,15 +148,15 @@
     servlet.service(req, res);
     mockControl.verify();
 
-    JsonAssert.assertJsonEquals("{id: 'id', data: {image-data:'" + new 
String(IMAGE_DATA) + 
+    JsonAssert.assertJsonEquals("{id: 'id', data: {image-data:'" + new 
String(IMAGE_DATA) +
         "', image-type:'" + IMAGE_TYPE + "', image-ref:'@" + IMAGE_FIELDNAME + 
"'}}", getOutput());
   }
-  
-  /** 
+
+  /**
    * Tests whether mime part with content type appliction/json is picked up as
    * request even when its fieldname is not "request".
    */
-  public void testPostMultipartFormDataWithNoExplicitRequestField() throws 
Exception {  
+  public void testPostMultipartFormDataWithNoExplicitRequestField() throws 
Exception {
     reset(multipartFormParser);
 
     handler.setMock(new TestHandler() {
@@ -178,12 +173,12 @@
     expect(req.getCharacterEncoding()).andStubReturn("UTF-8");
     res.setCharacterEncoding("UTF-8");
     res.setContentType("application/json");
-    
+
     List<FormDataItem> formItems = new ArrayList<FormDataItem>();
     String request = "{method:test.get,id:id,params:" +
         "{userId:5,groupId:@self,image-ref:@" + IMAGE_FIELDNAME + "}}";
     formItems.add(mockFormDataItem(IMAGE_FIELDNAME, IMAGE_TYPE, IMAGE_DATA, 
false));
-    formItems.add(mockFormDataItem("json", "application/json", 
+    formItems.add(mockFormDataItem("json", "application/json",
         request.getBytes(), true));
     expect(multipartFormParser.isMultipartContent(req)).andReturn(true);
     expect(multipartFormParser.parse(req)).andReturn(formItems);
@@ -194,11 +189,11 @@
     servlet.service(req, res);
     mockControl.verify();
 
-    JsonAssert.assertJsonEquals("{id: 'id', data: {image-data:'" + new 
String(IMAGE_DATA) + 
+    JsonAssert.assertJsonEquals("{id: 'id', data: {image-data:'" + new 
String(IMAGE_DATA) +
         "', image-type:'" + IMAGE_TYPE + "', image-ref:'@" + IMAGE_FIELDNAME + 
"'}}", getOutput());
   }
 
-  
+
   public void testInvalidService() throws Exception {
     setupRequest("{method:junk.get,id:id,params:{userId:5,groupId:@self}}");
 
@@ -228,7 +223,7 @@
     mockControl.replay();
     servlet.service(req, res);
     mockControl.verify();
-    
+
     JsonAssert.assertJsonEquals(
         "{id:id,error:{message:'badRequest: FAILURE_MESSAGE',code:400}}", 
getOutput());
   }

Modified: 
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/conversion/BeanJsonConverterTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/conversion/BeanJsonConverterTest.java?rev=755421&r1=755420&r2=755421&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/conversion/BeanJsonConverterTest.java
 (original)
+++ 
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/conversion/BeanJsonConverterTest.java
 Tue Mar 17 22:08:11 2009
@@ -23,10 +23,13 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.inject.Guice;
+import com.google.inject.TypeLiteral;
 
 import junit.framework.TestCase;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
@@ -39,6 +42,69 @@
     beanJsonConverter = new BeanJsonConverter(Guice.createInjector());
   }
 
+  public static class TestObject {
+    static String staticValue;
+    String hello;
+    int count;
+    List<TestObject> children;
+    TestEnum testEnum;
+
+    public static void setSomeStatic(String staticValue) {
+      TestObject.staticValue = staticValue;
+    }
+
+    public void setHello(String hello) {
+      this.hello = hello;
+    }
+
+    public void setCount(int count) {
+      this.count = count;
+    }
+
+    public void setChildren(List<TestObject> children) {
+      this.children = children;
+    }
+
+    public enum TestEnum {
+      foo, bar, baz;
+    }
+
+    public void setTestEnum(TestEnum testEnum) {
+      this.testEnum = testEnum;
+    }
+  }
+
+  public void testJsonToObject() throws Exception {
+    String json = "{" +
+        "hello:'world'," +
+        "count:10," +
+        "someStatic:'foo'," +
+        "testEnum:'bar'," +
+        "children:[{hello:'world-2',count:11},{hello:'world-3',count:12}]}";
+
+    TestObject object = beanJsonConverter.convertToObject(json, 
TestObject.class);
+
+    assertEquals("world", object.hello);
+    assertEquals(10, object.count);
+    assertEquals("world-2", object.children.get(0).hello);
+    assertEquals(11, object.children.get(0).count);
+    assertEquals("world-3", object.children.get(1).hello);
+    assertEquals(12, object.children.get(1).count);
+    assertNull("Should not set static values", TestObject.staticValue);
+    assertEquals(TestObject.TestEnum.bar, object.testEnum);
+  }
+
+  public void testJsonToPrimitives() throws Exception {
+    String simpleJson = "{hello:'world',count:10}";
+
+    Object object = beanJsonConverter.convertToObject(simpleJson, null);
+
+    Map<String, Object> map = (Map<String,  Object>) object;
+
+    assertEquals("world", map.get("hello"));
+    assertEquals(10, map.get("count"));
+  }
+
   public void testJsonToCar() throws Exception {
     String carJson = 
"{engine:[{value:DIESEL},{value:TURBO}],parkingTickets:{SF:$137,NY:'$301'}," +
             "passengers:[{gender:female,name:'Mum'}, 
{gender:male,name:'Dad'}]}";
@@ -59,12 +125,30 @@
     assertEquals(dad.getName(), "Dad");
   }
 
-  @SuppressWarnings("unchecked")
   public void testJsonToMap() throws Exception {
     String jsonActivity = "{count : 0, favoriteColor : 'yellow'}";
+    Map<String, Object> data = Maps.newHashMap();
+    data = beanJsonConverter.convertToObject(jsonActivity,
+        new TypeLiteral<Map<String, Object>>(){}.getType());
+
+    assertEquals(2, data.size());
+
+    for (Entry<String, Object> entry : data.entrySet()) {
+      String key = entry.getKey();
+      Object value = entry.getValue();
+      if (key.equals("count")) {
+        assertEquals(0, value);
+      } else if (key.equals("favoriteColor")) {
+        assertEquals("yellow", value);
+      }
+    }
+  }
+
+  public void testJsonToMapWithConversion() throws Exception {
+    String jsonActivity = "{count : 0, favoriteColor : 'yellow'}";
     Map<String, String> data = Maps.newHashMap();
     data = beanJsonConverter.convertToObject(jsonActivity,
-        (Class<Map<String, String>>) data.getClass());
+        new TypeLiteral<Map<String, String>>(){}.getType());
 
     assertEquals(2, data.size());
 
@@ -78,4 +162,16 @@
       }
     }
   }
+
+  public void testJsonToNestedGeneric() throws Exception {
+    String jsonActivity = "{key0:[0,1,2],key1:[3,4,5]}";
+    Map<String, List<Integer>> data = Maps.newHashMap();
+    data = beanJsonConverter.convertToObject(jsonActivity,
+        new TypeLiteral<Map<String, List<Integer>>>(){}.getType());
+
+    assertEquals(2, data.size());
+
+    assertEquals(Arrays.asList(0, 1, 2), data.get("key0"));
+    assertEquals(Arrays.asList(3, 4, 5), data.get("key1"));
+  }
 }

Modified: 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AppDataHandler.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AppDataHandler.java?rev=755421&r1=755420&r2=755421&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AppDataHandler.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AppDataHandler.java
 Tue Mar 17 22:08:11 2009
@@ -27,7 +27,6 @@
 
 import com.google.inject.Inject;
 
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Future;
@@ -93,8 +92,7 @@
     HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
     HandlerPreconditions.requireSingular(userIds, "Multiple userIds not 
supported");
 
-    @SuppressWarnings("unchecked")
-    Map<String, String> values = request.getTypedParameter("data", 
HashMap.class);
+    Map<String, String> values = request.getTypedParameter("data", Map.class);
     for (String key : values.keySet()) {
       if (!isValidKey(key)) {
         throw new SocialSpiException(ResponseError.BAD_REQUEST,

Modified: 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/AppDataHandlerTest.java
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/AppDataHandlerTest.java?rev=755421&r1=755420&r2=755421&view=diff
==============================================================================
--- 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/AppDataHandlerTest.java
 (original)
+++ 
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/AppDataHandlerTest.java
 Tue Mar 17 22:08:11 2009
@@ -17,6 +17,8 @@
  */
 package org.apache.shindig.social.opensocial.service;
 
+import static org.easymock.EasyMock.eq;
+
 import org.apache.shindig.common.EasyMockTestCase;
 import org.apache.shindig.common.testing.FakeGadgetToken;
 import org.apache.shindig.common.util.ImmediateFuture;
@@ -36,7 +38,6 @@
 import com.google.common.collect.Sets;
 
 import org.easymock.classextension.EasyMock;
-import static org.easymock.classextension.EasyMock.eq;
 
 import java.io.StringReader;
 import java.util.Collections;
@@ -147,7 +148,7 @@
     params.put("fields", new String[]{"pandas"});
 
     HashMap<String, String> values = Maps.newHashMap();
-    org.easymock.EasyMock.expect(converter.convertToObject(eq(jsonAppData), 
eq(HashMap.class)))
+    org.easymock.EasyMock.expect(converter.convertToObject(eq(jsonAppData), 
eq(Map.class)))
         .andReturn(values);
 
     
org.easymock.EasyMock.expect(appDataService.updatePersonData(eq(JOHN_DOE.iterator().next()),
@@ -184,7 +185,7 @@
     // create an invalid set of app data and inject
     values.put("Aokkey", "an ok key");
     values.put("", "an empty value");
-    org.easymock.EasyMock.expect(converter.convertToObject(eq(jsonAppData), 
eq(HashMap.class)))
+    org.easymock.EasyMock.expect(converter.convertToObject(eq(jsonAppData), 
eq(Map.class)))
         .andReturn(values);
 
     replay();
@@ -213,7 +214,7 @@
     // create an invalid set of app data and inject
     values.put("Aokkey", "an ok key");
     values.put("a bad key", "a good value");
-    org.easymock.EasyMock.expect(converter.convertToObject(eq(jsonAppData), 
eq(HashMap.class)))
+    org.easymock.EasyMock.expect(converter.convertToObject(eq(jsonAppData), 
eq(Map.class)))
         .andReturn(values);
 
     replay();


Reply via email to