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.

JsonCodec.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.server;
  5. import java.beans.IntrospectionException;
  6. import java.beans.Introspector;
  7. import java.beans.PropertyDescriptor;
  8. import java.io.Serializable;
  9. import java.lang.reflect.Array;
  10. import java.lang.reflect.InvocationTargetException;
  11. import java.lang.reflect.Method;
  12. import java.lang.reflect.ParameterizedType;
  13. import java.lang.reflect.Type;
  14. import java.util.ArrayList;
  15. import java.util.Arrays;
  16. import java.util.Collection;
  17. import java.util.HashMap;
  18. import java.util.HashSet;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.Map.Entry;
  23. import java.util.Set;
  24. import com.vaadin.Application;
  25. import com.vaadin.external.json.JSONArray;
  26. import com.vaadin.external.json.JSONException;
  27. import com.vaadin.external.json.JSONObject;
  28. import com.vaadin.terminal.gwt.client.Connector;
  29. import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
  30. import com.vaadin.terminal.gwt.client.communication.UidlValue;
  31. import com.vaadin.ui.Component;
  32. /**
  33. * Decoder for converting RPC parameters and other values from JSON in transfer
  34. * between the client and the server and vice versa.
  35. *
  36. * @since 7.0
  37. */
  38. public class JsonCodec implements Serializable {
  39. private static Map<Class<?>, String> typeToTransportType = new HashMap<Class<?>, String>();
  40. /**
  41. * Note! This does not contain primitives.
  42. * <p>
  43. */
  44. private static Map<String, Class<?>> transportTypeToType = new HashMap<String, Class<?>>();
  45. static {
  46. registerType(String.class, JsonEncoder.VTYPE_STRING);
  47. registerType(Connector.class, JsonEncoder.VTYPE_CONNECTOR);
  48. registerType(Boolean.class, JsonEncoder.VTYPE_BOOLEAN);
  49. registerType(boolean.class, JsonEncoder.VTYPE_BOOLEAN);
  50. registerType(Integer.class, JsonEncoder.VTYPE_INTEGER);
  51. registerType(int.class, JsonEncoder.VTYPE_INTEGER);
  52. registerType(Float.class, JsonEncoder.VTYPE_FLOAT);
  53. registerType(float.class, JsonEncoder.VTYPE_FLOAT);
  54. registerType(Double.class, JsonEncoder.VTYPE_DOUBLE);
  55. registerType(double.class, JsonEncoder.VTYPE_DOUBLE);
  56. registerType(Long.class, JsonEncoder.VTYPE_LONG);
  57. registerType(long.class, JsonEncoder.VTYPE_LONG);
  58. registerType(String[].class, JsonEncoder.VTYPE_STRINGARRAY);
  59. registerType(Object[].class, JsonEncoder.VTYPE_ARRAY);
  60. registerType(Map.class, JsonEncoder.VTYPE_MAP);
  61. registerType(HashMap.class, JsonEncoder.VTYPE_MAP);
  62. registerType(List.class, JsonEncoder.VTYPE_LIST);
  63. registerType(Set.class, JsonEncoder.VTYPE_SET);
  64. }
  65. private static void registerType(Class<?> type, String transportType) {
  66. typeToTransportType.put(type, transportType);
  67. if (!type.isPrimitive()) {
  68. transportTypeToType.put(transportType, type);
  69. }
  70. }
  71. public static boolean isInternalTransportType(String transportType) {
  72. return transportTypeToType.containsKey(transportType);
  73. }
  74. public static boolean isInternalType(Type type) {
  75. if (type instanceof Class && ((Class<?>) type).isPrimitive()) {
  76. if (type == byte.class || type == char.class) {
  77. // Almost all primitive types are handled internally
  78. return false;
  79. }
  80. // All primitive types are handled internally
  81. return true;
  82. } else if (type == UidlValue.class) {
  83. // UidlValue is a special internal type wrapping type info and a
  84. // value
  85. return true;
  86. }
  87. return typeToTransportType.containsKey(getClassForType(type));
  88. }
  89. private static Class<?> getClassForType(Type type) {
  90. if (type instanceof ParameterizedType) {
  91. return (Class<?>) (((ParameterizedType) type).getRawType());
  92. } else {
  93. return (Class<?>) type;
  94. }
  95. }
  96. private static Class<?> getType(String transportType) {
  97. return transportTypeToType.get(transportType);
  98. }
  99. public static Object decodeInternalOrCustomType(Type targetType,
  100. Object value, Application application) throws JSONException {
  101. if (isInternalType(targetType)) {
  102. return decodeInternalType(targetType, false, value, application);
  103. } else {
  104. return decodeCustomType(targetType, value, application);
  105. }
  106. }
  107. public static Object decodeCustomType(Type targetType, Object value,
  108. Application application) throws JSONException {
  109. if (isInternalType(targetType)) {
  110. throw new JSONException("decodeCustomType cannot be used for "
  111. + targetType + ", which is an internal type");
  112. }
  113. // Try to decode object using fields
  114. if (value == JSONObject.NULL) {
  115. return null;
  116. } else if (targetType == byte.class || targetType == Byte.class) {
  117. return Byte.valueOf(String.valueOf(value));
  118. } else if (targetType == char.class || targetType == Character.class) {
  119. return Character.valueOf(String.valueOf(value).charAt(0));
  120. } else if (targetType instanceof Class<?>
  121. && ((Class<?>) targetType).isArray()) {
  122. // Legacy Object[] and String[] handled elsewhere, this takes care
  123. // of generic arrays
  124. return decodeArray((Class<?>) targetType, (JSONArray) value,
  125. application);
  126. } else if (targetType == JSONObject.class
  127. || targetType == JSONArray.class) {
  128. return value;
  129. } else {
  130. return decodeObject(targetType, (JSONObject) value, application);
  131. }
  132. }
  133. private static Object decodeArray(Class<?> targetType, JSONArray value,
  134. Application application) throws JSONException {
  135. Class<?> componentType = targetType.getComponentType();
  136. Object array = Array.newInstance(componentType, value.length());
  137. for (int i = 0; i < value.length(); i++) {
  138. Object decodedValue = decodeInternalOrCustomType(componentType,
  139. value.get(i), application);
  140. Array.set(array, i, decodedValue);
  141. }
  142. return array;
  143. }
  144. /**
  145. * Decodes a value that is of an internal type.
  146. * <p>
  147. * Ensures the encoded value is of the same type as target type.
  148. * </p>
  149. * <p>
  150. * Allows restricting collections so that they must be declared using
  151. * generics. If this is used then all objects in the collection are encoded
  152. * using the declared type. Otherwise only internal types are allowed in
  153. * collections.
  154. * </p>
  155. *
  156. * @param targetType
  157. * The type that should be returned by this method
  158. * @param valueAndType
  159. * The encoded value and type array
  160. * @param application
  161. * A reference to the application
  162. * @param enforceGenericsInCollections
  163. * true if generics should be enforce, false to only allow
  164. * internal types in collections
  165. * @return
  166. * @throws JSONException
  167. */
  168. public static Object decodeInternalType(Type targetType,
  169. boolean restrictToInternalTypes, Object encodedJsonValue,
  170. Application application) throws JSONException {
  171. if (!isInternalType(targetType)) {
  172. throw new JSONException("Type " + targetType
  173. + " is not a supported internal type.");
  174. }
  175. String transportType = getInternalTransportType(targetType);
  176. if (encodedJsonValue == JSONObject.NULL) {
  177. return null;
  178. }
  179. // UidlValue
  180. if (targetType == UidlValue.class) {
  181. return decodeUidlValue((JSONArray) encodedJsonValue, application);
  182. }
  183. // Collections
  184. if (JsonEncoder.VTYPE_LIST.equals(transportType)) {
  185. return decodeList(targetType, restrictToInternalTypes,
  186. (JSONArray) encodedJsonValue, application);
  187. } else if (JsonEncoder.VTYPE_SET.equals(transportType)) {
  188. return decodeSet(targetType, restrictToInternalTypes,
  189. (JSONArray) encodedJsonValue, application);
  190. } else if (JsonEncoder.VTYPE_MAP.equals(transportType)) {
  191. return decodeMap(targetType, restrictToInternalTypes,
  192. encodedJsonValue, application);
  193. }
  194. // Arrays
  195. if (JsonEncoder.VTYPE_ARRAY.equals(transportType)) {
  196. return decodeObjectArray(targetType, (JSONArray) encodedJsonValue,
  197. application);
  198. } else if (JsonEncoder.VTYPE_STRINGARRAY.equals(transportType)) {
  199. return decodeStringArray((JSONArray) encodedJsonValue);
  200. }
  201. // Special Vaadin types
  202. String stringValue = String.valueOf(encodedJsonValue);
  203. if (JsonEncoder.VTYPE_CONNECTOR.equals(transportType)) {
  204. return application.getConnector(stringValue);
  205. }
  206. // Legacy types
  207. if (JsonEncoder.VTYPE_STRING.equals(transportType)) {
  208. return stringValue;
  209. } else if (JsonEncoder.VTYPE_INTEGER.equals(transportType)) {
  210. return Integer.valueOf(stringValue);
  211. } else if (JsonEncoder.VTYPE_LONG.equals(transportType)) {
  212. return Long.valueOf(stringValue);
  213. } else if (JsonEncoder.VTYPE_FLOAT.equals(transportType)) {
  214. return Float.valueOf(stringValue);
  215. } else if (JsonEncoder.VTYPE_DOUBLE.equals(transportType)) {
  216. return Double.valueOf(stringValue);
  217. } else if (JsonEncoder.VTYPE_BOOLEAN.equals(transportType)) {
  218. return Boolean.valueOf(stringValue);
  219. }
  220. throw new JSONException("Unknown type " + transportType);
  221. }
  222. private static UidlValue decodeUidlValue(JSONArray encodedJsonValue,
  223. Application application) throws JSONException {
  224. String type = encodedJsonValue.getString(0);
  225. Object decodedValue = decodeInternalType(getType(type), true,
  226. encodedJsonValue.get(1), application);
  227. return new UidlValue(decodedValue);
  228. }
  229. private static boolean transportTypesCompatible(
  230. String encodedTransportType, String transportType) {
  231. if (encodedTransportType == null) {
  232. return false;
  233. }
  234. if (encodedTransportType.equals(transportType)) {
  235. return true;
  236. }
  237. if (encodedTransportType.equals(JsonEncoder.VTYPE_NULL)) {
  238. return true;
  239. }
  240. return false;
  241. }
  242. private static Map<Object, Object> decodeMap(Type targetType,
  243. boolean restrictToInternalTypes, Object jsonMap,
  244. Application application) throws JSONException {
  245. if (jsonMap instanceof JSONArray) {
  246. // Client-side has no declared type information to determine
  247. // encoding method for empty maps, so these are handled separately.
  248. // See #8906.
  249. JSONArray jsonArray = (JSONArray) jsonMap;
  250. if (jsonArray.length() == 0) {
  251. return new HashMap<Object, Object>();
  252. }
  253. }
  254. if (!restrictToInternalTypes && targetType instanceof ParameterizedType) {
  255. Type keyType = ((ParameterizedType) targetType)
  256. .getActualTypeArguments()[0];
  257. Type valueType = ((ParameterizedType) targetType)
  258. .getActualTypeArguments()[1];
  259. if (keyType == String.class) {
  260. return decodeStringMap(valueType, (JSONObject) jsonMap,
  261. application);
  262. } else if (keyType == Connector.class) {
  263. return decodeConnectorMap(valueType, (JSONObject) jsonMap,
  264. application);
  265. } else {
  266. return decodeObjectMap(keyType, valueType, (JSONArray) jsonMap,
  267. application);
  268. }
  269. } else {
  270. return decodeStringMap(UidlValue.class, (JSONObject) jsonMap,
  271. application);
  272. }
  273. }
  274. private static Map<Object, Object> decodeObjectMap(Type keyType,
  275. Type valueType, JSONArray jsonMap, Application application)
  276. throws JSONException {
  277. Map<Object, Object> map = new HashMap<Object, Object>();
  278. JSONArray keys = jsonMap.getJSONArray(0);
  279. JSONArray values = jsonMap.getJSONArray(1);
  280. assert (keys.length() == values.length());
  281. for (int i = 0; i < keys.length(); i++) {
  282. Object key = decodeInternalOrCustomType(keyType, keys.get(i),
  283. application);
  284. Object value = decodeInternalOrCustomType(valueType, values.get(i),
  285. application);
  286. map.put(key, value);
  287. }
  288. return map;
  289. }
  290. private static Map<Object, Object> decodeConnectorMap(Type valueType,
  291. JSONObject jsonMap, Application application) throws JSONException {
  292. Map<Object, Object> map = new HashMap<Object, Object>();
  293. for (Iterator<?> iter = jsonMap.keys(); iter.hasNext();) {
  294. String key = (String) iter.next();
  295. Object value = decodeInternalOrCustomType(valueType,
  296. jsonMap.get(key), application);
  297. if (valueType == UidlValue.class) {
  298. value = ((UidlValue) value).getValue();
  299. }
  300. map.put(application.getConnector(key), value);
  301. }
  302. return map;
  303. }
  304. private static Map<Object, Object> decodeStringMap(Type valueType,
  305. JSONObject jsonMap, Application application) throws JSONException {
  306. Map<Object, Object> map = new HashMap<Object, Object>();
  307. for (Iterator<?> iter = jsonMap.keys(); iter.hasNext();) {
  308. String key = (String) iter.next();
  309. Object value = decodeInternalOrCustomType(valueType,
  310. jsonMap.get(key), application);
  311. if (valueType == UidlValue.class) {
  312. value = ((UidlValue) value).getValue();
  313. }
  314. map.put(key, value);
  315. }
  316. return map;
  317. }
  318. /**
  319. * @param targetType
  320. * @param restrictToInternalTypes
  321. * @param typeIndex
  322. * The index of a generic type to use to define the child type
  323. * that should be decoded
  324. * @param encodedValueAndType
  325. * @param application
  326. * @return
  327. * @throws JSONException
  328. */
  329. private static Object decodeParametrizedType(Type targetType,
  330. boolean restrictToInternalTypes, int typeIndex, Object value,
  331. Application application) throws JSONException {
  332. if (!restrictToInternalTypes && targetType instanceof ParameterizedType) {
  333. Type childType = ((ParameterizedType) targetType)
  334. .getActualTypeArguments()[typeIndex];
  335. // Only decode the given type
  336. return decodeInternalOrCustomType(childType, value, application);
  337. } else {
  338. // Only UidlValue when not enforcing a given type to avoid security
  339. // issues
  340. UidlValue decodeInternalType = (UidlValue) decodeInternalType(
  341. UidlValue.class, true, value, application);
  342. return decodeInternalType.getValue();
  343. }
  344. }
  345. private static Object decodeEnum(Class<? extends Enum> cls, JSONObject value) {
  346. String enumIdentifier = String.valueOf(value);
  347. return Enum.valueOf(cls, enumIdentifier);
  348. }
  349. private static String[] decodeStringArray(JSONArray jsonArray)
  350. throws JSONException {
  351. int length = jsonArray.length();
  352. List<String> tokens = new ArrayList<String>(length);
  353. for (int i = 0; i < length; ++i) {
  354. tokens.add(jsonArray.getString(i));
  355. }
  356. return tokens.toArray(new String[tokens.size()]);
  357. }
  358. private static Object[] decodeObjectArray(Type targetType,
  359. JSONArray jsonArray, Application application) throws JSONException {
  360. List list = decodeList(List.class, true, jsonArray, application);
  361. return list.toArray(new Object[list.size()]);
  362. }
  363. private static List<Object> decodeList(Type targetType,
  364. boolean restrictToInternalTypes, JSONArray jsonArray,
  365. Application application) throws JSONException {
  366. List<Object> list = new ArrayList<Object>();
  367. for (int i = 0; i < jsonArray.length(); ++i) {
  368. // each entry always has two elements: type and value
  369. Object encodedValue = jsonArray.get(i);
  370. Object decodedChild = decodeParametrizedType(targetType,
  371. restrictToInternalTypes, 0, encodedValue, application);
  372. list.add(decodedChild);
  373. }
  374. return list;
  375. }
  376. private static Set<Object> decodeSet(Type targetType,
  377. boolean restrictToInternalTypes, JSONArray jsonArray,
  378. Application application) throws JSONException {
  379. HashSet<Object> set = new HashSet<Object>();
  380. set.addAll(decodeList(targetType, restrictToInternalTypes, jsonArray,
  381. application));
  382. return set;
  383. }
  384. /**
  385. * Returns the name that should be used as field name in the JSON. We strip
  386. * "set" from the setter, keeping the result - this is easy to do on both
  387. * server and client, avoiding some issues with cASE. E.g setZIndex()
  388. * becomes "zIndex". Also ensures that both getter and setter are present,
  389. * returning null otherwise.
  390. *
  391. * @param pd
  392. * @return the name to be used or null if both getter and setter are not
  393. * found.
  394. */
  395. static String getTransportFieldName(PropertyDescriptor pd) {
  396. if (pd.getReadMethod() == null || pd.getWriteMethod() == null) {
  397. return null;
  398. }
  399. String fieldName = pd.getWriteMethod().getName().substring(3);
  400. fieldName = Character.toLowerCase(fieldName.charAt(0))
  401. + fieldName.substring(1);
  402. return fieldName;
  403. }
  404. private static Object decodeObject(Type targetType,
  405. JSONObject serializedObject, Application application)
  406. throws JSONException {
  407. Class<?> targetClass = getClassForType(targetType);
  408. if (Enum.class.isAssignableFrom(targetClass)) {
  409. return decodeEnum(targetClass.asSubclass(Enum.class),
  410. serializedObject);
  411. }
  412. try {
  413. Object decodedObject = targetClass.newInstance();
  414. for (PropertyDescriptor pd : Introspector.getBeanInfo(targetClass)
  415. .getPropertyDescriptors()) {
  416. String fieldName = getTransportFieldName(pd);
  417. if (fieldName == null) {
  418. continue;
  419. }
  420. Object encodedFieldValue = serializedObject.get(fieldName);
  421. Type fieldType = pd.getReadMethod().getGenericReturnType();
  422. Object decodedFieldValue = decodeInternalOrCustomType(
  423. fieldType, encodedFieldValue, application);
  424. pd.getWriteMethod().invoke(decodedObject, decodedFieldValue);
  425. }
  426. return decodedObject;
  427. } catch (IllegalArgumentException e) {
  428. throw new JSONException(e);
  429. } catch (IllegalAccessException e) {
  430. throw new JSONException(e);
  431. } catch (InvocationTargetException e) {
  432. throw new JSONException(e);
  433. } catch (InstantiationException e) {
  434. throw new JSONException(e);
  435. } catch (IntrospectionException e) {
  436. throw new JSONException(e);
  437. }
  438. }
  439. public static Object encode(Object value, Object referenceValue,
  440. Type valueType, Application application) throws JSONException {
  441. if (valueType == null) {
  442. throw new IllegalArgumentException("type must be defined");
  443. }
  444. if (null == value) {
  445. return encodeNull();
  446. }
  447. if (value instanceof String[]) {
  448. String[] array = (String[]) value;
  449. JSONArray jsonArray = new JSONArray();
  450. for (int i = 0; i < array.length; ++i) {
  451. jsonArray.put(array[i]);
  452. }
  453. return jsonArray;
  454. } else if (value instanceof String) {
  455. return value;
  456. } else if (value instanceof Boolean) {
  457. return value;
  458. } else if (value instanceof Number) {
  459. return value;
  460. } else if (value instanceof Character) {
  461. // Character is not a Number
  462. return value;
  463. } else if (value instanceof Collection) {
  464. Collection<?> collection = (Collection<?>) value;
  465. JSONArray jsonArray = encodeCollection(valueType, collection,
  466. application);
  467. return jsonArray;
  468. } else if (valueType instanceof Class<?>
  469. && ((Class<?>) valueType).isArray()) {
  470. JSONArray jsonArray = encodeArrayContents(value, application);
  471. return jsonArray;
  472. } else if (value instanceof Map) {
  473. Object jsonMap = encodeMap(valueType, (Map<?, ?>) value,
  474. application);
  475. return jsonMap;
  476. } else if (value instanceof Connector) {
  477. Connector connector = (Connector) value;
  478. if (value instanceof Component
  479. && !(AbstractCommunicationManager
  480. .isVisible((Component) value))) {
  481. return encodeNull();
  482. }
  483. return connector.getConnectorId();
  484. } else if (value instanceof Enum) {
  485. return encodeEnum((Enum<?>) value, application);
  486. } else if (value instanceof JSONArray || value instanceof JSONObject) {
  487. return value;
  488. } else {
  489. // Any object that we do not know how to encode we encode by looping
  490. // through fields
  491. return encodeObject(value, referenceValue, application);
  492. }
  493. }
  494. private static Object encodeNull() {
  495. return JSONObject.NULL;
  496. }
  497. private static Object encodeObject(Object value, Object referenceValue,
  498. Application application) throws JSONException {
  499. JSONObject jsonMap = new JSONObject();
  500. try {
  501. for (PropertyDescriptor pd : Introspector.getBeanInfo(
  502. value.getClass()).getPropertyDescriptors()) {
  503. String fieldName = getTransportFieldName(pd);
  504. if (fieldName == null) {
  505. continue;
  506. }
  507. Method getterMethod = pd.getReadMethod();
  508. // We can't use PropertyDescriptor.getPropertyType() as it does
  509. // not support generics
  510. Type fieldType = getterMethod.getGenericReturnType();
  511. Object fieldValue = getterMethod.invoke(value, (Object[]) null);
  512. boolean equals = false;
  513. Object referenceFieldValue = null;
  514. if (referenceValue != null) {
  515. referenceFieldValue = getterMethod.invoke(referenceValue,
  516. (Object[]) null);
  517. equals = equals(fieldValue, referenceFieldValue);
  518. }
  519. if (!equals) {
  520. if (jsonMap.has(fieldName)) {
  521. throw new RuntimeException(
  522. "Can't encode "
  523. + value.getClass().getName()
  524. + " as it has multiple fields with the name "
  525. + fieldName.toLowerCase()
  526. + ". This can happen if only casing distinguishes one property name from another.");
  527. }
  528. jsonMap.put(
  529. fieldName,
  530. encode(fieldValue, referenceFieldValue, fieldType,
  531. application));
  532. // } else {
  533. // System.out.println("Skipping field " + fieldName
  534. // + " of type " + fieldType.getName()
  535. // + " for object " + value.getClass().getName()
  536. // + " as " + fieldValue + "==" + referenceFieldValue);
  537. }
  538. }
  539. } catch (Exception e) {
  540. // TODO: Should exceptions be handled in a different way?
  541. throw new JSONException(e);
  542. }
  543. return jsonMap;
  544. }
  545. /**
  546. * Compares the value with the reference. If they match, returns true.
  547. *
  548. * @param fieldValue
  549. * @param referenceValue
  550. * @return
  551. */
  552. private static boolean equals(Object fieldValue, Object referenceValue) {
  553. if (fieldValue == null) {
  554. return referenceValue == null;
  555. }
  556. if (fieldValue.equals(referenceValue)) {
  557. return true;
  558. }
  559. return false;
  560. }
  561. private static String encodeEnum(Enum<?> e, Application application)
  562. throws JSONException {
  563. return e.name();
  564. }
  565. private static JSONArray encodeArrayContents(Object array,
  566. Application application) throws JSONException {
  567. JSONArray jsonArray = new JSONArray();
  568. Class<?> componentType = array.getClass().getComponentType();
  569. for (int i = 0; i < Array.getLength(array); i++) {
  570. jsonArray.put(encode(Array.get(array, i), null, componentType,
  571. application));
  572. }
  573. return jsonArray;
  574. }
  575. private static JSONArray encodeCollection(Type targetType,
  576. Collection collection, Application application)
  577. throws JSONException {
  578. JSONArray jsonArray = new JSONArray();
  579. for (Object o : collection) {
  580. jsonArray.put(encodeChild(targetType, 0, o, application));
  581. }
  582. return jsonArray;
  583. }
  584. private static Object encodeChild(Type targetType, int typeIndex, Object o,
  585. Application application) throws JSONException {
  586. if (targetType instanceof ParameterizedType) {
  587. Type childType = ((ParameterizedType) targetType)
  588. .getActualTypeArguments()[typeIndex];
  589. // Encode using the given type
  590. return encode(o, null, childType, application);
  591. } else {
  592. throw new JSONException("Collection is missing generics");
  593. }
  594. }
  595. private static Object encodeMap(Type mapType, Map<?, ?> map,
  596. Application application) throws JSONException {
  597. Type keyType, valueType;
  598. if (mapType instanceof ParameterizedType) {
  599. keyType = ((ParameterizedType) mapType).getActualTypeArguments()[0];
  600. valueType = ((ParameterizedType) mapType).getActualTypeArguments()[1];
  601. } else {
  602. throw new JSONException("Map is missing generics");
  603. }
  604. if (map.isEmpty()) {
  605. // Client -> server encodes empty map as an empty array because of
  606. // #8906. Do the same for server -> client to maintain symmetry.
  607. return new JSONArray();
  608. }
  609. if (keyType == String.class) {
  610. return encodeStringMap(valueType, map, application);
  611. } else if (keyType == Connector.class) {
  612. return encodeConnectorMap(valueType, map, application);
  613. } else {
  614. return encodeObjectMap(keyType, valueType, map, application);
  615. }
  616. }
  617. private static JSONArray encodeObjectMap(Type keyType, Type valueType,
  618. Map<?, ?> map, Application application) throws JSONException {
  619. JSONArray keys = new JSONArray();
  620. JSONArray values = new JSONArray();
  621. for (Entry<?, ?> entry : map.entrySet()) {
  622. Object encodedKey = encode(entry.getKey(), null, keyType,
  623. application);
  624. Object encodedValue = encode(entry.getValue(), null, valueType,
  625. application);
  626. keys.put(encodedKey);
  627. values.put(encodedValue);
  628. }
  629. return new JSONArray(Arrays.asList(keys, values));
  630. }
  631. private static JSONObject encodeConnectorMap(Type valueType, Map<?, ?> map,
  632. Application application) throws JSONException {
  633. JSONObject jsonMap = new JSONObject();
  634. for (Entry<?, ?> entry : map.entrySet()) {
  635. Connector key = (Connector) entry.getKey();
  636. Object encodedValue = encode(entry.getValue(), null, valueType,
  637. application);
  638. jsonMap.put(key.getConnectorId(), encodedValue);
  639. }
  640. return jsonMap;
  641. }
  642. private static JSONObject encodeStringMap(Type valueType, Map<?, ?> map,
  643. Application application) throws JSONException {
  644. JSONObject jsonMap = new JSONObject();
  645. for (Entry<?, ?> entry : map.entrySet()) {
  646. String key = (String) entry.getKey();
  647. Object encodedValue = encode(entry.getValue(), null, valueType,
  648. application);
  649. jsonMap.put(key, encodedValue);
  650. }
  651. return jsonMap;
  652. }
  653. /**
  654. * Gets the transport type for the given class. Returns null if no transport
  655. * type can be found.
  656. *
  657. * @param valueType
  658. * The type that should be transported
  659. * @return
  660. * @throws JSONException
  661. */
  662. private static String getInternalTransportType(Type valueType) {
  663. return typeToTransportType.get(getClassForType(valueType));
  664. }
  665. private static String getCustomTransportType(Class<?> targetType) {
  666. return targetType.getName();
  667. }
  668. }