diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..00a51aff5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..cc8a3746b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build + +*~ diff --git a/CDL.java b/CDL.java deleted file mode 100755 index a6b1787c3..000000000 --- a/CDL.java +++ /dev/null @@ -1,279 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/** - * This provides static methods to convert comma delimited text into a - * JSONArray, and to covert a JSONArray into comma delimited text. Comma - * delimited text is a very popular format for data interchange. It is - * understood by most database, spreadsheet, and organizer programs. - *

- * Each row of text represents a row in a table or a data record. Each row - * ends with a NEWLINE character. Each row contains one or more values. - * Values are separated by commas. A value can contain any character except - * for comma, unless is is wrapped in single quotes or double quotes. - *

- * The first row usually contains the names of the columns. - *

- * A comma delimited list can be converted into a JSONArray of JSONObjects. - * The names for the elements in the JSONObjects can be taken from the names - * in the first row. - * @author JSON.org - * @version 2010-12-24 - */ -public class CDL { - - /** - * Get the next value. The value can be wrapped in quotes. The value can - * be empty. - * @param x A JSONTokener of the source text. - * @return The value string, or null if empty. - * @throws JSONException if the quoted string is badly formed. - */ - private static String getValue(JSONTokener x) throws JSONException { - char c; - char q; - StringBuffer sb; - do { - c = x.next(); - } while (c == ' ' || c == '\t'); - switch (c) { - case 0: - return null; - case '"': - case '\'': - q = c; - sb = new StringBuffer(); - for (;;) { - c = x.next(); - if (c == q) { - break; - } - if (c == 0 || c == '\n' || c == '\r') { - throw x.syntaxError("Missing close quote '" + q + "'."); - } - sb.append(c); - } - return sb.toString(); - case ',': - x.back(); - return ""; - default: - x.back(); - return x.nextTo(','); - } - } - - /** - * Produce a JSONArray of strings from a row of comma delimited values. - * @param x A JSONTokener of the source text. - * @return A JSONArray of strings. - * @throws JSONException - */ - public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException { - JSONArray ja = new JSONArray(); - for (;;) { - String value = getValue(x); - char c = x.next(); - if (value == null || - (ja.length() == 0 && value.length() == 0 && c != ',')) { - return null; - } - ja.put(value); - for (;;) { - if (c == ',') { - break; - } - if (c != ' ') { - if (c == '\n' || c == '\r' || c == 0) { - return ja; - } - throw x.syntaxError("Bad character '" + c + "' (" + - (int)c + ")."); - } - c = x.next(); - } - } - } - - /** - * Produce a JSONObject from a row of comma delimited text, using a - * parallel JSONArray of strings to provides the names of the elements. - * @param names A JSONArray of names. This is commonly obtained from the - * first row of a comma delimited text file using the rowToJSONArray - * method. - * @param x A JSONTokener of the source text. - * @return A JSONObject combining the names and values. - * @throws JSONException - */ - public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) - throws JSONException { - JSONArray ja = rowToJSONArray(x); - return ja != null ? ja.toJSONObject(names) : null; - } - - /** - * Produce a comma delimited text row from a JSONArray. Values containing - * the comma character will be quoted. Troublesome characters may be - * removed. - * @param ja A JSONArray of strings. - * @return A string ending in NEWLINE. - */ - public static String rowToString(JSONArray ja) { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < ja.length(); i += 1) { - if (i > 0) { - sb.append(','); - } - Object object = ja.opt(i); - if (object != null) { - String string = object.toString(); - if (string.length() > 0 && (string.indexOf(',') >= 0 || - string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 || - string.indexOf(0) >= 0 || string.charAt(0) == '"')) { - sb.append('"'); - int length = string.length(); - for (int j = 0; j < length; j += 1) { - char c = string.charAt(j); - if (c >= ' ' && c != '"') { - sb.append(c); - } - } - sb.append('"'); - } else { - sb.append(string); - } - } - } - sb.append('\n'); - return sb.toString(); - } - - /** - * Produce a JSONArray of JSONObjects from a comma delimited text string, - * using the first row as a source of names. - * @param string The comma delimited text. - * @return A JSONArray of JSONObjects. - * @throws JSONException - */ - public static JSONArray toJSONArray(String string) throws JSONException { - return toJSONArray(new JSONTokener(string)); - } - - /** - * Produce a JSONArray of JSONObjects from a comma delimited text string, - * using the first row as a source of names. - * @param x The JSONTokener containing the comma delimited text. - * @return A JSONArray of JSONObjects. - * @throws JSONException - */ - public static JSONArray toJSONArray(JSONTokener x) throws JSONException { - return toJSONArray(rowToJSONArray(x), x); - } - - /** - * Produce a JSONArray of JSONObjects from a comma delimited text string - * using a supplied JSONArray as the source of element names. - * @param names A JSONArray of strings. - * @param string The comma delimited text. - * @return A JSONArray of JSONObjects. - * @throws JSONException - */ - public static JSONArray toJSONArray(JSONArray names, String string) - throws JSONException { - return toJSONArray(names, new JSONTokener(string)); - } - - /** - * Produce a JSONArray of JSONObjects from a comma delimited text string - * using a supplied JSONArray as the source of element names. - * @param names A JSONArray of strings. - * @param x A JSONTokener of the source text. - * @return A JSONArray of JSONObjects. - * @throws JSONException - */ - public static JSONArray toJSONArray(JSONArray names, JSONTokener x) - throws JSONException { - if (names == null || names.length() == 0) { - return null; - } - JSONArray ja = new JSONArray(); - for (;;) { - JSONObject jo = rowToJSONObject(names, x); - if (jo == null) { - break; - } - ja.put(jo); - } - if (ja.length() == 0) { - return null; - } - return ja; - } - - - /** - * Produce a comma delimited text from a JSONArray of JSONObjects. The - * first row will be a list of names obtained by inspecting the first - * JSONObject. - * @param ja A JSONArray of JSONObjects. - * @return A comma delimited text. - * @throws JSONException - */ - public static String toString(JSONArray ja) throws JSONException { - JSONObject jo = ja.optJSONObject(0); - if (jo != null) { - JSONArray names = jo.names(); - if (names != null) { - return rowToString(names) + toString(names, ja); - } - } - return null; - } - - /** - * Produce a comma delimited text from a JSONArray of JSONObjects using - * a provided list of names. The list of names is not included in the - * output. - * @param names A JSONArray of strings. - * @param ja A JSONArray of JSONObjects. - * @return A comma delimited text. - * @throws JSONException - */ - public static String toString(JSONArray names, JSONArray ja) - throws JSONException { - if (names == null || names.length() == 0) { - return null; - } - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < ja.length(); i += 1) { - JSONObject jo = ja.optJSONObject(i); - if (jo != null) { - sb.append(rowToString(jo.toJSONArray(names))); - } - } - return sb.toString(); - } -} diff --git a/Cookie.java b/Cookie.java deleted file mode 100755 index 9cf5ce2c5..000000000 --- a/Cookie.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/** - * Convert a web browser cookie specification to a JSONObject and back. - * JSON and Cookies are both notations for name/value pairs. - * @author JSON.org - * @version 2010-12-24 - */ -public class Cookie { - - /** - * Produce a copy of a string in which the characters '+', '%', '=', ';' - * and control characters are replaced with "%hh". This is a gentle form - * of URL encoding, attempting to cause as little distortion to the - * string as possible. The characters '=' and ';' are meta characters in - * cookies. By convention, they are escaped using the URL-encoding. This is - * only a convention, not a standard. Often, cookies are expected to have - * encoded values. We encode '=' and ';' because we must. We encode '%' and - * '+' because they are meta characters in URL encoding. - * @param string The source string. - * @return The escaped result. - */ - public static String escape(String string) { - char c; - String s = string.trim(); - StringBuffer sb = new StringBuffer(); - int length = s.length(); - for (int i = 0; i < length; i += 1) { - c = s.charAt(i); - if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') { - sb.append('%'); - sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16)); - sb.append(Character.forDigit((char)(c & 0x0f), 16)); - } else { - sb.append(c); - } - } - return sb.toString(); - } - - - /** - * Convert a cookie specification string into a JSONObject. The string - * will contain a name value pair separated by '='. The name and the value - * will be unescaped, possibly converting '+' and '%' sequences. The - * cookie properties may follow, separated by ';', also represented as - * name=value (except the secure property, which does not have a value). - * The name will be stored under the key "name", and the value will be - * stored under the key "value". This method does not do checking or - * validation of the parameters. It only converts the cookie string into - * a JSONObject. - * @param string The cookie specification string. - * @return A JSONObject containing "name", "value", and possibly other - * members. - * @throws JSONException - */ - public static JSONObject toJSONObject(String string) throws JSONException { - String name; - JSONObject jo = new JSONObject(); - Object value; - JSONTokener x = new JSONTokener(string); - jo.put("name", x.nextTo('=')); - x.next('='); - jo.put("value", x.nextTo(';')); - x.next(); - while (x.more()) { - name = unescape(x.nextTo("=;")); - if (x.next() != '=') { - if (name.equals("secure")) { - value = Boolean.TRUE; - } else { - throw x.syntaxError("Missing '=' in cookie parameter."); - } - } else { - value = unescape(x.nextTo(';')); - x.next(); - } - jo.put(name, value); - } - return jo; - } - - - /** - * Convert a JSONObject into a cookie specification string. The JSONObject - * must contain "name" and "value" members. - * If the JSONObject contains "expires", "domain", "path", or "secure" - * members, they will be appended to the cookie specification string. - * All other members are ignored. - * @param jo A JSONObject - * @return A cookie specification string - * @throws JSONException - */ - public static String toString(JSONObject jo) throws JSONException { - StringBuffer sb = new StringBuffer(); - - sb.append(escape(jo.getString("name"))); - sb.append("="); - sb.append(escape(jo.getString("value"))); - if (jo.has("expires")) { - sb.append(";expires="); - sb.append(jo.getString("expires")); - } - if (jo.has("domain")) { - sb.append(";domain="); - sb.append(escape(jo.getString("domain"))); - } - if (jo.has("path")) { - sb.append(";path="); - sb.append(escape(jo.getString("path"))); - } - if (jo.optBoolean("secure")) { - sb.append(";secure"); - } - return sb.toString(); - } - - /** - * Convert %hh sequences to single characters, and - * convert plus to space. - * @param string A string that may contain - * + (plus) and - * %hh sequences. - * @return The unescaped string. - */ - public static String unescape(String string) { - int length = string.length(); - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < length; ++i) { - char c = string.charAt(i); - if (c == '+') { - c = ' '; - } else if (c == '%' && i + 2 < length) { - int d = JSONTokener.dehexchar(string.charAt(i + 1)); - int e = JSONTokener.dehexchar(string.charAt(i + 2)); - if (d >= 0 && e >= 0) { - c = (char)(d * 16 + e); - i += 2; - } - } - sb.append(c); - } - return sb.toString(); - } -} diff --git a/CookieList.java b/CookieList.java deleted file mode 100755 index 7f4fe0751..000000000 --- a/CookieList.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import java.util.Iterator; - -/** - * Convert a web browser cookie list string to a JSONObject and back. - * @author JSON.org - * @version 2010-12-24 - */ -public class CookieList { - - /** - * Convert a cookie list into a JSONObject. A cookie list is a sequence - * of name/value pairs. The names are separated from the values by '='. - * The pairs are separated by ';'. The names and the values - * will be unescaped, possibly converting '+' and '%' sequences. - * - * To add a cookie to a cooklist, - * cookielistJSONObject.put(cookieJSONObject.getString("name"), - * cookieJSONObject.getString("value")); - * @param string A cookie list string - * @return A JSONObject - * @throws JSONException - */ - public static JSONObject toJSONObject(String string) throws JSONException { - JSONObject jo = new JSONObject(); - JSONTokener x = new JSONTokener(string); - while (x.more()) { - String name = Cookie.unescape(x.nextTo('=')); - x.next('='); - jo.put(name, Cookie.unescape(x.nextTo(';'))); - x.next(); - } - return jo; - } - - - /** - * Convert a JSONObject into a cookie list. A cookie list is a sequence - * of name/value pairs. The names are separated from the values by '='. - * The pairs are separated by ';'. The characters '%', '+', '=', and ';' - * in the names and values are replaced by "%hh". - * @param jo A JSONObject - * @return A cookie list string - * @throws JSONException - */ - public static String toString(JSONObject jo) throws JSONException { - boolean b = false; - Iterator keys = jo.keys(); - String string; - StringBuffer sb = new StringBuffer(); - while (keys.hasNext()) { - string = keys.next().toString(); - if (!jo.isNull(string)) { - if (b) { - sb.append(';'); - } - sb.append(Cookie.escape(string)); - sb.append("="); - sb.append(Cookie.escape(jo.getString(string))); - b = true; - } - } - return sb.toString(); - } -} diff --git a/HTTP.java b/HTTP.java deleted file mode 100755 index 0ce7a2161..000000000 --- a/HTTP.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -import java.util.Iterator; - -/** - * Convert an HTTP header to a JSONObject and back. - * @author JSON.org - * @version 2010-12-24 - */ -public class HTTP { - - /** Carriage return/line feed. */ - public static final String CRLF = "\r\n"; - - /** - * Convert an HTTP header string into a JSONObject. It can be a request - * header or a response header. A request header will contain - *

{
-     *    Method: "POST" (for example),
-     *    "Request-URI": "/" (for example),
-     *    "HTTP-Version": "HTTP/1.1" (for example)
-     * }
- * A response header will contain - *
{
-     *    "HTTP-Version": "HTTP/1.1" (for example),
-     *    "Status-Code": "200" (for example),
-     *    "Reason-Phrase": "OK" (for example)
-     * }
- * In addition, the other parameters in the header will be captured, using - * the HTTP field names as JSON names, so that
-     *    Date: Sun, 26 May 2002 18:06:04 GMT
-     *    Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
-     *    Cache-Control: no-cache
- * become - *
{...
-     *    Date: "Sun, 26 May 2002 18:06:04 GMT",
-     *    Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
-     *    "Cache-Control": "no-cache",
-     * ...}
- * It does no further checking or conversion. It does not parse dates. - * It does not do '%' transforms on URLs. - * @param string An HTTP header string. - * @return A JSONObject containing the elements and attributes - * of the XML string. - * @throws JSONException - */ - public static JSONObject toJSONObject(String string) throws JSONException { - JSONObject jo = new JSONObject(); - HTTPTokener x = new HTTPTokener(string); - String token; - - token = x.nextToken(); - if (token.toUpperCase().startsWith("HTTP")) { - -// Response - - jo.put("HTTP-Version", token); - jo.put("Status-Code", x.nextToken()); - jo.put("Reason-Phrase", x.nextTo('\0')); - x.next(); - - } else { - -// Request - - jo.put("Method", token); - jo.put("Request-URI", x.nextToken()); - jo.put("HTTP-Version", x.nextToken()); - } - -// Fields - - while (x.more()) { - String name = x.nextTo(':'); - x.next(':'); - jo.put(name, x.nextTo('\0')); - x.next(); - } - return jo; - } - - - /** - * Convert a JSONObject into an HTTP header. A request header must contain - *
{
-     *    Method: "POST" (for example),
-     *    "Request-URI": "/" (for example),
-     *    "HTTP-Version": "HTTP/1.1" (for example)
-     * }
- * A response header must contain - *
{
-     *    "HTTP-Version": "HTTP/1.1" (for example),
-     *    "Status-Code": "200" (for example),
-     *    "Reason-Phrase": "OK" (for example)
-     * }
- * Any other members of the JSONObject will be output as HTTP fields. - * The result will end with two CRLF pairs. - * @param jo A JSONObject - * @return An HTTP header string. - * @throws JSONException if the object does not contain enough - * information. - */ - public static String toString(JSONObject jo) throws JSONException { - Iterator keys = jo.keys(); - String string; - StringBuffer sb = new StringBuffer(); - if (jo.has("Status-Code") && jo.has("Reason-Phrase")) { - sb.append(jo.getString("HTTP-Version")); - sb.append(' '); - sb.append(jo.getString("Status-Code")); - sb.append(' '); - sb.append(jo.getString("Reason-Phrase")); - } else if (jo.has("Method") && jo.has("Request-URI")) { - sb.append(jo.getString("Method")); - sb.append(' '); - sb.append('"'); - sb.append(jo.getString("Request-URI")); - sb.append('"'); - sb.append(' '); - sb.append(jo.getString("HTTP-Version")); - } else { - throw new JSONException("Not enough material for an HTTP header."); - } - sb.append(CRLF); - while (keys.hasNext()) { - string = keys.next().toString(); - if (!string.equals("HTTP-Version") && !string.equals("Status-Code") && - !string.equals("Reason-Phrase") && !string.equals("Method") && - !string.equals("Request-URI") && !jo.isNull(string)) { - sb.append(string); - sb.append(": "); - sb.append(jo.getString(string)); - sb.append(CRLF); - } - } - sb.append(CRLF); - return sb.toString(); - } -} diff --git a/HTTPTokener.java b/HTTPTokener.java deleted file mode 100755 index f62b3d558..000000000 --- a/HTTPTokener.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.json; - -/* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/** - * The HTTPTokener extends the JSONTokener to provide additional methods - * for the parsing of HTTP headers. - * @author JSON.org - * @version 2010-12-24 - */ -public class HTTPTokener extends JSONTokener { - - /** - * Construct an HTTPTokener from a string. - * @param string A source string. - */ - public HTTPTokener(String string) { - super(string); - } - - - /** - * Get the next token or string. This is used in parsing HTTP headers. - * @throws JSONException - * @return A String. - */ - public String nextToken() throws JSONException { - char c; - char q; - StringBuffer sb = new StringBuffer(); - do { - c = next(); - } while (Character.isWhitespace(c)); - if (c == '"' || c == '\'') { - q = c; - for (;;) { - c = next(); - if (c < ' ') { - throw syntaxError("Unterminated string."); - } - if (c == q) { - return sb.toString(); - } - sb.append(c); - } - } - for (;;) { - if (c == 0 || Character.isWhitespace(c)) { - return sb.toString(); - } - sb.append(c); - c = next(); - } - } -} diff --git a/JSON-java/build.gradle b/JSON-java/build.gradle new file mode 100644 index 000000000..af6b3e23c --- /dev/null +++ b/JSON-java/build.gradle @@ -0,0 +1,105 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java library project to get you started. + * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle + * User Manual available at https://docs.gradle.org/7.3.3/userguide/building_java_projects.html + */ + +plugins { + id 'java-library' + id 'maven-publish' + id 'signing' +} + +group = 'org.fiennes' +version = '2.3.0' + +repositories { + mavenCentral() + maven { + def releasesRepoUrl = layout.buildDirectory.dir('repos/releases') + def snapshotsRepoUrl = layout.buildDirectory.dir('repos/snapshots') + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + } +} + +dependencies { + testImplementation 'junit:junit:4.13.2' + + // This dependency is exported to consumers, that is to say found on their compile classpath. + api 'com.google.guava:guava:31.0.1-jre' + + // This dependency is used internally, and not exposed to consumers on their own compile classpath. + implementation 'com.google.guava:guava:31.0.1-jre' +} + + +java { + withJavadocJar() + withSourcesJar() +} + +ext.isReleaseVersion = !version.endsWith("SNAPSHOT") + +javadoc { + if(JavaVersion.current().isJava9Compatible()) { + options.addBooleanOption('html5',true) + } +} + + +publishing { + repositories { + maven { + def releaseRepo = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" + def snapshotRepo = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + url = isReleaseVersion ? releaseRepo : snapshotRepo + credentials { + username ossrhUsername + password ossrhPassword + } + } + } + + publications { + mavenJava(MavenPublication) { + pom { + groupId = 'org.fiennes' + name = 'JSON-java' + description = 'JSON library for java' + url = 'https://github.com/alex-fiennes/JSON-java' + from components.java + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + name = 'Alex Fiennes' + email = 'alex@fiennes.org' + organization = 'fiennes.org ltd' + organizationUrl = 'https://www.fiennes.org' + } + } + scm { + connection = 'scm:git:git@github.com:alex-fiennes/JSON-java.git' + developerConnection = 'scm:git:git@github.com:alex-fiennes/JSON-java.git' + url = 'https://github.com/alex-fiennes/JSON-java' + } + } + } + } +} + + +signing { +// useGpgCmd() + sign publishing.publications.mavenJava +} +tasks.withType(Sign) { + onlyIf { isReleaseVersion } +} + diff --git a/JSON-java/src/main/java/org/json/AbstractUnmodifiableJSONObject.java b/JSON-java/src/main/java/org/json/AbstractUnmodifiableJSONObject.java new file mode 100644 index 000000000..077744b10 --- /dev/null +++ b/JSON-java/src/main/java/org/json/AbstractUnmodifiableJSONObject.java @@ -0,0 +1,74 @@ +package org.json; + +/** + * Abstract implementation of JSONObject that only implements final versions of all the methods that + * change the state of the JSONOBject all of which are implemented as throwing an exception. + * + * @author alex + */ +@Deprecated +public abstract class AbstractUnmodifiableJSONObject + implements JSONObject +{ + @Override + public final JSONObject put(String key, + boolean value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public final JSONObject put(String key, + double value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public final JSONObject put(String key, + int value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public final JSONObject put(String key, + long value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public final JSONObject put(String key, + Object value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public final JSONObject putOnce(String key, + Object value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public final JSONObject putOpt(String key, + Object value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public final Object remove(String key) + { + throw new UnsupportedOperationException(); + } +} diff --git a/JSON-java/src/main/java/org/json/ImmutableJSON.java b/JSON-java/src/main/java/org/json/ImmutableJSON.java new file mode 100644 index 000000000..6cbf9373b --- /dev/null +++ b/JSON-java/src/main/java/org/json/ImmutableJSON.java @@ -0,0 +1,75 @@ +package org.json; + +public class ImmutableJSON + extends JSONBuilder +{ + private static final ImmutableJSON INSTANCE = new ImmutableJSON(); + + public static final ImmutableJSON get() + { + return INSTANCE; + } + + /** + * @deprecated in preference of {@link #get()} + */ + @Deprecated + public static final ImmutableJSON getInstance() + { + return INSTANCE; + } + + private ImmutableJSON() + { + super(ImmutableJSONArray.class, + ImmutableJSONObject.class); + } + + @Override + public JSONArrayBuilder createJSONArrayBuilder() + { + return new ImmutableJSONArray.Builder(); + } + + @Override + public JSONObjectBuilder createJSONObjectBuilder() + { + return new ImmutableJSONObject.Builder(); + } + + /** + * @return Singleton immutable empty JSONObject + */ + @Override + public ImmutableJSONObject emptyJSONObject() + { + return ImmutableJSONObject.EMPTY; + } + + /** + * @return Singleton immutable empty JSONArray + */ + @Override + public ImmutableJSONArray emptyJSONArray() + { + return ImmutableJSONArray.EMPTY; + } + + @Override + public ImmutableJSONArray cast(JSONArray source) + { + if (source instanceof ImmutableJSONArray) { + return (ImmutableJSONArray) source; + } + return ImmutableJSON.get().toJSONArray(source); + } + + @Override + public ImmutableJSONObject cast(JSONObject source) + { + if (source instanceof ImmutableJSONObject) { + return (ImmutableJSONObject) source; + } + return ImmutableJSON.get().toJSONObject(source); + } +} diff --git a/JSON-java/src/main/java/org/json/ImmutableJSONArray.java b/JSON-java/src/main/java/org/json/ImmutableJSONArray.java new file mode 100644 index 000000000..1d1d57814 --- /dev/null +++ b/JSON-java/src/main/java/org/json/ImmutableJSONArray.java @@ -0,0 +1,151 @@ +package org.json; + +import com.google.common.collect.ImmutableList; + +public class ImmutableJSONArray + extends ListBasedJSONArray +{ + protected final static ImmutableJSONArray EMPTY = new ImmutableJSONArray(ImmutableList.of()); + + private ImmutableJSONArray(ImmutableList list) + { + super(list); + } + + // @Override + // public Appendable write(Appendable writer) + // throws IOException + // { + // JSONArrays.write(this, writer); + // return writer; + // } + + @SuppressWarnings("unchecked") + @Override + public A clone(JSONBuilder builder) + throws JSONException + { + return (A) (ImmutableJSON.get().equals(builder) ? this : super.clone(builder)); + } + + // @Override + // public WritableJSONArray writableClone() + // { + // return new WritableJSONArray(getBackingList()); + // } + + @Override + public ImmutableJSONArray getJSONArray(int index) + throws JSONException + { + return (ImmutableJSONArray) super.getJSONArray(index); + } + + @Override + public ImmutableJSONObject getJSONObject(int index) + throws JSONException + { + return (ImmutableJSONObject) super.getJSONObject(index); + } + + // @Override + // public String join(String separator) + // throws JSONException + // { + // return join(separator, + // ImmutableJSONObject.getJSONObjectBuilderSupplier(), + // ImmutableJSONArray.getJSONArrayBuilderSupplier()); + // } + + @Override + public ImmutableJSONArray optJSONArray(int index) + { + return (ImmutableJSONArray) super.optJSONArray(index); + } + + @Override + public ImmutableJSONObject optJSONObject(int index) + { + return (ImmutableJSONObject) super.optJSONObject(index); + } + + // @Override + // public ImmutableJSONArray put(Collection value) + // { + // put(create(value)); + // return this; + // } + + // public static ImmutableJSONArray create(Iterable values) + // { + // Builder builder = getJSONArrayBuilderSupplier().get(); + // Iterator i = values.iterator(); + // while (i.hasNext()) { + // builder.put(i.next()); + // } + // return builder.build(); + // } + + // @Override + // public ImmutableJSONArray put(int index, + // Collection value) + // throws JSONException + // { + // put(index, create(value)); + // return this; + // } + + // @Override + // public JSONArray put(int index, + // Map value) + // throws JSONException + // { + // put(index, ImmutableJSONObject.create(value)); + // return this; + // } + + // @Override + // public JSONObject toJSONObject(JSONArray names) + // throws JSONException + // { + // return toJSONObject(names, ImmutableJSONObject.getJSONObjectBuilderSupplier()); + // } + + // private static final Supplier BUILDERSUPPLIER = new Supplier() { + // @Override + // public Builder get() + // { + // return new Builder(); + // } + // }; + + // public static Supplier getJSONArrayBuilderSupplier() + // { + // return BUILDERSUPPLIER; + // } + + public static class Builder + implements JSONArrayBuilder + { + private final ImmutableList.Builder __builder; + + public Builder() + { + __builder = ImmutableList.builder(); + } + + @Override + public ImmutableJSONArray build() + { + return new ImmutableJSONArray(__builder.build()); + } + + @Override + public Builder put(Object value) + throws JSONException + { + __builder.add(ImmutableJSON.get().cast(value)); + return this; + } + } +} diff --git a/JSON-java/src/main/java/org/json/ImmutableJSONObject.java b/JSON-java/src/main/java/org/json/ImmutableJSONObject.java new file mode 100644 index 000000000..5c302918e --- /dev/null +++ b/JSON-java/src/main/java/org/json/ImmutableJSONObject.java @@ -0,0 +1,129 @@ +package org.json; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; + +public class ImmutableJSONObject + extends MapBasedJSONObject +{ + protected static final ImmutableJSONObject EMPTY = + new ImmutableJSONObject(ImmutableMap. of()); + + private final ImmutableMap __map; + + private ImmutableJSONObject(ImmutableMap map) + { + __map = map; + } + + @Override + public boolean equalsMap(Map map) + { + return __map.equals(map); + } + + @SuppressWarnings("unchecked") + @Override + public O clone(JSONBuilder builder) + throws JSONException + { + return (O) (ImmutableJSON.get().equals(builder) ? this : super.clone(builder)); + } + + @Override + public JSONObject put(String key, + boolean value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public JSONObject put(String key, + double value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public JSONObject put(String key, + int value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public JSONObject put(String key, + long value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public JSONObject put(String key, + Object value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public JSONObject putOnce(String key, + Object value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public JSONObject putOpt(String key, + Object value) + throws JSONException + { + throw new UnsupportedOperationException(); + } + + @Override + public Object remove(String key) + { + throw new UnsupportedOperationException(); + } + + @Override + protected ImmutableMap getMap() + { + return __map; + } + + public static class Builder + implements JSONObjectBuilder + { + private final ImmutableMap.Builder __builder; + + public Builder() + { + __builder = ImmutableMap.builder(); + } + + @Override + public ImmutableJSONObject build() + { + return new ImmutableJSONObject(__builder.build()); + } + + @Override + public Builder putOnce(String key, + Object value) + throws JSONException + { + __builder.put(key, ImmutableJSON.get().cast(value)); + return this; + } + + } + +} diff --git a/JSON-java/src/main/java/org/json/JSONArray.java b/JSON-java/src/main/java/org/json/JSONArray.java new file mode 100755 index 000000000..ec0ea80ae --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONArray.java @@ -0,0 +1,432 @@ +package org.json; + +import java.util.Iterator; +import java.util.List; + +public interface JSONArray + extends Iterable, JSONComponent +{ + @Override + public Iterator iterator(); + + public boolean equalsList(List list); + + public A clone(JSONBuilder builder) + throws JSONException; + + /** + * Get the object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + */ + public Object get(int index); + + /** + * Get the boolean value associated with an index. The string values "true" and "false" are + * converted to boolean. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + * @throws JSONException + * If there is no value for the index or if the value is not convertible to boolean. + */ + public boolean getBoolean(int index) + throws JSONException; + + /** + * Get the double value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted to a number. + */ + public double getDouble(int index) + throws JSONException; + + /** + * Get the int value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value is not a number. + */ + public int getInt(int index) + throws JSONException; + + /** + * Get the JSONArray associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @throws JSONException + * If there is no value for the index. or if the value is not a JSONArray + */ + public JSONArray getJSONArray(int index) + throws JSONException; + + /** + * Get the JSONObject associated with an index. + * + * @param index + * subscript + * @return A JSONObject value. + * @throws JSONException + * If there is no value for the index or if the value is not a JSONObject + */ + public JSONObject getJSONObject(int index) + throws JSONException; + + /** + * Get the long value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted to a number. + */ + public long getLong(int index) + throws JSONException; + + /** + * Get the string associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A string value. + * @throws JSONException + * If there is no value for the index. + */ + public String getString(int index) + throws JSONException; + + /** + * Determine if the value is null. + * + * @param index + * The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + */ + public boolean isNull(int index); + + /** + * Get the number of elements in the JSONArray, included nulls. + * + * @return The length (or size). + */ + public int length(); + + /** + * Get the optional object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object value, or null if there is no object at that index. + */ + public Object opt(int index); + + /** + * Get the optional boolean value associated with an index. It returns false if there is no value + * at that index, or if the value is not Boolean.TRUE or the String "true". + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + */ + public boolean optBoolean(int index); + + /** + * Get the optional boolean value associated with an index. It returns the defaultValue if there + * is no value at that index or if it is not a Boolean or the String "true" or "false" (case + * insensitive). + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * A boolean default. + * @return The truth. + */ + public boolean optBoolean(int index, + boolean defaultValue); + + /** + * Get the optional double value associated with an index. NaN is returned if there is no value + * for the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public double optDouble(int index); + + /** + * Get the optional double value associated with an index. The defaultValue is returned if there + * is no value for the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * subscript + * @param defaultValue + * The default value. + * @return The value. + */ + public double optDouble(int index, + double defaultValue); + + /** + * Get the optional int value associated with an index. Zero is returned if there is no value for + * the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public int optInt(int index); + + /** + * Get the optional int value associated with an index. The defaultValue is returned if there is + * no value for the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public int optInt(int index, + int defaultValue); + + /** + * Get the optional JSONArray associated with an index. + * + * @param index + * subscript + * @return A JSONArray value, or null if the index has no value, or if the value is not a + * JSONArray. + */ + public JSONArray optJSONArray(int index); + + /** + * Get the optional JSONObject associated with an index. Null is returned if the key is not found, + * or null if the index has no value, or if the value is not a JSONObject. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONObject value. + */ + public JSONObject optJSONObject(int index); + + /** + * Get the optional long value associated with an index. Zero is returned if there is no value for + * the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public long optLong(int index); + + /** + * Get the optional long value associated with an index. The defaultValue is returned if there is + * no value for the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public long optLong(int index, + long defaultValue); + + /** + * Get the optional string value associated with an index. It returns an empty string if there is + * no value at that index. If the value is not a string and is not null, then it is coverted to a + * string. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A String value. + */ + public String optString(int index); + + /** + * Get the optional string associated with an index. The defaultValue is returned if the key is + * not found. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return A String value. + */ + public String optString(int index, + String defaultValue); + + /** + * Append a boolean value. This increases the array's length by one. + * + * @param value + * A boolean value. + * @return this. + */ + public JSONArray put(boolean value); + + /** + * Append a double value. This increases the array's length by one. + * + * @param value + * A double value. + * @throws JSONException + * if the value is not finite. + * @return this. + */ + public JSONArray put(double value) + throws JSONException; + + /** + * Append an int value. This increases the array's length by one. + * + * @param value + * An int value. + * @return this. + */ + public JSONArray put(int value); + + /** + * Append an long value. This increases the array's length by one. + * + * @param value + * A long value. + * @return this. + */ + public JSONArray put(long value); + + // /** + // * Put a value in the JSONArray, where the value will be a JSONObject which is produced from a + // * Map. + // * + // * @param value + // * A Map value. + // * @return this. + // */ + // public JSONArray put(Map value); + + /** + * Append an object value. This increases the array's length by one. + * + * @param value + * An object value. The value should be a Boolean, Double, Integer, JSONArray, + * JSONObject, Long, or String, or the JSONObject.NULL object. + * @return this. + */ + public JSONArray put(Object value); + + /** + * Put or replace a boolean value in the JSONArray. If the index is greater than the length of the + * JSONArray, then null elements will be added as necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * A boolean value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, + boolean value) + throws JSONException; + + /** + * Put or replace a double value. If the index is greater than the length of the JSONArray, then + * null elements will be added as necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * A double value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is not finite. + */ + public JSONArray put(int index, + double value) + throws JSONException; + + /** + * Put or replace an int value. If the index is greater than the length of the JSONArray, then + * null elements will be added as necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * An int value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, + int value) + throws JSONException; + + /** + * Put or replace a long value. If the index is greater than the length of the JSONArray, then + * null elements will be added as necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * A long value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, + long value) + throws JSONException; + + /** + * Put or replace an object value in the JSONArray. If the index is greater than the length of the + * JSONArray, then null elements will be added as necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * The value to put into the array. The value should be a Boolean, Double, Integer, + * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the index is negative or if the the value is an invalid number. + */ + public JSONArray put(int index, + Object value) + throws JSONException; + + /** + * Remove an index and close the hole. + * + * @param index + * The index of the element to be removed. + * @return The value that was associated with the index, or null if there was no value. + */ + public Object remove(int index); + + /** + * Remove the first instance of an Object from the array. + * + * @param obj + * The object to be removed from the array. + * @return true if it is has been removed. + */ + public boolean remove(Object obj); + + public int indexOf(Object value); +} \ No newline at end of file diff --git a/JSON-java/src/main/java/org/json/JSONArrayBuilder.java b/JSON-java/src/main/java/org/json/JSONArrayBuilder.java new file mode 100644 index 000000000..7aee176ef --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONArrayBuilder.java @@ -0,0 +1,9 @@ +package org.json; + +public interface JSONArrayBuilder +{ + public JSONArrayBuilder put(Object value) + throws JSONException; + + public A build(); +} diff --git a/JSON-java/src/main/java/org/json/JSONArrays.java b/JSON-java/src/main/java/org/json/JSONArrays.java new file mode 100644 index 000000000..0f40b5286 --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONArrays.java @@ -0,0 +1,107 @@ +package org.json; + +import java.io.IOException; +import java.util.List; + +public class JSONArrays +{ + public static A clone(List values, + JSONBuilder builder) + throws JSONException + { + JSONArrayBuilder arrayBuilder = builder.createJSONArrayBuilder(); + for (int i = 0; i < values.size(); i++) { + arrayBuilder.put(values.get(i)); + } + return arrayBuilder.build(); + } + + public static String toString(JSONArray jArr) + { + StringBuilder buf = new StringBuilder(); + try { + write(jArr, buf); + } catch (IOException e) { + throw new RuntimeException("Impossible", e); + } + return buf.toString(); + } + + public static String toString(JSONArray jArr, + int indentFactor, + int indent) + { + StringBuilder buf = new StringBuilder(); + try { + write(jArr, buf, indentFactor, indent); + } catch (IOException e) { + throw new RuntimeException("Impossible", e); + } + return buf.toString(); + } + + /** + * Make a prettyprinted JSON text of this JSONArray. Warning: This method assumes that the data + * structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indention of the top level. + * @throws IOException + */ + public final static void write(JSONArray jArr, + Appendable buf, + int indentFactor, + int indent) + throws IOException + { + buf.append('['); + int len = jArr.length(); + switch (len) { + case 0: + break; + case 1: + JSONComponents.writeValue(jArr.get(0), buf, indentFactor, indent); + break; + default: + int newindent = indent + indentFactor; + buf.append('\n'); + for (int i = 0; i < len; i += 1) { + if (i > 0) { + buf.append(",\n"); + } + JSONComponents.indent(buf, newindent); + JSONComponents.writeValue(jArr.get(i), buf, indentFactor, newindent); + } + buf.append('\n'); + JSONComponents.indent(buf, indent); + } + buf.append(']'); + } + + /** + * Make a JSON text of this JSONArray. For compactness, no unnecessary whitespace is added. If it + * is not possible to produce a syntactically correct JSON text then null will be returned + * instead. This could occur if the array contains an invalid number. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @throws IOException + */ + public static void write(JSONArray jArr, + Appendable buf) + throws IOException + { + buf.append('['); + int len = jArr.length(); + for (int i = 0; i < len; i += 1) { + if (i > 0) { + buf.append(','); + } + JSONComponents.writeValue(jArr.get(i), buf); + } + buf.append(']'); + } + +} diff --git a/JSON-java/src/main/java/org/json/JSONBuilder.java b/JSON-java/src/main/java/org/json/JSONBuilder.java new file mode 100644 index 000000000..b425b706f --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONBuilder.java @@ -0,0 +1,226 @@ +package org.json; + +import java.io.Reader; +import java.io.StringReader; +import java.util.Iterator; + +import com.google.common.base.Preconditions; + +public abstract class JSONBuilder +{ + private final Class __jsonArrayClass; + private final Class __jsonObjectClass; + + public JSONBuilder(Class jsonArrayClass, + Class jsonObjectClass) + { + __jsonArrayClass = Preconditions.checkNotNull(jsonArrayClass, "jsonArrayClass"); + __jsonObjectClass = Preconditions.checkNotNull(jsonObjectClass, "jsonObjectClass"); + } + + public final Class getJSONArrayClass() + { + return __jsonArrayClass; + } + + public final Class getJSONObjectClass() + { + return __jsonObjectClass; + } + + public abstract JSONArrayBuilder createJSONArrayBuilder(); + public abstract JSONObjectBuilder createJSONObjectBuilder(); + + public Object cast(Object object) + throws JSONException + { + if (object == null) { + return Null.getInstance(); + } + if (object instanceof JSONObject) { + return cast((JSONObject) object); + } + if (object instanceof JSONArray) { + return cast((JSONArray) object); + } + if (object instanceof JSONString || object instanceof Byte || object instanceof Character + || object instanceof Short || object instanceof Integer || object instanceof Long + || object instanceof Boolean || object instanceof Float || object instanceof Double + || object instanceof String || object instanceof Null) { + return object; + } + throw new JSONException(String.format("Invalid JSON data value %s of class %s", + object, + object.getClass())); + } + + /** + * Wrap an object, if necessary. If the object is null, return the NULL object. If it is an array + * or collection, wrap it in a JSONArray. If it is a map, wrap it in a JSONObject. If it is a + * standard property (Double, String, et al) then it is already wrapped. Otherwise, if it comes + * from one of the java packages, turn it into a string. And if it doesn't, try to wrap it in a + * JSONObject. If the wrapping fails, then null is returned. + * + * @param object + * The object to wrap + * @return The wrapped value + */ + public Object wrap(Object object) + throws JSONException + { + if (object == null) { + return Null.getInstance(); + } + if (object instanceof JSONObject) { + return ((JSONObject) object).clone(this); + } + if (object instanceof JSONArray) { + return ((JSONArray) object).clone(this); + } + if (object instanceof JSONString || object instanceof Byte || object instanceof Character + || object instanceof Short || object instanceof Integer || object instanceof Long + || object instanceof Boolean || object instanceof Float || object instanceof Double + || object instanceof String || object instanceof Null) { + return object; + } + // if (object instanceof Collection) { + // return new WritableJSONArray((Collection) object); + // } + // if (object.getClass().isArray()) { + // return new WritableJSONArray(object); + // } + // if (object instanceof Map) { + // return new WritableJSONObject((Map) object); + // } + // Package objectPackage = object.getClass().getPackage(); + // String objectPackageName = (objectPackage != null ? objectPackage.getName() : ""); + // if (objectPackageName.startsWith("java.") || objectPackageName.startsWith("javax.") + // || object.getClass().getClassLoader() == null) { + // return object.toString(); + // } + // return new WritableJSONObject(object); + throw new JSONException(String.format("Invalid JSON data value %s of class %s", + object, + object.getClass())); + } + + public A clone(JSONArray source) + throws JSONException + { + return source.clone(this); + } + + public abstract A cast(JSONArray source); + public abstract O cast(JSONObject source); + + /** + * Create an empty JSONArray. If this JSONBuilder creates immutable implementations then this can + * be overridden with a singleton return. + */ + public A emptyJSONArray() + { + return createJSONArrayBuilder().build(); + } + + public A toJSONArray(Iterable values) + { + JSONArrayBuilder jsonArrayBuilder = createJSONArrayBuilder(); + Iterator i = values.iterator(); + while (i.hasNext()) { + Object value = i.next(); + try { + jsonArrayBuilder.put(value); + } catch (JSONException e) { + throw new JSONLogicException(String.format("Unable to clone %s from %s", value, values)); + } + } + return jsonArrayBuilder.build(); + } + + public A toJSONArray(JSONArray source) + { + JSONArrayBuilder jsonArrayBuilder = createJSONArrayBuilder(); + final int length = source.length(); + for (int i = 0; i < length; i++) { + Object value = source.opt(i); + try { + jsonArrayBuilder.put(value); + } catch (JSONException e) { + throw new JSONLogicException(String.format("Unable to clone %s from %s", value, source)); + } + } + return jsonArrayBuilder.build(); + } + + public A toJSONArray(JSONTokener source) + throws JSONException + { + JSONArrayBuilder jsonArrayBuilder = createJSONArrayBuilder(); + JSONParser.populateArrayBuilder(source, jsonArrayBuilder); + return jsonArrayBuilder.build(); + } + + public A toJSONArray(Reader source) + throws JSONException + { + return toJSONArray(new JSONTokenerReader(source, this)); + } + + public A toJSONArray(String source) + throws JSONException + { + return toJSONArray(new JSONTokenerString(source, this)); + } + + public O clone(JSONObject source) + throws JSONException + { + return source.clone(this); + } + + /** + * Create an empty JSONObject. If this JSONBuilder creates immutable implementations then this can + * be overridden with a singleton return. + */ + public O emptyJSONObject() + { + return createJSONObjectBuilder().build(); + } + + public O toJSONObject(JSONObject source) + { + JSONObjectBuilder jsonObjectBuilder = createJSONObjectBuilder(); + Iterator keys = source.keys(); + while (keys.hasNext()) { + String key = keys.next(); + Object value = source.opt(key); + try { + jsonObjectBuilder.putOnce(key, value); + } catch (JSONException e) { + throw new JSONLogicException(String.format("Unable to clone %s from %s", value, source), e); + } + } + return jsonObjectBuilder.build(); + } + + public O toJSONObject(JSONTokener source) + throws JSONException + { + JSONObjectBuilder jsonObjectBuilder = createJSONObjectBuilder(); + JSONParser.populateObjectBuilder(source, jsonObjectBuilder); + return jsonObjectBuilder.build(); + } + + public O toJSONObject(Reader source) + throws JSONException + { + return toJSONObject(new JSONTokenerReader(source, this)); + } + + public O toJSONObject(String source) + throws JSONException + { + return toJSONObject(new JSONTokenerString(source, this)); + } + +} diff --git a/JSON-java/src/main/java/org/json/JSONComponent.java b/JSON-java/src/main/java/org/json/JSONComponent.java new file mode 100644 index 000000000..76e6b5a3d --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONComponent.java @@ -0,0 +1,62 @@ +package org.json; + +import java.io.IOException; + +public interface JSONComponent +{ + /** + * Make a JSON text of this JSONObject. For compactness, no whitespace is added. If this would not + * result in a syntactically correct JSON text, then null will be returned instead. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, portable, transmittable representation of the object, + * beginning with { (left brace) and ending with + * } (right brace). + */ + @Override + public String toString(); + + /** + * Make a prettyprinted JSON text of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return a printable, displayable, portable, transmittable representation of the object, + * beginning with { (left brace) and ending with + * } (right brace). + */ + public String toString(int indentFactor); + + /** + * Make a prettyprinted JSON text of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indentation of the top level. + * @return a printable, displayable, transmittable representation of the object, beginning with + * { (left brace) and ending with } + *  (right brace). + * @throws RuntimeException + * If the object contains an invalid number. + */ + public String toString(int indentFactor, + int indent); + + /** + * Write the contents of the JSONComponent as JSON text to a writer. For compactness, no + * whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + */ + public Appendable write(Appendable writer) + throws IOException; + +} diff --git a/JSON-java/src/main/java/org/json/JSONComponents.java b/JSON-java/src/main/java/org/json/JSONComponents.java new file mode 100644 index 000000000..39926c7cd --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONComponents.java @@ -0,0 +1,388 @@ +package org.json; + +import java.io.IOException; + +public class JSONComponents +{ + + /** + * Append a string with backslash sequences in all the right places. A backslash will be inserted + * within </, producing <\/, allowing JSON text to be delivered in HTML. In JSON text, a string + * cannot contain a control character or an unescaped quote or backslash. + * + * @param string + * A String + * @param buf + * The Appendable that the escaped String will be appended onto. + * @throws IOException + * if it is not possible to write to the buf + */ + public static void escapeChars(String string, + Appendable buf) + throws IOException + { + char b; + char c = 0; + String hhhh; + int i; + int len = string.length(); + + for (i = 0; i < len; i += 1) { + b = c; + c = string.charAt(i); + switch (c) { + case '\\': + case '"': + buf.append('\\'); + buf.append(c); + break; + case '/': + if (b == '<') { + buf.append('\\'); + } + buf.append(c); + break; + case '\b': + buf.append("\\b"); + break; + case '\t': + buf.append("\\t"); + break; + case '\n': + buf.append("\\n"); + break; + case '\f': + buf.append("\\f"); + break; + case '\r': + buf.append("\\r"); + break; + default: + if (c < ' ' || (c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) { + hhhh = "000" + Integer.toHexString(c); + buf.append("\\u" + hhhh.substring(hhhh.length() - 4)); + } else { + buf.append(c); + } + } + } + } + + /** + * Try to convert a string into a number, boolean, or null. If the string can't be converted, + * return the string. + * + * @param string + * A String. + * @return A simple JSON value. + */ + public static Object stringToValue(String string) + { + switch (string.length()) { + case 0: + return string; + case 4: + if (string.equalsIgnoreCase("true")) { + return Boolean.TRUE; + } + if (string.equalsIgnoreCase("false")) { + return Boolean.FALSE; + } + if (string.equalsIgnoreCase("null")) { + return Null.getInstance(); + } + } + /* + * If it might be a number, try converting it. We support the non-standard 0x- convention. If a + * number cannot be produced, then the value will just be a string. Note that the 0x-, plus, and + * implied string conventions are non-standard. A JSON parser may accept non-JSON forms as long + * as it accepts all correct JSON forms. + */ + char b = string.charAt(0); + switch (b) { + case '0': + if (string.length() > 2 && (string.charAt(1) == 'x' || string.charAt(1) == 'X')) { + try { + return Integer.valueOf(Integer.parseInt(string.substring(2), 16)); + } catch (Exception ignore) { + } + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '.': + case '-': + case '+': + try { + if (string.indexOf('.') > -1 || string.indexOf('e') > -1 || string.indexOf('E') > -1) { + return Double.valueOf(string); + } else { + long myLong = Long.parseLong(string); + if (myLong < Integer.MAX_VALUE & myLong > Integer.MIN_VALUE) { + return Integer.valueOf((int) myLong); + } else { + return Long.valueOf(myLong); + } + } + } catch (Exception ignore) { + } + } + + return string; + } + + /** + * Produce a string in double quotes with backslash sequences in all the right places. A backslash + * will be inserted within </, producing <\/, allowing JSON text to be delivered in HTML. In JSON + * text, a string cannot contain a control character or an unescaped quote or backslash. + * + * @param string + * A String + * @return A String correctly formatted for insertion in a JSON text. + */ + public static String quote(String string) + { + if (string == null || string.length() == 0) { + return "\"\""; + } + StringBuilder sb = new StringBuilder(string.length() + 4); + try { + quote(string, sb); + } catch (IOException e) { + throw new RuntimeException("StringBuilder should not thrown an IOException", e); + } + return sb.toString(); + } + + public static void quote(String string, + Appendable buf) + throws IOException + { + buf.append('"'); + escapeChars(string, buf); + buf.append('"'); + } + + /** + * Throw an exception if the object is a NaN or infinite number. + * + * @param o + * The object to test. + * @throws JSONRuntimeException + * If o is a non-finite number. + */ + public static void testValidity(Object o) + throws JSONRuntimeException + { + if (o != null) { + if (o instanceof Double) { + if (((Double) o).isInfinite() || ((Double) o).isNaN()) { + throw new JSONRuntimeException("JSON does not allow non-finite numbers."); + } + } else if (o instanceof Float) { + if (((Float) o).isInfinite() || ((Float) o).isNaN()) { + throw new JSONRuntimeException("JSON does not allow non-finite numbers."); + } + } + } + } + + /** + * Produce a string from a Number. + * + * @param number + * A Number + * @return A String. + * @throws JSONRuntimeException + * If n is a non-finite number. + */ + public static String numberToString(Number number) + throws JSONRuntimeException + { + if (number == null) { + throw new JSONRuntimeException("Null pointer"); + } + testValidity(number); + // Shave off trailing zeros and decimal point, if possible. + String string = number.toString(); + if (string.indexOf('.') > 0 && string.indexOf('e') < 0 && string.indexOf('E') < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + /** + * Make a JSON text of an Object value. If the object has an value.toJSONString() method, then + * that method will be used to produce the JSON text. The method is required to produce a strictly + * conforming text. If the object does not contain a toJSONString method (which is the most common + * case), then a text will be produced by other means. If the value is an array or Collection, + * then a JSONArray will be made from it and its toJSONString method will be called. If the value + * is a MAP, then a JSONObject will be made from it and its toJSONString method will be called. + * Otherwise, the value's toString method will be called, and the result will be quoted. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param value + * The value to be serialized. + * @return a printable, displayable, transmittable representation of the object, beginning with + * { (left brace) and ending with } + *  (right brace). + * @throws JSONRuntimeException + * If the value is or contains an invalid number. + */ + public static String valueToString(Object value) + { + if (value == null || value.equals(null)) { + return "null"; + } + if (value instanceof JSONString) { + return ((JSONString) value).toJSONString(); + } + if (value instanceof Number) { + return numberToString((Number) value); + } + if (value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray) { + return value.toString(); + } + return quote(value.toString()); + } + + public static void writeValue(Object value, + Appendable buf) + throws IOException + { + if (value == null || value.equals(null)) { + buf.append("null"); + return; + } + if (value instanceof JSONString) { + ((JSONString) value).toJSONString(buf); + return; + } + if (value instanceof Number) { + buf.append(numberToString((Number) value)); + return; + } + if (value instanceof Boolean) { + buf.append(value.toString()); + return; + } + if (value instanceof JSONComponent) { + ((JSONComponent) value).write(buf); + return; + } + buf.append(quote(value.toString())); + } + + /** + * Make a prettyprinted JSON text of an object value. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param value + * The value to be serialized. + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indentation of the top level. + * @return a printable, displayable, transmittable representation of the object, beginning with + * { (left brace) and ending with } + *  (right brace). + * @throws JSONException + * If the object contains an invalid number. + */ + static final String valueToString(Object value, + int indentFactor, + int indent) + throws JSONException + { + if (value == null || value.equals(null)) { + return "null"; + } + try { + if (value instanceof JSONString) { + Object o = ((JSONString) value).toJSONString(); + if (o instanceof String) { + return (String) o; + } + } + } catch (Exception ignore) { + } + if (value instanceof Number) { + return numberToString((Number) value); + } + if (value instanceof Boolean) { + return value.toString(); + } + if (value instanceof JSONObject) { + return ((JSONObject) value).toString(indentFactor, indent); + } + if (value instanceof JSONArray) { + return ((JSONArray) value).toString(indentFactor, indent); + } + return quote(value.toString()); + } + + static final void writeValue(Object value, + Appendable buf, + int indentFactor, + int indent) + throws IOException + { + if (value == null || value.equals(Null.getInstance())) { + buf.append("null"); + return; + } + if (value instanceof JSONString) { + buf.append(((JSONString) value).toJSONString()); + return; + } + if (value instanceof Number) { + buf.append(numberToString((Number) value)); + return; + } + if (value instanceof Boolean) { + buf.append(value.toString()); + return; + } + if (value instanceof JSONObject) { + JSONObjects.write(((JSONObject) value), buf, indentFactor, indent); + return; + } + if (value instanceof JSONArray) { + JSONArrays.write(((JSONArray) value), buf, indentFactor, indent); + return; + } + // if (value instanceof Map) { + // return new WritableJSONObject((Map) value).toString(indentFactor, indent); + // } + // if (value instanceof Collection) { + // return new WritableJSONArray((Collection) value).toString(indentFactor, indent); + // } + // if (value.getClass().isArray()) { + // return new WritableJSONArray(value).toString(indentFactor, indent); + // } + buf.append(quote(value.toString())); + } + + static void indent(Appendable buf, + int depth) + throws IOException + { + for (int i = 0; i < depth; i++) { + buf.append(' '); + } + } + +} diff --git a/JSON-java/src/main/java/org/json/JSONException.java b/JSON-java/src/main/java/org/json/JSONException.java new file mode 100755 index 000000000..9de44f0f0 --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONException.java @@ -0,0 +1,38 @@ +package org.json; + +/** + * The JSONException is thrown by the JSON.org classes when things are amiss. + * + * @author JSON.org + * @version 2010-12-24 + */ +public class JSONException + extends Exception +{ + private static final long serialVersionUID = 0; + + /** + * Constructs a JSONException with an explanatory message. + * + * @param message + * Detail about the reason for the exception. + */ + public JSONException(String message) + { + super(message); + } + + public JSONException(String message, + Throwable cause) + { + super(message, + cause); + } + + public JSONException(Throwable cause) + { + super(cause.getMessage(), + cause); + } + +} diff --git a/JSON-java/src/main/java/org/json/JSONFactory.java b/JSON-java/src/main/java/org/json/JSONFactory.java new file mode 100644 index 000000000..f53537426 --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONFactory.java @@ -0,0 +1,11 @@ +package org.json; + +@Deprecated +public interface JSONFactory +{ + // public JSONObject toObject(JSONTokener tokener) + // throws JSONException; + // + // public JSONArray toArray(JSONTokener tokener) + // throws JSONException; +} diff --git a/JSON-java/src/main/java/org/json/JSONLogicException.java b/JSON-java/src/main/java/org/json/JSONLogicException.java new file mode 100644 index 000000000..dd4b88439 --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONLogicException.java @@ -0,0 +1,29 @@ +package org.json; + +public class JSONLogicException + extends JSONRuntimeException +{ + + public JSONLogicException() + { + super(); + } + + public JSONLogicException(String message, + Throwable cause) + { + super(message, + cause); + } + + public JSONLogicException(String message) + { + super(message); + } + + public JSONLogicException(Throwable cause) + { + super(cause); + } + +} diff --git a/JSON-java/src/main/java/org/json/JSONObject.java b/JSON-java/src/main/java/org/json/JSONObject.java new file mode 100644 index 000000000..b68009893 --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONObject.java @@ -0,0 +1,449 @@ +package org.json; + +import java.util.Iterator; +import java.util.Map; + +public interface JSONObject + extends JSONComponent +{ + /** + * @deprecated in preference of {@link ImmutableJSON#emptyJSONObject()} + */ + @Deprecated + public static final JSONObject EMPTY = + UnmodifiableJSONObject.getInstance(new WritableJSONObject()); + + public boolean equalsMap(Map map); + + public O clone(JSONBuilder builder) + throws JSONException; + + /** + * Get the value object associated with a key. + * + * @param key + * A key string. + * @return The object associated with the key. + * @throws JSONException + * if the key is not found. + */ + public Object get(String key) + throws JSONException; + + /** + * Get the boolean value associated with a key. + * + * @param key + * A key string. + * @return The truth. + * @throws JSONException + * if the value is not a Boolean or the String "true" or "false". + */ + public boolean getBoolean(String key) + throws JSONException; + + /** + * Get the double value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number object and cannot be + * converted to a number. + */ + public double getDouble(String key) + throws JSONException; + + /** + * Get the int value associated with a key. + * + * @param key + * A key string. + * @return The integer value. + * @throws JSONException + * if the key is not found or if the value cannot be converted to an integer. + */ + public int getInt(String key) + throws JSONException; + + /** + * Get the JSONArray value associated with a key. + * + * @param key + * A key string. + * @return A JSONArray which is the value. + * @throws JSONException + * if the key is not found or if the value is not a JSONArray. + */ + public JSONArray getJSONArray(String key) + throws JSONException; + + /** + * Get the JSONObject value associated with a key. + * + * @param key + * A key string. + * @return A JSONObject which is the value. + * @throws JSONException + * if the key is not found or if the value is not a JSONObject. + */ + public JSONObject getJSONObject(String key) + throws JSONException; + + /** + * Get the long value associated with a key. + * + * @param key + * A key string. + * @return The long value. + * @throws JSONException + * if the key is not found or if the value cannot be converted to a long. + */ + public long getLong(String key) + throws JSONException; + + /** + * Get the string associated with a key. + * + * @param key + * A key string. + * @return A string which is the value. + * @throws JSONException + * if the key is not found. + */ + public String getString(String key) + throws JSONException; + + /** + * Determine if the JSONObject contains a specific key. + * + * @param key + * A key string. + * @return true if the key exists in the JSONObject. + */ + public boolean has(String key); + + /** + * Determine if the value associated with the key is null or if there is no value. + * + * @param key + * A key string. + * @return true if there is no value associated with the key or if the value is the + * JSONObject.NULL object. + */ + public boolean isNull(String key); + + /** + * Get an enumeration of the keys of the JSONObject. + * + * @return An iterator of the keys. + */ + public Iterator keys(); + + /** + * Get the number of keys stored in the JSONObject. + * + * @return The number of keys in the JSONObject. + */ + public int length(); + + /** + * Get an optional value associated with a key. + * + * @param key + * A key string. + * @return An object which is the value, or null if there is no value. + */ + public Object opt(String key); + + /** + * Get an optional boolean associated with a key. It returns false if there is no such key, or if + * the value is not Boolean.TRUE or the String "true". + * + * @param key + * A key string. + * @return The truth. + */ + public boolean optBoolean(String key); + + /** + * Get an optional boolean associated with a key. It returns the defaultValue if there is no such + * key, or if it is not a Boolean or the String "true" or "false" (case insensitive). + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return The truth. + */ + public boolean optBoolean(String key, + boolean defaultValue); + + /** + * Get an optional double associated with a key, or NaN if there is no such key or if its value is + * not a number. If the value is a string, an attempt will be made to evaluate it as a number. + * + * @param key + * A string which is the key. + * @return An object which is the value. + */ + public double optDouble(String key); + + /** + * Get an optional double associated with a key, or the defaultValue if there is no such key or if + * its value is not a number. If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public double optDouble(String key, + double defaultValue); + + /** + * Get the compulsory number as a Double Object throwing a JSONException if this is not possible. + */ + public Double getDoubleObj(String key) + throws JSONException; + + /** + * Get the optional number as a Double Object returning the optional defaultValue if this is not + * possible. + */ + public Double optDoubleObj(String key, + Double defaultValue); + + /** + * Get an optional int value associated with a key, or zero if there is no such key or if the + * value is not a number. If the value is a string, an attempt will be made to evaluate it as a + * number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public int optInt(String key); + + /** + * Get an optional Integer value associated with the key, or null if there is no such key or if + * the value is not a number and the string representation cannot be parsed as an Integer. + */ + public Integer optInteger(String key); + + /** + * Get an optional int value associated with a key, or the default if there is no such key or if + * the value is not a number. If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public int optInt(String key, + int defaultValue); + + /** + * Get an optional Integer value associated with the key, or the specified defaultValue if there + * is no such key or if the value is not a number and the string representation cannot be parsed + * as an Integer. + */ + public Integer optInteger(String key, + Integer defaultValue); + + /** + * Get an optional JSONArray associated with a key. It returns null if there is no such key, or if + * its value is not a JSONArray. + * + * @param key + * A key string. + * @return A JSONArray which is the value. + */ + public JSONArray optJSONArray(String key); + + /** + * Get an optional JSONObject associated with a key. It returns null if there is no such key, or + * if its value is not a JSONObject. + * + * @param key + * A key string. + * @return A JSONObject which is the value. + */ + public JSONObject optJSONObject(String key); + + /** + * Get an optional long value associated with a key, or zero if there is no such key or if the + * value is not a number. If the value is a string, an attempt will be made to evaluate it as a + * number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public long optLong(String key); + + /** + * Get an optional long value associated with a key, or the default if there is no such key or if + * the value is not a number. If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public long optLong(String key, + long defaultValue); + + /** + * Get an optional string associated with a key. It returns an empty string if there is no such + * key. If the value is not a string and is not null, then it is converted to a string. + * + * @param key + * A key string. + * @return A string which is the value. + */ + public String optString(String key); + + /** + * Get an optional string associated with a key. It returns the defaultValue if there is no such + * key. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return A string which is the value. + */ + public String optString(String key, + String defaultValue); + + /** + * Put a key/boolean pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A boolean which is the value. + * @return this. + * @throws JSONException + * If the key is null. + */ + public JSONObject put(String key, + boolean value) + throws JSONException; + + /** + * Put a key/double pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A double which is the value. + * @return this. + * @throws JSONException + * If the key is null or if the number is invalid. + */ + public JSONObject put(String key, + double value) + throws JSONException; + + /** + * Put a key/int pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * An int which is the value. + * @return this. + * @throws JSONException + * If the key is null. + */ + public JSONObject put(String key, + int value) + throws JSONException; + + /** + * Put a key/long pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A long which is the value. + * @return this. + * @throws JSONException + * If the key is null. + */ + public JSONObject put(String key, + long value) + throws JSONException; + + /** + * Put a key/value pair in the JSONObject. If the value is null, then the key will be removed from + * the JSONObject if it is present. + * + * @param key + * A key string. + * @param value + * An object which is the value. It should be of one of these types: Boolean, Double, + * Integer, JSONArray, JSONObject, Long, String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the value is non-finite number or if the key is null. + */ + public JSONObject put(String key, + Object value) + throws JSONException; + + /** + * Put a key/value pair in the JSONObject, but only if the key and the value are both non-null, + * and only if there is not already a member with that name. + * + * @param key + * @param value + * @return his. + * @throws JSONException + * if the key is a duplicate + */ + public JSONObject putOnce(String key, + Object value) + throws JSONException; + + /** + * Put a key/value pair in the JSONObject, but only if the key and the value are both non-null. + * + * @param key + * A key string. + * @param value + * An object which is the value. It should be of one of these types: Boolean, Double, + * Integer, JSONArray, JSONObject, Long, String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the value is a non-finite number. + */ + public JSONObject putOpt(String key, + Object value) + throws JSONException; + + /** + * Remove a name and its value, if present. + * + * @param key + * The name to be removed. + * @return The value that was associated with the name, or null if there was no value. + */ + public Object remove(String key); + + /** + * Get an enumeration of the keys of the JSONObject. The keys will be sorted alphabetically. + * + * @return An iterator of the keys. + */ + public Iterator sortedKeys(); +} \ No newline at end of file diff --git a/JSON-java/src/main/java/org/json/JSONObjectBuilder.java b/JSON-java/src/main/java/org/json/JSONObjectBuilder.java new file mode 100644 index 000000000..310b75892 --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONObjectBuilder.java @@ -0,0 +1,17 @@ +package org.json; + +public interface JSONObjectBuilder +{ + /** + * @return self + */ + public JSONObjectBuilder putOnce(String key, + Object value) + throws JSONException; + + /** + * Compile the values supplied to {@link #putOnce(String, Object)} into a JSONObject + * representation. + */ + public O build(); +} diff --git a/JSON-java/src/main/java/org/json/JSONObjects.java b/JSON-java/src/main/java/org/json/JSONObjects.java new file mode 100644 index 000000000..650f47506 --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONObjects.java @@ -0,0 +1,106 @@ +package org.json; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +public class JSONObjects +{ + + public static O clone(Map values, + JSONBuilder builder) + throws JSONException + { + JSONObjectBuilder objBuilder = builder.createJSONObjectBuilder(); + for (Map.Entry entry : values.entrySet()) { + objBuilder.putOnce(entry.getKey(), builder.wrap(entry.getValue())); + } + return objBuilder.build(); + } + + public static String toString(JSONObject jObj) + { + StringBuilder buf = new StringBuilder(); + try { + write(jObj, buf); + } catch (IOException e) { + throw new RuntimeException("Impossible", e); + } + return buf.toString(); + } + + public static Appendable write(JSONObject jObj, + Appendable buf) + throws IOException + { + boolean commanate = false; + Iterator keys = jObj.keys(); + buf.append('{'); + + while (keys.hasNext()) { + if (commanate) { + buf.append(','); + } + String key = keys.next(); + buf.append(JSONComponents.quote(key.toString())); + buf.append(':'); + JSONComponents.writeValue(jObj.opt(key), buf); + commanate = true; + } + buf.append('}'); + return buf; + } + + public static String toString(JSONObject jObj, + int indentFactor, + int indent) + { + StringBuilder buf = new StringBuilder(); + try { + write(jObj, buf, indentFactor, indent); + } catch (IOException e) { + throw new RuntimeException("Impossible", e); + } + return buf.toString(); + } + + public static void write(JSONObject jObj, + Appendable buf, + int indentFactor, + int indent) + throws IOException + { + buf.append('{'); + String key; + switch (jObj.length()) { + case 0: + break; + case 1: + key = jObj.keys().next(); + buf.append(JSONComponents.quote(key.toString())).append(": "); + JSONComponents.writeValue(jObj.opt(key), buf, indentFactor, indent); + break; + default: + Iterator keys = jObj.sortedKeys(); + int newindent = indent + indentFactor; + boolean isFirst = true; + while (keys.hasNext()) { + key = keys.next(); + if (isFirst) { + buf.append('\n'); + isFirst = false; + } else { + buf.append(",\n"); + } + JSONComponents.indent(buf, newindent); + buf.append(JSONComponents.quote(key.toString())); + buf.append(": "); + JSONComponents.writeValue(jObj.opt(key), buf, indentFactor, newindent); + } + buf.append('\n'); + JSONComponents.indent(buf, indent); + } + buf.append('}'); + } + +} diff --git a/JSON-java/src/main/java/org/json/JSONParser.java b/JSON-java/src/main/java/org/json/JSONParser.java new file mode 100644 index 000000000..985a8298a --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONParser.java @@ -0,0 +1,94 @@ +package org.json; + +public class JSONParser +{ + public static void populateObjectBuilder(JSONTokener tokener, + JSONObjectBuilder builder) + throws JSONException + { + char c; + String key; + + if (tokener.nextClean() != '{') { + throw tokener.syntaxError("A JSONObject text must begin with '{'"); + } + for (;;) { + c = tokener.nextClean(); + switch (c) { + case 0: + throw tokener.syntaxError("A JSONObject text must end with '}'"); + case '}': + return; + default: + tokener.back(); + key = tokener.nextKey(); + } + + // The key is followed by ':'. We will also tolerate '=' or '=>'. + + c = tokener.nextClean(); + if (c == '=') { + if (tokener.next() != '>') { + tokener.back(); + } + } else if (c != ':') { + throw tokener.syntaxError("Expected a ':' after a key"); + } + builder.putOnce(key, tokener.nextValue()); + + // Pairs are separated by ','. We will also tolerate ';'. + + switch (tokener.nextClean()) { + case ';': + case ',': + if (tokener.nextClean() == '}') { + return; + } + tokener.back(); + break; + case '}': + return; + default: + throw tokener.syntaxError("Expected a ',' or '}'"); + } + } + } + + public static void populateArrayBuilder(JSONTokener tokener, + JSONArrayBuilder builder) + throws JSONException + { + if (tokener.nextClean() != '[') { + throw tokener.syntaxError("A JSONArray text must start with '['"); + } + + if (tokener.nextClean() != ']') { + tokener.back(); + for (;;) { + + if (tokener.nextClean() == ',') { + tokener.back(); + builder.put(Null.getInstance()); + } else { + tokener.back(); + builder.put(tokener.nextValue()); + } + + switch (tokener.nextClean()) { + case ';': + case ',': + if (tokener.nextClean() == ']') { + return; + } + tokener.back(); + break; + case ']': + return; + default: + throw tokener.syntaxError("Expected a ',' or ']'"); + } + + } + } + } +} diff --git a/JSON-java/src/main/java/org/json/JSONRuntimeException.java b/JSON-java/src/main/java/org/json/JSONRuntimeException.java new file mode 100644 index 000000000..1d4e8d86c --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONRuntimeException.java @@ -0,0 +1,29 @@ +package org.json; + +public class JSONRuntimeException + extends RuntimeException +{ + private static final long serialVersionUID = 6175761779024608329L; + + public JSONRuntimeException() + { + super(); + } + + public JSONRuntimeException(String message, + Throwable cause) + { + super(message, + cause); + } + + public JSONRuntimeException(String message) + { + super(message); + } + + public JSONRuntimeException(Throwable cause) + { + super(cause); + } +} diff --git a/JSON-java/src/main/java/org/json/JSONString.java b/JSON-java/src/main/java/org/json/JSONString.java new file mode 100755 index 000000000..d7f21f3cd --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONString.java @@ -0,0 +1,26 @@ +package org.json; + +import java.io.IOException; + +/** + * The JSONString interface allows a toJSONString() method so that a class + * can change the behavior of JSONObject.toString(), JSONArray.toString(), + * and JSONWriter.value(Object). The toJSONString method will + * be used instead of the default behavior of using the Object's toString() method and + * quoting the result. + */ +public interface JSONString +{ + /** + * The toJSONString method allows a class to produce its own JSON serialization. + * + * @return A strictly syntactically correct JSON text. + */ + public String toJSONString(); + + /** + * Serialize yourself to JSON and append the representation to the given Appendable. + */ + public void toJSONString(Appendable buf) + throws IOException; +} diff --git a/JSON-java/src/main/java/org/json/JSONTokener.java b/JSON-java/src/main/java/org/json/JSONTokener.java new file mode 100755 index 000000000..f17613ede --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONTokener.java @@ -0,0 +1,38 @@ +package org.json; + +/* + * Copyright (c) 2002 JSON.org Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: The above + * copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. The Software shall be used for Good, not Evil. THE SOFTWARE IS PROVIDED + * "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH + * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * A JSONTokener takes a source string and extracts characters and tokens from it. It is used by the + * JSONObject and JSONArray constructors to parse JSON source strings. + * + * @author JSON.org + * @version 2010-12-24 + */ +public interface JSONTokener +{ + public void back() + throws JSONException; + public char nextClean() + throws JSONException; + public char next() + throws JSONException; + public String nextKey() + throws JSONException; + public Object nextValue() + throws JSONException; + public JSONException syntaxError(String message); +} \ No newline at end of file diff --git a/JSON-java/src/main/java/org/json/JSONTokenerReader.java b/JSON-java/src/main/java/org/json/JSONTokenerReader.java new file mode 100644 index 000000000..cf22242ac --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONTokenerReader.java @@ -0,0 +1,474 @@ +package org.json; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +/* + * Copyright (c) 2002 JSON.org Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: The above + * copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. The Software shall be used for Good, not Evil. THE SOFTWARE IS PROVIDED + * "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH + * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * A JSONTokener takes a source string and extracts characters and tokens from it. It is used by the + * JSONObject and JSONArray constructors to parse JSON source strings. + * + * @author JSON.org + * @version 2010-12-24 + */ +public class JSONTokenerReader + implements JSONTokener +{ + private final JSONBuilder __jsonBuilder; + private final StringCharBuilder __sharedBuf = new StringCharBuilder(8); + + private int character; + private boolean eof; + private int index; + private int line; + private char previous; + private Reader reader; + private boolean usePrevious; + + /** + * Construct a JSONTokener from a Reader. + * + * @param reader + * A reader. + */ + public JSONTokenerReader(Reader reader, + JSONBuilder jsonBuilder) + { + this.reader = reader.markSupported() ? reader : new BufferedReader(reader); + __jsonBuilder = jsonBuilder; + this.eof = false; + this.usePrevious = false; + this.previous = 0; + this.index = 0; + this.character = 1; + this.line = 1; + } + + /** + * Back up one character. This provides a sort of lookahead capability, so that you can test for a + * digit or letter before attempting to parse the next number or identifier. + */ + @Override + public void back() + throws JSONException + { + if (usePrevious || index <= 0) { + throw new JSONException("Stepping back two steps is not supported"); + } + this.index -= 1; + this.character -= 1; + this.usePrevious = true; + this.eof = false; + } + + /** + * Get the hex value of a character (base16). + * + * @param c + * A character between '0' and '9' or between 'A' and 'F' or between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. + */ + public static int dehexchar(char c) + { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'A' && c <= 'F') { + return c - ('A' - 10); + } + if (c >= 'a' && c <= 'f') { + return c - ('a' - 10); + } + return -1; + } + + public boolean end() + { + return eof && !usePrevious; + } + + /** + * Determine if the source string still contains characters that next() can consume. + * + * @return true if not yet at the end of the source. + */ + public boolean more() + throws JSONException + { + next(); + if (end()) { + return false; + } + back(); + return true; + } + + /** + * Get the next character in the source string. + * + * @return The next character, or 0 if past the end of the source string. + */ + @Override + public char next() + throws JSONException + { + int c; + if (this.usePrevious) { + this.usePrevious = false; + c = this.previous; + } else { + try { + c = this.reader.read(); + } catch (IOException exception) { + throw new JSONException(exception); + } + + if (c <= 0) { // End of stream + this.eof = true; + c = 0; + } + } + this.index += 1; + if (this.previous == '\r') { + this.line += 1; + this.character = c == '\n' ? 0 : 1; + } else if (c == '\n') { + this.line += 1; + this.character = 0; + } else { + this.character += 1; + } + this.previous = (char) c; + return this.previous; + } + + /** + * Get the next n characters. + * + * @param n + * The number of characters to take. + * @return A string of n characters. + * @throws JSONException + * Substring bounds error if there are not n characters remaining in the source string. + */ + public String next(int n) + throws JSONException + { + if (n == 0) { + return ""; + } + + char[] chars = new char[n]; + int pos = 0; + + while (pos < n) { + chars[pos] = next(); + if (end()) { + throw syntaxError("Substring bounds error"); + } + pos += 1; + } + return new String(chars); + } + + /** + * Get the next char in the string, skipping whitespace. + * + * @throws JSONException + * @return A character, or 0 if there are no more characters. + */ + @Override + public char nextClean() + throws JSONException + { + for (;;) { + char c = next(); + if (c > ' ' | c == 0) { + return c; + } + } + } + + /** + * Return the characters up to the next close quote character. Backslash processing is done. The + * formal JSON format does not allow strings in single quotes, but an implementation is allowed to + * accept them. + * + * @param quote + * The quoting character, either " (double quote) or + * ' (single quote). + * @return A String. + * @throws JSONException + * Unterminated string. + */ + public String nextString(char quote) + throws JSONException + { + char c; + // StringBuilder sb = new StringBuilder(); + try (StringCharBuilder buf = __sharedBuf.open()) { + for (;;) { + c = next(); + switch (c) { + case 0: + case '\n': + case '\r': + throw syntaxError("Unterminated string"); + case '\\': + c = next(); + switch (c) { + case 'b': + buf.append('\b'); + break; + case 't': + buf.append('\t'); + break; + case 'n': + buf.append('\n'); + break; + case 'f': + buf.append('\f'); + break; + case 'r': + buf.append('\r'); + break; + case 'u': + buf.append((char) Integer.parseInt(next(4), 16)); + break; + case '"': + case '\'': + case '\\': + case '/': + buf.append(c); + break; + default: + throw syntaxError("Illegal escape."); + } + break; + default: + if (c == quote) { + return buf.toString(); + } + buf.append(c); + } + } + } + } + + /** + * Get the text up but not including the specified character or the end of line, whichever comes + * first. + * + * @param delimiter + * A delimiter character. + * @return A string. + */ + public String nextTo(char delimiter) + throws JSONException + { + // StringBuilder sb = new StringBuilder(); + try (StringCharBuilder buf = __sharedBuf.open()) { + for (;;) { + char c = next(); + if (c == delimiter || c == 0 || c == '\n' || c == '\r') { + if (c != 0) { + back(); + } + return buf.toString().trim(); + } + buf.append(c); + } + } + } + + /** + * Get the text up but not including one of the specified delimiter characters or the end of line, + * whichever comes first. + * + * @param delimiters + * A set of delimiter characters. + * @return A string, trimmed. + */ + public String nextTo(String delimiters) + throws JSONException + { + char c; + try (StringCharBuilder buf = __sharedBuf.open()) { + for (;;) { + c = next(); + if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') { + if (c != 0) { + back(); + } + return buf.toString().trim(); + } + buf.append(c); + } + + } + } + + /** + * Get the next value. The value can be a Boolean, Double, Integer, JSONArray, JSONObject, Long, + * or String, or the JSONObject.NULL object. + * + * @throws JSONException + * If syntax error. + * @return An object. + */ + @Override + public Object nextValue() + throws JSONException + { + char c = nextClean(); + switch (c) { + case '"': + case '\'': + return nextString(c); + case '{': + back(); + return __jsonBuilder.toJSONObject(this); + case '[': + back(); + return __jsonBuilder.toJSONArray(this); + } + return JSONComponents.stringToValue(getUnquotedText(c)); + } + + @Override + public String nextKey() + throws JSONException + { + char c = nextClean(); + switch (c) { + case '"': + case '\'': + return nextString(c); + } + return getUnquotedText(c); + } + + private String getUnquotedText(char c) + throws JSONException + { + while (c <= ' ') { + c = next(); + } + try (StringCharBuilder buf = __sharedBuf.open()) { + appendUnquotedText(buf, c); + back(); + + final int length = buf.length(); + if (length == 0) { + throw syntaxError("Missing value"); + } + int last; + for (last = length; last > 0; last--) { + if (buf.charAt(last - 1) > 32) { + break; + } + } + return last == length ? buf.toString() : buf.substring(0, last); + } + } + + private void appendUnquotedText(StringCharBuilder buf, + char c) + throws JSONException + { + while (c >= ' ') { + switch (c) { + case ',': + case ':': + case ']': + case '}': + case '/': + case '\\': + case '"': + case '[': + case '{': + case ';': + case '=': + case '#': + return; + default: + buf.append(c); + } + c = next(); + } + } + + /** + * Skip characters until the next character is the requested character. If the requested character + * is not found, no characters are skipped. + * + * @param to + * A character to skip to. + * @return The requested character, or zero if the requested character is not found. + */ + public char skipTo(char to) + throws JSONException + { + char c; + try { + int startIndex = this.index; + int startCharacter = this.character; + int startLine = this.line; + reader.mark(Integer.MAX_VALUE); + do { + c = next(); + if (c == 0) { + reader.reset(); + this.index = startIndex; + this.character = startCharacter; + this.line = startLine; + return c; + } + } while (c != to); + } catch (IOException exc) { + throw new JSONException(exc); + } + + back(); + return c; + } + + /** + * Make a JSONException to signal a syntax error. + * + * @param message + * The error message. + * @return A JSONException object, suitable for throwing + */ + @Override + public JSONException syntaxError(String message) + { + return new JSONException(message + toString()); + } + + /** + * Make a printable string of this JSONTokener. + * + * @return " at {index} [character {character} line {line}]" + */ + @Override + public String toString() + { + return " at " + index + " [character " + this.character + " line " + this.line + "]"; + } +} \ No newline at end of file diff --git a/JSON-java/src/main/java/org/json/JSONTokenerString.java b/JSON-java/src/main/java/org/json/JSONTokenerString.java new file mode 100644 index 000000000..5bc88d3cf --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONTokenerString.java @@ -0,0 +1,219 @@ +package org.json; + +import com.google.common.base.Preconditions; + +public class JSONTokenerString + implements JSONTokener +{ + private final JSONBuilder __jsonBuilder; + private final String __source; + private final int __sourceLength; + private int _index; + private StringCharBuilder _sharedBuf = null; + + public JSONTokenerString(String source, + JSONBuilder jsonBuilder) + { + __source = Preconditions.checkNotNull(source); + __sourceLength = __source.length(); + __jsonBuilder = Preconditions.checkNotNull(jsonBuilder); + _index = 0; + } + + private StringCharBuilder getSharedBuf() + { + if (_sharedBuf == null) { + _sharedBuf = new StringCharBuilder(8); + } + return _sharedBuf; + } + + @Override + public void back() + throws JSONException + { + _index--; + } + + @Override + public char nextClean() + throws JSONException + { + for (;;) { + char c = next(); + if (c > ' ' | c == 0) { + return c; + } + } + } + + @Override + public char next() + throws JSONException + { + return _index == __sourceLength ? 0 : __source.charAt(_index++); + } + + @Override + public String nextKey() + throws JSONException + { + char c = nextClean(); + switch (c) { + case '"': + case '\'': + return nextString(c); + } + return getUnquotedText(c); + } + + @Override + public Object nextValue() + throws JSONException + { + char c = nextClean(); + switch (c) { + case '"': + case '\'': + return nextString(c); + case '{': + back(); + return __jsonBuilder.toJSONObject(this); + case '[': + back(); + return __jsonBuilder.toJSONArray(this); + } + return JSONComponents.stringToValue(getUnquotedText(c)); + } + + @Override + public JSONException syntaxError(String message) + { + return new JSONException(message + ":" + _index); + } + + private String getUnquotedText(char c) + throws JSONException + { + int start = _index - 1; + appendUnquotedText(c); + back(); + + final int length = _index - start; + if (length == 0) { + throw syntaxError("Missing value"); + } + int last; + for (last = length; last > 0; last--) { + if (__source.charAt(start + last - 1) > 32) { + break; + } + } + return __source.substring(start, start + last); + } + + public String nextString(char quote) + throws JSONException + { + int start = _index; + char c; + for (;;) { + c = next(); + switch (c) { + case 0: + case '\n': + case '\r': + throw syntaxError("Unterminated string"); + case '\\': + _index = start; + return nextString(quote, getSharedBuf()); + default: + if (c == quote) { + return __source.substring(start, _index - 1); + } + } + } + } + + public String nextString(char quote, + StringCharBuilder sharedBuf) + throws JSONException + { + char c; + try (StringCharBuilder buf = sharedBuf.open()) { + for (;;) { + c = next(); + switch (c) { + case 0: + case '\n': + case '\r': + throw syntaxError("Unterminated string"); + case '\\': + c = next(); + switch (c) { + case 'b': + buf.append('\b'); + break; + case 't': + buf.append('\t'); + break; + case 'n': + buf.append('\n'); + break; + case 'f': + buf.append('\f'); + break; + case 'r': + buf.append('\r'); + break; + case 'u': + buf.append((char) Integer.parseInt(new String(__source.substring(_index, + _index + 4)), + 16)); + _index += 4; + break; + case '"': + case '\'': + case '\\': + case '/': + buf.append(c); + break; + default: + throw syntaxError("Illegal escape."); + } + break; + default: + if (c == quote) { + return buf.toString(); + } + buf.append(c); + } + } + } + } + + private void appendUnquotedText(char c) + throws JSONException + { + while (c >= ' ') { + switch (c) { + case ',': + case ':': + case ']': + case '}': + case '/': + case '\\': + case '"': + case '[': + case '{': + case ';': + case '=': + case '#': + return; + default: + c = next(); + } + } + } + +} diff --git a/JSON-java/src/main/java/org/json/JSONUtils.java b/JSON-java/src/main/java/org/json/JSONUtils.java new file mode 100644 index 000000000..cc6539898 --- /dev/null +++ b/JSON-java/src/main/java/org/json/JSONUtils.java @@ -0,0 +1,95 @@ +package org.json; + +import java.util.Iterator; + +@Deprecated +public class JSONUtils +{ + private JSONUtils() + { + + } + + @Deprecated + public static Object unmodifiable(Object obj) + { + if (obj instanceof JSONObject) { + return UnmodifiableJSONObject.getInstance((JSONObject) obj); + } + if (obj instanceof JSONArray) { + return UnmodifiableJSONArray.getInstance((JSONArray) obj); + } + return obj; + } + + @Deprecated + public static UnmodifiableJSONObject unmodifiable(JSONObject jObj) + { + return UnmodifiableJSONObject.getInstance(jObj); + } + + @Deprecated + public static UnmodifiableJSONArray unmodifiable(JSONArray jArr) + { + return UnmodifiableJSONArray.getInstance(jArr); + } + + @Deprecated + public static WritableJSONObject writableDeepCopy(JSONObject jObj) + throws JSONException + { + WritableJSONObject copy = new WritableJSONObject(); + Iterator keys = jObj.keys(); + while (keys.hasNext()) { + String key = keys.next(); + Object value = jObj.opt(key); + if (value instanceof JSONObject) { + copy.put(key, writableDeepCopy((JSONObject) value)); + } else if (value instanceof JSONArray) { + copy.put(key, writableDeepCopy((JSONArray) value)); + } else { + copy.put(key, value); + } + } + return copy; + } + + @Deprecated + public static WritableJSONArray writableDeepCopy(JSONArray jArr) + throws JSONException + { + WritableJSONArray copy = new WritableJSONArray(); + for (int i = 0; i < jArr.length(); i++) { + Object value = jArr.get(i); + if (value instanceof JSONObject) { + copy.put(writableDeepCopy((JSONObject) value)); + } else if (value instanceof JSONArray) { + copy.put(writableDeepCopy((JSONArray) value)); + } else { + copy.put(value); + } + } + return copy; + } + + /** + * Transform NULL into null + * + * @param obj + * The compulsory object + * @return obj or null if obj equalled NULL + * @throws NullPointerException + * if obj was null + */ + @Deprecated + public static Object stripNULL(Object obj) + { + if (obj == null) { + throw new NullPointerException(); + } + if (Null.getInstance().equals(obj)) { + return null; + } + return obj; + } +} diff --git a/JSON-java/src/main/java/org/json/ListBasedJSONArray.java b/JSON-java/src/main/java/org/json/ListBasedJSONArray.java new file mode 100644 index 000000000..a37fedeea --- /dev/null +++ b/JSON-java/src/main/java/org/json/ListBasedJSONArray.java @@ -0,0 +1,1031 @@ +package org.json; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +import com.google.common.base.Preconditions; + +public abstract class ListBasedJSONArray + implements JSONArray +{ + /** + * the backing List where the JSONArray's properties are kept. + */ + private final List __backingList; + + public ListBasedJSONArray(List backingList) + { + __backingList = Preconditions.checkNotNull(backingList, "backingList"); + } + + protected List getBackingList() + { + return __backingList; + } + + // /** + // * Construct an empty JSONArray. + // */ + // public ListBasedJSONArray() + // { + // this.__list = new ArrayList(); + // } + + // @Override + // public ListBasedJSONArray writableClone() + // { + // return this.clone(); + // } + + // @Override + // public ListBasedJSONArray clone() + // { + // ListBasedJSONArray clone = new ListBasedJSONArray(); + // try { + // for (Object value : this.myArrayList) { + // if (value instanceof Cloneable) { + // clone.put(value.getClass().getMethod("clone").invoke(value)); + // } else { + // clone.put(value); + // } + // } + // } catch (Exception e) { + // throw new RuntimeException(e); + // } + // return clone; + // } + + // /** + // * Construct a JSONArray from a JSONTokener. + // * + // * @param x + // * A JSONTokener + // * @throws JSONException + // * If there is a syntax error. + // */ + // public ListBasedJSONArray(JSONTokener x) throws JSONException + // { + // this(); + // JSONParser.populateArrayBuilder(x, this); + // } + + // /** + // * Construct a JSONArray from a source JSON text. + // * + // * @param source + // * A string that begins with [ (left bracket) and ends + // * with ] (right bracket). + // * @throws JSONException + // * If there is a syntax error. + // */ + // public ListBasedJSONArray(String source) throws JSONException + // { + // this(new JSONTokener(source, WritableJSONFactory.getInstance())); + // } + + // /** + // * Construct a JSONArray from a Collection. + // * + // * @param collection + // * A Collection. + // */ + // public ListBasedJSONArray(Collection collection) + // { + // this.myArrayList = new ArrayList(); + // if (collection != null) { + // Iterator iter = collection.iterator(); + // while (iter.hasNext()) { + // this.myArrayList.add(WritableJSONObject.wrap(iter.next())); + // } + // } + // } + + // /** + // * Construct a JSONArray from an array + // * + // * @throws JSONException + // * If not an array. + // */ + // public ListBasedJSONArray(Object array) throws JSONException + // { + // this(); + // if (array.getClass().isArray()) { + // int length = Array.getLength(array); + // for (int i = 0; i < length; i += 1) { + // this.put(WritableJSONObject.wrap(Array.get(array, i))); + // } + // } else { + // throw new JSONException("JSONArray initial value should be a string or collection or array."); + // } + // } + + /** + * Get the object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + */ + @Override + public Object get(int index) + { + Object object = opt(index); + if (object == null) { + throw new IllegalStateException("JSONArray[" + index + "] not found."); + } + return object; + } + + /** + * Get the boolean value associated with an index. The string values "true" and "false" are + * converted to boolean. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + * @throws JSONException + * If there is no value for the index or if the value is not convertible to boolean. + */ + @Override + public boolean getBoolean(int index) + throws JSONException + { + Object object = get(index); + if (object.equals(Boolean.FALSE) + || (object instanceof String && ((String) object).equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) + || (object instanceof String && ((String) object).equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONArray[" + index + "] is not a boolean."); + } + + /** + * Get the double value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted to a number. + */ + @Override + public double getDouble(int index) + throws JSONException + { + Object object = get(index); + try { + return object instanceof Number + ? ((Number) object).doubleValue() + : Double.parseDouble((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the int value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value is not a number. + */ + @Override + public int getInt(int index) + throws JSONException + { + Object object = get(index); + try { + return object instanceof Number + ? ((Number) object).intValue() + : Integer.parseInt((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the JSONArray associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @throws JSONException + * If there is no value for the index. or if the value is not a JSONArray + */ + @Override + public ListBasedJSONArray getJSONArray(int index) + throws JSONException + { + Object object = get(index); + if (object instanceof ListBasedJSONArray) { + return (ListBasedJSONArray) object; + } + throw new JSONException("JSONArray[" + index + "] is not a JSONArray."); + } + + /** + * Get the JSONObject associated with an index. + * + * @param index + * subscript + * @return A JSONObject value. + * @throws JSONException + * If there is no value for the index or if the value is not a JSONObject + */ + @Override + public MapBasedJSONObject getJSONObject(int index) + throws JSONException + { + Object object = get(index); + if (object instanceof MapBasedJSONObject) { + return (MapBasedJSONObject) object; + } + throw new JSONException("JSONArray[" + index + "] is not a JSONObject."); + } + + /** + * Get the long value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted to a number. + */ + @Override + public long getLong(int index) + throws JSONException + { + Object object = get(index); + try { + return object instanceof Number + ? ((Number) object).longValue() + : Long.parseLong((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the string associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A string value. + * @throws JSONException + * If there is no value for the index. + */ + @Override + public String getString(int index) + throws JSONException + { + Object object = get(index); + return Null.getInstance().equals(object) ? null : object.toString(); + } + + /** + * Determine if the value is null. + * + * @param index + * The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + */ + @Override + public boolean isNull(int index) + { + return Null.getInstance().equals(opt(index)); + } + + // /** + // * Make a string from the contents of this JSONArray. The separator string is + // * inserted between each element. Warning: This method assumes that the data structure is + // * acyclical. + // * + // * @param separator + // * A string that will be inserted between the elements. + // * @return a string. + // * @throws JSONException + // * If the array contains an invalid number. + // */ + // public final String join(String separator) + // // , + // // Supplier jsonObjectBuilderSupplier, + // // Supplier jsonArrayBuilderSupplier) + // throws JSONException + // { + // int len = length(); + // StringBuilder sb = new StringBuilder(); + // + // for (int i = 0; i < len; i += 1) { + // if (i > 0) { + // sb.append(separator); + // } + // sb.append(MapBasedJSONObject.valueToString(this.__backingList.get(i))); + // // jsonObjectBuilderSupplier, + // // jsonArrayBuilderSupplier)); + // } + // return sb.toString(); + // } + + /** + * Get the number of elements in the JSONArray, included nulls. + * + * @return The length (or size). + */ + @Override + public int length() + { + return this.__backingList.size(); + } + + /** + * Get the optional object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object value, or null if there is no object at that index. + */ + @Override + public Object opt(int index) + { + return (index < 0 || index >= length()) ? null : this.__backingList.get(index); + } + + /** + * Get the optional boolean value associated with an index. It returns false if there is no value + * at that index, or if the value is not Boolean.TRUE or the String "true". + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + */ + @Override + public boolean optBoolean(int index) + { + return optBoolean(index, false); + } + + /** + * Get the optional boolean value associated with an index. It returns the defaultValue if there + * is no value at that index or if it is not a Boolean or the String "true" or "false" (case + * insensitive). + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * A boolean default. + * @return The truth. + */ + @Override + public boolean optBoolean(int index, + boolean defaultValue) + { + try { + return getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional double value associated with an index. NaN is returned if there is no value + * for the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + @Override + public double optDouble(int index) + { + return optDouble(index, Double.NaN); + } + + /** + * Get the optional double value associated with an index. The defaultValue is returned if there + * is no value for the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * subscript + * @param defaultValue + * The default value. + * @return The value. + */ + @Override + public double optDouble(int index, + double defaultValue) + { + try { + return getDouble(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional int value associated with an index. Zero is returned if there is no value for + * the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + @Override + public int optInt(int index) + { + return optInt(index, 0); + } + + /** + * Get the optional int value associated with an index. The defaultValue is returned if there is + * no value for the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + @Override + public int optInt(int index, + int defaultValue) + { + try { + return getInt(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional JSONArray associated with an index. + * + * @param index + * subscript + * @return A JSONArray value, or null if the index has no value, or if the value is not a + * JSONArray. + */ + @Override + public ListBasedJSONArray optJSONArray(int index) + { + Object o = opt(index); + return o instanceof ListBasedJSONArray ? (ListBasedJSONArray) o : null; + } + + /** + * Get the optional JSONObject associated with an index. Null is returned if the key is not found, + * or null if the index has no value, or if the value is not a JSONObject. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONObject value. + */ + @Override + public JSONObject optJSONObject(int index) + { + Object o = opt(index); + return o instanceof JSONObject ? (JSONObject) o : null; + } + + /** + * Get the optional long value associated with an index. Zero is returned if there is no value for + * the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + @Override + public long optLong(int index) + { + return optLong(index, 0); + } + + /** + * Get the optional long value associated with an index. The defaultValue is returned if there is + * no value for the index, or if the value is not a number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + @Override + public long optLong(int index, + long defaultValue) + { + try { + return getLong(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional string value associated with an index. It returns an empty string if there is + * no value at that index. If the value is not a string and is not null, then it is coverted to a + * string. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A String value. + */ + @Override + public String optString(int index) + { + return optString(index, ""); + } + + /** + * Get the optional string associated with an index. The defaultValue is returned if the key is + * not found. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return A String value. + */ + @Override + public String optString(int index, + String defaultValue) + { + Object object = opt(index); + return object != null ? object.toString() : defaultValue; + } + + /** + * Append a boolean value. This increases the array's length by one. + * + * @param value + * A boolean value. + * @return this. + */ + @Override + public ListBasedJSONArray put(boolean value) + { + put(value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + // /** + // * Put a value in the JSONArray, where the value will be a JSONArray which is produced from a + // * Collection. + // * + // * @param value + // * A Collection value. + // * @return this. + // */ + // @Override + // public ListBasedJSONArray put(Collection value) + // { + // put(new ListBasedJSONArray(value)); + // return this; + // } + + /** + * Append a double value. This increases the array's length by one. + * + * @param value + * A double value. + * @throws JSONException + * if the value is not finite. + * @return this. + */ + @Override + public ListBasedJSONArray put(double value) + throws JSONException + { + Double d = Double.valueOf(value); + JSONComponents.testValidity(d); + put(d); + return this; + } + + /** + * Append an int value. This increases the array's length by one. + * + * @param value + * An int value. + * @return this. + */ + @Override + public ListBasedJSONArray put(int value) + { + put(Integer.valueOf(value)); + return this; + } + + /** + * Append an long value. This increases the array's length by one. + * + * @param value + * A long value. + * @return this. + */ + @Override + public ListBasedJSONArray put(long value) + { + put(Long.valueOf(value)); + return this; + } + + // /** + // * Put a value in the JSONArray, where the value will be a JSONObject which is produced from a + // * Map. + // * + // * @param value + // * A Map value. + // * @return this. + // */ + // @Override + // public ListBasedJSONArray put(Map value) + // { + // put(new WritableJSONObject(value)); + // return this; + // } + + /** + * Append an object value. This increases the array's length by one. + * + * @param value + * An object value. The value should be a Boolean, Double, Integer, JSONArray, + * JSONObject, Long, or String, or the JSONObject.NULL object. + * @return this. + */ + @Override + public ListBasedJSONArray put(Object value) + { + this.__backingList.add(value); + return this; + } + + /** + * Put or replace a boolean value in the JSONArray. If the index is greater than the length of the + * JSONArray, then null elements will be added as necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * A boolean value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + @Override + public ListBasedJSONArray put(int index, + boolean value) + throws JSONException + { + put(index, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + // /** + // * Put a value in the JSONArray, where the value will be a JSONArray which is produced from a + // * Collection. + // * + // * @param index + // * The subscript. + // * @param value + // * A Collection value. + // * @return this. + // * @throws JSONException + // * If the index is negative or if the value is not finite. + // */ + // @Override + // public ListBasedJSONArray put(int index, + // Collection value) + // throws JSONException + // { + // put(index, new ListBasedJSONArray(value)); + // return this; + // } + + /** + * Put or replace a double value. If the index is greater than the length of the JSONArray, then + * null elements will be added as necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * A double value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is not finite. + */ + @Override + public ListBasedJSONArray put(int index, + double value) + throws JSONException + { + put(index, Double.valueOf(value)); + return this; + } + + /** + * Put or replace an int value. If the index is greater than the length of the JSONArray, then + * null elements will be added as necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * An int value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + @Override + public ListBasedJSONArray put(int index, + int value) + throws JSONException + { + put(index, Integer.valueOf(value)); + return this; + } + + /** + * Put or replace a long value. If the index is greater than the length of the JSONArray, then + * null elements will be added as necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * A long value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + @Override + public ListBasedJSONArray put(int index, + long value) + throws JSONException + { + put(index, Long.valueOf(value)); + return this; + } + + // /** + // * Put a value in the JSONArray, where the value will be a JSONObject which is produced from a + // * Map. + // * + // * @param index + // * The subscript. + // * @param value + // * The Map value. + // * @return this. + // * @throws JSONException + // * If the index is negative or if the the value is an invalid number. + // */ + // @Override + // public ListBasedJSONArray put(int index, + // Map value) + // throws JSONException + // { + // put(index, new WritableJSONObject(value)); + // return this; + // } + + /** + * Put or replace an object value in the JSONArray. If the index is greater than the length of the + * JSONArray, then null elements will be added as necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * The value to put into the array. The value should be a Boolean, Double, Integer, + * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the index is negative or if the the value is an invalid number. + */ + @Override + public ListBasedJSONArray put(int index, + Object value) + throws JSONException + { + JSONComponents.testValidity(value); + if (index < 0) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + if (index < length()) { + this.__backingList.set(index, value); + } else { + while (index != length()) { + put(Null.getInstance()); + } + put(value); + } + return this; + } + + /** + * Remove an index and close the hole. + * + * @param index + * The index of the element to be removed. + * @return The value that was associated with the index, or null if there was no value. + */ + @Override + public Object remove(int index) + { + Object o = opt(index); + this.__backingList.remove(index); + return o; + } + + @Override + public boolean remove(Object object) + { + return this.__backingList.remove(object); + } + + // /** + // * Produce a JSONObject by combining a JSONArray of names with the values of this JSONArray. + // * + // * @param names + // * A JSONArray containing a list of key strings. These will be paired with the values. + // * @return A JSONObject, or null if there are no names or if this JSONArray has no values. + // * @throws JSONException + // * If any of the names are null. + // */ + // public JSONObject toJSONObject(JSONArray names, + // Supplier jsonObjectBuilderSupplier) + // throws JSONException + // { + // if (names == null || names.length() == 0 || length() == 0) { + // return null; + // } + // JSONObjectBuilder builder = jsonObjectBuilderSupplier.get(); + // for (int i = 0; i < names.length(); i += 1) { + // builder.putOnce(names.getString(i), this.opt(i)); + // } + // return builder.build(); + // } + + /** + * Make a JSON text of this JSONArray. For compactness, no unnecessary whitespace is added. If it + * is not possible to produce a syntactically correct JSON text then null will be returned + * instead. This could occur if the array contains an invalid number. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, transmittable representation of the array. + */ + @Override + public String toString() + { + int len = length(); + StringBuilder sb = new StringBuilder("["); + + for (int i = 0; i < len; i += 1) { + if (i > 0) { + sb.append(','); + } + sb.append(JSONComponents.valueToString(this.__backingList.get(i))); + // jsonObjectBuilderSupplier, + // jsonArrayBuilderSupplier)); + } + sb.append("]"); + return sb.toString(); + // + // try { + // return '[' + join(",") + ']'; + // } catch (Exception e) { + // return null; + // } + } + + /** + * Make a prettyprinted JSON text of this JSONArray. Warning: This method assumes that the data + * structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return a printable, displayable, transmittable representation of the object, beginning with + * [ (left bracket) and ending with ] + *  (right bracket). + */ + @Override + public String toString(int indentFactor) + { + return JSONArrays.toString(this); + // return toString(indentFactor, 0); + } + + /** + * Make a prettyprinted JSON text of this JSONArray. Warning: This method assumes that the data + * structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indention of the top level. + * @return a printable, displayable, transmittable representation of the array. + */ + @Override + public final String toString(int indentFactor, + int indent) + { + return JSONArrays.toString(this, indentFactor, indent); + // try { + // int len = length(); + // if (len == 0) { + // return "[]"; + // } + // int i; + // StringBuilder sb = new StringBuilder("["); + // if (len == 1) { + // sb.append(JSONComponents.valueToString(this.__backingList.get(0), indentFactor, indent)); + // } else { + // int newindent = indent + indentFactor; + // sb.append('\n'); + // for (i = 0; i < len; i += 1) { + // if (i > 0) { + // sb.append(",\n"); + // } + // for (int j = 0; j < newindent; j += 1) { + // sb.append(' '); + // } + // sb.append(JSONComponents.valueToString(this.__backingList.get(i), + // indentFactor, + // newindent)); + // } + // sb.append('\n'); + // for (i = 0; i < indent; i += 1) { + // sb.append(' '); + // } + // } + // sb.append(']'); + // return sb.toString(); + // } catch (JSONException e) { + // throw new RuntimeException(e); + // } + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For compactness, no whitespace is + * added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + */ + @Override + public Appendable write(Appendable writer) + throws IOException + { + JSONArrays.write(this, writer); + return writer; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj instanceof JSONArray) { + return ((JSONArray) obj).equalsList(__backingList); + } + return false; + } + + @Override + public int hashCode() + { + return __backingList.hashCode(); + } + + @Override + public boolean equalsList(List list) + { + return __backingList.equals(list); + } + + @Override + public Iterator iterator() + { + return __backingList.iterator(); + } + + @Override + public int indexOf(Object value) + { + return __backingList.indexOf(value); + } + + @Override + public A clone(JSONBuilder builder) + throws JSONException + { + return JSONArrays.clone(getBackingList(), builder); + } + +} diff --git a/JSON-java/src/main/java/org/json/MapBasedJSONObject.java b/JSON-java/src/main/java/org/json/MapBasedJSONObject.java new file mode 100755 index 000000000..18dada5b5 --- /dev/null +++ b/JSON-java/src/main/java/org/json/MapBasedJSONObject.java @@ -0,0 +1,641 @@ +package org.json; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeSet; + +public abstract class MapBasedJSONObject + implements JSONObject +{ + protected abstract Map getMap(); + + /** + * Get an optional value associated with a key. + * + * @param key + * A key string. + * @return An object which is the value, or null if there is no value. + */ + @Override + public final Object opt(String key) + { + return key == null ? null : getMap().get(key); + } + + /** + * Get the boolean value associated with a key. + * + * @param key + * A key string. + * @return The truth. + * @throws JSONException + * if the value is not a Boolean or the String "true" or "false". + */ + @Override + public final boolean getBoolean(String key) + throws JSONException + { + Object object = get(key); + if (object.equals(Boolean.FALSE) + || (object instanceof String && ((String) object).equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) + || (object instanceof String && ((String) object).equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONObject[" + JSONComponents.quote(key) + "] is not a Boolean."); + } + + public static String toString(MapBasedJSONObject jObj) + { + StringBuilder sb = new StringBuilder("{"); + for (Entry entry : jObj.getMap().entrySet()) { + sb.append(JSONComponents.quote(entry.getKey())); + sb.append(':'); + sb.append(JSONComponents.valueToString(entry.getValue())); + } + sb.append("}"); + return sb.toString(); + } + + @Override + public final boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj instanceof JSONObject) { + return ((JSONObject) obj).equalsMap(getMap()); + } + return false; + } + + @Override + public final int hashCode() + { + return getMap().hashCode(); + } + + /** + * Get the value object associated with a key. + * + * @param key + * A key string. + * @return The object associated with the key. + * @throws JSONException + * if the key is not found. + */ + @Override + public final Object get(String key) + throws JSONException + { + if (key == null) { + throw new JSONException("Null key."); + } + Object object = opt(key); + if (object == null) { + throw new JSONException("JSONObject[" + JSONComponents.quote(key) + "] not found in " + this); + } + return object; + } + + /** + * Get the double value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number object and cannot be + * converted to a number. + */ + @Override + public final double getDouble(String key) + throws JSONException + { + Object object = get(key); + try { + return object instanceof Number + ? ((Number) object).doubleValue() + : Double.parseDouble((String) object); + } catch (Exception e) { + throw new JSONException("JSONObject[" + JSONComponents.quote(key) + "] is not a number."); + } + } + + @Override + public final Double optDoubleObj(String key, + Double defaultValue) + { + Object object = opt(key); + if (object == null) { + return defaultValue; + } + if (object instanceof Double) { + return (Double) object; + } + if (object instanceof Number) { + return Double.valueOf(((Number) object).doubleValue()); + } + try { + return Double.valueOf(Double.parseDouble(object.toString())); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + @Override + public final Double getDoubleObj(String key) + throws JSONException + { + Object object = get(key); + if (object instanceof Double) { + return (Double) object; + } + if (object instanceof Number) { + return Double.valueOf(((Number) object).doubleValue()); + } + try { + return Double.valueOf(Double.parseDouble(object.toString())); + } catch (NumberFormatException e) { + throw new JSONException("JSONObject[" + JSONComponents.quote(key) + "] is not a number."); + } + } + + /** + * Get the int value associated with a key. + * + * @param key + * A key string. + * @return The integer value. + * @throws JSONException + * if the key is not found or if the value cannot be converted to an integer. + */ + @Override + public final int getInt(String key) + throws JSONException + { + Object object = get(key); + try { + return object instanceof Number + ? ((Number) object).intValue() + : Integer.parseInt((String) object); + } catch (Exception e) { + throw new JSONException("JSONObject[" + JSONComponents.quote(key) + "] is not an int."); + } + } + + /** + * Get the JSONArray value associated with a key. + * + * @param key + * A key string. + * @return A JSONArray which is the value. + * @throws JSONException + * if the key is not found or if the value is not a JSONArray. + */ + @Override + public JSONArray getJSONArray(String key) + throws JSONException + { + Object object = get(key); + if (object instanceof JSONArray) { + return (JSONArray) object; + } + throw new JSONException("JSONObject[" + JSONComponents.quote(key) + "] is not a JSONArray."); + } + + /** + * Get the JSONObject value associated with a key. + * + * @param key + * A key string. + * @return A JSONObject which is the value. + * @throws JSONException + * if the key is not found or if the value is not a JSONObject. + */ + @Override + public JSONObject getJSONObject(String key) + throws JSONException + { + Object object = get(key); + if (object instanceof JSONObject) { + return (JSONObject) object; + } + throw new JSONException("JSONObject[" + JSONComponents.quote(key) + "] is not a JSONObject."); + } + + /** + * Get the long value associated with a key. + * + * @param key + * A key string. + * @return The long value. + * @throws JSONException + * if the key is not found or if the value cannot be converted to a long. + */ + @Override + public final long getLong(String key) + throws JSONException + { + Object object = get(key); + try { + return object instanceof Number + ? ((Number) object).longValue() + : Long.parseLong((String) object); + } catch (Exception e) { + throw new JSONException("JSONObject[" + JSONComponents.quote(key) + "] is not a long."); + } + } + + /** + * Get the string associated with a key. + * + * @param key + * A key string. + * @return A string which is the value. + * @throws JSONException + * if the key is not found. + */ + @Override + public final String getString(String key) + throws JSONException + { + Object object = get(key); + return Null.getInstance().equals(object) ? null : object.toString(); + } + + /** + * Determine if the JSONObject contains a specific key. + * + * @param key + * A key string. + * @return true if the key exists in the JSONObject. + */ + @Override + public final boolean has(String key) + { + return getMap().containsKey(key); + } + + /** + * Determine if the value associated with the key is null or if there is no value. + * + * @param key + * A key string. + * @return true if there is no value associated with the key or if the value is the + * JSONObject.NULL object. + */ + @Override + public final boolean isNull(String key) + { + return Null.getInstance().equals(opt(key)); + } + + /** + * Get an enumeration of the keys of the JSONObject. + * + * @return An iterator of the keys. + */ + @Override + public final Iterator keys() + { + return getMap().keySet().iterator(); + } + + /** + * Get the number of keys stored in the JSONObject. + * + * @return The number of keys in the JSONObject. + */ + @Override + public final int length() + { + return getMap().size(); + } + + /** + * Get an optional boolean associated with a key. It returns false if there is no such key, or if + * the value is not Boolean.TRUE or the String "true". + * + * @param key + * A key string. + * @return The truth. + */ + @Override + public final boolean optBoolean(String key) + { + return optBoolean(key, false); + } + + /** + * Get an optional boolean associated with a key. It returns the defaultValue if there is no such + * key, or if it is not a Boolean or the String "true" or "false" (case insensitive). + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return The truth. + */ + @Override + public final boolean optBoolean(String key, + boolean defaultValue) + { + try { + return getBoolean(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional double associated with a key, or NaN if there is no such key or if its value is + * not a number. If the value is a string, an attempt will be made to evaluate it as a number. + * + * @param key + * A string which is the key. + * @return An object which is the value. + */ + @Override + public final double optDouble(String key) + { + return optDouble(key, Double.NaN); + } + + /** + * Get an optional double associated with a key, or the defaultValue if there is no such key or if + * its value is not a number. If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + @Override + public final double optDouble(String key, + double defaultValue) + { + try { + return getDouble(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional int value associated with a key, or zero if there is no such key or if the + * value is not a number. If the value is a string, an attempt will be made to evaluate it as a + * number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + @Override + public final int optInt(String key) + { + return optInt(key, 0); + } + + /** + * Get an optional int value associated with a key, or the default if there is no such key or if + * the value is not a number. If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + @Override + public final int optInt(String key, + int defaultValue) + { + try { + return getInt(key); + } catch (Exception e) { + return defaultValue; + } + } + + @Override + public final Integer optInteger(String key, + Integer defaultValue) + { + Object object = opt(key); + if (object == null) { + return defaultValue; + } + if (object instanceof Integer) { + return (Integer) object; + } + if (object instanceof Number) { + return Integer.valueOf(((Number) object).intValue()); + } + try { + Integer.parseInt(object.toString()); + } catch (Exception e) { + } + return defaultValue; + } + + @Override + public final Integer optInteger(String key) + { + return optInteger(key, null); + } + + /** + * Get an optional long value associated with a key, or zero if there is no such key or if the + * value is not a number. If the value is a string, an attempt will be made to evaluate it as a + * number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + @Override + public final long optLong(String key) + { + return optLong(key, 0); + } + + /** + * Get an optional long value associated with a key, or the default if there is no such key or if + * the value is not a number. If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + @Override + public final long optLong(String key, + long defaultValue) + { + try { + return getLong(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional string associated with a key. It returns an empty string if there is no such + * key. If the value is not a string and is not null, then it is converted to a string. + * + * @param key + * A key string. + * @return A string which is the value. + */ + @Override + public final String optString(String key) + { + return optString(key, ""); + } + + /** + * Get an optional string associated with a key. It returns the defaultValue if there is no such + * key. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return A string which is the value. + */ + @Override + public final String optString(String key, + String defaultValue) + { + Object object = opt(key); + return Null.getInstance().equals(object) ? defaultValue : object.toString(); + } + + /** + * Make a JSON text of this JSONObject. For compactness, no whitespace is added. If this would not + * result in a syntactically correct JSON text, then null will be returned instead. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, portable, transmittable representation of the object, + * beginning with { (left brace) and ending with + * } (right brace). + */ + @Override + public final String toString() + { + return JSONObjects.toString(this); + } + + /** + * Make a prettyprinted JSON text of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return a printable, displayable, portable, transmittable representation of the object, + * beginning with { (left brace) and ending with + * } (right brace). + */ + @Override + public final String toString(int indentFactor) + { + return toString(indentFactor, 0); + } + + /** + * Make a prettyprinted JSON text of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indentation of the top level. + * @return a printable, displayable, transmittable representation of the object, beginning with + * { (left brace) and ending with } + *  (right brace). + * @throws RuntimeException + * If the object contains an invalid number. + */ + @Override + public final String toString(int indentFactor, + int indent) + { + return JSONObjects.toString(this, indentFactor, indent); + } + + @Override + public final Appendable write(Appendable buf) + throws IOException + { + return JSONObjects.write(this, buf); + } + + /** + * Get an optional JSONArray associated with a key. It returns null if there is no such key, or if + * its value is not a JSONArray. + * + * @param key + * A key string. + * @return A JSONArray which is the value. + */ + @Override + public JSONArray optJSONArray(String key) + { + Object o = opt(key); + return o instanceof JSONArray ? (JSONArray) o : null; + } + + /** + * Get an optional JSONObject associated with a key. It returns null if there is no such key, or + * if its value is not a JSONObject. + * + * @param key + * A key string. + * @return A JSONObject which is the value. + */ + @Override + public JSONObject optJSONObject(String key) + { + Object object = opt(key); + return object instanceof JSONObject ? (JSONObject) object : null; + } + + /** + * Get an Iterator of the keys of the JSONObject. The keys will be sorted alphabetically. + * + * @return An iterator of the keys. + */ + @Override + public final Iterator sortedKeys() + { + Set unsortedKeys = getMap().keySet(); + switch (unsortedKeys.size()) { + case 0: + case 1: + return unsortedKeys.iterator(); + default: + return new TreeSet(unsortedKeys).iterator(); + } + } + + @Override + public O clone(JSONBuilder builder) + throws JSONException + { + return JSONObjects.clone(getMap(), builder); + } + +} diff --git a/JSON-java/src/main/java/org/json/Null.java b/JSON-java/src/main/java/org/json/Null.java new file mode 100644 index 000000000..116fd90ed --- /dev/null +++ b/JSON-java/src/main/java/org/json/Null.java @@ -0,0 +1,62 @@ +package org.json; + +/** + * JSONObject.NULL is equivalent to the value that JavaScript calls null, whilst Java's null is + * equivalent to the value that JavaScript calls undefined. + */ +public class Null +{ + private static final Null INSTANCE = new Null(); + + public static Null getInstance() + { + return INSTANCE; + } + + private Null() + { + } + + /** + * There is only intended to be a single instance of the NULL object, so the clone method returns + * itself. + * + * @return NULL. + */ + @Override + protected final Object clone() + { + return this; + } + + /** + * A Null object is equal to the null value and to itself. + * + * @param object + * An object to test for nullness. + * @return true if the object parameter is the JSONObject.NULL object or null. + */ + @Override + public boolean equals(Object object) + { + return object == null || object == this; + } + + @Override + public int hashCode() + { + return 0; + } + + /** + * Get the "null" string value. + * + * @return The string "null". + */ + @Override + public String toString() + { + return "null"; + } + +} diff --git a/JSON-java/src/main/java/org/json/StringCharBuilder.java b/JSON-java/src/main/java/org/json/StringCharBuilder.java new file mode 100644 index 000000000..85c689da1 --- /dev/null +++ b/JSON-java/src/main/java/org/json/StringCharBuilder.java @@ -0,0 +1,126 @@ +package org.json; + +import java.util.Arrays; + +/** + * simplified copy of StringBuilder that only supports appending characters and which also supports + * efficient resuse. You should invoke {@link #open()} before you start using it and + * {@link #close()} when you are finished with it. You cannot nest usage of a single + * StringCharBuilder and attempting to call {@link #open()} before it is {@link #close()}d will + * result in an {@link IllegalStateException}. + */ +public class StringCharBuilder + implements AutoCloseable +{ + public static final int UNCLAIMED = -1; + + char[] value; + int count = UNCLAIMED; + + StringCharBuilder(int capacity) + { + value = new char[capacity]; + } + + public int length() + { + return count; + } + + public int capacity() + { + return value.length; + } + + /** + * This method has the same contract as ensureCapacity, but is never synchronized. + */ + private void ensureCapacityInternal(int minimumCapacity) + { + if (minimumCapacity - value.length > 0) + expandCapacity(minimumCapacity); + } + + /** + * This implements the expansion semantics of ensureCapacity with no size check or + * synchronization. + */ + private void expandCapacity(int minimumCapacity) + { + int newCapacity = value.length * 2 + 2; + if (newCapacity - minimumCapacity < 0) + newCapacity = minimumCapacity; + if (newCapacity < 0) { + if (minimumCapacity < 0) // overflow + throw new OutOfMemoryError(); + newCapacity = Integer.MAX_VALUE; + } + value = Arrays.copyOf(value, newCapacity); + } + + public StringCharBuilder open() + { + if (count != UNCLAIMED) { + throw new IllegalStateException("Cannot claim a StringCharBuilder that is in use: " + + toStringInternal()); + } + count = 0; + return this; + } + + @Override + public void close() + { + if (count == UNCLAIMED) { + throw new IllegalStateException("Cannot release a StringCharBuilder that is unclaimed"); + } + count = UNCLAIMED; + } + + private void assertIsClaimed() + { + if (count == UNCLAIMED) { + throw new IllegalStateException("You must claim a StringCharBuilder before using it"); + } + } + + public char charAt(int index) + { + assertIsClaimed(); + if ((index < 0) || (index >= count)) + throw new StringIndexOutOfBoundsException(index); + return value[index]; + } + + public void append(char c) + { + assertIsClaimed(); + ensureCapacityInternal(count + 1); + value[count++] = c; + } + + public String substring(int start, + int end) + { + assertIsClaimed(); + if (start < 0) + throw new StringIndexOutOfBoundsException(start); + if (end > count) + throw new StringIndexOutOfBoundsException(end); + if (start > end) + throw new StringIndexOutOfBoundsException(end - start); + return new String(value, start, end - start); + } + + @Override + public String toString() + { + return count == UNCLAIMED ? "UNCLAIMED" : toStringInternal(); + } + + private String toStringInternal() + { + return new String(value, 0, count); + } + +} diff --git a/JSON-java/src/main/java/org/json/Test.java b/JSON-java/src/main/java/org/json/Test.java new file mode 100755 index 000000000..bf7559870 --- /dev/null +++ b/JSON-java/src/main/java/org/json/Test.java @@ -0,0 +1,1416 @@ +package org.json; + +import java.util.Objects; + +import com.google.common.base.Preconditions; + +/* + * Copyright (c) 2002 JSON.org Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: The above + * copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. The Software shall be used for Good, not Evil. THE SOFTWARE IS PROVIDED + * "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH + * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Test class. This file is not formally a member of the org.json library. It is just a test tool. + * Issue: JSONObject does not specify the ordering of keys, so simple-minded comparisons of + * .toString to a string literal are likely to fail. + * + * @author JSON.org + * @author yusuke at mac.com + * @version 2010-12-29 + */ +public class Test +// extends TestCase +{ + // public Test(String name) + // { + // super(name); + // } + // + // @Override + // protected void setUp() + // throws Exception + // { + // super.setUp(); + // } + // + // @Override + // protected void tearDown() + // throws Exception + // { + // super.tearDown(); + // } + + // public void testXML() + // throws Exception + // { + // WritableJSONObject jsonobject; + // String string; + // + // jsonobject = + // XML.toJSONObject(" + // Ignore the stuff past the end. "); + // assertEquals("{\"content\":\"This is a collection of test patterns and examples for + // org.json.\"}", + // jsonobject.toString()); + // assertEquals("This is a collection of test patterns and examples for org.json.", + // jsonobject.getString("content")); + // + // string = ""; + // jsonobject = XML.toJSONObject(string); + // assertEquals("{\"test\": {\n \"blank\": \"\",\n \"empty\": \"\"\n}}", jsonobject.toString(2)); + // assertEquals("", XML.toString(jsonobject)); + // } + + private void assertFalse(boolean v) + { + Preconditions.checkState(v == false); + } + + private void assertTrue(boolean v) + { + Preconditions.checkState(v == true); + } + + private void assertEquals(Object a, + Object b) + { + Preconditions.checkState(Objects.equals(a, b)); + } + + public void testNull() + throws Exception + { + WritableJSONObject jsonobject; + + jsonobject = WritableJSONObject.create("{\"message\":\"null\"}"); + System.out.println(jsonobject); + assertFalse(jsonobject.isNull("message")); + assertEquals("null", jsonobject.getString("message")); + + jsonobject = WritableJSONObject.create("{\"message\":null}"); + System.out.println(jsonobject); + assertTrue(jsonobject.isNull("message")); + assertEquals(null, jsonobject.getString("message")); + } + + private static void testArray(String source) + throws JSONException + { + System.err.println(String.format("\"%s\" --> %s", + source, + ImmutableJSON.get().toJSONArray(source))); + } + + public static void main(String[] args) + throws Exception + { + testArray("[]"); + testArray("[1]"); + testArray("[1,2]"); + testArray("[1,2,3]"); + testArray("[,]"); + testArray("[,1]"); + testArray("[1,,2]"); + testArray("[1,]"); + + Test test = new Test(); + test.testNull(); + + JSONObject jObj = + ImmutableJSON.get() + .toJSONObject("{\"a\":{\"b\":2, \"c\": [1,2,{\"d\":\"deep?\",\"e\":2.543}]}}"); + System.out.println(jObj); + System.out.println(jObj.getClass()); + System.out.println(jObj.getJSONObject("a").getClass()); + jObj = WritableJSON.get().cast(jObj); + System.out.println(jObj); + System.out.println(jObj.getClass()); + System.out.println(jObj.getJSONObject("a").getClass()); + + String s = "{ \"a\": \"quoted: \\\"xyz\\\"\" }"; + System.out.println(s); + JSONObject o = ImmutableJSON.get().toJSONObject(s); + System.out.println(o); + System.out.println(o.getString("a")); + } + + // public void testJSON() + // throws Exception + // { + // double eps = 2.220446049250313e-16; + // Iterator iterator; + // WritableJSONArray jsonarray; + // WritableJSONObject jsonobject; + // JSONStringer jsonstringer; + // Object object; + // String string; + // + // Beany beanie = new Beany("A beany object", 42, true); + // + // string = "[0.1]"; + // jsonarray = new WritableJSONArray(string); + // assertEquals("[0.1]", jsonarray.toString()); + // + // jsonobject = new WritableJSONObject(); + // object = null; + // jsonobject.put("booga", object); + // jsonobject.put("wooga", JSONObject.NULL); + // assertEquals("{\"wooga\":null}", jsonobject.toString()); + // assertTrue(jsonobject.isNull("booga")); + // + // jsonobject = new WritableJSONObject(); + // jsonobject.increment("two"); + // jsonobject.increment("two"); + // assertEquals("{\"two\":2}", jsonobject.toString()); + // assertEquals(2, jsonobject.getInt("two")); + // + // string = "{ \"list of lists\" : [ [1, 2, 3], [4, 5, 6], ] }"; + // jsonobject = new WritableJSONObject(string); + // assertEquals("{\"list of lists\": [\n" + " [\n" + " 1,\n" + " 2,\n" + // + " 3\n" + " ],\n" + " [\n" + " 4,\n" + " 5,\n" + // + " 6\n" + " ]\n" + "]}", + // jsonobject.toString(4)); + // // assertEquals("123456", + // // XML.toString(jsonobject)); + // + // // string = + // // " Basic + // // bread Flour Yeast Water Salt Mix all ingredients + // together. + // // Knead thoroughly. Cover with a cloth, and leave for one hour in warm + // // room. Knead again. Place in a bread baking tin. Cover + // // with a cloth, and leave for one hour in warm room. Bake in the oven at + // // 180(degrees)C for 30 minutes. "; + // // jsonobject = XML.toJSONObject(string); + // // assertEquals("{\"recipe\": {\n" + " \"cook_time\": \"3 hours\",\n" + // // + " \"ingredient\": [\n" + " {\n" + " \"amount\": 8,\n" + // // + " \"content\": \"Flour\",\n" + " \"unit\": \"dL\"\n" + // // + " },\n" + " {\n" + " \"amount\": 10,\n" + // // + " \"content\": \"Yeast\",\n" + " \"unit\": \"grams\"\n" + // // + " },\n" + " {\n" + " \"amount\": 4,\n" + // // + " \"content\": \"Water\",\n" + " \"state\": \"warm\",\n" + // // + " \"unit\": \"dL\"\n" + " },\n" + " {\n" + // // + " \"amount\": 1,\n" + " \"content\": \"Salt\",\n" + // // + " \"unit\": \"teaspoon\"\n" + " }\n" + " ],\n" + // // + " \"instructions\": {\"step\": [\n" + // // + " \"Mix all ingredients together.\",\n" + // // + " \"Knead thoroughly.\",\n" + // // + " \"Cover with a cloth, and leave for one hour in warm room.\",\n" + // // + " \"Knead again.\",\n" + " \"Place in a bread baking tin.\",\n" + // // + " \"Cover with a cloth, and leave for one hour in warm room.\",\n" + // // + " \"Bake in the oven at 180(degrees)C for 30 minutes.\"\n" + " ]},\n" + // // + " \"name\": \"bread\",\n" + " \"prep_time\": \"5 mins\",\n" + // // + " \"title\": \"Basic bread\"\n" + "}}", + // // jsonobject.toString(4)); + // + // // jsonobject = JSONML.toJSONObject(string); + // // assertEquals("{\"cook_time\":\"3 + // // + // hours\",\"name\":\"bread\",\"tagName\":\"recipe\",\"childNodes\":[{\"tagName\":\"title\",\"childNodes\":[\"Basic + // // + // bread\"]},{\"amount\":8,\"unit\":\"dL\",\"tagName\":\"ingredient\",\"childNodes\":[\"Flour\"]},{\"amount\":10,\"unit\":\"grams\",\"tagName\":\"ingredient\",\"childNodes\":[\"Yeast\"]},{\"amount\":4,\"unit\":\"dL\",\"tagName\":\"ingredient\",\"state\":\"warm\",\"childNodes\":[\"Water\"]},{\"amount\":1,\"unit\":\"teaspoon\",\"tagName\":\"ingredient\",\"childNodes\":[\"Salt\"]},{\"tagName\":\"instructions\",\"childNodes\":[{\"tagName\":\"step\",\"childNodes\":[\"Mix + // // all ingredients together.\"]},{\"tagName\":\"step\",\"childNodes\":[\"Knead + // // thoroughly.\"]},{\"tagName\":\"step\",\"childNodes\":[\"Cover with a cloth, and leave for + // one + // // hour in warm room.\"]},{\"tagName\":\"step\",\"childNodes\":[\"Knead + // // again.\"]},{\"tagName\":\"step\",\"childNodes\":[\"Place in a bread baking + // // tin.\"]},{\"tagName\":\"step\",\"childNodes\":[\"Cover with a cloth, and leave for one hour + // // in warm room.\"]},{\"tagName\":\"step\",\"childNodes\":[\"Bake in the oven at 180(degrees)C + // // for 30 minutes.\"]}]}],\"prep_time\":\"5 mins\"}", + // // jsonobject.toString()); + // // assertEquals("Basic + // // breadFlourYeastWaterSaltMix all ingredients + // // together.Knead thoroughly.Cover with a cloth, and leave for one + // // hour in warm room.Knead again.Place in a bread baking + // // tin.Cover with a cloth, and leave for one hour in warm room.Bake + // in + // // the oven at 180(degrees)C for 30 minutes.", + // // JSONML.toString(jsonobject)); + // // + // // jsonarray = JSONML.toJSONArray(string); + // // assertEquals("[\n" + " \"recipe\",\n" + " {\n" + " \"cook_time\": \"3 hours\",\n" + // // + " \"name\": \"bread\",\n" + " \"prep_time\": \"5 mins\"\n" + // // + " },\n" + " [\n" + " \"title\",\n" + " \"Basic bread\"\n" + // // + " ],\n" + " [\n" + " \"ingredient\",\n" + " {\n" + // // + " \"amount\": 8,\n" + " \"unit\": \"dL\"\n" + // // + " },\n" + " \"Flour\"\n" + " ],\n" + " [\n" + // // + " \"ingredient\",\n" + " {\n" + " \"amount\": 10,\n" + // // + " \"unit\": \"grams\"\n" + " },\n" + " \"Yeast\"\n" + // // + " ],\n" + " [\n" + " \"ingredient\",\n" + " {\n" + // // + " \"amount\": 4,\n" + " \"state\": \"warm\",\n" + // // + " \"unit\": \"dL\"\n" + " },\n" + " \"Water\"\n" + // // + " ],\n" + " [\n" + " \"ingredient\",\n" + " {\n" + // // + " \"amount\": 1,\n" + " \"unit\": \"teaspoon\"\n" + // // + " },\n" + " \"Salt\"\n" + " ],\n" + " [\n" + // // + " \"instructions\",\n" + " [\n" + " \"step\",\n" + // // + " \"Mix all ingredients together.\"\n" + " ],\n" + // // + " [\n" + " \"step\",\n" + " \"Knead thoroughly.\"\n" + // // + " ],\n" + " [\n" + " \"step\",\n" + // // + " \"Cover with a cloth, and leave for one hour in warm room.\"\n" + // // + " ],\n" + " [\n" + " \"step\",\n" + // // + " \"Knead again.\"\n" + " ],\n" + " [\n" + // // + " \"step\",\n" + " \"Place in a bread baking tin.\"\n" + // // + " ],\n" + " [\n" + " \"step\",\n" + // // + " \"Cover with a cloth, and leave for one hour in warm room.\"\n" + // // + " ],\n" + " [\n" + " \"step\",\n" + // // + " \"Bake in the oven at 180(degrees)C for 30 minutes.\"\n" + // // + " ]\n" + " ]\n" + "]", + // // jsonarray.toString(4)); + // // assertEquals("Basic + // // breadFlourYeastWaterSaltMix all ingredients + // // together.Knead thoroughly.Cover with a cloth, and leave for one + // // hour in warm room.Knead again.Place in a bread baking + // // tin.Cover with a cloth, and leave for one hour in warm room.Bake + // in + // // the oven at 180(degrees)C for 30 minutes.", + // // JSONML.toString(jsonarray)); + // + // string = + // "

"; + // jsonobject = JSONML.toJSONObject(string); + // assertEquals("{\n" + " \"childNodes\": [\n" + " {\n" + // + " \"childNodes\": [\n" + // + " \"JSONML is a transformation between\",\n" + // + " {\n" + " \"childNodes\": [\"JSON\"],\n" + // + " \"tagName\": \"b\"\n" + " },\n" + // + " \"and\",\n" + " {\n" + // + " \"childNodes\": [\"XML\"],\n" + // + " \"tagName\": \"b\"\n" + " },\n" + // + " \"that preserves ordering of document features.\"\n" + // + " ],\n" + " \"tagName\": \"p\"\n" + " },\n" + // + " {\n" + // + " \"childNodes\": [\"JSONML can work with JSON arrays or JSON objects.\"],\n" + // + " \"tagName\": \"p\"\n" + " },\n" + " {\n" + // + " \"childNodes\": [\n" + " \"Three\",\n" + // + " {\"tagName\": \"br\"},\n" + " \"little\",\n" + // + " {\"tagName\": \"br\"},\n" + " \"words\"\n" + // + " ],\n" + " \"tagName\": \"p\"\n" + " }\n" + // + " ],\n" + " \"class\": \"JSONML\",\n" + " \"id\": \"demo\",\n" + // + " \"tagName\": \"div\"\n" + "}", + // jsonobject.toString(4)); + // assertEquals("

JSONML is a transformation + // betweenJSONandXMLthat preserves ordering of document features.

JSONML can + // work with JSON arrays or JSON objects.

Three
little
words

", + // JSONML.toString(jsonobject)); + // + // jsonarray = JSONML.toJSONArray(string); + // assertEquals("[\n" + " \"div\",\n" + " {\n" + " \"class\": \"JSONML\",\n" + // + " \"id\": \"demo\"\n" + " },\n" + " [\n" + " \"p\",\n" + // + " \"JSONML is a transformation between\",\n" + " [\n" + // + " \"b\",\n" + " \"JSON\"\n" + " ],\n" + // + " \"and\",\n" + " [\n" + " \"b\",\n" + // + " \"XML\"\n" + " ],\n" + // + " \"that preserves ordering of document features.\"\n" + " ],\n" + // + " [\n" + " \"p\",\n" + // + " \"JSONML can work with JSON arrays or JSON objects.\"\n" + " ],\n" + // + " [\n" + " \"p\",\n" + " \"Three\",\n" + " [\"br\"],\n" + // + " \"little\",\n" + " [\"br\"],\n" + " \"words\"\n" + // + " ]\n" + "]", + // jsonarray.toString(4)); + // assertEquals("

JSONML is a transformation + // betweenJSONandXMLthat preserves ordering of document features.

JSONML can + // work with JSON arrays or JSON objects.

Three
little
words

", + // JSONML.toString(jsonarray)); + // + // string = + // "\n + // Robert\n Smith\n
\n + // 12345 Sixth Ave\n Anytown\n CA\n + // 98765-4321\n
\n
"; + // jsonobject = XML.toJSONObject(string); + // assertEquals("{\"person\": {\n" + " \"address\": {\n" + " \"city\": \"Anytown\",\n" + // + " \"postalCode\": \"98765-4321\",\n" + " \"state\": \"CA\",\n" + // + " \"street\": \"12345 Sixth Ave\",\n" + " \"type\": \"home\"\n" + // + " },\n" + " \"created\": \"2006-11-11T19:23\",\n" + // + " \"firstName\": \"Robert\",\n" + " \"lastName\": \"Smith\",\n" + // + " \"modified\": \"2006-12-31T23:59\"\n" + "}}", + // jsonobject.toString(4)); + // + // jsonobject = new WritableJSONObject(beanie); + // // assertEquals("{\"string\":\"A beany object\",\"BENT\":\"All uppercase + // // key\",\"boolean\":true,\"number\":42,\"x\":\"x\"}" + // // , jsonobject.toString()); + // + // string = + // "{ \"entity\": { \"imageURL\": \"\", \"name\": \"IXXXXXXXXXXXXX\", \"id\": 12336, + // \"ratingCount\": null, \"averageRating\": null } }"; + // jsonobject = new WritableJSONObject(string); + // assertEquals("{\"entity\": {\n" + " \"averageRating\": null,\n" + " \"id\": 12336,\n" + // + " \"imageURL\": \"\",\n" + " \"name\": \"IXXXXXXXXXXXXX\",\n" + // + " \"ratingCount\": null\n" + "}}", + // jsonobject.toString(2)); + // + // jsonstringer = new JSONStringer(); + // string = jsonstringer.object() + // .key("single") + // .value("MARIE HAA'S") + // .key("Johnny") + // .value("MARIE HAA\\'S") + // .key("foo") + // .value("bar") + // .key("baz") + // .array() + // .object() + // .key("quux") + // .value("Thanks, Josh!") + // .endObject() + // .endArray() + // .key("obj keys") + // .value(WritableJSONObject.getNames(beanie)) + // .endObject() + // .toString(); + // assertEquals("{\"single\":\"MARIE HAA'S\",\"Johnny\":\"MARIE + // HAA\\\\'S\",\"foo\":\"bar\",\"baz\":[{\"quux\":\"Thanks, Josh!\"}],\"obj + // keys\":[\"aString\",\"aNumber\",\"aBoolean\"]}", + // string); + // + // assertEquals("{\"a\":[[[\"b\"]]]}", + // new JSONStringer().object() + // .key("a") + // .array() + // .array() + // .array() + // .value("b") + // .endArray() + // .endArray() + // .endArray() + // .endObject() + // .toString()); + // + // jsonstringer = new JSONStringer(); + // jsonstringer.array(); + // jsonstringer.value(1); + // jsonstringer.array(); + // jsonstringer.value(null); + // jsonstringer.array(); + // jsonstringer.object(); + // jsonstringer.key("empty-array").array().endArray(); + // jsonstringer.key("answer").value(42); + // jsonstringer.key("null").value(null); + // jsonstringer.key("false").value(false); + // jsonstringer.key("true").value(true); + // jsonstringer.key("big").value(123456789e+88); + // jsonstringer.key("small").value(123456789e-88); + // jsonstringer.key("empty-object").object().endObject(); + // jsonstringer.key("long"); + // jsonstringer.value(9223372036854775807L); + // jsonstringer.endObject(); + // jsonstringer.value("two"); + // jsonstringer.endArray(); + // jsonstringer.value(true); + // jsonstringer.endArray(); + // jsonstringer.value(98.6); + // jsonstringer.value(-100.0); + // jsonstringer.object(); + // jsonstringer.endObject(); + // jsonstringer.object(); + // jsonstringer.key("one"); + // jsonstringer.value(1.00); + // jsonstringer.endObject(); + // jsonstringer.value(beanie); + // jsonstringer.endArray(); + // assertEquals("[1,[null,[{\"empty-array\":[],\"answer\":42,\"null\":null,\"false\":false,\"true\":true,\"big\":1.23456789E96,\"small\":1.23456789E-80,\"empty-object\":{},\"long\":9223372036854775807},\"two\"],true],98.6,-100,{},{\"one\":1},{\"A + // beany object\":42}]", + // jsonstringer.toString()); + // assertEquals("[\n" + " 1,\n" + " [\n" + " null,\n" + " [\n" + // + " {\n" + " \"answer\": 42,\n" + // + " \"big\": 1.23456789E96,\n" + // + " \"empty-array\": [],\n" + // + " \"empty-object\": {},\n" + " \"false\": false,\n" + // + " \"long\": 9223372036854775807,\n" + // + " \"null\": null,\n" + // + " \"small\": 1.23456789E-80,\n" + // + " \"true\": true\n" + " },\n" + " \"two\"\n" + // + " ],\n" + " true\n" + " ],\n" + " 98.6,\n" + " -100,\n" + // + " {},\n" + " {\"one\": 1},\n" + " {\"A beany object\": 42}\n" + "]", + // new WritableJSONArray(jsonstringer.toString()).toString(4)); + // + // int ar[] = { 1, 2, 3 }; + // WritableJSONArray ja = new WritableJSONArray(ar); + // assertEquals("[1,2,3]", ja.toString()); + // + // String sa[] = { "aString", "aNumber", "aBoolean" }; + // jsonobject = new WritableJSONObject(beanie, sa); + // jsonobject.put("Testing JSONString interface", beanie); + // assertEquals("{\n" + " \"Testing JSONString interface\": {\"A beany object\":42},\n" + // + " \"aBoolean\": true,\n" + " \"aNumber\": 42,\n" + // + " \"aString\": \"A beany object\"\n" + "}", + // jsonobject.toString(4)); + // + // jsonobject = + // new WritableJSONObject("{slashes: '///', closetag: '', backslash:'\\\\', ei: {quotes: + // '\"\\''},eo: {a: '\"quoted\"', b:\"don't\"}, quotes: [\"'\", '\"']}"); + // assertEquals("{\n" + " \"backslash\": \"\\\\\",\n" + " \"closetag\": \"<\\/script>\",\n" + // + " \"ei\": {\"quotes\": \"\\\"'\"},\n" + " \"eo\": {\n" + // + " \"a\": \"\\\"quoted\\\"\",\n" + " \"b\": \"don't\"\n" + " },\n" + // + " \"quotes\": [\n" + " \"'\",\n" + " \"\\\"\"\n" + " ],\n" + // + " \"slashes\": \"///\"\n" + "}", + // jsonobject.toString(2)); + // assertEquals("'"///"'don't
"quoted"</script>\\", + // XML.toString(jsonobject)); + // + // jsonobject = + // new WritableJSONObject("{foo: [true, false,9876543210, 0.0, 1.00000001, 1.000000000001, + // 1.00000000000000001," + // + " .00000000000000001, 2.00, 0.1, 2e100, -32,[],{}, \"string\"], " + // + " to : null, op : 'Good'," + "ten:10} postfix comment"); + // jsonobject.put("String", "98.6"); + // jsonobject.put("JSONObject", new WritableJSONObject()); + // jsonobject.put("JSONArray", new WritableJSONArray()); + // jsonobject.put("int", 57); + // jsonobject.put("double", 123456789012345678901234567890.); + // jsonobject.put("true", true); + // jsonobject.put("false", false); + // jsonobject.put("null", JSONObject.NULL); + // jsonobject.put("bool", "true"); + // jsonobject.put("zero", -0.0); + // jsonobject.put("\\u2028", "\u2028"); + // jsonobject.put("\\u2029", "\u2029"); + // jsonarray = jsonobject.getJSONArray("foo"); + // jsonarray.put(666); + // jsonarray.put(2001.99); + // jsonarray.put("so \"fine\"."); + // jsonarray.put("so ."); + // jsonarray.put(true); + // jsonarray.put(false); + // jsonarray.put(new WritableJSONArray()); + // jsonarray.put(new WritableJSONObject()); + // jsonobject.put("keys", WritableJSONObject.getNames(jsonobject)); + // assertEquals("{\n" + " \"JSONArray\": [],\n" + " \"JSONObject\": {},\n" + // + " \"String\": \"98.6\",\n" + " \"\\\\u2028\": \"\\u2028\",\n" + // + " \"\\\\u2029\": \"\\u2029\",\n" + " \"bool\": \"true\",\n" + // + " \"double\": 1.2345678901234568E29,\n" + " \"false\": false,\n" + // + " \"foo\": [\n" + " true,\n" + " false,\n" + // + " 9876543210,\n" + " 0,\n" + " 1.00000001,\n" + // + " 1.000000000001,\n" + " 1,\n" + " 1.0E-17,\n" + // + " 2,\n" + " 0.1,\n" + " 2.0E100,\n" + " -32,\n" + // + " [],\n" + " {},\n" + " \"string\",\n" + " 666,\n" + // + " 2001.99,\n" + " \"so \\\"fine\\\".\",\n" + // + " \"so .\",\n" + " true,\n" + " false,\n" + // + " [],\n" + " {}\n" + " ],\n" + " \"int\": 57,\n" + // + " \"keys\": [\n" + " \"to\",\n" + " \"ten\",\n" + // + " \"JSONObject\",\n" + " \"JSONArray\",\n" + " \"op\",\n" + // + " \"int\",\n" + " \"true\",\n" + " \"foo\",\n" + // + " \"zero\",\n" + " \"double\",\n" + " \"String\",\n" + // + " \"false\",\n" + " \"bool\",\n" + " \"\\\\u2028\",\n" + // + " \"\\\\u2029\",\n" + " \"null\"\n" + " ],\n" + // + " \"null\": null,\n" + " \"op\": \"Good\",\n" + " \"ten\": 10,\n" + // + " \"to\": null,\n" + " \"true\": true,\n" + " \"zero\": -0\n" + "}", + // jsonobject.toString(4)); + // // + // assertEquals("null10Good[Ljava.lang.String;@4d12512757truetruefalse98765432100.01.000000011.0000000000011.01.0E-172.00.12.0E100-32string6662001.99so + // // "fine".so + // // + // <fine>.truefalse-0.01.2345678901234568E2998.6falsetrue<\\u2028>?<\\u2029>?null", + // // XML.toString(j)); + // assertEquals(98.6d, jsonobject.getDouble("String"), eps); + // assertTrue(jsonobject.getBoolean("bool")); + // assertEquals(null, jsonobject.getString("to")); + // assertEquals("true", jsonobject.getString("true")); + // assertEquals("[true,false,9876543210,0,1.00000001,1.000000000001,1,1.0E-17,2,0.1,2.0E100,-32,[],{},\"string\",666,2001.99,\"so + // \\\"fine\\\".\",\"so .\",true,false,[],{}]", + // jsonobject.getJSONArray("foo").toString()); + // assertEquals("Good", jsonobject.getString("op")); + // assertEquals(10, jsonobject.getInt("ten")); + // assertFalse(jsonobject.optBoolean("oops")); + // + // string = + // "First \u0009<content> This is + // \"content\". 3 JSON does not preserve the sequencing of elements and + // contents. III T H R E EContent text is an implied + // structure in XML. JSON does not have implied + // structure:7everything is explicit.!]]>"; + // jsonobject = XML.toJSONObject(string); + // assertEquals("{\"xml\": {\n" + " \"content\": [\n" + " \"First \\t\",\n" + // + " \"This is \\\"content\\\".\",\n" + // + " \"JSON does not preserve the sequencing of elements and contents.\",\n" + // + " \"Content text is an implied structure in XML.\",\n" + // + " \"JSON does not have implied structure:\",\n" + // + " \"everything is explicit.\",\n" + " \"CDATA blocks!\"\n" + // + " ],\n" + " \"five\": [\n" + " \"\",\n" + " \"\"\n" + " ],\n" + // + " \"four\": \"\",\n" + " \"one\": 1,\n" + " \"seven\": 7,\n" + // + " \"six\": {\"content\": 6},\n" + " \"three\": [\n" + " 3,\n" + // + " \"III\",\n" + " \"T H R E E\"\n" + " ],\n" + // + " \"two\": \" \\\"2\\\" \"\n" + "}}", + // jsonobject.toString(2)); + // assertEquals("First \t<content>\n" + "This is "content".\n" + // + "JSON does not preserve the sequencing of elements and contents.\n" + // + "Content text is an implied structure in XML.\n" + // + "JSON does not have implied structure:\n" + "everything is explicit.\n" + // + "CDATA blocks<are><supported>! "2" + // 713IIIT H R + // E E6", + // XML.toString(jsonobject)); + // + // ja = JSONML.toJSONArray(string); + // assertEquals("[\n" + " \"xml\",\n" + " {\n" + " \"one\": 1,\n" + // + " \"two\": \" \\\"2\\\" \"\n" + " },\n" + " [\"five\"],\n" + // + " \"First \\t\",\n" + " [\"five\"],\n" + // + " \"This is \\\"content\\\".\",\n" + " [\n" + " \"three\",\n" + // + " 3\n" + " ],\n" + // + " \"JSON does not preserve the sequencing of elements and contents.\",\n" + // + " [\n" + " \"three\",\n" + " \"III\"\n" + " ],\n" + " [\n" + // + " \"three\",\n" + " \"T H R E E\"\n" + " ],\n" + // + " [\"four\"],\n" + " \"Content text is an implied structure in XML.\",\n" + // + " [\n" + " \"six\",\n" + " {\"content\": 6}\n" + " ],\n" + // + " \"JSON does not have implied structure:\",\n" + " [\n" + // + " \"seven\",\n" + " 7\n" + " ],\n" + // + " \"everything is explicit.\",\n" + " \"CDATA blocks!\"\n" + // + "]", + // ja.toString(4)); + // assertEquals("First \t<content>This + // is "content".JSON does not preserve the sequencing of elements and + // contents.IIIT H R E EContent text is an implied structure + // in XML.JSON does not have implied structure:everything is + // explicit.CDATA blocks<are><supported>!", + // JSONML.toString(ja)); + // + // string = + // "unodostrestruequatrocinqoseis"; + // ja = JSONML.toJSONArray(string); + // assertEquals("[\n" + " \"xml\",\n" + " {\"do\": \"0\"},\n" + " \"uno\",\n" + " [\n" + // + " \"a\",\n" + " {\n" + " \"mi\": 2,\n" + // + " \"re\": 1\n" + " },\n" + " \"dos\",\n" + " [\n" + // + " \"b\",\n" + " {\"fa\": 3}\n" + " ],\n" + // + " \"tres\",\n" + " [\n" + " \"c\",\n" + // + " true\n" + " ],\n" + " \"quatro\"\n" + " ],\n" + // + " \"cinqo\",\n" + " [\n" + " \"d\",\n" + " \"seis\",\n" + // + " [\"e\"]\n" + " ]\n" + "]", + // ja.toString(4)); + // assertEquals("unodostresquatrocinqoseis", + // JSONML.toString(ja)); + // + // string = + // " + // + // + // + // "; + // jsonobject = XML.toJSONObject(string); + // + // assertEquals("{\"mapping\": {\n" + " \"class\": [\n" + " {\n" + " \"field\": [\n" + // + " {\n" + " \"bind-xml\": {\n" + " \"name\": \"ID\",\n" + // + " \"node\": \"attribute\"\n" + " },\n" + // + " \"name\": \"ID\",\n" + " \"type\": \"string\"\n" + // + " },\n" + " {\n" + " \"name\": \"FirstName\",\n" + // + " \"type\": \"FirstName\"\n" + " },\n" + " {\n" + // + " \"name\": \"MI\",\n" + " \"type\": \"MI\"\n" + " },\n" + // + " {\n" + " \"name\": \"LastName\",\n" + // + " \"type\": \"LastName\"\n" + " }\n" + " ],\n" + // + " \"name\": \"Customer\"\n" + " },\n" + " {\n" + // + " \"field\": {\n" + " \"bind-xml\": {\n" + // + " \"name\": \"text\",\n" + " \"node\": \"text\"\n" + // + " },\n" + " \"name\": \"text\"\n" + " },\n" + // + " \"name\": \"FirstName\"\n" + " },\n" + " {\n" + // + " \"field\": {\n" + " \"bind-xml\": {\n" + // + " \"name\": \"text\",\n" + " \"node\": \"text\"\n" + // + " },\n" + " \"name\": \"text\"\n" + " },\n" + // + " \"name\": \"MI\"\n" + " },\n" + " {\n" + " \"field\": {\n" + // + " \"bind-xml\": {\n" + " \"name\": \"text\",\n" + // + " \"node\": \"text\"\n" + " },\n" + // + " \"name\": \"text\"\n" + " },\n" + " \"name\": \"LastName\"\n" + // + " }\n" + " ],\n" + " \"empty\": \"\"\n" + "}}", + // jsonobject.toString(2)); + // assertEquals("attributeIDIDstringFirstNameFirstNameMIMILastNameLastNameCustomertexttexttextFirstNametexttexttextMItexttexttextLastName", + // XML.toString(jsonobject)); + // ja = JSONML.toJSONArray(string); + // assertEquals("[\n" + " \"mapping\",\n" + " [\"empty\"],\n" + " [\n" + // + " \"class\",\n" + " {\"name\": \"Customer\"},\n" + " [\n" + // + " \"field\",\n" + " {\n" + // + " \"name\": \"ID\",\n" + " \"type\": \"string\"\n" + // + " },\n" + " [\n" + " \"bind-xml\",\n" + // + " {\n" + " \"name\": \"ID\",\n" + // + " \"node\": \"attribute\"\n" + " }\n" + // + " ]\n" + " ],\n" + " [\n" + " \"field\",\n" + // + " {\n" + " \"name\": \"FirstName\",\n" + // + " \"type\": \"FirstName\"\n" + " }\n" + " ],\n" + // + " [\n" + " \"field\",\n" + " {\n" + // + " \"name\": \"MI\",\n" + " \"type\": \"MI\"\n" + // + " }\n" + " ],\n" + " [\n" + " \"field\",\n" + // + " {\n" + " \"name\": \"LastName\",\n" + // + " \"type\": \"LastName\"\n" + " }\n" + " ]\n" + // + " ],\n" + " [\n" + " \"class\",\n" + // + " {\"name\": \"FirstName\"},\n" + " [\n" + // + " \"field\",\n" + " {\"name\": \"text\"},\n" + // + " [\n" + " \"bind-xml\",\n" + " {\n" + // + " \"name\": \"text\",\n" + // + " \"node\": \"text\"\n" + " }\n" + // + " ]\n" + " ]\n" + " ],\n" + " [\n" + // + " \"class\",\n" + " {\"name\": \"MI\"},\n" + " [\n" + // + " \"field\",\n" + " {\"name\": \"text\"},\n" + // + " [\n" + " \"bind-xml\",\n" + " {\n" + // + " \"name\": \"text\",\n" + // + " \"node\": \"text\"\n" + " }\n" + // + " ]\n" + " ]\n" + " ],\n" + " [\n" + // + " \"class\",\n" + " {\"name\": \"LastName\"},\n" + " [\n" + // + " \"field\",\n" + " {\"name\": \"text\"},\n" + // + " [\n" + " \"bind-xml\",\n" + " {\n" + // + " \"name\": \"text\",\n" + // + " \"node\": \"text\"\n" + " }\n" + // + " ]\n" + " ]\n" + " ]\n" + "]", + // ja.toString(4)); + // assertEquals("", + // JSONML.toString(ja)); + // + // jsonobject = + // XML.toJSONObject("Sample + // BookThis is chapter 1. It is not very long or + // interesting.This is chapter 2. Although it is longer than chapter + // 1, it is not any more interesting."); + // assertEquals("{\"Book\": {\n" + " \"Author\": \"Anonymous\",\n" + " \"Chapter\": [\n" + // + " {\n" + // + " \"content\": \"This is chapter 1. It is not very long or interesting.\",\n" + // + " \"id\": 1\n" + " },\n" + " {\n" + // + " \"content\": \"This is chapter 2. Although it is longer than chapter 1, it is not any more + // interesting.\",\n" + // + " \"id\": 2\n" + " }\n" + " ],\n" + " \"Title\": \"Sample Book\"\n" + // + "}}", + // jsonobject.toString(2)); + // assertEquals("This is chapter 1. It is not very long or + // interesting.1This is chapter 2. Although it is longer than chapter + // 1, it is not any more interesting.2AnonymousSample + // Book", + // XML.toString(jsonobject)); + // + // jsonobject = + // XML.toJSONObject(""); + // assertEquals("{\"bCard\": {\"bCard\": [\n" + " {\n" + " \"company\": \"MCI\",\n" + // + " \"email\": \"khare@mci.net\",\n" + " \"firstname\": \"Rohit\",\n" + // + " \"homepage\": \"http://pest.w3.org/\",\n" + " \"lastname\": \"Khare\"\n" + // + " },\n" + " {\n" + " \"company\": \"Caltech Infospheres Project\",\n" + // + " \"email\": \"adam@cs.caltech.edu\",\n" + " \"firstname\": \"Adam\",\n" + // + " \"homepage\": \"http://www.cs.caltech.edu/~adam/\",\n" + // + " \"lastname\": \"Rifkin\"\n" + " }\n" + "]}}", + // jsonobject.toString(2)); + // assertEquals("khare@mci.netMCIKhareRohithttp://pest.w3.org/adam@cs.caltech.eduCaltech + // Infospheres + // ProjectRifkinAdamhttp://www.cs.caltech.edu/~adam/", + // XML.toString(jsonobject)); + // + // jsonobject = + // XML.toJSONObject(" Fred + // fbs0001 Scerbo B + // "); + // assertEquals("{\"customer\": {\n" + " \"ID\": \"fbs0001\",\n" + // + " \"MI\": {\"text\": \"B\"},\n" + " \"firstName\": {\"text\": \"Fred\"},\n" + // + " \"lastName\": {\"text\": \"Scerbo\"}\n" + "}}", + // jsonobject.toString(2)); + // assertEquals("ScerboBfbs0001Fred", + // XML.toString(jsonobject)); + // + // jsonobject = + // XML.toJSONObject("Repository Address Special Collections LibraryABC + // UniversityMain Library, 40 Circle DriveOurtown, + // Pennsylvania17654 USA"); + // assertEquals("{\"list\":{\"item\":[\"Special Collections Library\",\"ABC University\",\"Main + // Library, 40 Circle Drive\",\"Ourtown, Pennsylvania\",\"17654 USA\"],\"head\":\"Repository + // Address\",\"type\":\"simple\"}}", + // jsonobject.toString()); + // assertEquals("Special Collections LibraryABC + // UniversityMain Library, 40 Circle DriveOurtown, + // Pennsylvania17654 USARepository + // Addresssimple", + // XML.toString(jsonobject)); + // + // jsonobject = + // XML.toJSONObject("deluxe&"toot"&toot;Aeksbonusbonus2"); + // assertEquals("{\"test\": {\n" + " \"blip\": {\n" + // + " \"content\": \"&\\\"toot\\\"&toot;A\",\n" + " \"sweet\": true\n" + // + " },\n" + " \"content\": \"deluxe\",\n" + " \"empty\": \"\",\n" + // + " \"intertag\": \"\",\n" + " \"status\": \"ok\",\n" + " \"w\": [\n" + // + " \"bonus\",\n" + " \"bonus2\"\n" + " ],\n" + " \"x\": \"eks\"\n" + "}}", + // jsonobject.toString(2)); + // assertEquals("bonusbonus2deluxeok&"toot"&toot;&#x41;trueeks", + // XML.toString(jsonobject)); + // + // jsonobject = + // HTTP.toJSONObject("GET / HTTP/1.0\nAccept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, + // application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, + // */*\nAccept-Language: en-us\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x + // 4.90; T312461; Q312461)\nHost: www.nokko.com\nConnection: keep-alive\nAccept-encoding: gzip, + // deflate\n"); + // assertEquals("{\n" + // + " \"Accept\": \"image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, + // application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*\",\n" + // + " \"Accept-Language\": \"en-us\",\n" + // + " \"Accept-encoding\": \"gzip, deflate\",\n" + // + " \"Connection\": \"keep-alive\",\n" + " \"HTTP-Version\": \"HTTP/1.0\",\n" + // + " \"Host\": \"www.nokko.com\",\n" + " \"Method\": \"GET\",\n" + // + " \"Request-URI\": \"/\",\n" + // + " \"User-Agent\": \"Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90; T312461; + // Q312461)\"\n" + // + "}", + // jsonobject.toString(2)); + // assertEquals("GET \"/\" HTTP/1.0\r\n" + "Accept-Language: en-us\r\n" + "Host: + // www.nokko.com\r\n" + // + "Accept-encoding: gzip, deflate\r\n" + // + "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90; T312461; + // Q312461)\r\n" + // + "Connection: keep-alive\r\n" + // + "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, + // application/vnd.ms-excel, application/msword, */*\r\n\r\n", + // HTTP.toString(jsonobject)); + // + // jsonobject = + // HTTP.toJSONObject("HTTP/1.1 200 Oki Doki\nDate: Sun, 26 May 2002 17:38:52 GMT\nServer: + // Apache/1.3.23 (Unix) mod_perl/1.26\nKeep-Alive: timeout=15, max=100\nConnection: + // Keep-Alive\nTransfer-Encoding: chunked\nContent-Type: text/html\n"); + // assertEquals("{\n" + " \"Connection\": \"Keep-Alive\",\n" + // + " \"Content-Type\": \"text/html\",\n" + // + " \"Date\": \"Sun, 26 May 2002 17:38:52 GMT\",\n" + // + " \"HTTP-Version\": \"HTTP/1.1\",\n" + // + " \"Keep-Alive\": \"timeout=15, max=100\",\n" + // + " \"Reason-Phrase\": \"Oki Doki\",\n" + // + " \"Server\": \"Apache/1.3.23 (Unix) mod_perl/1.26\",\n" + // + " \"Status-Code\": \"200\",\n" + " \"Transfer-Encoding\": \"chunked\"\n" + "}", + // jsonobject.toString(2)); + // assertEquals("HTTP/1.1 200 Oki Doki\r\n" + "Transfer-Encoding: chunked\r\n" + // + "Date: Sun, 26 May 2002 17:38:52 GMT\r\n" + "Keep-Alive: timeout=15, max=100\r\n" + // + "Content-Type: text/html\r\n" + "Connection: Keep-Alive\r\n" + // + "Server: Apache/1.3.23 (Unix) mod_perl/1.26\r\n\r\n", + // HTTP.toString(jsonobject)); + // + // jsonobject = + // new WritableJSONObject("{nix: null, nux: false, null: 'null', 'Request-URI': '/', Method: + // 'GET', 'HTTP-Version': 'HTTP/1.0'}"); + // assertEquals("{\n" + " \"HTTP-Version\": \"HTTP/1.0\",\n" + " \"Method\": \"GET\",\n" + // + " \"Request-URI\": \"/\",\n" + " \"nix\": null,\n" + " \"null\": \"null\",\n" + // + " \"nux\": false\n" + "}", + // jsonobject.toString(2)); + // assertTrue(jsonobject.isNull("nix")); + // assertTrue(jsonobject.has("nix")); + // assertEquals("/nullfalseGETHTTP/1.0null", + // XML.toString(jsonobject)); + // assertEquals("GET \"/\" HTTP/1.0\r\n" + "nux: false\r\n" + "null: null\r\n\r\n", + // HTTP.toString(jsonobject)); + // + // jsonobject = + // XML.toJSONObject("" + "\n\n" + "" + // + "" + // + "GOOGLEKEY '+search+' 0 10 true false latin1 latin1" + "" + // + ""); + // + // assertEquals("{\"SOAP-ENV:Envelope\": {\n" + " \"SOAP-ENV:Body\": {\"ns1:doGoogleSearch\": {\n" + // + " \"SOAP-ENV:encodingStyle\": \"http://schemas.xmlsoap.org/soap/encoding/\",\n" + // + " \"filter\": {\n" + " \"content\": true,\n" + // + " \"xsi:type\": \"xsd:boolean\"\n" + " },\n" + " \"ie\": {\n" + // + " \"content\": \"latin1\",\n" + " \"xsi:type\": \"xsd:string\"\n" + // + " },\n" + " \"key\": {\n" + " \"content\": \"GOOGLEKEY\",\n" + // + " \"xsi:type\": \"xsd:string\"\n" + " },\n" + // + " \"lr\": {\"xsi:type\": \"xsd:string\"},\n" + " \"maxResults\": {\n" + // + " \"content\": 10,\n" + " \"xsi:type\": \"xsd:int\"\n" + " },\n" + // + " \"oe\": {\n" + " \"content\": \"latin1\",\n" + // + " \"xsi:type\": \"xsd:string\"\n" + " },\n" + " \"q\": {\n" + // + " \"content\": \"'+search+'\",\n" + " \"xsi:type\": \"xsd:string\"\n" + // + " },\n" + " \"restrict\": {\"xsi:type\": \"xsd:string\"},\n" + // + " \"safeSearch\": {\n" + " \"content\": false,\n" + // + " \"xsi:type\": \"xsd:boolean\"\n" + " },\n" + " \"start\": {\n" + // + " \"content\": \"0\",\n" + " \"xsi:type\": \"xsd:int\"\n" + " },\n" + // + " \"xmlns:ns1\": \"urn:GoogleSearch\"\n" + " }},\n" + // + " \"xmlns:SOAP-ENV\": \"http://schemas.xmlsoap.org/soap/envelope/\",\n" + // + " \"xmlns:xsd\": \"http://www.w3.org/1999/XMLSchema\",\n" + // + " \"xmlns:xsi\": \"http://www.w3.org/1999/XMLSchema-instance\"\n" + "}}", + // jsonobject.toString(2)); + // + // assertEquals("latin1xsd:stringhttp://schemas.xmlsoap.org/soap/encoding/xsd:string0xsd:int'+search+'xsd:stringlatin1xsd:stringfalsexsd:booleanurn:GoogleSearchxsd:stringtruexsd:boolean10xsd:intGOOGLEKEYxsd:stringhttp://www.w3.org/1999/XMLSchemahttp://www.w3.org/1999/XMLSchema-instancehttp://schemas.xmlsoap.org/soap/envelope/", + // XML.toString(jsonobject)); + // + // jsonobject = + // new WritableJSONObject("{Envelope: {Body: {\"ns1:doGoogleSearch\": {oe: \"latin1\", filter: + // true, q: \"'+search+'\", key: \"GOOGLEKEY\", maxResults: 10, \"SOAP-ENV:encodingStyle\": + // \"http://schemas.xmlsoap.org/soap/encoding/\", start: 0, ie: \"latin1\", safeSearch:false, + // \"xmlns:ns1\": \"urn:GoogleSearch\"}}}}"); + // assertEquals("{\"Envelope\": {\"Body\": {\"ns1:doGoogleSearch\": {\n" + // + " \"SOAP-ENV:encodingStyle\": \"http://schemas.xmlsoap.org/soap/encoding/\",\n" + // + " \"filter\": true,\n" + " \"ie\": \"latin1\",\n" + // + " \"key\": \"GOOGLEKEY\",\n" + " \"maxResults\": 10,\n" + // + " \"oe\": \"latin1\",\n" + " \"q\": \"'+search+'\",\n" + // + " \"safeSearch\": false,\n" + " \"start\": 0,\n" + // + " \"xmlns:ns1\": \"urn:GoogleSearch\"\n" + "}}}}", + // jsonobject.toString(2)); + // assertEquals("latin1http://schemas.xmlsoap.org/soap/encoding/0'+search+'latin1falseurn:GoogleSearch10GOOGLEKEYtrue", + // XML.toString(jsonobject)); + // + // jsonobject = CookieList.toJSONObject(" f%oo = b+l=ah ; o;n%40e = t.wo "); + // assertEquals("{\n" + " \"f%oo\": \"b l=ah\",\n" + " \"o;n@e\": \"t.wo\"\n" + "}", + // jsonobject.toString(2)); + // assertEquals("o%3bn@e=t.wo;f%25oo=b l%3dah", CookieList.toString(jsonobject)); + // + // jsonobject = Cookie.toJSONObject("f%oo=blah; secure ;expires = April 24, 2002"); + // assertEquals("{\n" + " \"expires\": \"April 24, 2002\",\n" + " \"name\": \"f%oo\",\n" + // + " \"secure\": true,\n" + " \"value\": \"blah\"\n" + "}", + // jsonobject.toString(2)); + // assertEquals("f%25oo=blah;expires=April 24, 2002;secure", Cookie.toString(jsonobject)); + // + // jsonobject = + // new WritableJSONObject("{script: 'It is not allowed in HTML to send a close script tag in a + // stringso we insert a backslash before the /'}"); + // assertEquals("{\"script\":\"It is not allowed in HTML to send a close script tag in a + // string', backslash:'\\\\', ei: {quotes: '\"\\''},eo: {a: '\"quoted\"', b:\"don't\"}, quotes: [\"'\", '\"']}"); - assertEquals("{\n" + - " \"backslash\": \"\\\\\",\n" + - " \"closetag\": \"<\\/script>\",\n" + - " \"ei\": {\"quotes\": \"\\\"'\"},\n" + - " \"eo\": {\n" + - " \"a\": \"\\\"quoted\\\"\",\n" + - " \"b\": \"don't\"\n" + - " },\n" + - " \"quotes\": [\n" + - " \"'\",\n" + - " \"\\\"\"\n" + - " ],\n" + - " \"slashes\": \"///\"\n" + - "}", jsonobject.toString(2)); - assertEquals("'"///"'don't"quoted"</script>\\", - XML.toString(jsonobject)); - - jsonobject = new JSONObject( - "{foo: [true, false,9876543210, 0.0, 1.00000001, 1.000000000001, 1.00000000000000001," + - " .00000000000000001, 2.00, 0.1, 2e100, -32,[],{}, \"string\"], " + - " to : null, op : 'Good'," + - "ten:10} postfix comment"); - jsonobject.put("String", "98.6"); - jsonobject.put("JSONObject", new JSONObject()); - jsonobject.put("JSONArray", new JSONArray()); - jsonobject.put("int", 57); - jsonobject.put("double", 123456789012345678901234567890.); - jsonobject.put("true", true); - jsonobject.put("false", false); - jsonobject.put("null", JSONObject.NULL); - jsonobject.put("bool", "true"); - jsonobject.put("zero", -0.0); - jsonobject.put("\\u2028", "\u2028"); - jsonobject.put("\\u2029", "\u2029"); - jsonarray = jsonobject.getJSONArray("foo"); - jsonarray.put(666); - jsonarray.put(2001.99); - jsonarray.put("so \"fine\"."); - jsonarray.put("so ."); - jsonarray.put(true); - jsonarray.put(false); - jsonarray.put(new JSONArray()); - jsonarray.put(new JSONObject()); - jsonobject.put("keys", JSONObject.getNames(jsonobject)); - assertEquals("{\n" + - " \"JSONArray\": [],\n" + - " \"JSONObject\": {},\n" + - " \"String\": \"98.6\",\n" + - " \"\\\\u2028\": \"\\u2028\",\n" + - " \"\\\\u2029\": \"\\u2029\",\n" + - " \"bool\": \"true\",\n" + - " \"double\": 1.2345678901234568E29,\n" + - " \"false\": false,\n" + - " \"foo\": [\n" + - " true,\n" + - " false,\n" + - " 9876543210,\n" + - " 0,\n" + - " 1.00000001,\n" + - " 1.000000000001,\n" + - " 1,\n" + - " 1.0E-17,\n" + - " 2,\n" + - " 0.1,\n" + - " 2.0E100,\n" + - " -32,\n" + - " [],\n" + - " {},\n" + - " \"string\",\n" + - " 666,\n" + - " 2001.99,\n" + - " \"so \\\"fine\\\".\",\n" + - " \"so .\",\n" + - " true,\n" + - " false,\n" + - " [],\n" + - " {}\n" + - " ],\n" + - " \"int\": 57,\n" + - " \"keys\": [\n" + - " \"to\",\n" + - " \"ten\",\n" + - " \"JSONObject\",\n" + - " \"JSONArray\",\n" + - " \"op\",\n" + - " \"int\",\n" + - " \"true\",\n" + - " \"foo\",\n" + - " \"zero\",\n" + - " \"double\",\n" + - " \"String\",\n" + - " \"false\",\n" + - " \"bool\",\n" + - " \"\\\\u2028\",\n" + - " \"\\\\u2029\",\n" + - " \"null\"\n" + - " ],\n" + - " \"null\": null,\n" + - " \"op\": \"Good\",\n" + - " \"ten\": 10,\n" + - " \"to\": null,\n" + - " \"true\": true,\n" + - " \"zero\": -0\n" + - "}", jsonobject.toString(4)); -// assertEquals("null10Good[Ljava.lang.String;@4d12512757truetruefalse98765432100.01.000000011.0000000000011.01.0E-172.00.12.0E100-32string6662001.99so "fine".so <fine>.truefalse-0.01.2345678901234568E2998.6falsetrue<\\u2028>?<\\u2029>?null", -// XML.toString(j)); - assertEquals(98.6d, jsonobject.getDouble("String"), eps); - assertTrue(jsonobject.getBoolean("bool")); - assertEquals(null, jsonobject.getString("to")); - assertEquals("true", jsonobject.getString("true")); - assertEquals("[true,false,9876543210,0,1.00000001,1.000000000001,1,1.0E-17,2,0.1,2.0E100,-32,[],{},\"string\",666,2001.99,\"so \\\"fine\\\".\",\"so .\",true,false,[],{}]", - jsonobject.getJSONArray("foo").toString()); - assertEquals("Good", jsonobject.getString("op")); - assertEquals(10, jsonobject.getInt("ten")); - assertFalse(jsonobject.optBoolean("oops")); - - string = "First \u0009<content> This is \"content\". 3 JSON does not preserve the sequencing of elements and contents. III T H R E EContent text is an implied structure in XML. JSON does not have implied structure:7everything is explicit.!]]>"; - jsonobject = XML.toJSONObject(string); - assertEquals("{\"xml\": {\n" + - " \"content\": [\n" + - " \"First \\t\",\n" + - " \"This is \\\"content\\\".\",\n" + - " \"JSON does not preserve the sequencing of elements and contents.\",\n" + - " \"Content text is an implied structure in XML.\",\n" + - " \"JSON does not have implied structure:\",\n" + - " \"everything is explicit.\",\n" + - " \"CDATA blocks!\"\n" + - " ],\n" + - " \"five\": [\n" + - " \"\",\n" + - " \"\"\n" + - " ],\n" + - " \"four\": \"\",\n" + - " \"one\": 1,\n" + - " \"seven\": 7,\n" + - " \"six\": {\"content\": 6},\n" + - " \"three\": [\n" + - " 3,\n" + - " \"III\",\n" + - " \"T H R E E\"\n" + - " ],\n" + - " \"two\": \" \\\"2\\\" \"\n" + - "}}", jsonobject.toString(2)); - assertEquals("First \t<content>\n" + - "This is "content".\n" + - "JSON does not preserve the sequencing of elements and contents.\n" + - "Content text is an implied structure in XML.\n" + - "JSON does not have implied structure:\n" + - "everything is explicit.\n" + - "CDATA blocks<are><supported>! "2" 713IIIT H R E E6", - XML.toString(jsonobject)); - - ja = JSONML.toJSONArray(string); - assertEquals("[\n" + - " \"xml\",\n" + - " {\n" + - " \"one\": 1,\n" + - " \"two\": \" \\\"2\\\" \"\n" + - " },\n" + - " [\"five\"],\n" + - " \"First \\t\",\n" + - " [\"five\"],\n" + - " \"This is \\\"content\\\".\",\n" + - " [\n" + - " \"three\",\n" + - " 3\n" + - " ],\n" + - " \"JSON does not preserve the sequencing of elements and contents.\",\n" + - " [\n" + - " \"three\",\n" + - " \"III\"\n" + - " ],\n" + - " [\n" + - " \"three\",\n" + - " \"T H R E E\"\n" + - " ],\n" + - " [\"four\"],\n" + - " \"Content text is an implied structure in XML.\",\n" + - " [\n" + - " \"six\",\n" + - " {\"content\": 6}\n" + - " ],\n" + - " \"JSON does not have implied structure:\",\n" + - " [\n" + - " \"seven\",\n" + - " 7\n" + - " ],\n" + - " \"everything is explicit.\",\n" + - " \"CDATA blocks!\"\n" + - "]", ja.toString(4)); - assertEquals("First \t<content>This is "content".JSON does not preserve the sequencing of elements and contents.IIIT H R E EContent text is an implied structure in XML.JSON does not have implied structure:everything is explicit.CDATA blocks<are><supported>!", - JSONML.toString(ja)); - - string = "unodostrestruequatrocinqoseis"; - ja = JSONML.toJSONArray(string); - assertEquals("[\n" + - " \"xml\",\n" + - " {\"do\": \"0\"},\n" + - " \"uno\",\n" + - " [\n" + - " \"a\",\n" + - " {\n" + - " \"mi\": 2,\n" + - " \"re\": 1\n" + - " },\n" + - " \"dos\",\n" + - " [\n" + - " \"b\",\n" + - " {\"fa\": 3}\n" + - " ],\n" + - " \"tres\",\n" + - " [\n" + - " \"c\",\n" + - " true\n" + - " ],\n" + - " \"quatro\"\n" + - " ],\n" + - " \"cinqo\",\n" + - " [\n" + - " \"d\",\n" + - " \"seis\",\n" + - " [\"e\"]\n" + - " ]\n" + - "]", ja.toString(4)); - assertEquals("unodostresquatrocinqoseis", - JSONML.toString(ja)); - - string = " "; - jsonobject = XML.toJSONObject(string); - - assertEquals("{\"mapping\": {\n" + - " \"class\": [\n" + - " {\n" + - " \"field\": [\n" + - " {\n" + - " \"bind-xml\": {\n" + - " \"name\": \"ID\",\n" + - " \"node\": \"attribute\"\n" + - " },\n" + - " \"name\": \"ID\",\n" + - " \"type\": \"string\"\n" + - " },\n" + - " {\n" + - " \"name\": \"FirstName\",\n" + - " \"type\": \"FirstName\"\n" + - " },\n" + - " {\n" + - " \"name\": \"MI\",\n" + - " \"type\": \"MI\"\n" + - " },\n" + - " {\n" + - " \"name\": \"LastName\",\n" + - " \"type\": \"LastName\"\n" + - " }\n" + - " ],\n" + - " \"name\": \"Customer\"\n" + - " },\n" + - " {\n" + - " \"field\": {\n" + - " \"bind-xml\": {\n" + - " \"name\": \"text\",\n" + - " \"node\": \"text\"\n" + - " },\n" + - " \"name\": \"text\"\n" + - " },\n" + - " \"name\": \"FirstName\"\n" + - " },\n" + - " {\n" + - " \"field\": {\n" + - " \"bind-xml\": {\n" + - " \"name\": \"text\",\n" + - " \"node\": \"text\"\n" + - " },\n" + - " \"name\": \"text\"\n" + - " },\n" + - " \"name\": \"MI\"\n" + - " },\n" + - " {\n" + - " \"field\": {\n" + - " \"bind-xml\": {\n" + - " \"name\": \"text\",\n" + - " \"node\": \"text\"\n" + - " },\n" + - " \"name\": \"text\"\n" + - " },\n" + - " \"name\": \"LastName\"\n" + - " }\n" + - " ],\n" + - " \"empty\": \"\"\n" + - "}}", jsonobject.toString(2)); - assertEquals("attributeIDIDstringFirstNameFirstNameMIMILastNameLastNameCustomertexttexttextFirstNametexttexttextMItexttexttextLastName", - XML.toString(jsonobject)); - ja = JSONML.toJSONArray(string); - assertEquals("[\n" + - " \"mapping\",\n" + - " [\"empty\"],\n" + - " [\n" + - " \"class\",\n" + - " {\"name\": \"Customer\"},\n" + - " [\n" + - " \"field\",\n" + - " {\n" + - " \"name\": \"ID\",\n" + - " \"type\": \"string\"\n" + - " },\n" + - " [\n" + - " \"bind-xml\",\n" + - " {\n" + - " \"name\": \"ID\",\n" + - " \"node\": \"attribute\"\n" + - " }\n" + - " ]\n" + - " ],\n" + - " [\n" + - " \"field\",\n" + - " {\n" + - " \"name\": \"FirstName\",\n" + - " \"type\": \"FirstName\"\n" + - " }\n" + - " ],\n" + - " [\n" + - " \"field\",\n" + - " {\n" + - " \"name\": \"MI\",\n" + - " \"type\": \"MI\"\n" + - " }\n" + - " ],\n" + - " [\n" + - " \"field\",\n" + - " {\n" + - " \"name\": \"LastName\",\n" + - " \"type\": \"LastName\"\n" + - " }\n" + - " ]\n" + - " ],\n" + - " [\n" + - " \"class\",\n" + - " {\"name\": \"FirstName\"},\n" + - " [\n" + - " \"field\",\n" + - " {\"name\": \"text\"},\n" + - " [\n" + - " \"bind-xml\",\n" + - " {\n" + - " \"name\": \"text\",\n" + - " \"node\": \"text\"\n" + - " }\n" + - " ]\n" + - " ]\n" + - " ],\n" + - " [\n" + - " \"class\",\n" + - " {\"name\": \"MI\"},\n" + - " [\n" + - " \"field\",\n" + - " {\"name\": \"text\"},\n" + - " [\n" + - " \"bind-xml\",\n" + - " {\n" + - " \"name\": \"text\",\n" + - " \"node\": \"text\"\n" + - " }\n" + - " ]\n" + - " ]\n" + - " ],\n" + - " [\n" + - " \"class\",\n" + - " {\"name\": \"LastName\"},\n" + - " [\n" + - " \"field\",\n" + - " {\"name\": \"text\"},\n" + - " [\n" + - " \"bind-xml\",\n" + - " {\n" + - " \"name\": \"text\",\n" + - " \"node\": \"text\"\n" + - " }\n" + - " ]\n" + - " ]\n" + - " ]\n" + - "]", ja.toString(4)); - assertEquals("", - JSONML.toString(ja)); - - jsonobject = XML.toJSONObject("Sample BookThis is chapter 1. It is not very long or interesting.This is chapter 2. Although it is longer than chapter 1, it is not any more interesting."); - assertEquals("{\"Book\": {\n" + - " \"Author\": \"Anonymous\",\n" + - " \"Chapter\": [\n" + - " {\n" + - " \"content\": \"This is chapter 1. It is not very long or interesting.\",\n" + - " \"id\": 1\n" + - " },\n" + - " {\n" + - " \"content\": \"This is chapter 2. Although it is longer than chapter 1, it is not any more interesting.\",\n" + - " \"id\": 2\n" + - " }\n" + - " ],\n" + - " \"Title\": \"Sample Book\"\n" + - "}}", jsonobject.toString(2)); - assertEquals("This is chapter 1. It is not very long or interesting.1This is chapter 2. Although it is longer than chapter 1, it is not any more interesting.2AnonymousSample Book", - XML.toString(jsonobject)); - - jsonobject = XML.toJSONObject(""); - assertEquals("{\"bCard\": {\"bCard\": [\n" + - " {\n" + - " \"company\": \"MCI\",\n" + - " \"email\": \"khare@mci.net\",\n" + - " \"firstname\": \"Rohit\",\n" + - " \"homepage\": \"http://pest.w3.org/\",\n" + - " \"lastname\": \"Khare\"\n" + - " },\n" + - " {\n" + - " \"company\": \"Caltech Infospheres Project\",\n" + - " \"email\": \"adam@cs.caltech.edu\",\n" + - " \"firstname\": \"Adam\",\n" + - " \"homepage\": \"http://www.cs.caltech.edu/~adam/\",\n" + - " \"lastname\": \"Rifkin\"\n" + - " }\n" + - "]}}", jsonobject.toString(2)); - assertEquals("khare@mci.netMCIKhareRohithttp://pest.w3.org/adam@cs.caltech.eduCaltech Infospheres ProjectRifkinAdamhttp://www.cs.caltech.edu/~adam/", - XML.toString(jsonobject)); - - jsonobject = XML.toJSONObject(" Fred fbs0001 Scerbo B "); - assertEquals("{\"customer\": {\n" + - " \"ID\": \"fbs0001\",\n" + - " \"MI\": {\"text\": \"B\"},\n" + - " \"firstName\": {\"text\": \"Fred\"},\n" + - " \"lastName\": {\"text\": \"Scerbo\"}\n" + - "}}", jsonobject.toString(2)); - assertEquals("ScerboBfbs0001Fred", - XML.toString(jsonobject)); - - jsonobject = XML.toJSONObject("Repository Address Special Collections LibraryABC UniversityMain Library, 40 Circle DriveOurtown, Pennsylvania17654 USA"); - assertEquals("{\"list\":{\"item\":[\"Special Collections Library\",\"ABC University\",\"Main Library, 40 Circle Drive\",\"Ourtown, Pennsylvania\",\"17654 USA\"],\"head\":\"Repository Address\",\"type\":\"simple\"}}", - jsonobject.toString()); - assertEquals("Special Collections LibraryABC UniversityMain Library, 40 Circle DriveOurtown, Pennsylvania17654 USARepository Addresssimple", - XML.toString(jsonobject)); - - jsonobject = XML.toJSONObject("deluxe&"toot"&toot;Aeksbonusbonus2"); - assertEquals("{\"test\": {\n" + - " \"blip\": {\n" + - " \"content\": \"&\\\"toot\\\"&toot;A\",\n" + - " \"sweet\": true\n" + - " },\n" + - " \"content\": \"deluxe\",\n" + - " \"empty\": \"\",\n" + - " \"intertag\": \"\",\n" + - " \"status\": \"ok\",\n" + - " \"w\": [\n" + - " \"bonus\",\n" + - " \"bonus2\"\n" + - " ],\n" + - " \"x\": \"eks\"\n" + - "}}", jsonobject.toString(2)); - assertEquals("bonusbonus2deluxeok&"toot"&toot;&#x41;trueeks", - XML.toString(jsonobject)); - - jsonobject = HTTP.toJSONObject("GET / HTTP/1.0\nAccept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*\nAccept-Language: en-us\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90; T312461; Q312461)\nHost: www.nokko.com\nConnection: keep-alive\nAccept-encoding: gzip, deflate\n"); - assertEquals("{\n" + - " \"Accept\": \"image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*\",\n" + - " \"Accept-Language\": \"en-us\",\n" + - " \"Accept-encoding\": \"gzip, deflate\",\n" + - " \"Connection\": \"keep-alive\",\n" + - " \"HTTP-Version\": \"HTTP/1.0\",\n" + - " \"Host\": \"www.nokko.com\",\n" + - " \"Method\": \"GET\",\n" + - " \"Request-URI\": \"/\",\n" + - " \"User-Agent\": \"Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90; T312461; Q312461)\"\n" + - "}", jsonobject.toString(2)); - assertEquals("GET \"/\" HTTP/1.0\r\n" + - "Accept-Language: en-us\r\n" + - "Host: www.nokko.com\r\n" + - "Accept-encoding: gzip, deflate\r\n" + - "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90; T312461; Q312461)\r\n" + - "Connection: keep-alive\r\n" + - "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*\r\n\r\n", - HTTP.toString(jsonobject)); - - jsonobject = HTTP.toJSONObject("HTTP/1.1 200 Oki Doki\nDate: Sun, 26 May 2002 17:38:52 GMT\nServer: Apache/1.3.23 (Unix) mod_perl/1.26\nKeep-Alive: timeout=15, max=100\nConnection: Keep-Alive\nTransfer-Encoding: chunked\nContent-Type: text/html\n"); - assertEquals("{\n" + - " \"Connection\": \"Keep-Alive\",\n" + - " \"Content-Type\": \"text/html\",\n" + - " \"Date\": \"Sun, 26 May 2002 17:38:52 GMT\",\n" + - " \"HTTP-Version\": \"HTTP/1.1\",\n" + - " \"Keep-Alive\": \"timeout=15, max=100\",\n" + - " \"Reason-Phrase\": \"Oki Doki\",\n" + - " \"Server\": \"Apache/1.3.23 (Unix) mod_perl/1.26\",\n" + - " \"Status-Code\": \"200\",\n" + - " \"Transfer-Encoding\": \"chunked\"\n" + - "}", jsonobject.toString(2)); - assertEquals("HTTP/1.1 200 Oki Doki\r\n" + - "Transfer-Encoding: chunked\r\n" + - "Date: Sun, 26 May 2002 17:38:52 GMT\r\n" + - "Keep-Alive: timeout=15, max=100\r\n" + - "Content-Type: text/html\r\n" + - "Connection: Keep-Alive\r\n" + - "Server: Apache/1.3.23 (Unix) mod_perl/1.26\r\n\r\n", - HTTP.toString(jsonobject)); - - jsonobject = new JSONObject("{nix: null, nux: false, null: 'null', 'Request-URI': '/', Method: 'GET', 'HTTP-Version': 'HTTP/1.0'}"); - assertEquals("{\n" + - " \"HTTP-Version\": \"HTTP/1.0\",\n" + - " \"Method\": \"GET\",\n" + - " \"Request-URI\": \"/\",\n" + - " \"nix\": null,\n" + - " \"null\": \"null\",\n" + - " \"nux\": false\n" + - "}", jsonobject.toString(2)); - assertTrue(jsonobject.isNull("nix")); - assertTrue(jsonobject.has("nix")); - assertEquals("/nullfalseGETHTTP/1.0null", - XML.toString(jsonobject)); - assertEquals("GET \"/\" HTTP/1.0\r\n" + - "nux: false\r\n" + - "null: null\r\n\r\n", HTTP.toString(jsonobject)); - - jsonobject = XML.toJSONObject("" + "\n\n" + "" + - "" + - "GOOGLEKEY '+search+' 0 10 true false latin1 latin1" + - "" + - ""); - - assertEquals("{\"SOAP-ENV:Envelope\": {\n" + - " \"SOAP-ENV:Body\": {\"ns1:doGoogleSearch\": {\n" + - " \"SOAP-ENV:encodingStyle\": \"http://schemas.xmlsoap.org/soap/encoding/\",\n" + - " \"filter\": {\n" + - " \"content\": true,\n" + - " \"xsi:type\": \"xsd:boolean\"\n" + - " },\n" + - " \"ie\": {\n" + - " \"content\": \"latin1\",\n" + - " \"xsi:type\": \"xsd:string\"\n" + - " },\n" + - " \"key\": {\n" + - " \"content\": \"GOOGLEKEY\",\n" + - " \"xsi:type\": \"xsd:string\"\n" + - " },\n" + - " \"lr\": {\"xsi:type\": \"xsd:string\"},\n" + - " \"maxResults\": {\n" + - " \"content\": 10,\n" + - " \"xsi:type\": \"xsd:int\"\n" + - " },\n" + - " \"oe\": {\n" + - " \"content\": \"latin1\",\n" + - " \"xsi:type\": \"xsd:string\"\n" + - " },\n" + - " \"q\": {\n" + - " \"content\": \"'+search+'\",\n" + - " \"xsi:type\": \"xsd:string\"\n" + - " },\n" + - " \"restrict\": {\"xsi:type\": \"xsd:string\"},\n" + - " \"safeSearch\": {\n" + - " \"content\": false,\n" + - " \"xsi:type\": \"xsd:boolean\"\n" + - " },\n" + - " \"start\": {\n" + - " \"content\": \"0\",\n" + - " \"xsi:type\": \"xsd:int\"\n" + - " },\n" + - " \"xmlns:ns1\": \"urn:GoogleSearch\"\n" + - " }},\n" + - " \"xmlns:SOAP-ENV\": \"http://schemas.xmlsoap.org/soap/envelope/\",\n" + - " \"xmlns:xsd\": \"http://www.w3.org/1999/XMLSchema\",\n" + - " \"xmlns:xsi\": \"http://www.w3.org/1999/XMLSchema-instance\"\n" + - "}}", jsonobject.toString(2)); - - assertEquals("latin1xsd:stringhttp://schemas.xmlsoap.org/soap/encoding/xsd:string0xsd:int'+search+'xsd:stringlatin1xsd:stringfalsexsd:booleanurn:GoogleSearchxsd:stringtruexsd:boolean10xsd:intGOOGLEKEYxsd:stringhttp://www.w3.org/1999/XMLSchemahttp://www.w3.org/1999/XMLSchema-instancehttp://schemas.xmlsoap.org/soap/envelope/", - XML.toString(jsonobject)); - - jsonobject = new JSONObject("{Envelope: {Body: {\"ns1:doGoogleSearch\": {oe: \"latin1\", filter: true, q: \"'+search+'\", key: \"GOOGLEKEY\", maxResults: 10, \"SOAP-ENV:encodingStyle\": \"http://schemas.xmlsoap.org/soap/encoding/\", start: 0, ie: \"latin1\", safeSearch:false, \"xmlns:ns1\": \"urn:GoogleSearch\"}}}}"); - assertEquals("{\"Envelope\": {\"Body\": {\"ns1:doGoogleSearch\": {\n" + - " \"SOAP-ENV:encodingStyle\": \"http://schemas.xmlsoap.org/soap/encoding/\",\n" + - " \"filter\": true,\n" + - " \"ie\": \"latin1\",\n" + - " \"key\": \"GOOGLEKEY\",\n" + - " \"maxResults\": 10,\n" + - " \"oe\": \"latin1\",\n" + - " \"q\": \"'+search+'\",\n" + - " \"safeSearch\": false,\n" + - " \"start\": 0,\n" + - " \"xmlns:ns1\": \"urn:GoogleSearch\"\n" + - "}}}}", jsonobject.toString(2)); - assertEquals("latin1http://schemas.xmlsoap.org/soap/encoding/0'+search+'latin1falseurn:GoogleSearch10GOOGLEKEYtrue", - XML.toString(jsonobject)); - - jsonobject = CookieList.toJSONObject(" f%oo = b+l=ah ; o;n%40e = t.wo "); - assertEquals("{\n" + - " \"f%oo\": \"b l=ah\",\n" + - " \"o;n@e\": \"t.wo\"\n" + - "}", jsonobject.toString(2)); - assertEquals("o%3bn@e=t.wo;f%25oo=b l%3dah", - CookieList.toString(jsonobject)); - - jsonobject = Cookie.toJSONObject("f%oo=blah; secure ;expires = April 24, 2002"); - assertEquals("{\n" + - " \"expires\": \"April 24, 2002\",\n" + - " \"name\": \"f%oo\",\n" + - " \"secure\": true,\n" + - " \"value\": \"blah\"\n" + - "}", jsonobject.toString(2)); - assertEquals("f%25oo=blah;expires=April 24, 2002;secure", - Cookie.toString(jsonobject)); - - jsonobject = new JSONObject("{script: 'It is not allowed in HTML to send a close script tag in a stringso we insert a backslash before the /'}"); - assertEquals("{\"script\":\"It is not allowed in HTML to send a close script tag in a string