Support and validation of null values in JSON Requests
Review at http://gwt-code-reviews.appspot.com/871801
Review by: cromwellian@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8776 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java b/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
index a2f6f84..21f0c21 100644
--- a/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
+++ b/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
@@ -183,7 +183,8 @@
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws JSONException
- * @throws SecurityException
+ * @throws SecurityException
+ * @throws NullPointerException
*/
public Object decodeParameterValue(Type genericParameterType,
String parameterValue) throws SecurityException, JSONException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException {
@@ -197,6 +198,9 @@
parameterType = (Class<?>) pType.getActualTypeArguments()[0];
}
}
+ if (parameterValue == null) {
+ return null;
+ }
if (String.class == parameterType) {
return parameterValue;
}
@@ -283,7 +287,7 @@
public Object encodePropertyValue(Object value) {
if (value == null) {
- return value;
+ return null;
}
Class<?> type = value.getClass();
if (Boolean.class == type) {
@@ -385,7 +389,7 @@
}
} else {
Object propertyValue = null;
- if (EntityProxy.class.isAssignableFrom(dtoType)) {
+ if (!recordObject.isNull(key) && EntityProxy.class.isAssignableFrom(dtoType)) {
EntityKey propKey = getEntityKey(recordObject.getString(key));
Object cacheValue = cachedEntityLookup.get(propKey);
if (cachedEntityLookup.containsKey(propKey)) {
@@ -599,7 +603,8 @@
*/
public Object getPropertyValueFromRequest(JSONObject recordObject, String key,
Class<?> propertyType) throws JSONException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException {
- return decodeParameterValue(propertyType, recordObject.get(key).toString());
+ return decodeParameterValue(propertyType,
+ recordObject.isNull(key) ? null : recordObject.get(key).toString());
}
@SuppressWarnings("unchecked")
@@ -745,8 +750,8 @@
Iterator<?> keyIterator = before.keys();
while (keyIterator.hasNext()) {
String key = keyIterator.next().toString();
- Object beforeValue = before.get(key);
- Object afterValue = after.get(key);
+ Object beforeValue = before.isNull(key) ? null : before.get(key);
+ Object afterValue = after.isNull(key) ? null : after.get(key);
if (beforeValue == null) {
if (afterValue == null) {
continue;
diff --git a/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java b/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java
index a46b852..9cdb252 100644
--- a/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java
+++ b/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java
@@ -16,6 +16,7 @@
package com.google.gwt.requestfactory.server;
import com.google.gwt.requestfactory.shared.RequestData;
+import com.google.gwt.requestfactory.shared.SimpleBarProxy;
import com.google.gwt.requestfactory.shared.SimpleEnum;
import com.google.gwt.requestfactory.shared.SimpleFooProxy;
import com.google.gwt.requestfactory.shared.WriteOperation;
@@ -40,7 +41,7 @@
BAR, BAZ
}
- private JsonRequestProcessor requestProcessor;;
+ private JsonRequestProcessor requestProcessor;
@Override
public void setUp() {
@@ -73,6 +74,20 @@
dec.toBigInteger().toString());
// enums
assertTypeAndValueEquals(Foo.class, Foo.BAR, "" + Foo.BAR.ordinal());
+
+ // nulls
+ assertTypeAndValueEquals(String.class, null, null);
+ assertTypeAndValueEquals(Integer.class, null, null);
+ assertTypeAndValueEquals(Byte.class, null, null);
+ assertTypeAndValueEquals(Short.class, null, null);
+ assertTypeAndValueEquals(Float.class, null, null);
+ assertTypeAndValueEquals(Double.class, null, null);
+ assertTypeAndValueEquals(Long.class, null, null);
+ assertTypeAndValueEquals(Boolean.class, null, null);
+ assertTypeAndValueEquals(Date.class, null, null);
+ assertTypeAndValueEquals(BigDecimal.class, null, null);
+ assertTypeAndValueEquals(BigInteger.class, null, null);
+ assertTypeAndValueEquals(Foo.class, null, null);
}
public void testEncodePropertyValue() {
@@ -91,102 +106,119 @@
assertEncodedType(Double.class, Foo.BAR);
assertEncodedType(Boolean.class, true);
assertEncodedType(Boolean.class, false);
+ // nulls stay null
+ assertNull(requestProcessor.encodePropertyValue(null));
}
- public void testEndToEnd() {
+ public void testEndToEnd() throws Exception {
com.google.gwt.requestfactory.server.SimpleFoo.reset();
- try {
- // fetch object
- JSONObject foo = fetchVerifyAndGetInitialObject();
+ // fetch object
+ JSONObject foo = fetchVerifyAndGetInitialObject();
- // modify fields and sync
- foo.put("intId", 45);
- foo.put("userName", "JSC");
- foo.put("longField", "" + 9L);
- foo.put("enumField", SimpleEnum.BAR.ordinal());
- foo.put("boolField", false);
- Date now = new Date();
- foo.put("created", "" + now.getTime());
+ // modify fields and sync
+ foo.put("intId", 45);
+ foo.put("userName", "JSC");
+ foo.put("longField", "" + 9L);
+ foo.put("enumField", SimpleEnum.BAR.ordinal());
+ foo.put("boolField", false);
+ Date now = new Date();
+ foo.put("created", "" + now.getTime());
- JSONObject result = getResultFromServer(foo);
+ JSONObject result = getResultFromServer(foo);
- // check modified fields and no violations
- SimpleFoo fooResult = SimpleFoo.getSingleton();
- JSONArray updateArray = result.getJSONObject("sideEffects").getJSONArray(
- "UPDATE");
- assertEquals(1, updateArray.length());
- assertEquals(1, updateArray.getJSONObject(0).length());
- assertTrue(updateArray.getJSONObject(0).has("id"));
- assertFalse(updateArray.getJSONObject(0).has("violations"));
- assertEquals(45, (int) fooResult.getIntId());
- assertEquals("JSC", fooResult.getUserName());
- assertEquals(now, fooResult.getCreated());
- assertEquals(9L, (long) fooResult.getLongField());
- assertEquals(com.google.gwt.requestfactory.shared.SimpleEnum.BAR,
- fooResult.getEnumField());
- assertEquals(false, (boolean) fooResult.getBoolField());
-
- } catch (Exception e) {
- e.printStackTrace();
- fail(e.toString());
- }
+ // check modified fields and no violations
+ SimpleFoo fooResult = SimpleFoo.getSingleton();
+ JSONArray updateArray = result.getJSONObject("sideEffects").getJSONArray(
+ "UPDATE");
+ assertEquals(1, updateArray.length());
+ assertEquals(1, updateArray.getJSONObject(0).length());
+ assertTrue(updateArray.getJSONObject(0).has("id"));
+ assertFalse(updateArray.getJSONObject(0).has("violations"));
+ assertEquals(45, (int) fooResult.getIntId());
+ assertEquals("JSC", fooResult.getUserName());
+ assertEquals(now, fooResult.getCreated());
+ assertEquals(9L, (long) fooResult.getLongField());
+ assertEquals(com.google.gwt.requestfactory.shared.SimpleEnum.BAR,
+ fooResult.getEnumField());
+ assertEquals(false, (boolean) fooResult.getBoolField());
}
- public void testEndToEndSmartDiff_NoChange() {
+ public void testEndToEndSmartDiff_NoChange() throws Exception {
com.google.gwt.requestfactory.server.SimpleFoo.reset();
- try {
- // fetch object
- JSONObject foo = fetchVerifyAndGetInitialObject();
+ // fetch object
+ JSONObject foo = fetchVerifyAndGetInitialObject();
- // change the value on the server behind the back
- SimpleFoo fooResult = SimpleFoo.getSingleton();
- fooResult.setUserName("JSC");
- fooResult.setIntId(45);
+ // change the value on the server behind the back
+ SimpleFoo fooResult = SimpleFoo.getSingleton();
+ fooResult.setUserName("JSC");
+ fooResult.setIntId(45);
- // modify fields and sync -- there should be no change on the server.
- foo.put("intId", 45);
- foo.put("userName", "JSC");
- JSONObject result = getResultFromServer(foo);
+ // modify fields and sync -- there should be no change on the server.
+ foo.put("intId", 45);
+ foo.put("userName", "JSC");
+ JSONObject result = getResultFromServer(foo);
- // check modified fields and no violations
- assertFalse(result.getJSONObject("sideEffects").has("UPDATE"));
- fooResult = SimpleFoo.getSingleton();
- assertEquals(45, (int) fooResult.getIntId());
- assertEquals("JSC", fooResult.getUserName());
- } catch (Exception e) {
- e.printStackTrace();
- fail(e.toString());
- }
+ // check modified fields and no violations
+ assertFalse(result.getJSONObject("sideEffects").has("UPDATE"));
+ fooResult = SimpleFoo.getSingleton();
+ assertEquals(45, (int) fooResult.getIntId());
+ assertEquals("JSC", fooResult.getUserName());
}
- public void testEndToEndSmartDiff_SomeChange() {
+ public void testEndToEndSmartDiff_SomeChangeWithNull() throws Exception {
com.google.gwt.requestfactory.server.SimpleFoo.reset();
- try {
- // fetch object
- JSONObject foo = fetchVerifyAndGetInitialObject();
+ // fetch object
+ JSONObject foo = fetchVerifyAndGetInitialObject();
- // change some fields but don't change other fields.
- SimpleFoo fooResult = SimpleFoo.getSingleton();
- fooResult.setUserName("JSC");
- fooResult.setIntId(45);
- foo.put("userName", "JSC");
- foo.put("intId", 45);
- Date now = new Date();
- long newTime = now.getTime() + 10000;
- foo.put("created", "" + newTime);
+ // change some fields but don't change other fields.
+ SimpleFoo fooResult = SimpleFoo.getSingleton();
+ fooResult.setUserName("JSC");
+ fooResult.setIntId(45);
+ fooResult.setNullField(null);
+ fooResult.setBarField(SimpleBar.getSingleton());
+ fooResult.setBarNullField(null);
+ foo.put("userName", JSONObject.NULL);
+ foo.put("intId", 45);
+ foo.put("nullField", "test");
+ foo.put("barNullField", SimpleBar.getSingleton().getId()
+ + "-IS-" + SimpleBarProxy.class.getName());
+ foo.put("barField", JSONObject.NULL);
- JSONObject result = getResultFromServer(foo);
+ JSONObject result = getResultFromServer(foo);
- // check modified fields and no violations
- assertTrue(result.getJSONObject("sideEffects").has("UPDATE"));
- fooResult = SimpleFoo.getSingleton();
- assertEquals(45, (int) fooResult.getIntId());
- assertEquals("JSC", fooResult.getUserName());
- assertEquals(newTime, fooResult.getCreated().getTime());
- } catch (Exception e) {
- e.printStackTrace();
- fail(e.toString());
- }
+ // check modified fields and no violations
+ assertTrue(result.getJSONObject("sideEffects").has("UPDATE"));
+ fooResult = SimpleFoo.getSingleton();
+ assertEquals(45, (int) fooResult.getIntId());
+ assertNull(fooResult.getUserName());
+ assertEquals("test", fooResult.getNullField());
+ assertEquals(SimpleBar.getSingleton(), fooResult.getBarNullField());
+ assertNull(fooResult.getBarField());
+ }
+
+ public void testEndToEndSmartDiff_SomeChange() throws Exception {
+ com.google.gwt.requestfactory.server.SimpleFoo.reset();
+ // fetch object
+ JSONObject foo = fetchVerifyAndGetInitialObject();
+
+ // change some fields but don't change other fields.
+ SimpleFoo fooResult = SimpleFoo.getSingleton();
+ fooResult.setUserName("JSC");
+ fooResult.setIntId(45);
+ foo.put("userName", "JSC");
+ foo.put("intId", 45);
+ Date now = new Date();
+ long newTime = now.getTime() + 10000;
+ foo.put("created", "" + newTime);
+
+ JSONObject result = getResultFromServer(foo);
+
+ // check modified fields and no violations
+ assertTrue(result.getJSONObject("sideEffects").has("UPDATE"));
+ fooResult = SimpleFoo.getSingleton();
+ assertEquals(45, (int) fooResult.getIntId());
+ assertEquals("JSC", fooResult.getUserName());
+ assertEquals(newTime, fooResult.getCreated().getTime());
}
private void assertEncodedType(Class<?> expected, Object value) {
@@ -199,7 +231,9 @@
JSONException, IllegalAccessException, InvocationTargetException,
NoSuchMethodException, InstantiationException {
Object val = requestProcessor.decodeParameterValue(expectedType, paramValue);
- assertEquals(expectedType, val.getClass());
+ if (val != null) {
+ assertEquals(expectedType, val.getClass());
+ }
assertEquals(expectedValue, val);
}
diff --git a/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java b/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
index 86cbbc6..ae2ded1 100644
--- a/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
+++ b/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
@@ -97,6 +97,9 @@
private SimpleBar barField;
private SimpleFoo fooField;
+
+ private String nullField;
+ private SimpleBar barNullField;
public SimpleFoo() {
intId = 42;
@@ -107,6 +110,8 @@
created = new Date();
barField = SimpleBar.getSingleton();
boolField = true;
+ nullField = null;
+ barNullField = null;
}
public Long countSimpleFooWithUserNameSideEffect() {
@@ -118,6 +123,10 @@
return barField;
}
+ public SimpleBar getBarNullField() {
+ return barNullField;
+ }
+
/**
* @return the bigDecimalField
*/
@@ -188,6 +197,10 @@
return longField;
}
+ public String getNullField() {
+ return nullField;
+ }
+
/**
* @return the otherBoolField
*/
@@ -230,6 +243,10 @@
this.barField = barField;
}
+ public void setBarNullField(SimpleBar barNullField) {
+ this.barNullField = barNullField;
+ }
+
/**
* @param bigDecimalField the bigDecimalField to set
*/
@@ -300,6 +317,10 @@
this.longField = longField;
}
+ public void setNullField(String nullField) {
+ this.nullField = nullField;
+ }
+
/**
* @param otherBoolField the otherBoolField to set
*/
diff --git a/user/test/com/google/gwt/requestfactory/shared/SimpleFooProxy.java b/user/test/com/google/gwt/requestfactory/shared/SimpleFooProxy.java
index 03af72d..b3851bf 100644
--- a/user/test/com/google/gwt/requestfactory/shared/SimpleFooProxy.java
+++ b/user/test/com/google/gwt/requestfactory/shared/SimpleFooProxy.java
@@ -58,11 +58,19 @@
Property<SimpleBarProxy> barField = new Property<SimpleBarProxy>("barField",
SimpleBarProxy.class);
+ Property<SimpleBarProxy> barNullField = new Property<SimpleBarProxy>("barNullField",
+ SimpleBarProxy.class);
+
Property<SimpleFooProxy> fooField = new Property<SimpleFooProxy>("fooField",
SimpleFooProxy.class);
+ Property<String> nullField = new Property<String>("nullField", "A nullable field",
+ String.class);
+
SimpleBarProxy getBarField();
+ SimpleBarProxy getBarNullField();
+
BigDecimal getBigDecimalField();
BigInteger getBigIntField();
@@ -87,6 +95,8 @@
Long getLongField();
+ String getNullField();
+
Boolean getOtherBoolField();
String getPassword();
@@ -97,6 +107,8 @@
void setBarField(SimpleBarProxy barField);
+ void setBarNullField(SimpleBarProxy barNullField);
+
void setBigDecimalField(BigDecimal d);
void setBigIntField(BigInteger i);
@@ -119,6 +131,8 @@
void setLongField(Long longField);
+ void setNullField(String nullField);
+
void setOtherBoolField(Boolean boolField);
void setPassword(String password);