1/*
2 * Copyright (C) 2010 The Android Open Source Project
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 org.json;
18
19import java.util.ArrayList;
20import java.util.Arrays;
21import java.util.HashMap;
22import java.util.Iterator;
23import java.util.List;
24import java.util.Map;
25import junit.framework.TestCase;
26
27public class ParsingTest extends TestCase {
28
29    public void testParsingNoObjects() {
30        try {
31            new JSONTokener("").nextValue();
32            fail();
33        } catch (JSONException e) {
34        }
35    }
36
37    public void testParsingLiterals() throws JSONException {
38        assertParsed(Boolean.TRUE, "true");
39        assertParsed(Boolean.FALSE, "false");
40        assertParsed(JSONObject.NULL, "null");
41        assertParsed(JSONObject.NULL, "NULL");
42        assertParsed(Boolean.FALSE, "False");
43        assertParsed(Boolean.TRUE, "truE");
44    }
45
46    public void testParsingQuotedStrings() throws JSONException {
47        assertParsed("abc", "\"abc\"");
48        assertParsed("123", "\"123\"");
49        assertParsed("foo\nbar", "\"foo\\nbar\"");
50        assertParsed("foo bar", "\"foo\\u0020bar\"");
51        assertParsed("\"{}[]/\\:,=;#", "\"\\\"{}[]/\\\\:,=;#\"");
52    }
53
54    public void testParsingSingleQuotedStrings() throws JSONException {
55        assertParsed("abc", "'abc'");
56        assertParsed("123", "'123'");
57        assertParsed("foo\nbar", "'foo\\nbar'");
58        assertParsed("foo bar", "'foo\\u0020bar'");
59        assertParsed("\"{}[]/\\:,=;#", "'\\\"{}[]/\\\\:,=;#'");
60    }
61
62    public void testParsingUnquotedStrings() throws JSONException {
63        assertParsed("abc", "abc");
64        assertParsed("123abc", "123abc");
65        assertParsed("123e0x", "123e0x");
66        assertParsed("123e", "123e");
67        assertParsed("123ee21", "123ee21");
68        assertParsed("0xFFFFFFFFFFFFFFFFF", "0xFFFFFFFFFFFFFFFFF");
69    }
70
71    /**
72     * Unfortunately the original implementation attempts to figure out what
73     * Java number type best suits an input value.
74     */
75    public void testParsingNumbersThatAreBestRepresentedAsLongs() throws JSONException {
76        assertParsed(9223372036854775807L, "9223372036854775807");
77        assertParsed(9223372036854775806L, "9223372036854775806");
78        assertParsed(-9223372036854775808L, "-9223372036854775808");
79        assertParsed(-9223372036854775807L, "-9223372036854775807");
80    }
81
82    public void testParsingNumbersThatAreBestRepresentedAsIntegers() throws JSONException {
83        assertParsed(0, "0");
84        assertParsed(5, "5");
85        assertParsed(-2147483648, "-2147483648");
86        assertParsed(2147483647, "2147483647");
87    }
88
89    public void testParsingNegativeZero() throws JSONException {
90        assertParsed(0, "-0");
91    }
92
93    public void testParsingIntegersWithAdditionalPrecisionYieldDoubles() throws JSONException {
94        assertParsed(1d, "1.00");
95        assertParsed(1d, "1.0");
96        assertParsed(0d, "0.0");
97        assertParsed(-0d, "-0.0");
98    }
99
100    public void testParsingNumbersThatAreBestRepresentedAsDoubles() throws JSONException {
101        assertParsed(9.223372036854776E18, "9223372036854775808");
102        assertParsed(-9.223372036854776E18, "-9223372036854775809");
103        assertParsed(1.7976931348623157E308, "1.7976931348623157e308");
104        assertParsed(2.2250738585072014E-308, "2.2250738585072014E-308");
105        assertParsed(4.9E-324, "4.9E-324");
106        assertParsed(4.9E-324, "4.9e-324");
107    }
108
109    public void testParsingOctalNumbers() throws JSONException {
110        assertParsed(5, "05");
111        assertParsed(8, "010");
112        assertParsed(1046, "02026");
113    }
114
115    public void testParsingHexNumbers() throws JSONException {
116        assertParsed(5, "0x5");
117        assertParsed(16, "0x10");
118        assertParsed(8230, "0x2026");
119        assertParsed(180150010, "0xABCDEFA");
120        assertParsed(2077093803, "0x7BCDEFAB");
121    }
122
123    public void testParsingLargeHexValues() throws JSONException {
124        assertParsed(Integer.MAX_VALUE, "0x7FFFFFFF");
125        String message = "Hex values are parsed as Strings if their signed " +
126                "value is greater than Integer.MAX_VALUE.";
127        assertParsed(message, 0x80000000L, "0x80000000");
128    }
129
130    public void test64BitHexValues() throws JSONException {
131        assertParsed("Large hex longs shouldn't be yield ints or strings",
132                -1L, "0xFFFFFFFFFFFFFFFF");
133    }
134
135    public void testParsingWithCommentsAndWhitespace() throws JSONException {
136        assertParsed("baz", "  // foo bar \n baz");
137        assertParsed("baz", "  // foo bar \r baz");
138        assertParsed("baz", "  // foo bar \r\n baz");
139        assertParsed("baz", "  # foo bar \n baz");
140        assertParsed("baz", "  # foo bar \r baz");
141        assertParsed("baz", "  # foo bar \r\n baz");
142        assertParsed(5, "  /* foo bar \n baz */ 5");
143        assertParsed(5, "  /* foo bar \n baz */ 5 // quux");
144        assertParsed(5, "  5   ");
145        assertParsed(5, "  5  \r\n\t ");
146        assertParsed(5, "\r\n\t   5 ");
147    }
148
149    public void testParsingArrays() throws JSONException {
150        assertParsed(array(), "[]");
151        assertParsed(array(5, 6, true), "[5,6,true]");
152        assertParsed(array(5, 6, array()), "[5,6,[]]");
153        assertParsed(array(5, 6, 7), "[5;6;7]");
154        assertParsed(array(5, 6, 7), "[5  , 6 \t; \r\n 7\n]");
155        assertParsed(array(5, 6, 7, null), "[5,6,7,]");
156        assertParsed(array(null, null), "[,]");
157        assertParsed(array(5, null, null, null, 5), "[5,,,,5]");
158        assertParsed(array(null, 5), "[,5]");
159        assertParsed(array(null, null, null), "[,,]");
160        assertParsed(array(null, null, null, 5), "[,,,5]");
161    }
162
163    public void testParsingObjects() throws JSONException {
164        assertParsed(object("foo", 5), "{\"foo\": 5}");
165        assertParsed(object("foo", 5), "{foo: 5}");
166        assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\": 5, \"bar\": \"baz\"}");
167        assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\": 5; \"bar\": \"baz\"}");
168        assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\"= 5; \"bar\"= \"baz\"}");
169        assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\"=> 5; \"bar\"=> \"baz\"}");
170        assertParsed(object("foo", object(), "bar", array()), "{\"foo\"=> {}; \"bar\"=> []}");
171        assertParsed(object("foo", object("foo", array(5, 6))), "{\"foo\": {\"foo\": [5, 6]}}");
172        assertParsed(object("foo", object("foo", array(5, 6))), "{\"foo\":\n\t{\t \"foo\":[5,\r6]}}");
173    }
174
175    public void testSyntaxProblemUnterminatedObject() {
176        assertParseFail("{");
177        assertParseFail("{\"foo\"");
178        assertParseFail("{\"foo\":");
179        assertParseFail("{\"foo\":bar");
180        assertParseFail("{\"foo\":bar,");
181        assertParseFail("{\"foo\":bar,\"baz\"");
182        assertParseFail("{\"foo\":bar,\"baz\":");
183        assertParseFail("{\"foo\":bar,\"baz\":true");
184        assertParseFail("{\"foo\":bar,\"baz\":true,");
185    }
186
187    public void testSyntaxProblemEmptyString() {
188        assertParseFail("");
189    }
190
191    public void testSyntaxProblemUnterminatedArray() {
192        assertParseFail("[");
193        assertParseFail("[,");
194        assertParseFail("[,,");
195        assertParseFail("[true");
196        assertParseFail("[true,");
197        assertParseFail("[true,,");
198    }
199
200    public void testSyntaxProblemMalformedObject() {
201        assertParseFail("{:}");
202        assertParseFail("{\"key\":}");
203        assertParseFail("{:true}");
204        assertParseFail("{\"key\":true:}");
205        assertParseFail("{null:true}");
206        assertParseFail("{true:true}");
207        assertParseFail("{0xFF:true}");
208    }
209
210    private void assertParseFail(String malformedJson) {
211        try {
212            new JSONTokener(malformedJson).nextValue();
213            fail("Successfully parsed: \"" + malformedJson + "\"");
214        } catch (JSONException e) {
215        } catch (StackOverflowError e) {
216            fail("Stack overflowed on input: \"" + malformedJson + "\"");
217        }
218    }
219
220    private JSONArray array(Object... elements) {
221        return new JSONArray(Arrays.asList(elements));
222    }
223
224    private JSONObject object(Object... keyValuePairs) throws JSONException {
225        JSONObject result = new JSONObject();
226        for (int i = 0; i < keyValuePairs.length; i+=2) {
227            result.put((String) keyValuePairs[i], keyValuePairs[i+1]);
228        }
229        return result;
230    }
231
232    private void assertParsed(String message, Object expected, String json) throws JSONException {
233        Object actual = new JSONTokener(json).nextValue();
234        actual = canonicalize(actual);
235        expected = canonicalize(expected);
236        assertEquals("For input \"" + json + "\" " + message, expected, actual);
237    }
238
239    private void assertParsed(Object expected, String json) throws JSONException {
240        assertParsed("", expected, json);
241    }
242
243    /**
244     * Since they don't implement equals or hashCode properly, this recursively
245     * replaces JSONObjects with an equivalent HashMap, and JSONArrays with the
246     * equivalent ArrayList.
247     */
248    private Object canonicalize(Object input) throws JSONException {
249        if (input instanceof JSONArray) {
250            JSONArray array = (JSONArray) input;
251            List<Object> result = new ArrayList<Object>();
252            for (int i = 0; i < array.length(); i++) {
253                result.add(canonicalize(array.opt(i)));
254            }
255            return result;
256        } else if (input instanceof JSONObject) {
257            JSONObject object = (JSONObject) input;
258            Map<String, Object> result = new HashMap<String, Object>();
259            for (Iterator<?> i = object.keys(); i.hasNext(); ) {
260                String key = (String) i.next();
261                result.put(key, canonicalize(object.get(key)));
262            }
263            return result;
264        } else if (input == null || input.equals(JSONObject.NULL)) {
265            return JSONObject.NULL;
266        } else {
267            return input;
268        }
269    }
270}
271