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 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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 Map) {
  78. Map<Object, Object> map = (Map<Object, Object>) value;
  79. JSONObject jsonMap = new JSONObject();
  80. String type = VTYPE_MAP;
  81. for (Object mapKey : map.keySet()) {
  82. Object mapValue = map.get(mapKey);
  83. if (mapKey instanceof Connector) {
  84. mapKey = ((Connector) mapKey).getConnectorId();
  85. type = VTYPE_MAP_CONNECTOR;
  86. }
  87. if (!(mapKey instanceof String)) {
  88. throw new RuntimeException(
  89. "Only Map<String,?> and Map<Connector,?> is currently supported."
  90. + " Failed map used "
  91. + mapKey.getClass().getName() + " as keys");
  92. }
  93. jsonMap.put((String) mapKey,
  94. encode(mapValue, connectorMap, connection));
  95. }
  96. return combineTypeAndValue(type, jsonMap);
  97. } else if (value instanceof Connector) {
  98. Connector connector = (Connector) value;
  99. return combineTypeAndValue(VTYPE_CONNECTOR, new JSONString(
  100. connector.getConnectorId()));
  101. } else if (value instanceof Collection) {
  102. return encodeCollection((Collection) value, connectorMap,
  103. connection);
  104. } else {
  105. String transportType = getTransportType(value);
  106. if (transportType != null) {
  107. return combineTypeAndValue(transportType,
  108. new JSONString(String.valueOf(value)));
  109. } else {
  110. // Try to find a generated serializer object, class name is the
  111. // type
  112. transportType = value.getClass().getName();
  113. JSONSerializer serializer = JsonDecoder.serializerMap
  114. .getSerializer(transportType);
  115. // TODO handle case with no serializer found
  116. return combineTypeAndValue(transportType,
  117. serializer.serialize(value, connectorMap, connection));
  118. }
  119. }
  120. }
  121. private static JSONValue encodeObjectArray(Object[] array,
  122. ConnectorMap connectorMap, ApplicationConnection connection) {
  123. JSONArray jsonArray = new JSONArray();
  124. for (int i = 0; i < array.length; ++i) {
  125. // TODO handle object graph loops?
  126. jsonArray.set(i, encode(array[i], connectorMap, connection));
  127. }
  128. return combineTypeAndValue(VTYPE_ARRAY, jsonArray);
  129. }
  130. private static JSONValue encodeCollection(Collection collection,
  131. ConnectorMap connectorMap, ApplicationConnection connection) {
  132. JSONArray jsonArray = new JSONArray();
  133. int idx = 0;
  134. for (Object o : collection) {
  135. JSONValue encodedObject = encode(o, connectorMap, connection);
  136. jsonArray.set(idx++, encodedObject);
  137. }
  138. if (collection instanceof Set) {
  139. return combineTypeAndValue(VTYPE_SET, jsonArray);
  140. } else if (collection instanceof List) {
  141. return combineTypeAndValue(VTYPE_LIST, jsonArray);
  142. } else {
  143. throw new RuntimeException("Unsupport collection type: "
  144. + collection.getClass().getName());
  145. }
  146. }
  147. private static JSONValue combineTypeAndValue(String type, JSONValue value) {
  148. JSONArray outerArray = new JSONArray();
  149. outerArray.set(0, new JSONString(type));
  150. outerArray.set(1, value);
  151. return outerArray;
  152. }
  153. private static String getTransportType(Object value) {
  154. if (value == null) {
  155. return VTYPE_NULL;
  156. } else if (value instanceof String) {
  157. return VTYPE_STRING;
  158. } else if (value instanceof Connector) {
  159. return VTYPE_CONNECTOR;
  160. } else if (value instanceof Boolean) {
  161. return VTYPE_BOOLEAN;
  162. } else if (value instanceof Integer) {
  163. return VTYPE_INTEGER;
  164. } else if (value instanceof Float) {
  165. return VTYPE_FLOAT;
  166. } else if (value instanceof Double) {
  167. return VTYPE_DOUBLE;
  168. } else if (value instanceof Long) {
  169. return VTYPE_LONG;
  170. } else if (value instanceof List) {
  171. return VTYPE_LIST;
  172. } else if (value instanceof Set) {
  173. return VTYPE_SET;
  174. } else if (value instanceof Enum) {
  175. return VTYPE_STRING; // transported as string representation
  176. } else if (value instanceof String[]) {
  177. return VTYPE_STRINGARRAY;
  178. } else if (value instanceof Object[]) {
  179. return VTYPE_ARRAY;
  180. } else if (value instanceof Map) {
  181. return VTYPE_MAP;
  182. }
  183. return null;
  184. }
  185. }