import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
import com.vaadin.terminal.gwt.client.communication.RpcManager;
+import com.vaadin.terminal.gwt.client.communication.SerializerMap;
import com.vaadin.terminal.gwt.client.communication.SharedState;
import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
public static final String PARAM_UNLOADBURST = "onunloadburst";
+ private static SerializerMap serializerMap = GWT
+ .create(SerializerMap.class);
+
/**
* A string that, if found in a non-JSON response to a UIDL request, will
* cause the browser to refresh the page. If followed by a colon, optional
states.getJavaScriptObject(connectorId));
Object state = JsonDecoder.decodeValue(
- stateDataAndType, connectorMap,
- ApplicationConnection.this);
+ stateDataAndType, connector.getState(),
+ connectorMap, ApplicationConnection.this);
connector.setState((SharedState) state);
StateChangeEvent event = GWT
Object[] parameters = new Object[parametersJson.size()];
for (int j = 0; j < parametersJson.size(); ++j) {
parameters[j] = JsonDecoder.decodeValue(
- (JSONArray) parametersJson.get(j), getConnectorMap(), this);
+ (JSONArray) parametersJson.get(j), null, getConnectorMap(),
+ this);
}
return new MethodInvocation(connectorId, interfaceName, methodName,
parameters);
LayoutManager getLayoutManager() {
return layoutManager;
}
+
+ public SerializerMap getSerializerMap() {
+ return serializerMap;
+ }
}
*
* @param jsonValue
* JSON map from property name to property value
+ * @param target
+ * The object to write the deserialized values to
* @param idMapper
* mapper from paintable id to paintable, used to decode
* references to paintables
* @return A deserialized object
*/
- T deserialize(JSONObject jsonValue, ConnectorMap idMapper,
+ T deserialize(JSONObject jsonValue, T target, ConnectorMap idMapper,
ApplicationConnection connection);
/**
import java.util.Map;
import java.util.Set;
-import com.google.gwt.core.client.GWT;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
* @since 7.0
*/
public class JsonDecoder {
- static SerializerMap serializerMap = GWT.create(SerializerMap.class);
/**
* Decode a JSON array with two elements (type and value) into a client-side
* reference to the current ApplicationConnection
* @return decoded value (does not contain JSON types)
*/
- public static Object decodeValue(JSONArray jsonArray,
+ public static Object decodeValue(JSONArray jsonArray, Object target,
ConnectorMap idMapper, ApplicationConnection connection) {
String type = ((JSONString) jsonArray.get(0)).stringValue();
- return decodeValue(type, jsonArray.get(1), idMapper, connection);
+ return decodeValue(type, jsonArray.get(1), target, idMapper, connection);
}
private static Object decodeValue(String variableType, Object value,
- ConnectorMap idMapper, ApplicationConnection connection) {
+ Object target, ConnectorMap idMapper,
+ ApplicationConnection connection) {
Object val = null;
// TODO type checks etc.
if (JsonEncoder.VTYPE_NULL.equals(variableType)) {
} else if (JsonEncoder.VTYPE_CONNECTOR.equals(variableType)) {
val = idMapper.getConnector(((JSONString) value).stringValue());
} else {
- // object, class name as type
- JSONSerializer serializer = serializerMap
- .getSerializer(variableType);
- // TODO handle case with no serializer found
- Object object = serializer.deserialize((JSONObject) value,
+ return decodeObject(variableType, (JSONObject) value, target,
idMapper, connection);
- return object;
}
return val;
}
+ private static Object decodeObject(String variableType,
+ JSONObject encodedValue, Object target, ConnectorMap idMapper,
+ ApplicationConnection connection) {
+ // object, class name as type
+ JSONSerializer<Object> serializer = connection.getSerializerMap()
+ .getSerializer(variableType);
+ // TODO handle case with no serializer found
+ Object object = serializer.deserialize(encodedValue, target, idMapper,
+ connection);
+ return object;
+ }
+
private static Map<String, Object> decodeMap(JSONObject jsonMap,
ConnectorMap idMapper, ApplicationConnection connection) {
HashMap<String, Object> map = new HashMap<String, Object>();
while (it.hasNext()) {
String key = it.next();
map.put(key,
- decodeValue((JSONArray) jsonMap.get(key), idMapper,
+ decodeValue((JSONArray) jsonMap.get(key), null, idMapper,
connection));
}
return map;
String connectorId = it.next();
Connector connector = idMapper.getConnector(connectorId);
map.put(connector,
- decodeValue((JSONArray) jsonMap.get(connectorId), idMapper,
- connection));
+ decodeValue((JSONArray) jsonMap.get(connectorId), null,
+ idMapper, connection));
}
return map;
}
for (int i = 0; i < jsonArray.size(); ++i) {
// each entry always has two elements: type and value
JSONArray entryArray = (JSONArray) jsonArray.get(i);
- tokens.add(decodeValue(entryArray, idMapper, connection));
+ tokens.add(decodeValue(entryArray, null, idMapper, connection));
}
return tokens;
}
for (int i = 0; i < jsonArray.size(); ++i) {
// each entry always has two elements: type and value
JSONArray entryArray = (JSONArray) jsonArray.get(i);
- tokens.add(decodeValue(entryArray, idMapper, connection));
+ tokens.add(decodeValue(entryArray, null, idMapper, connection));
}
return tokens;
}
// Try to find a generated serializer object, class name is the
// type
transportType = value.getClass().getName();
- JSONSerializer serializer = JsonDecoder.serializerMap
+ JSONSerializer serializer = connection.getSerializerMap()
.getSerializer(transportType);
// TODO handle case with no serializer found
public class URLReference_Serializer implements JSONSerializer<URLReference> {
- public URLReference deserialize(JSONObject jsonValue,
+ public URLReference deserialize(JSONObject jsonValue, URLReference target,
ConnectorMap idMapper, ApplicationConnection connection) {
URLReference reference = GWT.create(URLReference.class);
- JSONArray jsonURL = (JSONArray) jsonValue.get("URL");
- String URL = (String) JsonDecoder.decodeValue(jsonURL, idMapper,
- connection);
- reference.setURL(connection.translateVaadinUri(URL));
+ if (jsonValue.containsKey("URL")) {
+ JSONArray jsonURL = (JSONArray) jsonValue.get("URL");
+ String URL = (String) JsonDecoder.decodeValue(jsonURL, null,
+ idMapper, connection);
+ reference.setURL(connection.translateVaadinUri(URL));
+ }
return reference;
}
// client after component creation but before legacy UIDL
// processing.
JSONObject sharedStates = new JSONObject();
- for (Connector connector : dirtyVisibleConnectors) {
+ for (ClientConnector connector : dirtyVisibleConnectors) {
SharedState state = connector.getState();
if (null != state) {
// encode and send shared state
try {
- // FIXME Use declared type
+ Class<? extends SharedState> stateType = connector
+ .getStateType();
+ SharedState referenceState = null;
+ if (repaintAll) {
+ // Use an empty state object as reference for full
+ // repaints
+ try {
+ referenceState = stateType.newInstance();
+ } catch (Exception e) {
+ logger.log(Level.WARNING,
+ "Error creating reference object for state of type "
+ + stateType.getName());
+ }
+ }
JSONArray stateJsonArray = JsonCodec.encode(state,
- state.getClass(), application);
+ referenceState, stateType, application);
+
sharedStates
.put(connector.getConnectorId(), stateJsonArray);
} catch (JSONException e) {
invocationJson.put(invocation.getMethodName());
JSONArray paramJson = new JSONArray();
for (int i = 0; i < invocation.getParameterTypes().length; ++i) {
+ Class<?> parameterType = invocation.getParameterTypes()[i];
+ Object referenceParameter = null;
+ // TODO Use default values for RPC parameter types
+ // if (!JsonCodec.isInternalType(parameterType)) {
+ // try {
+ // referenceParameter = parameterType.newInstance();
+ // } catch (Exception e) {
+ // logger.log(Level.WARNING,
+ // "Error creating reference object for parameter of type "
+ // + parameterType.getName());
+ // }
+ // }
paramJson.put(JsonCodec.encode(
- invocation.getParameters()[i],
- invocation.getParameterTypes()[i], application));
+ invocation.getParameters()[i], referenceParameter,
+ parameterType, application));
}
invocationJson.put(paramJson);
rpcCalls.put(invocationJson);
import java.util.List;
import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.communication.SharedState;
/**
* Interface implemented by all connectors that are capable of communicating
* @return true if the connector can receive messages, false otherwise
*/
public boolean isConnectorEnabled();
+
+ /**
+ * Returns the type of the shared state for this connector
+ *
+ * @return The type of the state. Must never return null.
+ */
+ public Class<? extends SharedState> getStateType();
+
}
// TODO Use rpc for drag'n'drop
return null;
}
+
+ public Class<? extends SharedState> getStateType() {
+ return SharedState.class;
+ }
}
@Deprecated
private static JSONArray encode(Object value, Application application)
throws JSONException {
- return encode(value, null, application);
+ return encode(value, null, null, application);
}
- public static JSONArray encode(Object value, Class<?> valueType,
- Application application) throws JSONException {
+ public static JSONArray encode(Object value, Object referenceValue,
+ Type valueType, Application application) throws JSONException {
if (null == value) {
return encodeNull();
"Unable to serialize unsupported type: " + valueType);
}
Collection<?> collection = (Collection<?>) value;
- JSONArray jsonArray = encodeCollection(collection, application);
+ JSONArray jsonArray = encodeCollection(valueType, collection,
+ application);
return combineTypeAndValue(internalTransportType, jsonArray);
} else if (value instanceof Object[]) {
} else {
// Any object that we do not know how to encode we encode by looping
// through fields
- return combineTypeAndValue(getCustomTransportType(valueType),
- encodeObject(value, application));
+ return combineTypeAndValue(
+ getCustomTransportType((Class<?>) valueType),
+ encodeObject(value, referenceValue, application));
}
}
return combineTypeAndValue(JsonEncoder.VTYPE_NULL, JSONObject.NULL);
}
- private static Object encodeObject(Object value, Application application)
- throws JSONException {
+ private static Object encodeObject(Object value, Object referenceValue,
+ Application application) throws JSONException {
JSONObject jsonMap = new JSONObject();
try {
for (PropertyDescriptor pd : Introspector.getBeanInfo(
value.getClass()).getPropertyDescriptors()) {
- Class<?> fieldType = pd.getPropertyType();
String fieldName = getTransportFieldName(pd);
if (fieldName == null) {
continue;
}
Method getterMethod = pd.getReadMethod();
+ // We can't use PropertyDescriptor.getPropertyType() as it does
+ // not support generics
+ Type fieldType = getterMethod.getGenericReturnType();
Object fieldValue = getterMethod.invoke(value, (Object[]) null);
- jsonMap.put(fieldName,
- encode(fieldValue, fieldType, application));
+ boolean equals = false;
+ Object referenceFieldValue = null;
+ if (referenceValue != null) {
+ referenceFieldValue = getterMethod.invoke(referenceValue,
+ (Object[]) null);
+ equals = equals(fieldValue, referenceFieldValue);
+ }
+ if (!equals) {
+ jsonMap.put(
+ fieldName,
+ encode(fieldValue, referenceFieldValue, fieldType,
+ application));
+ // } else {
+ // System.out.println("Skipping field " + fieldName
+ // + " of type " + fieldType.getName()
+ // + " for object " + value.getClass().getName()
+ // + " as " + fieldValue + "==" + referenceFieldValue);
+ }
}
} catch (Exception e) {
// TODO: Should exceptions be handled in a different way?
return jsonMap;
}
+ /**
+ * Compares the value with the reference. If they match, returns true.
+ *
+ * @param fieldValue
+ * @param referenceValue
+ * @return
+ */
+ private static boolean equals(Object fieldValue, Object referenceValue) {
+ if (fieldValue == null) {
+ return referenceValue == null;
+ }
+
+ if (fieldValue.equals(referenceValue)) {
+ return true;
+ }
+
+ return false;
+ }
+
private static JSONArray encodeArrayContents(Object[] array,
Application application) throws JSONException {
JSONArray jsonArray = new JSONArray();
for (Object o : array) {
- jsonArray.put(encode(o, null, application));
+ jsonArray.put(encode(o, null, null, application));
}
return jsonArray;
}
- private static JSONArray encodeCollection(Collection collection,
- Application application) throws JSONException {
+ private static JSONArray encodeCollection(Type targetType,
+ Collection collection, Application application)
+ throws JSONException {
JSONArray jsonArray = new JSONArray();
for (Object o : collection) {
- jsonArray.put(encode(o, application));
+ jsonArray.put(encodeChild(targetType, 0, o, application));
}
return jsonArray;
}
+ private static JSONArray encodeChild(Type targetType, int typeIndex,
+ Object o, Application application) throws JSONException {
+ if (targetType instanceof ParameterizedType) {
+ Type childType = ((ParameterizedType) targetType)
+ .getActualTypeArguments()[typeIndex];
+ // Encode using the given type
+ return encode(o, null, childType, application);
+ } else {
+ return encode(o, application);
+ }
+ }
+
private static JSONObject encodeMapContents(Map<Object, Object> map,
Application application) throws JSONException {
JSONObject jsonMap = new JSONObject();
"Only maps with String/Connector keys are currently supported (#8602)");
}
- jsonMap.put((String) mapKey, encode(mapValue, null, application));
+ jsonMap.put((String) mapKey,
+ encode(mapValue, null, null, application));
}
return jsonMap;
}
composer.addImport(JsonDecoder.class.getName());
// composer.addImport(VaadinSerializer.class.getName());
- composer.addImplementedInterface(JSONSerializer.class.getName());
+ composer.addImplementedInterface(JSONSerializer.class.getName() + "<"
+ + beanQualifiedSourceName + ">");
SourceWriter sourceWriter = composer.createSourceWriter(context,
printWriter);
// public JSONValue serialize(Object value, ConnectorMap idMapper,
// ApplicationConnection connection) {
sourceWriter.println("public " + JSONObject.class.getName()
- + " serialize(" + Object.class.getName() + " value, "
+ + " serialize(" + beanQualifiedSourceName + " value, "
+ ConnectorMap.class.getName() + " idMapper, "
+ ApplicationConnection.class.getName() + " connection) {");
sourceWriter.indent();
// Deserializer
sourceWriter.println("public " + beanQualifiedSourceName
+ " deserialize(" + JSONObject.class.getName() + " jsonValue, "
+ + beanQualifiedSourceName + " target, "
+ ConnectorMap.class.getName() + " idMapper, "
+ ApplicationConnection.class.getName() + " connection) {");
sourceWriter.indent();
- // VButtonState state = GWT.create(VButtonState.class);
- sourceWriter.println(beanQualifiedSourceName + " state = GWT.create("
- + beanQualifiedSourceName + ".class);");
+ // if (target == null) {
+ sourceWriter.println("if (target == null) {");
+ sourceWriter.indent();
+ // target = GWT.create(VButtonState.class);
+ sourceWriter.println("target = GWT.create(" + beanQualifiedSourceName
+ + ".class);");
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+
for (JMethod method : getSetters(beanType)) {
String setterName = method.getName();
String fieldName = setterName.substring(3); // setZIndex() -> ZIndex
logger.log(Type.DEBUG, "* Processing field " + fieldName + " in "
+ beanQualifiedSourceName + " (" + beanType.getName() + ")");
+ // if (jsonValue.containsKey("height")) {
+ sourceWriter.println("if (jsonValue.containsKey(\"" + fieldName
+ + "\")) {");
+ sourceWriter.indent();
String jsonFieldName = "json_" + fieldName;
// JSONArray json_Height = (JSONArray) jsonValue.get("height");
sourceWriter.println("JSONArray " + jsonFieldName
+ " = (JSONArray) jsonValue.get(\"" + fieldName + "\");");
- // state.setHeight((String)
- // JsonDecoder.decodeValue(jsonFieldValue,idMapper, connection));
-
String fieldType;
+ String getterName = "get" + fieldName;
JPrimitiveType primitiveType = setterParameterType.isPrimitive();
if (primitiveType != null) {
// This is a primitive type -> must used the boxed type
fieldType = primitiveType.getQualifiedBoxedSourceName();
+ if (primitiveType == JPrimitiveType.BOOLEAN) {
+ getterName = "is" + fieldName;
+ }
} else {
fieldType = setterParameterType.getQualifiedSourceName();
}
- sourceWriter.println("state." + setterName + "((" + fieldType
+ // String referenceValue;
+ sourceWriter.println(fieldType + " referenceValue;");
+ // if (target == null) {
+ sourceWriter.println("if (target == null) {");
+ sourceWriter.indent();
+ // referenceValue = null;
+ sourceWriter.println("referenceValue = null;");
+ // } else {
+ sourceWriter.println("} else {");
+ // referenceValue = target.getHeight();
+ sourceWriter.println("referenceValue = target." + getterName
+ + "();");
+ // }
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+
+ // target.setHeight((String)
+ // JsonDecoder.decodeValue(jsonFieldValue,referenceValue, idMapper,
+ // connection));
+ sourceWriter.println("target." + setterName + "((" + fieldType
+ ") " + JsonDecoder.class.getName() + ".decodeValue("
- + jsonFieldName + ", idMapper, connection));");
+ + jsonFieldName
+ + ", referenceValue, idMapper, connection));");
+
+ // } ... end of if contains
+ sourceWriter.println("}");
+ sourceWriter.outdent();
}
- // return state;
- sourceWriter.println("return state;");
+ // return target;
+ sourceWriter.println("return target;");
sourceWriter.println("}");
sourceWriter.outdent();
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
* @return new shared state object
*/
protected ComponentState createState() {
+ try {
+ return getStateType().newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Error creating state of type " + getStateType().getName()
+ + " for " + getClass().getName(), e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.vaadin.terminal.gwt.server.ClientConnector#getStateType()
+ */
+ public Class<? extends ComponentState> getStateType() {
try {
Method m = getClass().getMethod("getState", (Class[]) null);
Class<? extends ComponentState> type = (Class<? extends ComponentState>) m
.getReturnType();
- return type.newInstance();
+ return type;
} catch (Exception e) {
- getLogger().log(
- Level.INFO,
- "Error determining state object class for "
- + getClass().getName());
+ throw new RuntimeException("Error finding state type for "
+ + getClass().getName(), e);
}
-
- // Fall back to ComponentState if detection fails for some reason.
- return new ComponentState();
}
/* Documentation copied from interface */
import com.vaadin.terminal.Vaadin6Component;
import com.vaadin.terminal.WrappedRequest;
import com.vaadin.terminal.WrappedRequest.BrowserDetails;
-import com.vaadin.terminal.gwt.client.ComponentState;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
import com.vaadin.terminal.gwt.client.ui.notification.VNotification;
import com.vaadin.terminal.gwt.client.ui.root.RootServerRpc;
}
@Override
- protected ComponentState createState() {
+ public Class<? extends RootState> getStateType() {
// This is a workaround for a problem with creating the correct state
// object during build
- return new RootState();
+ return RootState.class;
}
/**