aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java
blob: cd255ada95526e9bae4f2b469e4514c18c2cba64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
@VaadinApache2LicenseForJavaFiles@
 */

package com.vaadin.terminal.gwt.client.communication;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.Connector;
import com.vaadin.terminal.gwt.client.ConnectorMap;
import com.vaadin.terminal.gwt.client.ServerConnector;

/**
 * Client side decoder for decodeing shared state and other values from JSON
 * received from the server.
 * 
 * Currently, basic data types as well as Map, String[] and Object[] are
 * supported, where maps and Object[] can contain other supported data types.
 * 
 * TODO extensible type support
 * 
 * @since 7.0
 */
public class JsonDecoder {

    /**
     * Decode a JSON array with two elements (type and value) into a client-side
     * type, recursively if necessary.
     * 
     * @param jsonArray
     *            JSON array with two elements
     * @param idMapper
     *            mapper between connector ID and {@link ServerConnector}
     *            objects
     * @param connection
     *            reference to the current ApplicationConnection
     * @return decoded value (does not contain JSON types)
     */
    public static Object decodeValue(Type type, JSONArray jsonArray,
            Object target, ApplicationConnection connection) {
        return decodeValue(type, jsonArray.get(1), target, connection);
    }

    private static Object decodeValue(Type type, JSONValue jsonValue,
            Object target, ApplicationConnection connection) {

        // Null is null, regardless of type
        if (jsonValue.isNull() != null) {
            return null;
        }

        String baseTypeName = type.getBaseTypeName();
        if (baseTypeName.endsWith("[]")) {
            return decodeArray(type, (JSONArray) jsonValue, connection);
        } else if (Map.class.getName().equals(baseTypeName)
                || HashMap.class.getName().equals(baseTypeName)) {
            return decodeMap(type, (JSONObject) jsonValue, connection);
        } else if (List.class.getName().equals(baseTypeName)
                || ArrayList.class.getName().equals(baseTypeName)) {
            return decodeList(type, (JSONArray) jsonValue, connection);
        } else if (Set.class.getName().equals(baseTypeName)) {
            return decodeSet(type, (JSONArray) jsonValue, connection);
        } else if (String.class.getName().equals(baseTypeName)) {
            return ((JSONString) jsonValue).stringValue();
        } else if (Integer.class.getName().equals(baseTypeName)) {
            return Integer.valueOf(String.valueOf(jsonValue));
        } else if (Long.class.getName().equals(baseTypeName)) {
            // TODO handle properly
            return Long.valueOf(String.valueOf(jsonValue));
        } else if (Float.class.getName().equals(baseTypeName)) {
            // TODO handle properly
            return Float.valueOf(String.valueOf(jsonValue));
        } else if (Double.class.getName().equals(baseTypeName)) {
            // TODO handle properly
            return Double.valueOf(String.valueOf(jsonValue));
        } else if (Boolean.class.getName().equals(baseTypeName)) {
            // TODO handle properly
            return Boolean.valueOf(String.valueOf(jsonValue));
        } else if (Connector.class.getName().equals(baseTypeName)) {
            return ConnectorMap.get(connection).getConnector(
                    ((JSONString) jsonValue).stringValue());
        } else {
            return decodeObject(type, jsonValue, target, connection);
        }
    }

    private static Object decodeObject(Type type, JSONValue jsonValue,
            Object target, ApplicationConnection connection) {
        JSONSerializer<Object> serializer = connection.getSerializerMap()
                .getSerializer(type.getBaseTypeName());
        // TODO handle case with no serializer found
        // Currently getSerializer throws exception if not found

        if (target != null && serializer instanceof DiffJSONSerializer<?>) {
            DiffJSONSerializer<Object> diffSerializer = (DiffJSONSerializer<Object>) serializer;
            diffSerializer.update(target, type, jsonValue, connection);
            return target;
        } else {
            Object object = serializer.deserialize(type, jsonValue, connection);
            return object;
        }
    }

    private static Map<Object, Object> decodeMap(Type type, JSONObject jsonMap,
            ApplicationConnection connection) {
        HashMap<Object, Object> map = new HashMap<Object, Object>();
        Iterator<String> it = jsonMap.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            JSONArray encodedKey = (JSONArray) JSONParser.parseStrict(key);
            JSONArray encodedValue = (JSONArray) jsonMap.get(key);
            Object decodedKey = decodeValue(type.getParameterTypes()[0],
                    encodedKey, null, connection);
            Object decodedValue = decodeValue(type.getParameterTypes()[1],
                    encodedValue, null, connection);
            map.put(decodedKey, decodedValue);
        }
        return map;
    }

    private static Object[] decodeArray(Type type, JSONArray jsonArray,
            ApplicationConnection connection) {
        String arrayTypeName = type.getBaseTypeName();
        String chldTypeName = arrayTypeName.substring(0,
                arrayTypeName.length() - 2);
        List<Object> list = decodeList(new Type(chldTypeName, null), jsonArray,
                connection);
        return list.toArray(new Object[list.size()]);
    }

    private static List<Object> decodeList(Type type, JSONArray jsonArray,
            ApplicationConnection connection) {
        List<Object> tokens = new ArrayList<Object>();
        decodeIntoCollection(type.getParameterTypes()[0], jsonArray,
                connection, tokens);
        return tokens;
    }

    private static Set<Object> decodeSet(Type type, JSONArray jsonArray,
            ApplicationConnection connection) {
        Set<Object> tokens = new HashSet<Object>();
        decodeIntoCollection(type.getParameterTypes()[0], jsonArray,
                connection, tokens);
        return tokens;
    }

    private static void decodeIntoCollection(Type childType,
            JSONArray jsonArray, ApplicationConnection connection,
            Collection<Object> 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(childType, entryArray, null, connection));
        }
    }
}