| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.json; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import junit.framework.TestCase; |
| |
| public class ParsingTest extends TestCase { |
| |
| public void testParsingNoObjects() { |
| try { |
| new JSONTokener("").nextValue(); |
| fail(); |
| } catch (JSONException e) { |
| } |
| } |
| |
| public void testParsingLiterals() throws JSONException { |
| assertParsed(Boolean.TRUE, "true"); |
| assertParsed(Boolean.FALSE, "false"); |
| assertParsed(JSONObject.NULL, "null"); |
| assertParsed(JSONObject.NULL, "NULL"); |
| assertParsed(Boolean.FALSE, "False"); |
| assertParsed(Boolean.TRUE, "truE"); |
| } |
| |
| public void testParsingQuotedStrings() throws JSONException { |
| assertParsed("abc", "\"abc\""); |
| assertParsed("123", "\"123\""); |
| assertParsed("foo\nbar", "\"foo\\nbar\""); |
| assertParsed("foo bar", "\"foo\\u0020bar\""); |
| assertParsed("\"{}[]/\\:,=;#", "\"\\\"{}[]/\\\\:,=;#\""); |
| } |
| |
| public void testParsingSingleQuotedStrings() throws JSONException { |
| assertParsed("abc", "'abc'"); |
| assertParsed("123", "'123'"); |
| assertParsed("foo\nbar", "'foo\\nbar'"); |
| assertParsed("foo bar", "'foo\\u0020bar'"); |
| assertParsed("\"{}[]/\\:,=;#", "'\\\"{}[]/\\\\:,=;#'"); |
| } |
| |
| public void testParsingUnquotedStrings() throws JSONException { |
| assertParsed("abc", "abc"); |
| assertParsed("123abc", "123abc"); |
| assertParsed("123e0x", "123e0x"); |
| assertParsed("123e", "123e"); |
| assertParsed("123ee21", "123ee21"); |
| assertParsed("0xFFFFFFFFFFFFFFFFF", "0xFFFFFFFFFFFFFFFFF"); |
| } |
| |
| /** |
| * Unfortunately the original implementation attempts to figure out what |
| * Java number type best suits an input value. |
| */ |
| public void testParsingNumbersThatAreBestRepresentedAsLongs() throws JSONException { |
| assertParsed(9223372036854775807L, "9223372036854775807"); |
| assertParsed(9223372036854775806L, "9223372036854775806"); |
| assertParsed(-9223372036854775808L, "-9223372036854775808"); |
| assertParsed(-9223372036854775807L, "-9223372036854775807"); |
| } |
| |
| public void testParsingNumbersThatAreBestRepresentedAsIntegers() throws JSONException { |
| assertParsed(0, "0"); |
| assertParsed(5, "5"); |
| assertParsed(-2147483648, "-2147483648"); |
| assertParsed(2147483647, "2147483647"); |
| } |
| |
| public void testParsingNegativeZero() throws JSONException { |
| assertParsed(0, "-0"); |
| } |
| |
| public void testParsingIntegersWithAdditionalPrecisionYieldDoubles() throws JSONException { |
| assertParsed(1d, "1.00"); |
| assertParsed(1d, "1.0"); |
| assertParsed(0d, "0.0"); |
| assertParsed(-0d, "-0.0"); |
| } |
| |
| public void testParsingNumbersThatAreBestRepresentedAsDoubles() throws JSONException { |
| assertParsed(9.223372036854776E18, "9223372036854775808"); |
| assertParsed(-9.223372036854776E18, "-9223372036854775809"); |
| assertParsed(1.7976931348623157E308, "1.7976931348623157e308"); |
| assertParsed(2.2250738585072014E-308, "2.2250738585072014E-308"); |
| assertParsed(4.9E-324, "4.9E-324"); |
| assertParsed(4.9E-324, "4.9e-324"); |
| } |
| |
| public void testParsingOctalNumbers() throws JSONException { |
| assertParsed(5, "05"); |
| assertParsed(8, "010"); |
| assertParsed(1046, "02026"); |
| } |
| |
| public void testParsingHexNumbers() throws JSONException { |
| assertParsed(5, "0x5"); |
| assertParsed(16, "0x10"); |
| assertParsed(8230, "0x2026"); |
| assertParsed(180150010, "0xABCDEFA"); |
| assertParsed(2077093803, "0x7BCDEFAB"); |
| } |
| |
| public void testParsingLargeHexValues() throws JSONException { |
| assertParsed(Integer.MAX_VALUE, "0x7FFFFFFF"); |
| String message = "Hex values are parsed as Strings if their signed " + |
| "value is greater than Integer.MAX_VALUE."; |
| assertParsed(message, 0x80000000L, "0x80000000"); |
| } |
| |
| public void test64BitHexValues() throws JSONException { |
| assertParsed("Large hex longs shouldn't be yield ints or strings", |
| -1L, "0xFFFFFFFFFFFFFFFF"); |
| } |
| |
| public void testParsingWithCommentsAndWhitespace() throws JSONException { |
| assertParsed("baz", " // foo bar \n baz"); |
| assertParsed("baz", " // foo bar \r baz"); |
| assertParsed("baz", " // foo bar \r\n baz"); |
| assertParsed("baz", " # foo bar \n baz"); |
| assertParsed("baz", " # foo bar \r baz"); |
| assertParsed("baz", " # foo bar \r\n baz"); |
| assertParsed(5, " /* foo bar \n baz */ 5"); |
| assertParsed(5, " /* foo bar \n baz */ 5 // quux"); |
| assertParsed(5, " 5 "); |
| assertParsed(5, " 5 \r\n\t "); |
| assertParsed(5, "\r\n\t 5 "); |
| } |
| |
| public void testParsingArrays() throws JSONException { |
| assertParsed(array(), "[]"); |
| assertParsed(array(5, 6, true), "[5,6,true]"); |
| assertParsed(array(5, 6, array()), "[5,6,[]]"); |
| assertParsed(array(5, 6, 7), "[5;6;7]"); |
| assertParsed(array(5, 6, 7), "[5 , 6 \t; \r\n 7\n]"); |
| assertParsed(array(5, 6, 7, null), "[5,6,7,]"); |
| assertParsed(array(null, null), "[,]"); |
| assertParsed(array(5, null, null, null, 5), "[5,,,,5]"); |
| assertParsed(array(null, 5), "[,5]"); |
| assertParsed(array(null, null, null), "[,,]"); |
| assertParsed(array(null, null, null, 5), "[,,,5]"); |
| } |
| |
| public void testParsingObjects() throws JSONException { |
| assertParsed(object("foo", 5), "{\"foo\": 5}"); |
| assertParsed(object("foo", 5), "{foo: 5}"); |
| assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\": 5, \"bar\": \"baz\"}"); |
| assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\": 5; \"bar\": \"baz\"}"); |
| assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\"= 5; \"bar\"= \"baz\"}"); |
| assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\"=> 5; \"bar\"=> \"baz\"}"); |
| assertParsed(object("foo", object(), "bar", array()), "{\"foo\"=> {}; \"bar\"=> []}"); |
| assertParsed(object("foo", object("foo", array(5, 6))), "{\"foo\": {\"foo\": [5, 6]}}"); |
| assertParsed(object("foo", object("foo", array(5, 6))), "{\"foo\":\n\t{\t \"foo\":[5,\r6]}}"); |
| } |
| |
| public void testSyntaxProblemUnterminatedObject() { |
| assertParseFail("{"); |
| assertParseFail("{\"foo\""); |
| assertParseFail("{\"foo\":"); |
| assertParseFail("{\"foo\":bar"); |
| assertParseFail("{\"foo\":bar,"); |
| assertParseFail("{\"foo\":bar,\"baz\""); |
| assertParseFail("{\"foo\":bar,\"baz\":"); |
| assertParseFail("{\"foo\":bar,\"baz\":true"); |
| assertParseFail("{\"foo\":bar,\"baz\":true,"); |
| } |
| |
| public void testSyntaxProblemEmptyString() { |
| assertParseFail(""); |
| } |
| |
| public void testSyntaxProblemUnterminatedArray() { |
| assertParseFail("["); |
| assertParseFail("[,"); |
| assertParseFail("[,,"); |
| assertParseFail("[true"); |
| assertParseFail("[true,"); |
| assertParseFail("[true,,"); |
| } |
| |
| public void testSyntaxProblemMalformedObject() { |
| assertParseFail("{:}"); |
| assertParseFail("{\"key\":}"); |
| assertParseFail("{:true}"); |
| assertParseFail("{\"key\":true:}"); |
| assertParseFail("{null:true}"); |
| assertParseFail("{true:true}"); |
| assertParseFail("{0xFF:true}"); |
| } |
| |
| private void assertParseFail(String malformedJson) { |
| try { |
| new JSONTokener(malformedJson).nextValue(); |
| fail("Successfully parsed: \"" + malformedJson + "\""); |
| } catch (JSONException e) { |
| } catch (StackOverflowError e) { |
| fail("Stack overflowed on input: \"" + malformedJson + "\""); |
| } |
| } |
| |
| private JSONArray array(Object... elements) { |
| return new JSONArray(Arrays.asList(elements)); |
| } |
| |
| private JSONObject object(Object... keyValuePairs) throws JSONException { |
| JSONObject result = new JSONObject(); |
| for (int i = 0; i < keyValuePairs.length; i+=2) { |
| result.put((String) keyValuePairs[i], keyValuePairs[i+1]); |
| } |
| return result; |
| } |
| |
| private void assertParsed(String message, Object expected, String json) throws JSONException { |
| Object actual = new JSONTokener(json).nextValue(); |
| actual = canonicalize(actual); |
| expected = canonicalize(expected); |
| assertEquals("For input \"" + json + "\" " + message, expected, actual); |
| } |
| |
| private void assertParsed(Object expected, String json) throws JSONException { |
| assertParsed("", expected, json); |
| } |
| |
| /** |
| * Since they don't implement equals or hashCode properly, this recursively |
| * replaces JSONObjects with an equivalent HashMap, and JSONArrays with the |
| * equivalent ArrayList. |
| */ |
| private Object canonicalize(Object input) throws JSONException { |
| if (input instanceof JSONArray) { |
| JSONArray array = (JSONArray) input; |
| List<Object> result = new ArrayList<Object>(); |
| for (int i = 0; i < array.length(); i++) { |
| result.add(canonicalize(array.opt(i))); |
| } |
| return result; |
| } else if (input instanceof JSONObject) { |
| JSONObject object = (JSONObject) input; |
| Map<String, Object> result = new HashMap<String, Object>(); |
| for (Iterator<?> i = object.keys(); i.hasNext(); ) { |
| String key = (String) i.next(); |
| result.put(key, canonicalize(object.get(key))); |
| } |
| return result; |
| } else { |
| return input; |
| } |
| } |
| } |