1/*
2 * Copyright (C) 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.i18n.addressinput;
18
19import org.json.JSONArray;
20import org.json.JSONException;
21import org.json.JSONObject;
22import org.json.JSONTokener;
23
24import java.util.ArrayList;
25import java.util.Iterator;
26
27/**
28 * Compatibility methods on top of the JSON data.
29 */
30class JsoMap extends JSONObject {
31
32    /**
33     * Construct a JsoMap object given some json text. This method directly evaluates the String
34     * that you pass in; no error or safety checking is performed, so be very careful about the
35     * source of your data.
36     *
37     * @param json JSON text describing an address format
38     * @return a JsoMap object made from the supplied JSON.
39     */
40    static JsoMap buildJsoMap(String json) throws JSONException {
41        return new JsoMap(new JSONTokener(json));
42    }
43
44    /**
45     * Construct an empty JsoMap.
46     *
47     * @return the empty object.
48     */
49    static JsoMap createEmptyJsoMap() {
50        return new JsoMap();
51    }
52
53    /**
54     * constructor.
55     */
56    protected JsoMap() {
57    }
58
59    private JsoMap(JSONTokener readFrom) throws JSONException {
60        super(readFrom);
61    }
62
63    private JsoMap(JSONObject copyFrom, String[] names) throws JSONException {
64        super(copyFrom, names);
65    }
66
67    /**
68     * Remove the specified key.
69     *
70     * @param key key name.
71     */
72    void delKey(String key) {
73        super.remove(key);
74    }
75
76    /**
77     * Retrieve the string value for specified key.
78     *
79     * @param key key name.
80     * @return string value.
81     * @throws ClassCastException, IllegalArgumentException.
82     */
83    @Override
84    public String get(String key) {  // throws ClassCastException, IllegalArgumentException
85        try {
86            Object o = super.get(key);
87            if (o instanceof String) {
88                return (String) o;
89            } else if (o instanceof Integer) {
90                throw new IllegalArgumentException();
91            } else {
92                throw new ClassCastException();
93            }
94        } catch (JSONException e) {
95            return null;
96        }
97    }
98
99    /**
100     * Access JSONObject.get(String) which is shadowed by JsoMap.get(String).
101     *
102     * @param name A key string.
103     * @return The object associated with the key.
104     * @throws JSONException if the key is not found.
105     */
106    private Object getObject(String name) throws JSONException {
107        return super.get(name);
108    }
109
110    /**
111     * Retrieves the integer value for specified key.
112     *
113     * @return integer value or -1 if value is undefined.
114     */
115    @Override
116    public int getInt(String key) {
117        try {
118            Object o = super.get(key);
119            if (o instanceof Integer) {
120                return ((Integer) o).intValue();
121            } else {
122                throw new RuntimeException("Something other than an int was returned");
123            }
124        } catch (JSONException e) {
125            return -1;
126        }
127    }
128
129    /**
130     * Collect all the keys and return as a JSONArray.
131     *
132     * @return A JSONArray that contains all the keys.
133     */
134    JSONArray getKeys() {
135        return super.names();
136    }
137
138    /**
139     * Retrieve the JsoMap object for specified key.
140     *
141     * @param key key name.
142     * @return JsoMap object.
143     * @throws ClassCastException, IllegalArgumentException.
144     */
145    @SuppressWarnings("unchecked")
146    // JSONObject.keys() has no type information.
147    JsoMap getObj(String key)
148            throws ClassCastException, IllegalArgumentException {
149        try {
150            Object o = super.get(key);
151            if (o instanceof JSONObject) {
152                JSONObject value = (JSONObject) o;
153                ArrayList<String> keys = new ArrayList<String>(value.length());
154                for (Iterator<String> it = value.keys(); it.hasNext();) {
155                    keys.add(it.next());
156                }
157                String[] names = new String[keys.size()];
158                return new JsoMap(value, keys.toArray(names));
159            } else if (o instanceof Integer) {
160                throw new IllegalArgumentException();
161            } else {
162                throw new ClassCastException();
163            }
164        } catch (JSONException e) {
165            return null;
166        }
167    }
168
169    /**
170     * Check if the object has specified key.
171     *
172     * @param key The key name to be checked.
173     * @return true if key can be found.
174     */
175    boolean containsKey(String key) {
176        return super.has(key);
177    }
178
179    /**
180     * Merge those keys not found in this object from specified object.
181     *
182     * @param obj The other object to be merged.
183     */
184    void mergeData(JsoMap obj) {
185        if (obj == null) {
186            return;
187        }
188
189        JSONArray names = obj.names();
190        if (names == null) {
191            return;
192        }
193
194        for (int i = 0; i < names.length(); i++) {
195            try {
196                String name = names.getString(i);
197                try {
198                    if (!super.has(name)) {
199                        super.put(name, obj.getObject(name));
200                    }
201                } catch (JSONException e) {
202                    throw new RuntimeException(e);
203                }
204            } catch (JSONException e) {
205                // Ignored.
206            }
207        }
208    }
209
210    /**
211     * Save a string to string mapping into this map.
212     *
213     * @param key   the string key.
214     * @param value the String value.
215     */
216    void put(String key, String value) {
217        try {
218            super.put(key, value);
219        } catch (JSONException e) {
220            throw new RuntimeException(e);
221        }
222    }
223
224    /**
225     * Save a string to integer mapping into this map.
226     *
227     * @param key   the string key.
228     * @param value the integer value.
229     */
230    void putInt(String key, int value) {
231        try {
232            super.put(key, value);
233        } catch (JSONException e) {
234            throw new RuntimeException(e);
235        }
236    }
237
238    /**
239     * Save a string to JSONObject mapping into this map.
240     *
241     * @param key   the string key.
242     * @param value a JSONObject as value.
243     */
244    void putObj(String key, JSONObject value) {
245        try {
246            super.put(key, value);
247        } catch (JSONException e) {
248            throw new RuntimeException(e);
249        }
250    }
251
252    String string() throws ClassCastException, IllegalArgumentException {
253        StringBuilder sb = new StringBuilder("JsoMap[\n");
254        JSONArray keys = getKeys();
255        for (int i = 0; i < keys.length(); i++) {
256            String key;
257            try {
258                key = keys.getString(i);
259            } catch (JSONException e) {
260                throw new RuntimeException(e);
261            }
262            sb.append('(').append(key).append(':').append(get(key)).append(')').append('\n');
263        }
264        sb.append(']');
265        return sb.toString();
266    }
267
268    String map() throws ClassCastException, IllegalArgumentException {
269        StringBuilder sb = new StringBuilder("JsoMap[\n");
270        JSONArray keys = getKeys();
271        for (int i = 0; i < keys.length(); i++) {
272            String key;
273            try {
274                key = keys.getString(i);
275            } catch (JSONException e) {
276                throw new RuntimeException(e);
277            }
278            sb.append('(').append(key).append(':').append(getObj(key).string()).append(')')
279                    .append('\n');
280        }
281        sb.append(']');
282        return sb.toString();
283    }
284}
285