]> source.dussan.org Git - vaadin-framework.git/commitdiff
Change diff state to not depend on bean.equals semantics (#9406, #9026)
authorLeif Åstrand <leif@vaadin.com>
Mon, 27 Aug 2012 11:53:36 +0000 (14:53 +0300)
committerLeif Åstrand <leif@vaadin.com>
Mon, 27 Aug 2012 11:53:36 +0000 (14:53 +0300)
server/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
server/src/com/vaadin/terminal/gwt/server/EncodeResult.java [new file with mode: 0644]
server/src/com/vaadin/terminal/gwt/server/JsonCodec.java
tests/client-side/com/vaadin/terminal/gwt/server/JSONSerializerTest.java

index 94fd086b0b85f72a4df78224a77a76edc4daf5c2..64a6e76e7ecfcdddbc35d65ba3fed0b422daba9e 100644 (file)
@@ -986,9 +986,10 @@ public abstract class AbstractCommunicationManager implements Serializable {
                     // + parameterType.getName());
                     // }
                     // }
-                    paramJson.put(JsonCodec.encode(
+                    EncodeResult encodeResult = JsonCodec.encode(
                             invocation.getParameters()[i], referenceParameter,
-                            parameterType, ui.getConnectorTracker()));
+                            parameterType, ui.getConnectorTracker());
+                    paramJson.put(encodeResult.getEncodedValue());
                 }
                 invocationJson.put(paramJson);
                 rpcCalls.put(invocationJson);
@@ -1252,30 +1253,31 @@ public abstract class AbstractCommunicationManager implements Serializable {
         ConnectorTracker connectorTracker = uI.getConnectorTracker();
         Class<? extends SharedState> stateType = connector.getStateType();
         Object diffState = connectorTracker.getDiffState(connector);
-        if (diffState == null) {
+        boolean supportsDiffState = !JavaScriptConnectorState.class
+                .isAssignableFrom(stateType);
+        if (diffState == null && supportsDiffState) {
             // Use an empty state object as reference for full
             // repaints
 
-            boolean supportsDiffState = !JavaScriptConnectorState.class
-                    .isAssignableFrom(stateType);
-            if (supportsDiffState) {
-                diffState = new JSONObject();
-                try {
-                    SharedState referenceState = stateType.newInstance();
-                    diffState = JsonCodec.encode(referenceState, null,
-                            stateType, uI.getConnectorTracker());
-                } catch (Exception e) {
-                    getLogger().log(
-                            Level.WARNING,
-                            "Error creating reference object for state of type "
-                                    + stateType.getName());
-                }
-                connectorTracker.setDiffState(connector, diffState);
+            try {
+                SharedState referenceState = stateType.newInstance();
+                EncodeResult encodeResult = JsonCodec.encode(referenceState,
+                        null, stateType, uI.getConnectorTracker());
+                diffState = encodeResult.getEncodedValue();
+            } catch (Exception e) {
+                getLogger().log(
+                        Level.WARNING,
+                        "Error creating reference object for state of type "
+                                + stateType.getName());
             }
         }
-        JSONObject stateJson = (JSONObject) JsonCodec.encode(state, diffState,
+        EncodeResult encodeResult = JsonCodec.encode(state, diffState,
                 stateType, uI.getConnectorTracker());
-        return stateJson;
+        if (supportsDiffState) {
+            connectorTracker.setDiffState(connector,
+                    encodeResult.getEncodedValue());
+        }
+        return (JSONObject) encodeResult.getDiff();
     }
 
     /**
diff --git a/server/src/com/vaadin/terminal/gwt/server/EncodeResult.java b/server/src/com/vaadin/terminal/gwt/server/EncodeResult.java
new file mode 100644 (file)
index 0000000..a62df2e
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+public class EncodeResult {
+    private final Object encodedValue;
+    private final Object diff;
+
+    public EncodeResult(Object encodedValue) {
+        this(encodedValue, null);
+    }
+
+    public EncodeResult(Object encodedValue, Object diff) {
+        this.encodedValue = encodedValue;
+        this.diff = diff;
+    }
+
+    public Object getEncodedValue() {
+        return encodedValue;
+    }
+
+    public Object getDiff() {
+        return diff;
+    }
+
+    public Object getDiffOrValue() {
+        Object diff = getDiff();
+        if (diff != null) {
+            return diff;
+        } else {
+            return getEncodedValue();
+        }
+    }
+}
index 1eee9c4f529e25525fc60fef8919be8a83e5db80..177d12004c4f9aca055a22f56a190e3c2dda4207 100644 (file)
@@ -595,8 +595,9 @@ public class JsonCodec implements Serializable {
         }
     }
 
-    public static Object encode(Object value, Object diffState, Type valueType,
-            ConnectorTracker connectorTracker) throws JSONException {
+    public static EncodeResult encode(Object value, Object diffState,
+            Type valueType, ConnectorTracker connectorTracker)
+            throws JSONException {
 
         if (valueType == null) {
             throw new IllegalArgumentException("type must be defined");
@@ -617,37 +618,37 @@ public class JsonCodec implements Serializable {
             for (int i = 0; i < array.length; ++i) {
                 jsonArray.put(array[i]);
             }
-            return jsonArray;
+            return new EncodeResult(jsonArray);
         } else if (value instanceof String) {
-            return value;
+            return new EncodeResult(value);
         } else if (value instanceof Boolean) {
-            return value;
+            return new EncodeResult(value);
         } else if (value instanceof Number) {
-            return value;
+            return new EncodeResult(value);
         } else if (value instanceof Character) {
             // Character is not a Number
-            return value;
+            return new EncodeResult(value);
         } else if (value instanceof Collection) {
             Collection<?> collection = (Collection<?>) value;
             JSONArray jsonArray = encodeCollection(valueType, collection,
                     connectorTracker);
-            return jsonArray;
+            return new EncodeResult(jsonArray);
         } else if (valueType instanceof Class<?>
                 && ((Class<?>) valueType).isArray()) {
             JSONArray jsonArray = encodeArrayContents(
                     ((Class<?>) valueType).getComponentType(), value,
                     connectorTracker);
-            return jsonArray;
+            return new EncodeResult(jsonArray);
         } else if (valueType instanceof GenericArrayType) {
             Type componentType = ((GenericArrayType) valueType)
                     .getGenericComponentType();
             JSONArray jsonArray = encodeArrayContents(componentType, value,
                     connectorTracker);
-            return jsonArray;
+            return new EncodeResult(jsonArray);
         } else if (value instanceof Map) {
             Object jsonMap = encodeMap(valueType, (Map<?, ?>) value,
                     connectorTracker);
-            return jsonMap;
+            return new EncodeResult(jsonMap);
         } else if (value instanceof Connector) {
             Connector connector = (Connector) value;
             if (value instanceof Component
@@ -655,11 +656,11 @@ public class JsonCodec implements Serializable {
                             .isVisible((Component) value))) {
                 return encodeNull();
             }
-            return connector.getConnectorId();
+            return new EncodeResult(connector.getConnectorId());
         } else if (value instanceof Enum) {
             return encodeEnum((Enum<?>) value, connectorTracker);
         } else if (value instanceof JSONArray || value instanceof JSONObject) {
-            return value;
+            return new EncodeResult(value);
         } else {
             // Any object that we do not know how to encode we encode by looping
             // through fields
@@ -667,8 +668,8 @@ public class JsonCodec implements Serializable {
         }
     }
 
-    private static Object encodeNull() {
-        return JSONObject.NULL;
+    private static EncodeResult encodeNull() {
+        return new EncodeResult(JSONObject.NULL);
     }
 
     public static Collection<BeanProperty> getProperties(Class<?> type)
@@ -681,9 +682,11 @@ public class JsonCodec implements Serializable {
         return properties;
     }
 
-    private static Object encodeObject(Object value, JSONObject diffState,
-            ConnectorTracker connectorTracker) throws JSONException {
-        JSONObject jsonMap = new JSONObject();
+    private static EncodeResult encodeObject(Object value,
+            JSONObject referenceValue, ConnectorTracker connectorTracker)
+            throws JSONException {
+        JSONObject encoded = new JSONObject();
+        JSONObject diff = new JSONObject();
 
         try {
             for (BeanProperty property : getProperties(value.getClass())) {
@@ -692,49 +695,39 @@ public class JsonCodec implements Serializable {
                 // not support generics
                 Type fieldType = property.getType();
                 Object fieldValue = property.getValue(value);
-                boolean equals = false;
-                Object diffStateValue = null;
-                if (diffState != null && diffState.has(fieldName)) {
-                    diffStateValue = diffState.get(fieldName);
-                    Object referenceFieldValue = decodeInternalOrCustomType(
-                            fieldType, diffStateValue, connectorTracker);
-                    if (JSONObject.NULL.equals(diffStateValue)) {
-                        diffStateValue = null;
-                    }
-                    equals = equals(fieldValue, referenceFieldValue);
+
+                if (encoded.has(fieldName)) {
+                    throw new RuntimeException(
+                            "Can't encode "
+                                    + value.getClass().getName()
+                                    + " as it has multiple fields with the name "
+                                    + fieldName.toLowerCase()
+                                    + ". This can happen if only casing distinguishes one property name from another.");
                 }
-                if (!equals) {
-                    if (jsonMap.has(fieldName)) {
-                        throw new RuntimeException(
-                                "Can't encode "
-                                        + value.getClass().getName()
-                                        + " as it has multiple fields with the name "
-                                        + fieldName.toLowerCase()
-                                        + ". This can happen if only casing distinguishes one property name from another.");
-                    }
-                    jsonMap.put(
-                            fieldName,
-                            encode(fieldValue, diffStateValue, fieldType,
-                                    connectorTracker));
-                    if (diffState != null) {
-                        diffState.put(
-                                fieldName,
-                                encode(fieldValue, null, fieldType,
-                                        connectorTracker));
+
+                Object fieldReference;
+                if (referenceValue != null) {
+                    fieldReference = referenceValue.get(fieldName);
+                    if (JSONObject.NULL.equals(fieldReference)) {
+                        fieldReference = null;
                     }
+                } else {
+                    fieldReference = null;
+                }
+
+                EncodeResult encodeResult = encode(fieldValue, fieldReference,
+                        fieldType, connectorTracker);
+                encoded.put(fieldName, encodeResult.getEncodedValue());
 
-                    // } else {
-                    // System.out.println("Skipping field " + fieldName
-                    // + " of type " + fieldType.getName()
-                    // + " for object " + value.getClass().getName()
-                    // + " as " + fieldValue + "==" + referenceFieldValue);
+                if (!jsonEquals(encodeResult.getEncodedValue(), fieldReference)) {
+                    diff.put(fieldName, encodeResult.getDiffOrValue());
                 }
             }
         } catch (Exception e) {
             // TODO: Should exceptions be handled in a different way?
             throw new JSONException(e);
         }
-        return jsonMap;
+        return new EncodeResult(encoded, diff);
     }
 
     /**
@@ -744,21 +737,19 @@ public class JsonCodec implements Serializable {
      * @param referenceValue
      * @return
      */
-    private static boolean equals(Object fieldValue, Object referenceValue) {
-        if (fieldValue == null) {
-            return referenceValue == null;
-        }
-
-        if (fieldValue.equals(referenceValue)) {
+    private static boolean jsonEquals(Object fieldValue, Object referenceValue) {
+        if (fieldValue == referenceValue) {
             return true;
+        } else if (fieldValue == null || referenceValue == null) {
+            return false;
+        } else {
+            return fieldValue.toString().equals(referenceValue.toString());
         }
-
-        return false;
     }
 
-    private static String encodeEnum(Enum<?> e,
+    private static EncodeResult encodeEnum(Enum<?> e,
             ConnectorTracker connectorTracker) throws JSONException {
-        return e.name();
+        return new EncodeResult(e.name());
     }
 
     private static JSONArray encodeArrayContents(Type componentType,
@@ -766,8 +757,9 @@ public class JsonCodec implements Serializable {
             throws JSONException {
         JSONArray jsonArray = new JSONArray();
         for (int i = 0; i < Array.getLength(array); i++) {
-            jsonArray.put(encode(Array.get(array, i), null, componentType,
-                    connectorTracker));
+            EncodeResult encodeResult = encode(Array.get(array, i), null,
+                    componentType, connectorTracker);
+            jsonArray.put(encodeResult.getEncodedValue());
         }
         return jsonArray;
     }
@@ -788,7 +780,9 @@ public class JsonCodec implements Serializable {
             Type childType = ((ParameterizedType) targetType)
                     .getActualTypeArguments()[typeIndex];
             // Encode using the given type
-            return encode(o, null, childType, connectorTracker);
+            EncodeResult encodeResult = encode(o, null, childType,
+                    connectorTracker);
+            return encodeResult.getEncodedValue();
         } else {
             throw new JSONException("Collection is missing generics");
         }
@@ -827,13 +821,13 @@ public class JsonCodec implements Serializable {
         JSONArray values = new JSONArray();
 
         for (Entry<?, ?> entry : map.entrySet()) {
-            Object encodedKey = encode(entry.getKey(), null, keyType,
-                    connectorTracker);
-            Object encodedValue = encode(entry.getValue(), null, valueType,
+            EncodeResult encodedKey = encode(entry.getKey(), null, keyType,
                     connectorTracker);
+            EncodeResult encodedValue = encode(entry.getValue(), null,
+                    valueType, connectorTracker);
 
-            keys.put(encodedKey);
-            values.put(encodedValue);
+            keys.put(encodedKey.getEncodedValue());
+            values.put(encodedValue.getEncodedValue());
         }
 
         return new JSONArray(Arrays.asList(keys, values));
@@ -845,9 +839,9 @@ public class JsonCodec implements Serializable {
 
         for (Entry<?, ?> entry : map.entrySet()) {
             Connector key = (Connector) entry.getKey();
-            Object encodedValue = encode(entry.getValue(), null, valueType,
-                    connectorTracker);
-            jsonMap.put(key.getConnectorId(), encodedValue);
+            EncodeResult encodedValue = encode(entry.getValue(), null,
+                    valueType, connectorTracker);
+            jsonMap.put(key.getConnectorId(), encodedValue.getEncodedValue());
         }
 
         return jsonMap;
@@ -859,9 +853,9 @@ public class JsonCodec implements Serializable {
 
         for (Entry<?, ?> entry : map.entrySet()) {
             String key = (String) entry.getKey();
-            Object encodedValue = encode(entry.getValue(), null, valueType,
-                    connectorTracker);
-            jsonMap.put(key, encodedValue);
+            EncodeResult encodedValue = encode(entry.getValue(), null,
+                    valueType, connectorTracker);
+            jsonMap.put(key, encodedValue.getEncodedValue());
         }
 
         return jsonMap;
index 7775b667a1079496818af32ed3d900c822618c85..3738da3c5c6470f0cf5e7a82febf60d8baa09cc3 100644 (file)
@@ -52,7 +52,7 @@ public class JSONSerializerTest extends TestCase {
         stringToStateMap.put("String - state 2", s2);
 
         Object encodedMap = JsonCodec.encode(stringToStateMap, null, mapType,
-                null);
+                null).getEncodedValue();
 
         ensureDecodedCorrectly(stringToStateMap, encodedMap, mapType);
     }
@@ -69,7 +69,7 @@ public class JSONSerializerTest extends TestCase {
         stateToStringMap.put(s2, "String - state 2");
 
         Object encodedMap = JsonCodec.encode(stateToStringMap, null, mapType,
-                null);
+                null).getEncodedValue();
 
         ensureDecodedCorrectly(stateToStringMap, encodedMap, mapType);
     }