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.

JsonDecoder.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.communication;
  17. import java.util.ArrayList;
  18. import java.util.Collection;
  19. import java.util.HashMap;
  20. import java.util.HashSet;
  21. import java.util.List;
  22. import java.util.Map;
  23. import java.util.Set;
  24. import com.google.gwt.json.client.JSONArray;
  25. import com.google.gwt.json.client.JSONObject;
  26. import com.google.gwt.json.client.JSONString;
  27. import com.google.gwt.json.client.JSONValue;
  28. import com.vaadin.client.ApplicationConnection;
  29. import com.vaadin.client.ConnectorMap;
  30. import com.vaadin.client.FastStringSet;
  31. import com.vaadin.client.JsArrayObject;
  32. import com.vaadin.client.Profiler;
  33. import com.vaadin.client.metadata.NoDataException;
  34. import com.vaadin.client.metadata.Property;
  35. import com.vaadin.client.metadata.Type;
  36. import com.vaadin.shared.Connector;
  37. /**
  38. * Client side decoder for decodeing shared state and other values from JSON
  39. * received from the server.
  40. *
  41. * Currently, basic data types as well as Map, String[] and Object[] are
  42. * supported, where maps and Object[] can contain other supported data types.
  43. *
  44. * TODO extensible type support
  45. *
  46. * @since 7.0
  47. */
  48. public class JsonDecoder {
  49. private static final FastStringSet decodedWithoutReference = FastStringSet
  50. .create();
  51. static {
  52. decodedWithoutReference.add(String.class.getName());
  53. decodedWithoutReference.add(Boolean.class.getName());
  54. decodedWithoutReference.add(Byte.class.getName());
  55. decodedWithoutReference.add(Character.class.getName());
  56. decodedWithoutReference.add(Short.class.getName());
  57. decodedWithoutReference.add(Integer.class.getName());
  58. decodedWithoutReference.add(Long.class.getName());
  59. decodedWithoutReference.add(Float.class.getName());
  60. decodedWithoutReference.add(Double.class.getName());
  61. decodedWithoutReference.add(Connector.class.getName());
  62. decodedWithoutReference.add(Map.class.getName());
  63. decodedWithoutReference.add(List.class.getName());
  64. decodedWithoutReference.add(Set.class.getName());
  65. }
  66. /**
  67. * Decode a JSON array with two elements (type and value) into a client-side
  68. * type, recursively if necessary.
  69. *
  70. * @param jsonValue
  71. * JSON value with encoded data
  72. * @param connection
  73. * reference to the current ApplicationConnection
  74. * @return decoded value (does not contain JSON types)
  75. */
  76. public static Object decodeValue(Type type, JSONValue jsonValue,
  77. Object target, ApplicationConnection connection) {
  78. // Null is null, regardless of type
  79. if (jsonValue.isNull() != null) {
  80. return null;
  81. }
  82. String baseTypeName = type.getBaseTypeName();
  83. if (Map.class.getName().equals(baseTypeName)
  84. || HashMap.class.getName().equals(baseTypeName)) {
  85. return decodeMap(type, jsonValue, connection);
  86. } else if (List.class.getName().equals(baseTypeName)
  87. || ArrayList.class.getName().equals(baseTypeName)) {
  88. return decodeList(type, (JSONArray) jsonValue, connection);
  89. } else if (Set.class.getName().equals(baseTypeName)) {
  90. return decodeSet(type, (JSONArray) jsonValue, connection);
  91. } else if (String.class.getName().equals(baseTypeName)) {
  92. return ((JSONString) jsonValue).stringValue();
  93. } else if (Integer.class.getName().equals(baseTypeName)) {
  94. return Integer.valueOf(String.valueOf(jsonValue));
  95. } else if (Long.class.getName().equals(baseTypeName)) {
  96. // TODO handle properly
  97. return Long.valueOf(String.valueOf(jsonValue));
  98. } else if (Float.class.getName().equals(baseTypeName)) {
  99. // TODO handle properly
  100. return Float.valueOf(String.valueOf(jsonValue));
  101. } else if (Double.class.getName().equals(baseTypeName)) {
  102. // TODO handle properly
  103. return Double.valueOf(String.valueOf(jsonValue));
  104. } else if (Boolean.class.getName().equals(baseTypeName)) {
  105. // TODO handle properly
  106. return Boolean.valueOf(String.valueOf(jsonValue));
  107. } else if (Byte.class.getName().equals(baseTypeName)) {
  108. // TODO handle properly
  109. return Byte.valueOf(String.valueOf(jsonValue));
  110. } else if (Character.class.getName().equals(baseTypeName)) {
  111. // TODO handle properly
  112. return Character.valueOf(((JSONString) jsonValue).stringValue()
  113. .charAt(0));
  114. } else if (Connector.class.getName().equals(baseTypeName)) {
  115. return ConnectorMap.get(connection).getConnector(
  116. ((JSONString) jsonValue).stringValue());
  117. } else {
  118. return decodeObject(type, jsonValue, target, connection);
  119. }
  120. }
  121. private static Object decodeObject(Type type, JSONValue jsonValue,
  122. Object target, ApplicationConnection connection) {
  123. Profiler.enter("JsonDecoder.decodeObject");
  124. JSONSerializer<Object> serializer = (JSONSerializer<Object>) type
  125. .findSerializer();
  126. if (serializer != null) {
  127. if (target != null && serializer instanceof DiffJSONSerializer<?>) {
  128. DiffJSONSerializer<Object> diffSerializer = (DiffJSONSerializer<Object>) serializer;
  129. diffSerializer.update(target, type, jsonValue, connection);
  130. Profiler.leave("JsonDecoder.decodeObject");
  131. return target;
  132. } else {
  133. Object object = serializer.deserialize(type, jsonValue,
  134. connection);
  135. Profiler.leave("JsonDecoder.decodeObject");
  136. return object;
  137. }
  138. } else {
  139. try {
  140. Profiler.enter("JsonDecoder.decodeObject meta data processing");
  141. JsArrayObject<Property> properties = type
  142. .getPropertiesAsArray();
  143. if (target == null) {
  144. target = type.createInstance();
  145. }
  146. JSONObject jsonObject = jsonValue.isObject();
  147. int size = properties.size();
  148. for (int i = 0; i < size; i++) {
  149. Property property = properties.get(i);
  150. JSONValue encodedPropertyValue = jsonObject.get(property
  151. .getName());
  152. if (encodedPropertyValue == null) {
  153. continue;
  154. }
  155. Type propertyType = property.getType();
  156. Object propertyReference;
  157. if (needsReferenceValue(propertyType)) {
  158. propertyReference = property.getValue(target);
  159. } else {
  160. propertyReference = null;
  161. }
  162. Profiler.leave("JsonDecoder.decodeObject meta data processing");
  163. Object decodedValue = decodeValue(propertyType,
  164. encodedPropertyValue, propertyReference, connection);
  165. Profiler.enter("JsonDecoder.decodeObject meta data processing");
  166. property.setValue(target, decodedValue);
  167. }
  168. Profiler.leave("JsonDecoder.decodeObject meta data processing");
  169. Profiler.leave("JsonDecoder.decodeObject");
  170. return target;
  171. } catch (NoDataException e) {
  172. Profiler.leave("JsonDecoder.decodeObject meta data processing");
  173. Profiler.leave("JsonDecoder.decodeObject");
  174. throw new RuntimeException("Can not deserialize "
  175. + type.getSignature(), e);
  176. }
  177. }
  178. }
  179. private static boolean needsReferenceValue(Type type) {
  180. return !decodedWithoutReference.contains(type.getBaseTypeName());
  181. }
  182. private static Map<Object, Object> decodeMap(Type type, JSONValue jsonMap,
  183. ApplicationConnection connection) {
  184. // Client -> server encodes empty map as an empty array because of
  185. // #8906. Do the same for server -> client to maintain symmetry.
  186. if (jsonMap instanceof JSONArray) {
  187. JSONArray array = (JSONArray) jsonMap;
  188. if (array.size() == 0) {
  189. return new HashMap<Object, Object>();
  190. }
  191. }
  192. Type keyType = type.getParameterTypes()[0];
  193. Type valueType = type.getParameterTypes()[1];
  194. if (keyType.getBaseTypeName().equals(String.class.getName())) {
  195. return decodeStringMap(valueType, jsonMap, connection);
  196. } else if (keyType.getBaseTypeName().equals(Connector.class.getName())) {
  197. return decodeConnectorMap(valueType, jsonMap, connection);
  198. } else {
  199. return decodeObjectMap(keyType, valueType, jsonMap, connection);
  200. }
  201. }
  202. private static Map<Object, Object> decodeObjectMap(Type keyType,
  203. Type valueType, JSONValue jsonValue,
  204. ApplicationConnection connection) {
  205. Map<Object, Object> map = new HashMap<Object, Object>();
  206. JSONArray mapArray = (JSONArray) jsonValue;
  207. JSONArray keys = (JSONArray) mapArray.get(0);
  208. JSONArray values = (JSONArray) mapArray.get(1);
  209. assert (keys.size() == values.size());
  210. for (int i = 0; i < keys.size(); i++) {
  211. Object decodedKey = decodeValue(keyType, keys.get(i), null,
  212. connection);
  213. Object decodedValue = decodeValue(valueType, values.get(i), null,
  214. connection);
  215. map.put(decodedKey, decodedValue);
  216. }
  217. return map;
  218. }
  219. private static Map<Object, Object> decodeConnectorMap(Type valueType,
  220. JSONValue jsonValue, ApplicationConnection connection) {
  221. Map<Object, Object> map = new HashMap<Object, Object>();
  222. JSONObject jsonMap = (JSONObject) jsonValue;
  223. ConnectorMap connectorMap = ConnectorMap.get(connection);
  224. for (String connectorId : jsonMap.keySet()) {
  225. Object value = decodeValue(valueType, jsonMap.get(connectorId),
  226. null, connection);
  227. map.put(connectorMap.getConnector(connectorId), value);
  228. }
  229. return map;
  230. }
  231. private static Map<Object, Object> decodeStringMap(Type valueType,
  232. JSONValue jsonValue, ApplicationConnection connection) {
  233. Map<Object, Object> map = new HashMap<Object, Object>();
  234. JSONObject jsonMap = (JSONObject) jsonValue;
  235. for (String key : jsonMap.keySet()) {
  236. Object value = decodeValue(valueType, jsonMap.get(key), null,
  237. connection);
  238. map.put(key, value);
  239. }
  240. return map;
  241. }
  242. private static List<Object> decodeList(Type type, JSONArray jsonArray,
  243. ApplicationConnection connection) {
  244. List<Object> tokens = new ArrayList<Object>();
  245. decodeIntoCollection(type.getParameterTypes()[0], jsonArray,
  246. connection, tokens);
  247. return tokens;
  248. }
  249. private static Set<Object> decodeSet(Type type, JSONArray jsonArray,
  250. ApplicationConnection connection) {
  251. Set<Object> tokens = new HashSet<Object>();
  252. decodeIntoCollection(type.getParameterTypes()[0], jsonArray,
  253. connection, tokens);
  254. return tokens;
  255. }
  256. private static void decodeIntoCollection(Type childType,
  257. JSONArray jsonArray, ApplicationConnection connection,
  258. Collection<Object> tokens) {
  259. for (int i = 0; i < jsonArray.size(); ++i) {
  260. // each entry always has two elements: type and value
  261. JSONValue entryValue = jsonArray.get(i);
  262. tokens.add(decodeValue(childType, entryValue, null, connection));
  263. }
  264. }
  265. }