From 0e95b201c996378794af6726d841b740418e4370 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Mon, 27 Aug 2012 14:53:36 +0300 Subject: [PATCH] Change diff state to not depend on bean.equals semantics (#9406, #9026) --- .../server/AbstractCommunicationManager.java | 42 ++--- .../terminal/gwt/server/EncodeResult.java | 48 ++++++ .../vaadin/terminal/gwt/server/JsonCodec.java | 146 +++++++++--------- .../gwt/server/JSONSerializerTest.java | 4 +- 4 files changed, 142 insertions(+), 98 deletions(-) create mode 100644 server/src/com/vaadin/terminal/gwt/server/EncodeResult.java diff --git a/server/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/server/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 94fd086b0b..64a6e76e7e 100644 --- a/server/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/server/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -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 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 index 0000000000..a62df2e632 --- /dev/null +++ b/server/src/com/vaadin/terminal/gwt/server/EncodeResult.java @@ -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(); + } + } +} diff --git a/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java index 1eee9c4f52..177d12004c 100644 --- a/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java +++ b/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java @@ -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 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; diff --git a/tests/client-side/com/vaadin/terminal/gwt/server/JSONSerializerTest.java b/tests/client-side/com/vaadin/terminal/gwt/server/JSONSerializerTest.java index 7775b667a1..3738da3c5c 100644 --- a/tests/client-side/com/vaadin/terminal/gwt/server/JSONSerializerTest.java +++ b/tests/client-side/com/vaadin/terminal/gwt/server/JSONSerializerTest.java @@ -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); } -- 2.39.5