You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JsonEncoder.java 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.communication;
  5. import java.util.Collection;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Set;
  9. import com.google.gwt.json.client.JSONArray;
  10. import com.google.gwt.json.client.JSONBoolean;
  11. import com.google.gwt.json.client.JSONNull;
  12. import com.google.gwt.json.client.JSONObject;
  13. import com.google.gwt.json.client.JSONString;
  14. import com.google.gwt.json.client.JSONValue;
  15. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  16. import com.vaadin.terminal.gwt.client.Connector;
  17. import com.vaadin.terminal.gwt.client.ConnectorMap;
  18. /**
  19. * Encoder for converting RPC parameters and other values to JSON for transfer
  20. * between the client and the server.
  21. *
  22. * Currently, basic data types as well as Map, String[] and Object[] are
  23. * supported, where maps and Object[] can contain other supported data types.
  24. *
  25. * TODO extensible type support
  26. *
  27. * @since 7.0
  28. */
  29. public class JsonEncoder {
  30. public static final String VTYPE_CONNECTOR = "c";
  31. public static final String VTYPE_BOOLEAN = "b";
  32. public static final String VTYPE_DOUBLE = "d";
  33. public static final String VTYPE_FLOAT = "f";
  34. public static final String VTYPE_LONG = "l";
  35. public static final String VTYPE_INTEGER = "i";
  36. public static final String VTYPE_STRING = "s";
  37. public static final String VTYPE_ARRAY = "a";
  38. public static final String VTYPE_STRINGARRAY = "S";
  39. public static final String VTYPE_MAP = "m";
  40. // Hack to support Map<Connector,?>. Should be replaced by generic support
  41. // for any object as key (#8602)
  42. @Deprecated
  43. public static final String VTYPE_MAP_CONNECTOR = "M";
  44. public static final String VTYPE_LIST = "L";
  45. public static final String VTYPE_SET = "q";
  46. public static final String VTYPE_NULL = "n";
  47. /**
  48. * Encode a value to a JSON representation for transport from the client to
  49. * the server.
  50. *
  51. * @param value
  52. * value to convert
  53. * @param connectorMap
  54. * mapper from connectors to connector IDs
  55. * @param connection
  56. * @return JSON representation of the value
  57. */
  58. public static JSONValue encode(Object value, ConnectorMap connectorMap,
  59. ApplicationConnection connection) {
  60. if (null == value) {
  61. return combineTypeAndValue(VTYPE_NULL, JSONNull.getInstance());
  62. } else if (value instanceof String[]) {
  63. String[] array = (String[]) value;
  64. JSONArray jsonArray = new JSONArray();
  65. for (int i = 0; i < array.length; ++i) {
  66. jsonArray.set(i, new JSONString(array[i]));
  67. }
  68. return combineTypeAndValue(VTYPE_STRINGARRAY, jsonArray);
  69. } else if (value instanceof String) {
  70. return combineTypeAndValue(VTYPE_STRING, new JSONString(
  71. (String) value));
  72. } else if (value instanceof Boolean) {
  73. return combineTypeAndValue(VTYPE_BOOLEAN,
  74. JSONBoolean.getInstance((Boolean) value));
  75. } else if (value instanceof Object[]) {
  76. return encodeObjectArray((Object[]) value, connectorMap, connection);
  77. } else if (value instanceof Enum) {
  78. Enum e = (Enum) value;
  79. return encodeEnum(e, connectorMap, connection);
  80. } else if (value instanceof Map) {
  81. Map<Object, Object> map = (Map<Object, Object>) value;
  82. JSONObject jsonMap = new JSONObject();
  83. String type = VTYPE_MAP;
  84. for (Object mapKey : map.keySet()) {
  85. Object mapValue = map.get(mapKey);
  86. if (mapKey instanceof Connector) {
  87. mapKey = ((Connector) mapKey).getConnectorId();
  88. type = VTYPE_MAP_CONNECTOR;
  89. }
  90. if (!(mapKey instanceof String)) {
  91. throw new RuntimeException(
  92. "Only Map<String,?> and Map<Connector,?> is currently supported."
  93. + " Failed map used "
  94. + mapKey.getClass().getName() + " as keys");
  95. }
  96. jsonMap.put((String) mapKey,
  97. encode(mapValue, connectorMap, connection));
  98. }
  99. return combineTypeAndValue(type, jsonMap);
  100. } else if (value instanceof Connector) {
  101. Connector connector = (Connector) value;
  102. return combineTypeAndValue(VTYPE_CONNECTOR, new JSONString(
  103. connector.getConnectorId()));
  104. } else if (value instanceof Collection) {
  105. return encodeCollection((Collection) value, connectorMap,
  106. connection);
  107. } else {
  108. String transportType = getTransportType(value);
  109. if (transportType != null) {
  110. return combineTypeAndValue(transportType,
  111. new JSONString(String.valueOf(value)));
  112. } else {
  113. // Try to find a generated serializer object, class name is the
  114. // type
  115. transportType = value.getClass().getName();
  116. JSONSerializer serializer = connection.getSerializerMap()
  117. .getSerializer(transportType);
  118. // TODO handle case with no serializer found
  119. return combineTypeAndValue(transportType,
  120. serializer.serialize(value, connectorMap, connection));
  121. }
  122. }
  123. }
  124. private static JSONValue encodeEnum(Enum e, ConnectorMap connectorMap,
  125. ApplicationConnection connection) {
  126. return combineTypeAndValue(e.getClass().getName(),
  127. new JSONString(e.toString()));
  128. }
  129. private static JSONValue encodeObjectArray(Object[] array,
  130. ConnectorMap connectorMap, ApplicationConnection connection) {
  131. JSONArray jsonArray = new JSONArray();
  132. for (int i = 0; i < array.length; ++i) {
  133. // TODO handle object graph loops?
  134. jsonArray.set(i, encode(array[i], connectorMap, connection));
  135. }
  136. return combineTypeAndValue(VTYPE_ARRAY, jsonArray);
  137. }
  138. private static JSONValue encodeCollection(Collection collection,
  139. ConnectorMap connectorMap, ApplicationConnection connection) {
  140. JSONArray jsonArray = new JSONArray();
  141. int idx = 0;
  142. for (Object o : collection) {
  143. JSONValue encodedObject = encode(o, connectorMap, connection);
  144. jsonArray.set(idx++, encodedObject);
  145. }
  146. if (collection instanceof Set) {
  147. return combineTypeAndValue(VTYPE_SET, jsonArray);
  148. } else if (collection instanceof List) {
  149. return combineTypeAndValue(VTYPE_LIST, jsonArray);
  150. } else {
  151. throw new RuntimeException("Unsupport collection type: "
  152. + collection.getClass().getName());
  153. }
  154. }
  155. private static JSONValue combineTypeAndValue(String type, JSONValue value) {
  156. JSONArray outerArray = new JSONArray();
  157. outerArray.set(0, new JSONString(type));
  158. outerArray.set(1, value);
  159. return outerArray;
  160. }
  161. /**
  162. * Returns the transport type for the given value. Only returns a transport
  163. * type for internally handled values.
  164. *
  165. * @param value
  166. * The value that should be transported
  167. * @return One of the JsonEncode.VTYPE_ constants or null if the value
  168. * cannot be transported using an internally handled type.
  169. */
  170. private static String getTransportType(Object value) {
  171. if (value == null) {
  172. return VTYPE_NULL;
  173. } else if (value instanceof String) {
  174. return VTYPE_STRING;
  175. } else if (value instanceof Connector) {
  176. return VTYPE_CONNECTOR;
  177. } else if (value instanceof Boolean) {
  178. return VTYPE_BOOLEAN;
  179. } else if (value instanceof Integer) {
  180. return VTYPE_INTEGER;
  181. } else if (value instanceof Float) {
  182. return VTYPE_FLOAT;
  183. } else if (value instanceof Double) {
  184. return VTYPE_DOUBLE;
  185. } else if (value instanceof Long) {
  186. return VTYPE_LONG;
  187. } else if (value instanceof List) {
  188. return VTYPE_LIST;
  189. } else if (value instanceof Set) {
  190. return VTYPE_SET;
  191. } else if (value instanceof String[]) {
  192. return VTYPE_STRINGARRAY;
  193. } else if (value instanceof Object[]) {
  194. return VTYPE_ARRAY;
  195. } else if (value instanceof Map) {
  196. return VTYPE_MAP;
  197. }
  198. return null;
  199. }
  200. }