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 
17 package org.json;
18 
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import junit.framework.TestCase;
26 
27 public class ParsingTest extends TestCase {
28 
testParsingNoObjects()29     public void testParsingNoObjects() {
30         try {
31             new JSONTokener("").nextValue();
32             fail();
33         } catch (JSONException e) {
34         }
35     }
36 
testParsingLiterals()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 
testParsingQuotedStrings()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 
testParsingSingleQuotedStrings()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 
testParsingUnquotedStrings()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      */
testParsingNumbersThatAreBestRepresentedAsLongs()75     public void testParsingNumbersThatAreBestRepresentedAsLongs() throws JSONException {
76         assertParsed(9223372036854775807L, "9223372036854775807");
77         assertParsed(9223372036854775806L, "9223372036854775806");
78         assertParsed(-9223372036854775808L, "-9223372036854775808");
79         assertParsed(-9223372036854775807L, "-9223372036854775807");
80     }
81 
testParsingNumbersThatAreBestRepresentedAsIntegers()82     public void testParsingNumbersThatAreBestRepresentedAsIntegers() throws JSONException {
83         assertParsed(0, "0");
84         assertParsed(5, "5");
85         assertParsed(-2147483648, "-2147483648");
86         assertParsed(2147483647, "2147483647");
87     }
88 
testParsingNegativeZero()89     public void testParsingNegativeZero() throws JSONException {
90         assertParsed(0, "-0");
91     }
92 
testParsingIntegersWithAdditionalPrecisionYieldDoubles()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 
testParsingNumbersThatAreBestRepresentedAsDoubles()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 
testParsingOctalNumbers()109     public void testParsingOctalNumbers() throws JSONException {
110         assertParsed(5, "05");
111         assertParsed(8, "010");
112         assertParsed(1046, "02026");
113     }
114 
testParsingHexNumbers()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 
testParsingLargeHexValues()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 
test64BitHexValues()130     public void test64BitHexValues() throws JSONException {
131         assertParsed("Large hex longs shouldn't be yield ints or strings",
132                 -1L, "0xFFFFFFFFFFFFFFFF");
133     }
134 
testParsingWithCommentsAndWhitespace()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 
testParsingArrays()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 
testParsingObjects()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 
testSyntaxProblemUnterminatedObject()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 
testSyntaxProblemEmptyString()187     public void testSyntaxProblemEmptyString() {
188         assertParseFail("");
189     }
190 
testSyntaxProblemUnterminatedArray()191     public void testSyntaxProblemUnterminatedArray() {
192         assertParseFail("[");
193         assertParseFail("[,");
194         assertParseFail("[,,");
195         assertParseFail("[true");
196         assertParseFail("[true,");
197         assertParseFail("[true,,");
198     }
199 
testSyntaxProblemMalformedObject()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 
assertParseFail(String malformedJson)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 
array(Object... elements)220     private JSONArray array(Object... elements) {
221         return new JSONArray(Arrays.asList(elements));
222     }
223 
object(Object... keyValuePairs)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 
assertParsed(String message, Object expected, String json)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 
assertParsed(Object expected, String json)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      */
canonicalize(Object input)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