diff --git a/LICENSE.txt b/LICENSE.txt index 2fc56c5c7dd18fc24a6128f7368ffc333ff921c6..4ddcfc7f0f4845abcf3fd7ebdb025036cdab2dda 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -89,8 +89,9 @@ Public domain except as listed below: From Apache HttpClient 4.4.1 and HttpCore 4.4.1 See licenses/LICENSE-Apache2.0.txt - json-simple 1.1.1 + json-simple 2.3.0 (not included in most distribution packages) + Copyright 2016 Clifton Labs See licenses/LICENSE-Apache2.0.txt Noise library: diff --git a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Error.java b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Error.java index 9dd81c75b9098bed8c1bf97ba16a1e5b1832d623..dceb9c39af2727470334fb1cf4bf19ce5922953e 100644 --- a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Error.java +++ b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Error.java @@ -1,7 +1,7 @@ package com.thetransactioncompany.jsonrpc2; -import org.json.simple.JSONObject; +import org.json.simple.JsonObject; /** @@ -220,7 +220,7 @@ public class JSONRPC2Error extends Exception { * @see #toJSONObject */ @Deprecated - public JSONObject toJSON() { + public JsonObject toJSON() { return toJSONObject(); } @@ -231,9 +231,9 @@ public class JSONRPC2Error extends Exception { * * @return A JSON object representing this error object. */ - public JSONObject toJSONObject() { + public JsonObject toJSONObject() { - JSONObject out = new JSONObject(); + JsonObject out = new JsonObject(); out.put("code", code); out.put("message", super.getMessage()); diff --git a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Message.java b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Message.java index 71cd416df7c3de2bb4f2df6706f958a960021ef5..53bb8b53169b08a88a806437a69aaa5c251cfaf2 100644 --- a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Message.java +++ b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Message.java @@ -5,8 +5,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.json.simple.JSONAware; -import org.json.simple.JSONObject; +import org.json.simple.JsonObject; /** @@ -54,7 +53,7 @@ import org.json.simple.JSONObject; * * @author Vladimir Dzhuvinov */ -public abstract class JSONRPC2Message implements JSONAware { +public abstract class JSONRPC2Message { /** @@ -220,7 +219,7 @@ public abstract class JSONRPC2Message implements JSONAware { * * @return The JSON object. */ - public abstract JSONObject toJSONObject(); + public abstract JsonObject toJSONObject(); /** @@ -246,6 +245,6 @@ public abstract class JSONRPC2Message implements JSONAware { @Override public String toString() { - return toJSONObject().toString(); + return toJSONObject().toJson(); } } diff --git a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Notification.java b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Notification.java index ae2d66b4d13462df69ebd2ff6b0ec70d48dac294..8e896ee938c91b370170ac72ef0ffa2c047fc31d 100644 --- a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Notification.java +++ b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Notification.java @@ -4,7 +4,7 @@ package com.thetransactioncompany.jsonrpc2; import java.util.List; import java.util.Map; -import org.json.simple.JSONObject; +import org.json.simple.JsonObject; /** @@ -414,9 +414,9 @@ public class JSONRPC2Notification extends JSONRPC2Message { @Override - public JSONObject toJSONObject() { + public JsonObject toJSONObject() { - JSONObject notf = new JSONObject(); + JsonObject notf = new JsonObject(); notf.put("method", method); diff --git a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Parser.java b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Parser.java index 4f1a26640c8e99d7d07724024f9c90bb2381955a..c27524ca990e09b5f001836e1a30d3898da4ca32 100644 --- a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Parser.java +++ b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Parser.java @@ -4,9 +4,8 @@ package com.thetransactioncompany.jsonrpc2; import java.util.List; import java.util.Map; -import org.json.simple.parser.ContainerFactory; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +import org.json.simple.Jsoner; +import org.json.simple.DeserializationException; /** @@ -56,12 +55,6 @@ import org.json.simple.parser.ParseException; public class JSONRPC2Parser { - /** - * Reusable JSON parser. Not thread-safe! - */ - private final JSONParser parser; - - /** * If {@code true} the order of the parsed JSON object members must be * preserved. @@ -153,10 +146,7 @@ public class JSONRPC2Parser { public JSONRPC2Parser(final boolean preserveOrder, final boolean ignoreVersion, final boolean parseNonStdAttributes) { - - // Numbers parsed as long/double, requires JSON Smart 1.0.9+ - parser = new JSONParser(); - + this.preserveOrder = preserveOrder; this.ignoreVersion = ignoreVersion; this.parseNonStdAttributes = parseNonStdAttributes; @@ -189,13 +179,9 @@ public class JSONRPC2Parser { // Parse the JSON string try { - //if (preserveOrder) - // json = parser.parse(jsonString, ContainerFactory.FACTORY_ORDERED); - - //else - json = parser.parse(jsonString); + json = Jsoner.deserialize(jsonString); - } catch (ParseException e) { + } catch (DeserializationException e) { // Terse message, do not include full parse exception message throw new JSONRPC2ParseException("Invalid JSON", diff --git a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Request.java b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Request.java index e687b47cbf921a05a0f9459fcd4309c56be14f5d..31695d837feea2544c912d4c3e3a8f6ef1a107fb 100644 --- a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Request.java +++ b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Request.java @@ -4,7 +4,7 @@ package com.thetransactioncompany.jsonrpc2; import java.util.List; import java.util.Map; -import org.json.simple.JSONObject; +import org.json.simple.JsonObject; /** @@ -472,9 +472,9 @@ public class JSONRPC2Request extends JSONRPC2Message { @Override - public JSONObject toJSONObject() { + public JsonObject toJSONObject() { - JSONObject req = new JSONObject(); + JsonObject req = new JsonObject(); req.put("method", method); diff --git a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Response.java b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Response.java index 5ef94fb0524162d9af10c7da6ded95dbfe023f51..ec58883b8c8ba4bb08ea5a1576c5ded5e7968333 100644 --- a/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Response.java +++ b/apps/i2pcontrol/java/com/thetransactioncompany/jsonrpc2/JSONRPC2Response.java @@ -3,7 +3,7 @@ package com.thetransactioncompany.jsonrpc2; import java.util.Map; -import org.json.simple.JSONObject; +import org.json.simple.JsonObject; /** @@ -384,9 +384,9 @@ public class JSONRPC2Response extends JSONRPC2Message { @Override - public JSONObject toJSONObject() { + public JsonObject toJSONObject() { - JSONObject out = new JSONObject(); + JsonObject out = new JsonObject(); // Result and error are mutually exclusive if (error != null) { diff --git a/apps/i2pcontrol/java/net/i2p/i2pcontrol/SocketController.java b/apps/i2pcontrol/java/net/i2p/i2pcontrol/SocketController.java index b2e5ca5d07561538864f2b8241b84f5b838d7406..bd518491c8397ec0c0e1675888707c3e81fb068f 100644 --- a/apps/i2pcontrol/java/net/i2p/i2pcontrol/SocketController.java +++ b/apps/i2pcontrol/java/net/i2p/i2pcontrol/SocketController.java @@ -37,8 +37,8 @@ import net.i2p.util.I2PSSLSocketFactory; import net.i2p.util.Log; import net.i2p.util.PortMapper; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +import org.json.simple.Jsoner; +import org.json.simple.DeserializationException; import net.i2p.i2pcontrol.security.KeyStoreProvider; import net.i2p.i2pcontrol.security.SecurityManager; @@ -194,13 +194,12 @@ public class SocketController implements RouterApp { public void run() { try { final BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8")); - final JSONParser parser = new JSONParser(); while (true) { - Object o = parser.parse(reader); + Object o = Jsoner.deserialize(reader); // TODO System.out.println("i2pcontrol got: " + o); } - } catch (ParseException pe) { + } catch (DeserializationException pe) { _log.error("i2pcontrol handler", pe); return; } catch (IOException ioe) { diff --git a/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AuthenticateHandler.java b/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AuthenticateHandler.java index a7f2f60a6a3587f0f5998019f659f3c3da9a4eea..a8259f4eff1d0f0283bb7e8883516cd261f9a27d 100644 --- a/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AuthenticateHandler.java +++ b/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/AuthenticateHandler.java @@ -86,7 +86,7 @@ public class AuthenticateHandler implements RequestHandler { Integer apiVersion; try { - apiVersion = ((Long) api).intValue(); + apiVersion = ((Number) api).intValue(); } catch (ClassCastException e) { e.printStackTrace(); return JSONRPC2ExtendedError.UNSPECIFIED_API_VERSION; diff --git a/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/GetRateHandler.java b/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/GetRateHandler.java index a484ce38d26db57ae6ea2fff10b9f9de7d1d9782..db07855e0817b496205c409a3b7acc74d1c13ad4 100644 --- a/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/GetRateHandler.java +++ b/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/GetRateHandler.java @@ -56,12 +56,10 @@ public class GetRateHandler implements RequestHandler { if (input == null) { return new JSONRPC2Response(JSONRPC2Error.INVALID_PARAMS, req.getID()); } - long period; - try { - period = (Long) inParams.get("Period"); - } catch (NumberFormatException e) { + Number p = (Number) inParams.get("Period"); + if (p == null) return new JSONRPC2Response(JSONRPC2Error.INVALID_PARAMS, req.getID()); - } + long period = p.longValue(); RateStat rateStat = I2PAppContext.getGlobalContext().statManager().getRate(input); diff --git a/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/JSONRPC2ExtendedError.java b/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/JSONRPC2ExtendedError.java index 43de081726f47badd5849834ba7e715d1b66a89f..e4f2b5f855a1d16ce9aa4a8581d32199d8457ebb 100644 --- a/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/JSONRPC2ExtendedError.java +++ b/apps/i2pcontrol/java/net/i2p/i2pcontrol/servlets/jsonrpc2handlers/JSONRPC2ExtendedError.java @@ -1,7 +1,6 @@ package net.i2p.i2pcontrol.servlets.jsonrpc2handlers; import com.thetransactioncompany.jsonrpc2.JSONRPC2Error; -import org.json.simple.JSONObject; /* * Copyright 2011 hottuna (dev@robertfoss.se) diff --git a/apps/routerconsole/java/src/com/vuze/plugins/mlab/MLabRunner.java b/apps/routerconsole/java/src/com/vuze/plugins/mlab/MLabRunner.java index d1c13cb2ce4e9066e65e0b7d371cbd4963aa56cc..858b5289915e78398d9bb4013802f8a9abde3fa7 100644 --- a/apps/routerconsole/java/src/com/vuze/plugins/mlab/MLabRunner.java +++ b/apps/routerconsole/java/src/com/vuze/plugins/mlab/MLabRunner.java @@ -30,9 +30,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import edu.internet2.ndt.Tcpbw100; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +import org.json.simple.JsonObject; +import org.json.simple.Jsoner; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; @@ -153,10 +152,9 @@ public class MLabRunner { int code = eepget.getStatusCode(); if (code != 200) throw new IOException("ns fetch failed: " + code); - JSONParser parser = new JSONParser(); byte[] b = baos.toByteArray(); String s = new String(b, "ISO-8859-1"); - JSONObject map = (JSONObject) parser.parse(s); + JsonObject map = (JsonObject) Jsoner.deserialize(s); if (map == null) { throw new IOException("no map"); } diff --git a/apps/routerconsole/java/src/edu/internet2/ndt/JSONUtils.java b/apps/routerconsole/java/src/edu/internet2/ndt/JSONUtils.java index 267e66d342e8ff5c4d20b48624b6e0e7376164b1..c386414c1b8b71f42e3f1c65e9e4af6bf36a67e0 100644 --- a/apps/routerconsole/java/src/edu/internet2/ndt/JSONUtils.java +++ b/apps/routerconsole/java/src/edu/internet2/ndt/JSONUtils.java @@ -1,7 +1,7 @@ package edu.internet2.ndt; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; +import org.json.simple.JsonObject; +import org.json.simple.Jsoner; import java.util.Iterator; import java.util.Map; @@ -27,8 +27,7 @@ public class JSONUtils { * @return {int} obtained value from JSON map */ public static String getValueFromJsonObj(String jsonTxt, String key) { - JSONValue jsonParser = new JSONValue(); - Map json = (Map)jsonParser.parse(new String(jsonTxt)); + Map json = (Map)Jsoner.deserialize(new String(jsonTxt), (JsonObject)null); if (json == null) return null; Iterator iter = json.entrySet().iterator(); @@ -49,11 +48,12 @@ public class JSONUtils { * @return {String} json object with added value. */ public static String addValueToJsonObj(String jsonTxt, String key, String value) { - JSONValue jsonParser = new JSONValue(); - JSONObject json = (JSONObject)jsonParser.parse(new String(jsonTxt)); + JsonObject json = Jsoner.deserialize(new String(jsonTxt), (JsonObject)null); + if (json == null) + json = new JsonObject(); json.put(key, value); - return json.toJSONString(); + return json.toJson(); } /** @@ -63,10 +63,10 @@ public class JSONUtils { * @return {byte[]} json object represented by jsontext and encodes into a sequence of bytes */ public static byte[] createJsonObj(byte[] msg) { - JSONObject obj = new JSONObject(); + JsonObject obj = new JsonObject(); obj.put("msg", new String(msg)); - return obj.toJSONString().getBytes(); + return obj.toJson().getBytes(); } /** @@ -77,9 +77,9 @@ public class JSONUtils { * @since 0.9.45 */ public static byte[] createJsonLoginObj(byte[] msg, byte tests) { - JSONObject obj = new JSONObject(); + JsonObject obj = new JsonObject(); obj.put("msg", new String(msg)); obj.put("tests", Integer.toString(tests & 0xff)); - return obj.toJSONString().getBytes(); + return obj.toJson().getBytes(); } } diff --git a/build.xml b/build.xml index 1dc6aa5f1ffffc28d171793dee11bc01928a0a77..09721827de3d9c6942eddf5e9ad3fd17962fa160 100644 --- a/build.xml +++ b/build.xml @@ -880,7 +880,7 @@ additionalparam="-notimestamp" doctitle="I2P Javadocs for Release ${release.number} Build ${i2p.build.number}${build.extra}" windowtitle="I2P Anonymous Network - Java Documentation - Version ${release.number}"> - <group title="Core SDK (i2p.jar)" packages="net.i2p:net.i2p.*:net.i2p.client:net.i2p.client.*:net.i2p.internal:net.i2p.internal.*:freenet.support.CPUInformation:gnu.crypto.*:gnu.getopt:gnu.gettext:com.nettgryppa.security:org.apache.http.conn.ssl:org.apache.http.conn.util:org.apache.http.util:org.json.simple:org.json.simple.*:com.southernstorm.noise.crypto.x25519:com.southernstorm.noise.crypto.chacha20" /> + <group title="Core SDK (i2p.jar)" packages="net.i2p:net.i2p.*:net.i2p.client:net.i2p.client.*:net.i2p.internal:net.i2p.internal.*:freenet.support.CPUInformation:gnu.crypto.*:gnu.getopt:gnu.gettext:com.nettgryppa.security:org.apache.http.conn.ssl:org.apache.http.conn.util:org.apache.http.util:org.json.simple:org.json.simple:com.southernstorm.noise.crypto.x25519:com.southernstorm.noise.crypto.chacha20" /> <group title="Streaming Library" packages="net.i2p.client.streaming:net.i2p.client.streaming.impl" /> <group title="Router" packages="net.i2p.router:net.i2p.router.*:net.i2p.data.i2np:net.i2p.data.router:org.cybergarage:org.cybergarage.*:org.freenetproject:org.xlattice.crypto.filters:com.maxmind.*:com.southernstorm.noise.*" /> <group title="Router Console" packages="net.i2p.router.web:net.i2p.router.web.*:net.i2p.router.update:net.i2p.router.sybil:edu.internet2.ndt:net.i2p.router.news:com.vuze.*" /> @@ -2876,8 +2876,8 @@ <file name="../i2p-${Extended.Version}/settings.gradle" /> <fileset dir="../i2p-${Extended.Version}/core/java/src/gnu/gettext" /> <fileset dir="../i2p-${Extended.Version}/core/java/src/gnu/getopt" /> - <fileset dir="../i2p-${Extended.Version}/core/java/src/org/json" /> <!-- + <fileset dir="../i2p-${Extended.Version}/core/java/src/org/json" /> <fileset dir="../i2p-${Extended.Version}/core/java/src/org/apache/http" /> --> <file name="../i2p-${Extended.Version}/installer/resources/geoip.txt" /> diff --git a/core/java/src/net/i2p/util/DNSOverHTTPS.java b/core/java/src/net/i2p/util/DNSOverHTTPS.java index b3dcf1ac71cb6994965ef2eb9a049b67cba2b2c0..94b56a8cba6a3752079c327562e55422a2ade009 100644 --- a/core/java/src/net/i2p/util/DNSOverHTTPS.java +++ b/core/java/src/net/i2p/util/DNSOverHTTPS.java @@ -12,9 +12,9 @@ import java.util.Map; import gnu.getopt.Getopt; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; +import org.json.simple.JsonArray; +import org.json.simple.JsonObject; +import org.json.simple.Jsoner; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; @@ -31,7 +31,6 @@ import net.i2p.data.DataHelper; public class DNSOverHTTPS implements EepGet.StatusListener { private final I2PAppContext ctx; private final Log _log; - private final JSONParser parser; private final ByteArrayOutputStream baos; private SSLEepGet.SSLState state; private long fetchStart; @@ -105,7 +104,6 @@ public class DNSOverHTTPS implements EepGet.StatusListener { _log = ctx.logManager().getLog(DNSOverHTTPS.class); state = sslState; baos = new ByteArrayOutputStream(512); - parser = new JSONParser(); } public enum Type { V4_ONLY, V6_ONLY, V4_PREFERRED, V6_PREFERRED } @@ -263,7 +261,7 @@ public class DNSOverHTTPS implements EepGet.StatusListener { byte[] b = baos.toByteArray(); try { String s = new String(b, "ISO-8859-1"); - JSONObject map = (JSONObject) parser.parse(s); + JsonObject map = (JsonObject) Jsoner.deserialize(s); if (map == null) { log("No map"); return null; @@ -273,7 +271,7 @@ public class DNSOverHTTPS implements EepGet.StatusListener { log("Bad status: " + status); return null; } - JSONArray list = (JSONArray) map.get("Answer"); + JsonArray list = (JsonArray) map.get("Answer"); if (list == null || list.isEmpty()) { log("No answer"); return null; @@ -282,7 +280,7 @@ public class DNSOverHTTPS implements EepGet.StatusListener { String hostAnswer = host + '.'; for (Object o : list) { try { - JSONObject a = (JSONObject) o; + JsonObject a = (JsonObject) o; String data = (String) a.get("data"); if (data == null) { log("no data"); diff --git a/core/java/src/org/json/simple/DeserializationException.java b/core/java/src/org/json/simple/DeserializationException.java new file mode 100644 index 0000000000000000000000000000000000000000..f215cdec133726529c766a1f5aba8a9445c87daf --- /dev/null +++ b/core/java/src/org/json/simple/DeserializationException.java @@ -0,0 +1,93 @@ +/* Copyright 2016-2017 Clifton Labs + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ +package org.json.simple; + +/** DeserializationException explains how and where the problem occurs in the source JSON text during deserialization. + * @since 2.0.0 */ +public class DeserializationException extends Exception{ + /** The kinds of exceptions that can trigger a DeserializationException. */ + public enum Problems{ + @SuppressWarnings("javadoc") + DISALLOWED_TOKEN, + /** @since 2.3.0 to consolidate exceptions that occur during deserialization. */ + IOEXCEPTION, + @SuppressWarnings("javadoc") + UNEXPECTED_CHARACTER, + @SuppressWarnings("javadoc") + UNEXPECTED_EXCEPTION, + @SuppressWarnings("javadoc") + UNEXPECTED_TOKEN; + } + + private static final long serialVersionUID = 1L; + private final int position; + private final Problems problemType; + private final Object unexpectedObject; + + /** Instantiates a DeserializationException without assumptions. + * @param position where the exception occurred. + * @param problemType how the exception occurred. + * @param unexpectedObject what caused the exception. */ + public DeserializationException(final int position, final Problems problemType, final Object unexpectedObject){ + this.position = position; + this.problemType = problemType; + this.unexpectedObject = unexpectedObject; + if(Problems.IOEXCEPTION.equals(problemType) || Problems.UNEXPECTED_EXCEPTION.equals(problemType)){ + if(unexpectedObject instanceof Throwable){ + this.initCause((Throwable)unexpectedObject); + } + } + } + + @Override + public String getMessage(){ + final StringBuilder sb = new StringBuilder(); + switch(this.problemType){ + case DISALLOWED_TOKEN: + sb.append("The disallowed token (").append(this.unexpectedObject).append(") was found at position ").append(this.position).append(". If this is in error, try again with a parse that allows the token instead. Otherwise, fix the parsable string and try again."); + break; + case IOEXCEPTION: + sb.append("An IOException was encountered, ensure the reader is properly instantiated, isn't closed, or that it is ready before trying again.\n").append(this.unexpectedObject); + break; + case UNEXPECTED_CHARACTER: + sb.append("The unexpected character (").append(this.unexpectedObject).append(") was found at position ").append(this.position).append(". Fix the parsable string and try again."); + break; + case UNEXPECTED_TOKEN: + sb.append("The unexpected token ").append(this.unexpectedObject).append(" was found at position ").append(this.position).append(". Fix the parsable string and try again."); + break; + case UNEXPECTED_EXCEPTION: + sb.append("Please report this to the library's maintainer. The unexpected exception that should be addressed before trying again occurred at position ").append(this.position).append(":\n").append(this.unexpectedObject); + break; + default: + sb.append("Please report this to the library's maintainer. An error at position ").append(this.position).append(" occurred. There are no recovery recommendations available."); + break; + } + return sb.toString(); + } + + /** Helps debug the location of a problem. + * @return an index of the string character the error type occurred at. */ + public int getPosition(){ + return this.position; + } + + /** Helps find an appropriate solution for a problem. + * @return the enumeration for how the exception occurred. */ + public Problems getProblemType(){ + return this.problemType; + } + + /** Helps identify the problem. + * @return a representation of what caused the exception. */ + public Object getUnexpectedObject(){ + return this.unexpectedObject; + } +} diff --git a/core/java/src/org/json/simple/ItemList.java b/core/java/src/org/json/simple/ItemList.java deleted file mode 100644 index a9504f058cb1b158283b7c370d4afd3f75b65221..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/ItemList.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * $Id: ItemList.java,v 1.1 2006/04/15 14:10:48 platform Exp $ - * Created on 2006-3-24 - */ -package org.json.simple; - -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; - -/** - * |a:b:c| => |a|,|b|,|c| - * |:| => ||,|| - * |a:| => |a|,|| - * @author FangYidong fangyidong@yahoo.com.cn - */ -public class ItemList { - private String sp=","; - List items=new ArrayList(); - - - public ItemList(){} - - - public ItemList(String s){ - this.split(s,sp,items); - } - - public ItemList(String s,String sp){ - this.sp=s; - this.split(s,sp,items); - } - - public ItemList(String s,String sp,boolean isMultiToken){ - split(s,sp,items,isMultiToken); - } - - public List getItems(){ - return this.items; - } - - public String[] getArray(){ - return (String[])this.items.toArray(); - } - - public void split(String s,String sp,List append,boolean isMultiToken){ - if(s==null || sp==null) - return; - if(isMultiToken){ - StringTokenizer tokens=new StringTokenizer(s,sp); - while(tokens.hasMoreTokens()){ - append.add(tokens.nextToken().trim()); - } - } - else{ - this.split(s,sp,append); - } - } - - public void split(String s,String sp,List append){ - if(s==null || sp==null) - return; - int pos=0; - int prevPos=0; - do{ - prevPos=pos; - pos=s.indexOf(sp,pos); - if(pos==-1) - break; - append.add(s.substring(prevPos,pos).trim()); - pos+=sp.length(); - }while(pos!=-1); - append.add(s.substring(prevPos).trim()); - } - - public void setSP(String sp){ - this.sp=sp; - } - - public void add(int i,String item){ - if(item==null) - return; - items.add(i,item.trim()); - } - - public void add(String item){ - if(item==null) - return; - items.add(item.trim()); - } - - public void addAll(ItemList list){ - items.addAll(list.items); - } - - public void addAll(String s){ - this.split(s,sp,items); - } - - public void addAll(String s,String sp){ - this.split(s,sp,items); - } - - public void addAll(String s,String sp,boolean isMultiToken){ - this.split(s,sp,items,isMultiToken); - } - - /** - * @param i 0-based - * @return the value - */ - public String get(int i){ - return (String)items.get(i); - } - - public int size(){ - return items.size(); - } - - public String toString(){ - return toString(sp); - } - - public String toString(String sp){ - StringBuffer sb=new StringBuffer(); - - for(int i=0;i<items.size();i++){ - if(i==0) - sb.append(items.get(i)); - else{ - sb.append(sp); - sb.append(items.get(i)); - } - } - return sb.toString(); - - } - - public void clear(){ - items.clear(); - } - - public void reset(){ - sp=","; - items.clear(); - } -} diff --git a/core/java/src/org/json/simple/JSONArray.java b/core/java/src/org/json/simple/JSONArray.java deleted file mode 100644 index e484d9e2288ed37c653e10e55589f9c8f83fb375..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/JSONArray.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * $Id: JSONArray.java,v 1.1 2006/04/15 14:10:48 platform Exp $ - * Created on 2006-4-10 - */ -package org.json.simple; - -import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - - -/** - * A JSON array. JSONObject supports java.util.List interface. - * - * @author FangYidong fangyidong@yahoo.com.cn - */ -public class JSONArray extends ArrayList implements List, JSONAware, JSONStreamAware { - private static final long serialVersionUID = 3957988303675231981L; - - /** - * Encode a list into JSON text and write it to out. - * If this list is also a JSONStreamAware or a JSONAware, JSONStreamAware and JSONAware specific behaviours will be ignored at this top level. - * - * @see org.json.simple.JSONValue#writeJSONString(Object, Writer) - * - * @param list - * @param out - */ - public static void writeJSONString(List list, Writer out) throws IOException{ - if(list == null){ - out.write("null"); - return; - } - - boolean first = true; - Iterator iter=list.iterator(); - - out.write('['); - while(iter.hasNext()){ - if(first) - first = false; - else - out.write(','); - - Object value=iter.next(); - if(value == null){ - out.write("null"); - continue; - } - - JSONValue.writeJSONString(value, out); - } - out.write(']'); - } - - public void writeJSONString(Writer out) throws IOException{ - writeJSONString(this, out); - } - - /** - * Convert a list to JSON text. The result is a JSON array. - * If this list is also a JSONAware, JSONAware specific behaviours will be omitted at this top level. - * - * @see org.json.simple.JSONValue#toJSONString(Object) - * - * @param list - * @return JSON text, or "null" if list is null. - */ - public static String toJSONString(List list){ - if(list == null) - return "null"; - - boolean first = true; - StringBuffer sb = new StringBuffer(); - Iterator iter=list.iterator(); - - sb.append('['); - while(iter.hasNext()){ - if(first) - first = false; - else - sb.append(','); - - Object value=iter.next(); - if(value == null){ - sb.append("null"); - continue; - } - sb.append(JSONValue.toJSONString(value)); - } - sb.append(']'); - return sb.toString(); - } - - public String toJSONString(){ - return toJSONString(this); - } - - public String toString() { - return toJSONString(); - } - - - -} diff --git a/core/java/src/org/json/simple/JSONAware.java b/core/java/src/org/json/simple/JSONAware.java deleted file mode 100644 index b92771113e7a9660a1bcb9d34763858e8d45ef3c..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/JSONAware.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.json.simple; - -/** - * Beans that support customized output of JSON text shall implement this interface. - * @author FangYidong fangyidong@yahoo.com.cn - */ -public interface JSONAware { - /** - * @return JSON text - */ - String toJSONString(); -} diff --git a/core/java/src/org/json/simple/JSONObject.java b/core/java/src/org/json/simple/JSONObject.java deleted file mode 100644 index 60eeecbb2c18865313b0d85553700469e2e0b0fe..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/JSONObject.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * $Id: JSONObject.java,v 1.1 2006/04/15 14:10:48 platform Exp $ - * Created on 2006-4-10 - */ -package org.json.simple; - -import java.io.IOException; -import java.io.Writer; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -/** - * A JSON object. Key value pairs are unordered. JSONObject supports java.util.Map interface. - * - * @author FangYidong fangyidong@yahoo.com.cn - */ -public class JSONObject extends HashMap implements Map, JSONAware, JSONStreamAware{ - - private static final long serialVersionUID = -503443796854799292L; - - - public JSONObject() { - super(); - } - - /** - * Allows creation of a JSONObject from a Map. After that, both the - * generated JSONObject and the Map can be modified independently. - * - * @param map - */ - public JSONObject(Map map) { - super(map); - } - - - /** - * Encode a map into JSON text and write it to out. - * If this map is also a JSONAware or JSONStreamAware, JSONAware or JSONStreamAware specific behaviours will be ignored at this top level. - * - * @see org.json.simple.JSONValue#writeJSONString(Object, Writer) - * - * @param map - * @param out - */ - public static void writeJSONString(Map map, Writer out) throws IOException { - if(map == null){ - out.write("null"); - return; - } - - boolean first = true; - Iterator iter=map.entrySet().iterator(); - - out.write('{'); - while(iter.hasNext()){ - if(first) - first = false; - else - out.write(','); - Map.Entry entry=(Map.Entry)iter.next(); - out.write('\"'); - out.write(escape(String.valueOf(entry.getKey()))); - out.write('\"'); - out.write(':'); - JSONValue.writeJSONString(entry.getValue(), out); - } - out.write('}'); - } - - public void writeJSONString(Writer out) throws IOException{ - writeJSONString(this, out); - } - - /** - * Convert a map to JSON text. The result is a JSON object. - * If this map is also a JSONAware, JSONAware specific behaviours will be omitted at this top level. - * - * @see org.json.simple.JSONValue#toJSONString(Object) - * - * @param map - * @return JSON text, or "null" if map is null. - */ - public static String toJSONString(Map map){ - if(map == null) - return "null"; - - StringBuffer sb = new StringBuffer(); - boolean first = true; - Iterator iter=map.entrySet().iterator(); - - sb.append('{'); - while(iter.hasNext()){ - if(first) - first = false; - else - sb.append(','); - - Map.Entry entry=(Map.Entry)iter.next(); - toJSONString(String.valueOf(entry.getKey()),entry.getValue(), sb); - } - sb.append('}'); - return sb.toString(); - } - - public String toJSONString(){ - return toJSONString(this); - } - - private static String toJSONString(String key,Object value, StringBuffer sb){ - sb.append('\"'); - if(key == null) - sb.append("null"); - else - JSONValue.escape(key, sb); - sb.append('\"').append(':'); - - sb.append(JSONValue.toJSONString(value)); - - return sb.toString(); - } - - public String toString(){ - return toJSONString(); - } - - public static String toString(String key,Object value){ - StringBuffer sb = new StringBuffer(); - toJSONString(key, value, sb); - return sb.toString(); - } - - /** - * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F). - * It's the same as JSONValue.escape() only for compatibility here. - * - * @see org.json.simple.JSONValue#escape(String) - * - * @param s - * @return the value - */ - public static String escape(String s){ - return JSONValue.escape(s); - } -} diff --git a/core/java/src/org/json/simple/JSONStreamAware.java b/core/java/src/org/json/simple/JSONStreamAware.java deleted file mode 100644 index 29e3ce9bfa6b3c920cd3cab87b22cc655e944d87..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/JSONStreamAware.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.json.simple; - -import java.io.IOException; -import java.io.Writer; - -/** - * Beans that support customized output of JSON text to a writer shall implement this interface. - * @author FangYidong fangyidong@yahoo.com.cn - */ -public interface JSONStreamAware { - /** - * write JSON string to out. - */ - void writeJSONString(Writer out) throws IOException; -} diff --git a/core/java/src/org/json/simple/JSONValue.java b/core/java/src/org/json/simple/JSONValue.java deleted file mode 100644 index a236401b721adc0c275a0dd5212f172dc236066a..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/JSONValue.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * $Id: JSONValue.java,v 1.1 2006/04/15 14:37:04 platform Exp $ - * Created on 2006-4-15 - */ -package org.json.simple; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.io.Writer; -import java.util.List; -import java.util.Map; - -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; - - -/** - * @author FangYidong fangyidong@yahoo.com.cn - */ -public class JSONValue { - /** - * Parse JSON text into java object from the input source. - * Please use parseWithException() if you don't want to ignore the exception. - * - * @see org.json.simple.parser.JSONParser#parse(Reader) - * @see #parseWithException(Reader) - * - * @param in - * @return Instance of the following: - * org.json.simple.JSONObject, - * org.json.simple.JSONArray, - * java.lang.String, - * java.lang.Number, - * java.lang.Boolean, - * null - * - */ - public static Object parse(Reader in){ - try{ - JSONParser parser=new JSONParser(); - return parser.parse(in); - } - catch(Exception e){ - return null; - } - } - - public static Object parse(String s){ - StringReader in=new StringReader(s); - return parse(in); - } - - /** - * Parse JSON text into java object from the input source. - * - * @see org.json.simple.parser.JSONParser - * - * @param in - * @return Instance of the following: - * org.json.simple.JSONObject, - * org.json.simple.JSONArray, - * java.lang.String, - * java.lang.Number, - * java.lang.Boolean, - * null - * - * @throws IOException - * @throws ParseException - */ - public static Object parseWithException(Reader in) throws IOException, ParseException{ - JSONParser parser=new JSONParser(); - return parser.parse(in); - } - - public static Object parseWithException(String s) throws ParseException{ - JSONParser parser=new JSONParser(); - return parser.parse(s); - } - - /** - * Encode an object into JSON text and write it to out. - * <p> - * If this object is a Map or a List, and it's also a JSONStreamAware or a JSONAware, JSONStreamAware or JSONAware will be considered firstly. - * <p> - * DO NOT call this method from writeJSONString(Writer) of a class that implements both JSONStreamAware and (Map or List) with - * "this" as the first parameter, use JSONObject.writeJSONString(Map, Writer) or JSONArray.writeJSONString(List, Writer) instead. - * - * @see org.json.simple.JSONObject#writeJSONString(Map, Writer) - * @see org.json.simple.JSONArray#writeJSONString(List, Writer) - * - * @param value - * @param out - */ - public static void writeJSONString(Object value, Writer out) throws IOException { - if(value == null){ - out.write("null"); - return; - } - - if(value instanceof String){ - out.write('\"'); - out.write(escape((String)value)); - out.write('\"'); - return; - } - - if(value instanceof Double){ - if(((Double)value).isInfinite() || ((Double)value).isNaN()) - out.write("null"); - else - out.write(value.toString()); - return; - } - - if(value instanceof Float){ - if(((Float)value).isInfinite() || ((Float)value).isNaN()) - out.write("null"); - else - out.write(value.toString()); - return; - } - - if(value instanceof Number){ - out.write(value.toString()); - return; - } - - if(value instanceof Boolean){ - out.write(value.toString()); - return; - } - - if((value instanceof JSONStreamAware)){ - ((JSONStreamAware)value).writeJSONString(out); - return; - } - - if((value instanceof JSONAware)){ - out.write(((JSONAware)value).toJSONString()); - return; - } - - if(value instanceof Map){ - JSONObject.writeJSONString((Map)value, out); - return; - } - - if(value instanceof List){ - JSONArray.writeJSONString((List)value, out); - return; - } - - out.write(value.toString()); - } - - /** - * Convert an object to JSON text. - * <p> - * If this object is a Map or a List, and it's also a JSONAware, JSONAware will be considered firstly. - * <p> - * DO NOT call this method from toJSONString() of a class that implements both JSONAware and Map or List with - * "this" as the parameter, use JSONObject.toJSONString(Map) or JSONArray.toJSONString(List) instead. - * - * @see org.json.simple.JSONObject#toJSONString(Map) - * @see org.json.simple.JSONArray#toJSONString(List) - * - * @param value - * @return JSON text, or "null" if value is null or it's an NaN or an INF number. - */ - public static String toJSONString(Object value){ - if(value == null) - return "null"; - - if(value instanceof String) - return "\""+escape((String)value)+"\""; - - if(value instanceof Double){ - if(((Double)value).isInfinite() || ((Double)value).isNaN()) - return "null"; - else - return value.toString(); - } - - if(value instanceof Float){ - if(((Float)value).isInfinite() || ((Float)value).isNaN()) - return "null"; - else - return value.toString(); - } - - if(value instanceof Number) - return value.toString(); - - if(value instanceof Boolean) - return value.toString(); - - if((value instanceof JSONAware)) - return ((JSONAware)value).toJSONString(); - - if(value instanceof Map) - return JSONObject.toJSONString((Map)value); - - if(value instanceof List) - return JSONArray.toJSONString((List)value); - - return value.toString(); - } - - /** - * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F). - * @param s - * @return the value - */ - public static String escape(String s){ - if(s==null) - return null; - StringBuffer sb = new StringBuffer(); - escape(s, sb); - return sb.toString(); - } - - /** - * @param s - Must not be null. - * @param sb - */ - static void escape(String s, StringBuffer sb) { - for(int i=0;i<s.length();i++){ - char ch=s.charAt(i); - switch(ch){ - case '"': - sb.append("\\\""); - break; - case '\\': - sb.append("\\\\"); - break; - case '\b': - sb.append("\\b"); - break; - case '\f': - sb.append("\\f"); - break; - case '\n': - sb.append("\\n"); - break; - case '\r': - sb.append("\\r"); - break; - case '\t': - sb.append("\\t"); - break; - case '/': - sb.append("\\/"); - break; - default: - //Reference: http://www.unicode.org/versions/Unicode5.1.0/ - if((ch>='\u0000' && ch<='\u001F') || (ch>='\u007F' && ch<='\u009F') || (ch>='\u2000' && ch<='\u20FF')){ - String ss=Integer.toHexString(ch); - sb.append("\\u"); - for(int k=0;k<4-ss.length();k++){ - sb.append('0'); - } - sb.append(ss.toUpperCase()); - } - else{ - sb.append(ch); - } - } - }//for - } - -} diff --git a/core/java/src/org/json/simple/JsonArray.java b/core/java/src/org/json/simple/JsonArray.java new file mode 100644 index 0000000000000000000000000000000000000000..57615d6a315be7fd4c869e2d56b08956364f7edb --- /dev/null +++ b/core/java/src/org/json/simple/JsonArray.java @@ -0,0 +1,350 @@ +/* Copyright 2016-2017 Clifton Labs + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ +package org.json.simple; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** JsonArray is a common non-thread safe data format for a collection of data. The contents of a JsonArray are only + * validated as JSON values on serialization. Meaning all values added to a JsonArray must be recognized by the Jsoner + * for it to be a true 'JsonArray', so it is really a JsonableArrayList that will serialize to a JsonArray if all of + * its contents are valid JSON. + * @see Jsoner + * @since 2.0.0 */ +public class JsonArray extends ArrayList<Object> implements Jsonable{ + /** The serialization version this class is compatible + * with. This value doesn't need to be incremented if and only if the only changes to occur were updating comments, + * updating javadocs, adding new + * fields to the class, changing the fields from static to non-static, or changing the fields from transient to non + * transient. All other changes require this number be incremented. */ + private static final long serialVersionUID = 1L; + + /** Instantiates an empty JsonArray. */ + public JsonArray(){ + super(); + } + + /** Instantiate a new JsonArray using ArrayList's constructor of the same type. + * @param collection represents the elements to produce the JsonArray with. */ + public JsonArray(final Collection<?> collection){ + super(collection); + } + + /** A convenience method that assumes every element of the JsonArray is castable to T before adding it to a + * collection of Ts. + * @param <T> represents the type that all of the elements of the JsonArray should be cast to and the type the + * collection will contain. + * @param destination represents where all of the elements of the JsonArray are added to after being cast to the + * generic type + * provided. + * @throws ClassCastException if the unchecked cast of an element to T fails. */ + @SuppressWarnings("unchecked") + public <T> void asCollection(final Collection<T> destination){ + for(final Object o : this){ + destination.add((T)o); + } + } + + /** A convenience method that assumes there is a BigDecimal, Number, or String at the given index. If a Number or + * String is there it is used to construct a new BigDecimal. + * @param index representing where the value is expected to be at. + * @return the value stored at the key or the default provided if the key doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return types. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal. + * @see BigDecimal + * @see Number#doubleValue() */ + public BigDecimal getBigDecimal(final int index){ + Object returnable = this.get(index); + if(returnable instanceof BigDecimal){ + /* Success there was a BigDecimal. */ + }else if(returnable instanceof Number){ + /* A number can be used to construct a BigDecimal. */ + returnable = new BigDecimal(returnable.toString()); + }else if(returnable instanceof String){ + /* A number can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return (BigDecimal)returnable; + } + + /** A convenience method that assumes there is a Boolean or String value at the given index. + * @param index represents where the value is expected to be at. + * @return the value at the index provided cast to a boolean. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. */ + public Boolean getBoolean(final int index){ + Object returnable = this.get(index); + if(returnable instanceof String){ + returnable = Boolean.valueOf((String)returnable); + } + return (Boolean)returnable; + } + + /** A convenience method that assumes there is a Number or String value at the given index. + * @param index represents where the value is expected to be at. + * @return the value at the index provided cast to a byte. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. + * @see Number */ + public Byte getByte(final int index){ + Object returnable = this.get(index); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).byteValue(); + } + + /** A convenience method that assumes there is a Collection value at the given index. + * @param <T> the kind of collection to expect at the index. Note unless manually added, collection values will be a + * JsonArray. + * @param index represents where the value is expected to be at. + * @return the value at the index provided cast to a Collection. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. + * @see Collection */ + @SuppressWarnings("unchecked") + public <T extends Collection<?>> T getCollection(final int index){ + /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will + * work. */ + return (T)this.get(index); + } + + /** A convenience method that assumes there is a Number or String value at the given index. + * @param index represents where the value is expected to be at. + * @return the value at the index provided cast to a double. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. + * @see Number */ + public Double getDouble(final int index){ + Object returnable = this.get(index); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).doubleValue(); + } + + /** A convenience method that assumes there is a String value at the given index representing a fully qualified name + * in dot notation of an enum. + * @param index representing where the value is expected to be at. + * @param <T> the Enum type the value at the index is expected to belong to. + * @return the enum based on the string found at the index, or null if the value at the index was null. + * @throws ClassNotFoundException if the element was a String but the declaring enum type couldn't be determined + * with it. + * @throws ClassCastException if the element at the index was not a String or if the fully qualified enum name is of + * the wrong type. + * @throws IllegalArgumentException if an enum type was dynamically determined but it doesn't define an enum with + * the dynamically determined name. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. + * @see Enum#valueOf(Class, String) + * @deprecated 2.3.0 Jsoner deprecated automatically serializing enums as Strings. */ + @Deprecated + @SuppressWarnings("unchecked") + public <T extends Enum<T>> T getEnum(final int index) throws ClassNotFoundException{ + /* Supressing the unchecked warning because the returnType is dynamically identified and could lead to a + * ClassCastException when returnType is cast to Class<T>, which is expected by the method's contract. */ + T returnable; + final String element; + final String[] splitValues; + final int numberOfValues; + final StringBuilder returnTypeName; + final StringBuilder enumName; + final Class<T> returnType; + /* Make sure the element at the index is a String. */ + element = this.getString(index); + if(element == null){ + return null; + } + /* Get the package, class, and enum names. */ + splitValues = element.split("\\."); + numberOfValues = splitValues.length; + returnTypeName = new StringBuilder(); + enumName = new StringBuilder(); + for(int i = 0; i < numberOfValues; i++){ + if(i == (numberOfValues - 1)){ + /* If it is the last split value then it should be the name of the Enum since dots are not allowed in + * enum names. */ + enumName.append(splitValues[i]); + }else if(i == (numberOfValues - 2)){ + /* If it is the penultimate split value then it should be the end of the package/enum type and not need + * a dot appended to it. */ + returnTypeName.append(splitValues[i]); + }else{ + /* Must be part of the package/enum type and will need a dot appended to it since they got removed in + * the split. */ + returnTypeName.append(splitValues[i]); + returnTypeName.append("."); + } + } + /* Use the package/class and enum names to get the Enum<T>. */ + returnType = (Class<T>)Class.forName(returnTypeName.toString()); + returnable = Enum.valueOf(returnType, enumName.toString()); + return returnable; + } + + /** A convenience method that assumes there is a Number or String value at the given index. + * @param index represents where the value is expected to be at. + * @return the value at the index provided cast to a float. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. + * @see Number */ + public Float getFloat(final int index){ + Object returnable = this.get(index); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).floatValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given index. + * @param index represents where the value is expected to be at. + * @return the value at the index provided cast to a int. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. + * @see Number */ + public Integer getInteger(final int index){ + Object returnable = this.get(index); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).intValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given index. + * @param index represents where the value is expected to be at. + * @return the value at the index provided cast to a long. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. + * @see Number */ + public Long getLong(final int index){ + Object returnable = this.get(index); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).longValue(); + } + + /** A convenience method that assumes there is a Map value at the given index. + * @param <T> the kind of map to expect at the index. Note unless manually added, Map values will be a JsonObject. + * @param index represents where the value is expected to be at. + * @return the value at the index provided cast to a Map. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. + * @see Map */ + @SuppressWarnings("unchecked") + public <T extends Map<?, ?>> T getMap(final int index){ + /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will + * work. */ + return (T)this.get(index); + } + + /** A convenience method that assumes there is a Number or String value at the given index. + * @param index represents where the value is expected to be at. + * @return the value at the index provided cast to a short. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. + * @see Number */ + public Short getShort(final int index){ + Object returnable = this.get(index); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).shortValue(); + } + + /** A convenience method that assumes there is a Boolean, Number, or String value at the given index. + * @param index represents where the value is expected to be at. + * @return the value at the index provided cast to a String. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. */ + public String getString(final int index){ + Object returnable = this.get(index); + if(returnable instanceof Boolean){ + returnable = returnable.toString(); + }else if(returnable instanceof Number){ + returnable = returnable.toString(); + } + return (String)returnable; + } + + /* (non-Javadoc) + * @see org.json.simple.Jsonable#asJsonString() */ + @Override + public String toJson(){ + final StringWriter writable = new StringWriter(); + try{ + this.toJson(writable); + }catch(final IOException caught){ + /* See java.io.StringWriter. */ + } + return writable.toString(); + } + + /* (non-Javadoc) + * @see org.json.simple.Jsonable#toJsonString(java.io.Writer) */ + @Override + public void toJson(final Writer writable) throws IOException{ + boolean isFirstElement = true; + final Iterator<Object> elements = this.iterator(); + writable.write('['); + while(elements.hasNext()){ + if(isFirstElement){ + isFirstElement = false; + }else{ + writable.write(','); + } + writable.write(Jsoner.serialize(elements.next())); + } + writable.write(']'); + } +} diff --git a/core/java/src/org/json/simple/JsonKey.java b/core/java/src/org/json/simple/JsonKey.java new file mode 100644 index 0000000000000000000000000000000000000000..46fb3a70850a45a5c1ba995b356e842778c0b342 --- /dev/null +++ b/core/java/src/org/json/simple/JsonKey.java @@ -0,0 +1,13 @@ +package org.json.simple; + +/** Should be implemented by Enums so that keys are easily maintained. + * @since 2.3.0 */ +public interface JsonKey{ + /** The json-simple library uses a String for its keys. + * @return a String representing the JsonKey. */ + public String getKey(); + + /** A reasonable value for the key; such as a valid default, error value, or null. + * @return an Object representing a reasonable general case value for the key. */ + public Object getValue(); +} diff --git a/core/java/src/org/json/simple/JsonObject.java b/core/java/src/org/json/simple/JsonObject.java new file mode 100644 index 0000000000000000000000000000000000000000..ba1478b519e2fc055b49b3c48e9ddb5f0e11fcb8 --- /dev/null +++ b/core/java/src/org/json/simple/JsonObject.java @@ -0,0 +1,1337 @@ +/* Copyright 2016-2017 Clifton Labs + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ +package org.json.simple; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +/** JsonObject is a common non-thread safe data format for string to data mappings. The contents of a JsonObject are + * only validated as JSON values on serialization. Meaning all values added to a JsonObject must be recognized by the + * Jsoner for it to be a true 'JsonObject', so it is really a JsonableHashMap that will serialize to a JsonObject if all of + * its contents are valid JSON. + * @see Jsoner + * @since 2.0.0 */ +public class JsonObject extends HashMap<String, Object> implements Jsonable{ + /** The serialization version this class is compatible + * with. This value doesn't need to be incremented if and only if the only changes to occur were updating comments, + * updating javadocs, adding new + * fields to the class, changing the fields from static to non-static, or changing the fields from transient to non + * transient. All other changes require this number be incremented. */ + private static final long serialVersionUID = 2L; + + /** Instantiates an empty JsonObject. */ + public JsonObject(){ + super(); + } + + /** Instantiate a new JsonObject by accepting a map's entries, which could lead to de/serialization issues of the + * resulting JsonObject since the entry values aren't validated as JSON values. + * @param map represents the mappings to produce the JsonObject with. */ + public JsonObject(final Map<String, ?> map){ + super(map); + } + + /** Ensures the given keys are present. + * @param keys represents the keys that must be present. + * @throws NoSuchElementException if any of the given keys are missing. + * @since 2.3.0 to ensure critical keys are in the JsonObject. */ + public void requireKeys(final JsonKey... keys){ + /* Track all of the missing keys. */ + final Set<JsonKey> missing = new HashSet<>(); + for(final JsonKey k : keys){ + if(!this.containsKey(k.getKey())){ + missing.add(k); + } + } + if(!missing.isEmpty()){ + /* Report any missing keys in the exception. */ + final StringBuilder sb = new StringBuilder(); + for(final JsonKey k : missing){ + sb.append(k.getKey()).append(", "); + } + sb.setLength(sb.length() - 2); + final String s = missing.size() > 1 ? "s" : ""; + throw new NoSuchElementException("A JsonObject is missing required key" + s + ": " + sb.toString()); + } + } + + /** A convenience method that assumes there is a BigDecimal, Number, or String at the given key. If a Number is + * there its Number#toString() is used to construct a new BigDecimal(String). If a String is there it is used to + * construct a new BigDecimal(String). + * @param key representing where the value ought to be paired with. + * @return a BigDecimal representing the value paired with the key. + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see BigDecimal + * @see Number#toString() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public BigDecimal getBigDecimal(final JsonKey key){ + Object returnable = this.get(key.getKey()); + if(returnable instanceof BigDecimal){ + /* Success there was a BigDecimal or it defaulted. */ + }else if(returnable instanceof Number){ + /* A number can be used to construct a BigDecimal */ + returnable = new BigDecimal(returnable.toString()); + }else if(returnable instanceof String){ + /* A number can be used to construct a BigDecimal */ + returnable = new BigDecimal((String)returnable); + } + return (BigDecimal)returnable; + } + + /** A convenience method that assumes there is a BigDecimal, Number, or String at the given key. If a Number is + * there its Number#toString() is used to construct a new BigDecimal(String). If a String is there it is used to + * construct a new BigDecimal(String). + * @param key representing where the value ought to be stored at. + * @return the value stored at the key. + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see BigDecimal + * @see Number#toString() + * @deprecated 2.3.0 in favor of {@link #getBigDecimal(JsonKey)} */ + @Deprecated + public BigDecimal getBigDecimal(final String key){ + Object returnable = this.get(key); + if(returnable instanceof BigDecimal){ + /* Success there was a BigDecimal or it defaulted. */ + }else if(returnable instanceof Number){ + /* A number can be used to construct a BigDecimal */ + returnable = new BigDecimal(returnable.toString()); + }else if(returnable instanceof String){ + /* A number can be used to construct a BigDecimal */ + returnable = new BigDecimal((String)returnable); + } + return (BigDecimal)returnable; + } + + /** A convenience method that assumes there is a BigDecimal, Number, or String at the given key. If a Number is + * there its Number#toString() is used to construct a new BigDecimal(String). If a String is there it is used to + * construct a new BigDecimal(String). + * @param key representing where the value ought to be paired with. + * @return a BigDecimal representing the value paired with the key or JsonKey#getValue() if the key isn't present. + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see BigDecimal + * @see Number#toString() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public BigDecimal getBigDecimalOrDefault(final JsonKey key){ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + if(returnable instanceof BigDecimal){ + /* Success there was a BigDecimal or it defaulted. */ + }else if(returnable instanceof Number){ + /* A number can be used to construct a BigDecimal */ + returnable = new BigDecimal(returnable.toString()); + }else if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal */ + returnable = new BigDecimal((String)returnable); + } + return (BigDecimal)returnable; + } + + /** A convenience method that assumes there is a BigDecimal, Number, or String at the given key. If a Number is + * there its Number#toString() is used to construct a new BigDecimal(String). If a String is there it is used to + * construct a new BigDecimal(String). + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key or the default provided if the key doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return types. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see BigDecimal + * @see Number#toString() + * @deprecated 2.3.0 in favor of {@link #getBigDecimalOrDefault(JsonKey)} */ + @Deprecated + public BigDecimal getBigDecimalOrDefault(final String key, final BigDecimal defaultValue){ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + return defaultValue; + } + if(returnable instanceof BigDecimal){ + /* Success there was a BigDecimal or it defaulted. */ + }else if(returnable instanceof Number){ + /* A number can be used to construct a BigDecimal */ + returnable = new BigDecimal(returnable.toString()); + }else if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal */ + returnable = new BigDecimal((String)returnable); + } + return (BigDecimal)returnable; + } + + /** A convenience method that assumes there is a Boolean or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Boolean representing the value paired with the key. + * @throws ClassCastException if the value didn't match the assumed return type. + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Boolean getBoolean(final JsonKey key){ + Object returnable = this.get(key.getKey()); + if(returnable instanceof String){ + returnable = Boolean.valueOf((String)returnable); + } + return (Boolean)returnable; + } + + /** A convenience method that assumes there is a Boolean or String value at the given key. + * @param key representing where the value ought to be stored at. + * @return the value stored at the key. + * @throws ClassCastException if the value didn't match the assumed return type. + * @deprecated 2.3.0 in favor of {@link #getBoolean(JsonKey)} */ + @Deprecated + public Boolean getBoolean(final String key){ + Object returnable = this.get(key); + if(returnable instanceof String){ + returnable = Boolean.valueOf((String)returnable); + } + return (Boolean)returnable; + } + + /** A convenience method that assumes there is a Boolean or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Boolean representing the value paired with the key or JsonKey#getValue() if the key isn't present. + * @throws ClassCastException if the value didn't match the assumed return type. + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Boolean getBooleanOrDefault(final JsonKey key){ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + if(returnable instanceof String){ + returnable = Boolean.valueOf((String)returnable); + } + return (Boolean)returnable; + } + + /** A convenience method that assumes there is a Boolean or String value at the given key. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key or the default provided if the key doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @deprecated 2.3.0 in favor of {@link #getBooleanOrDefault(JsonKey)} */ + @Deprecated + public Boolean getBooleanOrDefault(final String key, final boolean defaultValue){ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + return defaultValue; + } + if(returnable instanceof String){ + returnable = Boolean.valueOf((String)returnable); + } + return (Boolean)returnable; + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Byte representing the value paired with the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#byteValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Byte getByte(final JsonKey key){ + Object returnable = this.get(key.getKey()); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).byteValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @return the value stored at the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#byteValue() + * @deprecated 2.3.0 in favor of {@link #getByte(JsonKey)} */ + @Deprecated + public Byte getByte(final String key){ + Object returnable = this.get(key); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).byteValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Byte representing the value paired with the key or JsonKey#getValue() if the key isn't present (which + * may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#byteValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Byte getByteOrDefault(final JsonKey key){ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).byteValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key (which may involve rounding or truncation) or the default provided if the key + * doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#byteValue() + * @deprecated 2.3.0 in favor of {@link #getByteOrDefault(JsonKey)} */ + @Deprecated + public Byte getByteOrDefault(final String key, final byte defaultValue){ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + return defaultValue; + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).byteValue(); + } + + /** A convenience method that assumes there is a Collection at the given key. + * @param <T> the kind of collection to expect at the key. Note unless manually added, collection values will be a + * JsonArray. + * @param key representing where the value ought to be paired with. + * @return a Collection representing the value paired with the key. + * @throws ClassCastException if the value didn't match the assumed return type. + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + @SuppressWarnings("unchecked") + public <T extends Collection<?>> T getCollection(final JsonKey key){ + /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will + * work. */ + return (T)this.get(key.getKey()); + } + + /** A convenience method that assumes there is a Collection at the given key. + * @param <T> the kind of collection to expect at the key. Note unless manually added, collection values will be a + * JsonArray. + * @param key representing where the value ought to be stored at. + * @return the value stored at the key. + * @throws ClassCastException if the value didn't match the assumed return type. + * @deprecated 2.3.0 in favor of {@link #getCollection(JsonKey)} */ + @Deprecated + @SuppressWarnings("unchecked") + public <T extends Collection<?>> T getCollection(final String key){ + /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will + * work. */ + return (T)this.get(key); + } + + /** A convenience method that assumes there is a Collection at the given key. + * @param <T> the kind of collection to expect at the key. Note unless manually added, collection values will be a + * JsonArray. + * @param key representing where the value ought to be paired with. + * @return a Collection representing the value paired with the key or JsonKey#getValue() if the key isn't present.. + * @throws ClassCastException if the value didn't match the assumed return type. + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + @SuppressWarnings("unchecked") + public <T extends Collection<?>> T getCollectionOrDefault(final JsonKey key){ + /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will + * work. */ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + return (T)returnable; + } + + /** A convenience method that assumes there is a Collection at the given key. + * @param <T> the kind of collection to expect at the key. Note unless manually added, collection values will be a + * JsonArray. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key or the default provided if the key doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @deprecated 2.3.0 in favor of {@link #getCollectionOrDefault(JsonKey)} */ + @Deprecated + @SuppressWarnings("unchecked") + public <T extends Collection<?>> T getCollectionOrDefault(final String key, final T defaultValue){ + /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will + * work. */ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + return defaultValue; + } + return (T)returnable; + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Double representing the value paired with the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#doubleValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Double getDouble(final JsonKey key){ + Object returnable = this.get(key.getKey()); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).doubleValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @return the value stored at the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#doubleValue() + * @deprecated 2.3.0 in favor of {@link #getDouble(JsonKey)} */ + @Deprecated + public Double getDouble(final String key){ + Object returnable = this.get(key); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).doubleValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Double representing the value paired with the key or JsonKey#getValue() if the key isn't present (which + * may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#doubleValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Double getDoubleOrDefault(final JsonKey key){ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).doubleValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key (which may involve rounding or truncation) or the default provided if the key + * doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#doubleValue() + * @deprecated 2.3.0 in favor of {@link #getDoubleOrDefault(JsonKey)} */ + @Deprecated + public Double getDoubleOrDefault(final String key, final double defaultValue){ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + return defaultValue; + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).doubleValue(); + } + + /** A convenience method that assumes there is a String value at the given key representing a fully qualified name + * in dot notation of an enum. + * @param key representing where the value ought to be paired with. + * @param <T> the Enum type the value at the key is expected to belong to. + * @return an Enum representing the value paired with the key. + * @throws ClassNotFoundException if the value was a String but the declaring enum type couldn't be determined with + * it. + * @throws ClassCastException if the element at the index was not a String or if the fully qualified enum name is of + * the wrong type. + * @throws IllegalArgumentException if an enum type was determined but it doesn't define an enum with the determined + * name. + * @see Enum#valueOf(Class, String) + * @see JsonKey + * @since 2.3.0 to utilize JsonKey + * @deprecated 2.3.0 Jsoner deprecated automatically serializing enums as Strings. */ + @Deprecated + @SuppressWarnings("unchecked") + public <T extends Enum<T>> T getEnum(final JsonKey key) throws ClassNotFoundException{ + /* Supressing the unchecked warning because the returnType is dynamically identified and could lead to a + * ClassCastException when returnType is cast to Class<T>, which is acceptable by the method's contract. */ + T returnable; + final String value; + final String[] splitValues; + final int numberOfSplitValues; + final StringBuilder returnTypeName; + final StringBuilder enumName; + final Class<T> returnType; + /* Make sure the value at the key is a String. */ + value = this.getString(key); + if(value == null){ + return null; + } + /* Get the package, class, and enum names. */ + splitValues = value.split("\\."); + numberOfSplitValues = splitValues.length; + returnTypeName = new StringBuilder(); + enumName = new StringBuilder(); + for(int i = 0; i < numberOfSplitValues; i++){ + if(i == (numberOfSplitValues - 1)){ + /* If it is the last split value then it should be the name of the Enum since dots are not allowed + * in enum names. */ + enumName.append(splitValues[i]); + }else if(i == (numberOfSplitValues - 2)){ + /* If it is the penultimate split value then it should be the end of the package/enum type and not + * need a dot appended to it. */ + returnTypeName.append(splitValues[i]); + }else{ + /* Must be part of the package/enum type and will need a dot appended to it since they got removed + * in the split. */ + returnTypeName.append(splitValues[i]); + returnTypeName.append("."); + } + } + /* Use the package/class and enum names to get the Enum<T>. */ + returnType = (Class<T>)Class.forName(returnTypeName.toString()); + returnable = Enum.valueOf(returnType, enumName.toString()); + return returnable; + } + + /** A convenience method that assumes there is a String value at the given key representing a fully qualified name + * in dot notation of an enum. + * @param key representing where the value ought to be stored at. + * @param <T> the Enum type the value at the key is expected to belong to. + * @return the enum based on the string found at the key, or null if the value paired with the provided key is null. + * @throws ClassNotFoundException if the value was a String but the declaring enum type couldn't be determined with + * it. + * @throws ClassCastException if the element at the index was not a String or if the fully qualified enum name is of + * the wrong type. + * @throws IllegalArgumentException if an enum type was determined but it doesn't define an enum with the determined + * name. + * @see Enum#valueOf(Class, String) + * @deprecated 2.3.0 in favor of {@link #getEnum(JsonKey)} */ + @Deprecated + @SuppressWarnings("unchecked") + public <T extends Enum<T>> T getEnum(final String key) throws ClassNotFoundException{ + /* Supressing the unchecked warning because the returnType is dynamically identified and could lead to a + * ClassCastException when returnType is cast to Class<T>, which is expected by the method's contract. */ + T returnable; + final String value; + final String[] splitValues; + final int numberOfSplitValues; + final StringBuilder returnTypeName; + final StringBuilder enumName; + final Class<T> returnType; + /* Make sure the value at the key is a String. */ + value = this.getStringOrDefault(key, ""); + if(value == null){ + return null; + } + /* Get the package, class, and enum names. */ + splitValues = value.split("\\."); + numberOfSplitValues = splitValues.length; + returnTypeName = new StringBuilder(); + enumName = new StringBuilder(); + for(int i = 0; i < numberOfSplitValues; i++){ + if(i == (numberOfSplitValues - 1)){ + /* If it is the last split value then it should be the name of the Enum since dots are not allowed + * in enum names. */ + enumName.append(splitValues[i]); + }else if(i == (numberOfSplitValues - 2)){ + /* If it is the penultimate split value then it should be the end of the package/enum type and not + * need a dot appended to it. */ + returnTypeName.append(splitValues[i]); + }else{ + /* Must be part of the package/enum type and will need a dot appended to it since they got removed + * in the split. */ + returnTypeName.append(splitValues[i]); + returnTypeName.append("."); + } + } + /* Use the package/class and enum names to get the Enum<T>. */ + returnType = (Class<T>)Class.forName(returnTypeName.toString()); + returnable = Enum.valueOf(returnType, enumName.toString()); + return returnable; + } + + /** A convenience method that assumes there is a String value at the given key representing a fully qualified name + * in dot notation of an enum. + * @param key representing where the value ought to be paired with. + * @param <T> the Enum type the value at the key is expected to belong to. + * @return an Enum representing the value paired with the key or JsonKey#getValue() if the key isn't present. + * @throws ClassNotFoundException if the value was a String but the declaring enum type couldn't be determined with + * it. + * @throws ClassCastException if the element at the index was not a String or if the fully qualified enum name is of + * the wrong type. + * @throws IllegalArgumentException if an enum type was determined but it doesn't define an enum with the determined + * name. + * @see Enum#valueOf(Class, String) + * @see JsonKey + * @since 2.3.0 to utilize JsonKey + * @deprecated 2.3.0 Jsoner deprecated automatically serializing enums as Strings. */ + @Deprecated + @SuppressWarnings("unchecked") + public <T extends Enum<T>> T getEnumOrDefault(final JsonKey key) throws ClassNotFoundException{ + /* Supressing the unchecked warning because the returnType is dynamically identified and could lead to a + * ClassCastException when returnType is cast to Class<T>, which is acceptable by the method's contract. */ + T returnable; + final String value; + final String[] splitValues; + final int numberOfSplitValues; + final StringBuilder returnTypeName; + final StringBuilder enumName; + final Class<T> returnType; + /* Check to make sure the key is there. */ + if(this.containsKey(key)){ + /* Make sure the value at the key is a String. */ + value = this.getStringOrDefault(key.getKey(), ""); + if(value == null){ + return null; + } + /* Get the package, class, and enum names. */ + splitValues = value.split("\\."); + numberOfSplitValues = splitValues.length; + returnTypeName = new StringBuilder(); + enumName = new StringBuilder(); + for(int i = 0; i < numberOfSplitValues; i++){ + if(i == (numberOfSplitValues - 1)){ + /* If it is the last split value then it should be the name of the Enum since dots are not allowed + * in enum names. */ + enumName.append(splitValues[i]); + }else if(i == (numberOfSplitValues - 2)){ + /* If it is the penultimate split value then it should be the end of the package/enum type and not + * need a dot appended to it. */ + returnTypeName.append(splitValues[i]); + }else{ + /* Must be part of the package/enum type and will need a dot appended to it since they got removed + * in the split. */ + returnTypeName.append(splitValues[i]); + returnTypeName.append("."); + } + } + /* Use the package/class and enum names to get the Enum<T>. */ + returnType = (Class<T>)Class.forName(returnTypeName.toString()); + returnable = Enum.valueOf(returnType, enumName.toString()); + }else{ + /* It wasn't there and according to the method's contract we return the default value. */ + returnable = (T)key.getValue(); + } + return returnable; + } + + /** A convenience method that assumes there is a String value at the given key representing a fully qualified name + * in dot notation of an enum. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @param <T> the Enum type the value at the key is expected to belong to. + * @return the enum based on the string found at the key, or the defaultValue provided if the key doesn't exist, or + * null if the value paired with provided key is null. + * @throws ClassNotFoundException if the value was a String but the declaring enum type couldn't be determined with + * it. + * @throws ClassCastException if the element at the index was not a String or if the fully qualified enum name is of + * the wrong type. + * @throws IllegalArgumentException if an enum type was determined but it doesn't define an enum with the determined + * name. + * @see Enum#valueOf(Class, String) + * @deprecated 2.3.0 in favor of {@link #getEnumOrDefault(JsonKey)} */ + @Deprecated + @SuppressWarnings("unchecked") + public <T extends Enum<T>> T getEnumOrDefault(final String key, final T defaultValue) throws ClassNotFoundException{ + /* Supressing the unchecked warning because the returnType is dynamically identified and could lead to a + * ClassCastException when returnType is cast to Class<T>, which is expected by the method's contract. */ + T returnable; + final String value; + final String[] splitValues; + final int numberOfSplitValues; + final StringBuilder returnTypeName; + final StringBuilder enumName; + final Class<T> returnType; + /* Check to make sure the key wasn't actually there and wasn't coincidentally the defaulted String as its + * value. */ + if(this.containsKey(key)){ + /* Make sure the value at the key is a String. */ + value = this.getStringOrDefault(key, ""); + if(value == null){ + return null; + } + /* Get the package, class, and enum names. */ + splitValues = value.split("\\."); + numberOfSplitValues = splitValues.length; + returnTypeName = new StringBuilder(); + enumName = new StringBuilder(); + for(int i = 0; i < numberOfSplitValues; i++){ + if(i == (numberOfSplitValues - 1)){ + /* If it is the last split value then it should be the name of the Enum since dots are not allowed + * in enum names. */ + enumName.append(splitValues[i]); + }else if(i == (numberOfSplitValues - 2)){ + /* If it is the penultimate split value then it should be the end of the package/enum type and not + * need a dot appended to it. */ + returnTypeName.append(splitValues[i]); + }else{ + /* Must be part of the package/enum type and will need a dot appended to it since they got removed + * in the split. */ + returnTypeName.append(splitValues[i]); + returnTypeName.append("."); + } + } + /* Use the package/class and enum names to get the Enum<T>. */ + returnType = (Class<T>)Class.forName(returnTypeName.toString()); + returnable = Enum.valueOf(returnType, enumName.toString()); + }else{ + /* It wasn't there and according to the method's contract we return the default value. */ + return defaultValue; + } + return returnable; + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Float representing the value paired with the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#floatValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Float getFloat(final JsonKey key){ + Object returnable = this.get(key.getKey()); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).floatValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @return the value stored at the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#floatValue() + * @deprecated 2.3.0 in favor of {@link #getFloat(JsonKey)} */ + @Deprecated + public Float getFloat(final String key){ + Object returnable = this.get(key); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).floatValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Float representing the value paired with the key or JsonKey#getValue() if the key isn't present (which + * may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#floatValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Float getFloatOrDefault(final JsonKey key){ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).floatValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key (which may involve rounding or truncation) or the default provided if the key + * doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#floatValue() + * @deprecated 2.3.0 in favor of {@link #getFloatOrDefault(JsonKey)} */ + @Deprecated + public Float getFloatOrDefault(final String key, final float defaultValue){ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + return defaultValue; + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).floatValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return an Integer representing the value paired with the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#intValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Integer getInteger(final JsonKey key){ + Object returnable = this.get(key.getKey()); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).intValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @return the value stored at the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#intValue() + * @deprecated 2.3.0 in favor of {@link #getInteger(JsonKey)} */ + @Deprecated + public Integer getInteger(final String key){ + Object returnable = this.get(key); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).intValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return an Integer representing the value paired with the key or JsonKey#getValue() if the key isn't present + * (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#intValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Integer getIntegerOrDefault(final JsonKey key){ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).intValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key (which may involve rounding or truncation) or the default provided if the key + * doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#intValue() + * @deprecated 2.3.0 in favor of {@link #getIntegerOrDefault(JsonKey)} */ + @Deprecated + public Integer getIntegerOrDefault(final String key, final int defaultValue){ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + return defaultValue; + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).intValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Long representing the value paired with the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#longValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Long getLong(final JsonKey key){ + Object returnable = this.get(key.getKey()); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).longValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @return the value stored at the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#longValue() + * @deprecated 2.3.0 in favor of {@link #getLong(JsonKey)} */ + @Deprecated + public Long getLong(final String key){ + Object returnable = this.get(key); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).longValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Long representing the value paired with the key or JsonKey#getValue() if the key isn't present (which + * may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#longValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Long getLongOrDefault(final JsonKey key){ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).longValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key (which may involve rounding or truncation) or the default provided if the key + * doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#longValue() + * @deprecated 2.3.0 in favor of {@link #getLongOrDefault(JsonKey)} */ + @Deprecated + public Long getLongOrDefault(final String key, final long defaultValue){ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + return defaultValue; + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).longValue(); + } + + /** A convenience method that assumes there is a Map at the given key. + * @param <T> the kind of map to expect at the key. Note unless manually added, Map values will be a JsonObject. + * @param key representing where the value ought to be paired with. + * @return a Map representing the value paired with the key. + * @throws ClassCastException if the value didn't match the assumed return type. + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + @SuppressWarnings("unchecked") + public <T extends Map<?, ?>> T getMap(final JsonKey key){ + /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will + * work. */ + return (T)this.get(key.getKey()); + } + + /** A convenience method that assumes there is a Map at the given key. + * @param <T> the kind of map to expect at the key. Note unless manually added, Map values will be a JsonObject. + * @param key representing where the value ought to be stored at. + * @return the value stored at the key. + * @throws ClassCastException if the value didn't match the assumed return type. + * @deprecated 2.3.0 in favor of {@link #getMap(JsonKey)} */ + @Deprecated + @SuppressWarnings("unchecked") + public <T extends Map<?, ?>> T getMap(final String key){ + /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will + * work. */ + return (T)this.get(key); + } + + /** A convenience method that assumes there is a Map at the given key. + * @param <T> the kind of map to expect at the key. Note unless manually added, Map values will be a JsonObject. + * @param key representing where the value ought to be paired with. + * @return a Map representing the value paired with the key or JsonKey#getValue() if the key isn't present. + * @throws ClassCastException if the value didn't match the assumed return type. + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + @SuppressWarnings("unchecked") + public <T extends Map<?, ?>> T getMapOrDefault(final JsonKey key){ + /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will + * work. */ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + return (T)returnable; + } + + /** A convenience method that assumes there is a Map at the given key. + * @param <T> the kind of map to expect at the key. Note unless manually added, Map values will be a JsonObject. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key or the default provided if the key doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @deprecated 2.3.0 in favor of {@link #getMapOrDefault(JsonKey)} */ + @Deprecated + @SuppressWarnings("unchecked") + public <T extends Map<?, ?>> T getMapOrDefault(final String key, final T defaultValue){ + /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will + * work. */ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + returnable = defaultValue; + } + return (T)returnable; + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Short representing the value paired with the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#shortValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Short getShort(final JsonKey key){ + Object returnable = this.get(key.getKey()); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).shortValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @return the value stored at the key (which may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#shortValue() + * @deprecated 2.3.0 in favor of {@link #getShort(JsonKey)} */ + @Deprecated + public Short getShort(final String key){ + Object returnable = this.get(key); + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).shortValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a Short representing the value paired with the key or JsonKey#getValue() if the key isn't present (which + * may involve rounding or truncation). + * @throws ClassCastException if the value didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#shortValue() + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public Short getShortOrDefault(final JsonKey key){ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).shortValue(); + } + + /** A convenience method that assumes there is a Number or String value at the given key. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key (which may involve rounding or truncation) or the default provided if the key + * doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number + * represents the double or float Infinity or NaN. + * @see Number#shortValue() + * @deprecated 2.3.0 in favor of {@link #getShortOrDefault(JsonKey)} */ + @Deprecated + public Short getShortOrDefault(final String key, final short defaultValue){ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + return defaultValue; + } + if(returnable == null){ + return null; + } + if(returnable instanceof String){ + /* A String can be used to construct a BigDecimal. */ + returnable = new BigDecimal((String)returnable); + } + return ((Number)returnable).shortValue(); + } + + /** A convenience method that assumes there is a Boolean, Number, or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a String representing the value paired with the key. + * @throws ClassCastException if the value didn't match the assumed return type. + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public String getString(final JsonKey key){ + Object returnable = this.get(key.getKey()); + if(returnable instanceof Boolean){ + returnable = returnable.toString(); + }else if(returnable instanceof Number){ + returnable = returnable.toString(); + } + return (String)returnable; + } + + /** A convenience method that assumes there is a Boolean, Number, or String value at the given key. + * @param key representing where the value ought to be stored at. + * @return the value stored at the key. + * @throws ClassCastException if the value didn't match the assumed return type. + * @deprecated 2.3.0 in favor of {@link #getString(JsonKey)} */ + @Deprecated + public String getString(final String key){ + Object returnable = this.get(key); + if(returnable instanceof Boolean){ + returnable = returnable.toString(); + }else if(returnable instanceof Number){ + returnable = returnable.toString(); + } + return (String)returnable; + } + + /** A convenience method that assumes there is a Boolean, Number, or String value at the given key. + * @param key representing where the value ought to be paired with. + * @return a String representing the value paired with the key or JsonKey#getValue() if the key isn't present. + * @throws ClassCastException if the value didn't match the assumed return type. + * @see JsonKey + * @since 2.3.0 to utilize JsonKey */ + public String getStringOrDefault(final JsonKey key){ + Object returnable; + if(this.containsKey(key.getKey())){ + returnable = this.get(key.getKey()); + }else{ + returnable = key.getValue(); + } + if(returnable instanceof Boolean){ + returnable = returnable.toString(); + }else if(returnable instanceof Number){ + returnable = returnable.toString(); + } + return (String)returnable; + } + + /** A convenience method that assumes there is a Boolean, Number, or String value at the given key. + * @param key representing where the value ought to be stored at. + * @param defaultValue representing what is returned when the key isn't in the JsonObject. + * @return the value stored at the key or the default provided if the key doesn't exist. + * @throws ClassCastException if there was a value but didn't match the assumed return type. + * @deprecated 2.3.0 in favor of {@link #getStringOrDefault(JsonKey)} */ + @Deprecated + public String getStringOrDefault(final String key, final String defaultValue){ + Object returnable; + if(this.containsKey(key)){ + returnable = this.get(key); + }else{ + return defaultValue; + } + if(returnable instanceof Boolean){ + returnable = returnable.toString(); + }else if(returnable instanceof Number){ + returnable = returnable.toString(); + } + return (String)returnable; + } + + /* (non-Javadoc) + * @see org.json.simple.Jsonable#asJsonString() */ + @Override + public String toJson(){ + final StringWriter writable = new StringWriter(); + try{ + this.toJson(writable); + }catch(final IOException caught){ + /* See java.io.StringWriter. */ + } + return writable.toString(); + } + + /* (non-Javadoc) + * @see org.json.simple.Jsonable#toJsonString(java.io.Writer) */ + @Override + public void toJson(final Writer writable) throws IOException{ + /* Writes the map in JSON object format. */ + boolean isFirstEntry = true; + final Iterator<Map.Entry<String, Object>> entries = this.entrySet().iterator(); + writable.write('{'); + while(entries.hasNext()){ + if(isFirstEntry){ + isFirstEntry = false; + }else{ + writable.write(','); + } + final Map.Entry<String, Object> entry = entries.next(); + writable.write(Jsoner.serialize(entry.getKey())); + writable.write(':'); + writable.write(Jsoner.serialize(entry.getValue())); + } + writable.write('}'); + } +} diff --git a/core/java/src/org/json/simple/Jsonable.java b/core/java/src/org/json/simple/Jsonable.java new file mode 100644 index 0000000000000000000000000000000000000000..198bc3bf4ffc9ebc06bbe67c91f59682efd44484 --- /dev/null +++ b/core/java/src/org/json/simple/Jsonable.java @@ -0,0 +1,28 @@ +/* Copyright 2016 Clifton Labs + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ +package org.json.simple; + +import java.io.IOException; +import java.io.Writer; + +/** Jsonables can be serialized in java script object notation (JSON). Deserializing a String produced by a Jsonable + * should represent the Jsonable in JSON form. + * @since 2.0.0 */ +public interface Jsonable{ + /** Serialize to a JSON formatted string. + * @return a string, formatted in JSON, that represents the Jsonable. */ + public String toJson(); + + /** Serialize to a JSON formatted stream. + * @param writable where the resulting JSON text should be sent. + * @throws IOException when the writable encounters an I/O error. */ + public void toJson(Writer writable) throws IOException; +} diff --git a/core/java/src/org/json/simple/Jsoner.java b/core/java/src/org/json/simple/Jsoner.java new file mode 100644 index 0000000000000000000000000000000000000000..827f5b62ec2bab89c928227b5460ea5200544295 --- /dev/null +++ b/core/java/src/org/json/simple/Jsoner.java @@ -0,0 +1,864 @@ +/* Copyright 2016 Clifton Labs + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ +package org.json.simple; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; + +/** Jsoner provides JSON utilities for escaping strings to be JSON compatible, thread safe parsing (RFC 4627) JSON + * strings, and serializing data to strings in JSON format. + * @since 2.0.0 */ +public class Jsoner{ + /** Flags to tweak the behavior of the primary deserialization method. */ + private static enum DeserializationOptions{ + /** Whether multiple JSON values can be deserialized as a root element. */ + ALLOW_CONCATENATED_JSON_VALUES, + /** Whether a JsonArray can be deserialized as a root element. */ + ALLOW_JSON_ARRAYS, + /** Whether a boolean, null, Number, or String can be deserialized as a root element. */ + ALLOW_JSON_DATA, + /** Whether a JsonObject can be deserialized as a root element. */ + ALLOW_JSON_OBJECTS; + } + + /** Flags to tweak the behavior of the primary serialization method. */ + private static enum SerializationOptions{ + /** Instead of aborting serialization on non-JSON values that are Enums it will continue serialization with the + * Enums' "${PACKAGE}.${DECLARING_CLASS}.${NAME}". + * @see Enum + * @deprecated 2.3.0 the enum should implement Jsonable instead. */ + @Deprecated + ALLOW_FULLY_QUALIFIED_ENUMERATIONS, + /** Instead of aborting serialization on non-JSON values it will continue serialization by serializing the + * non-JSON value directly into the now invalid JSON. Be mindful that invalid JSON will not successfully + * deserialize. */ + ALLOW_INVALIDS, + /** Instead of aborting serialization on non-JSON values that implement Jsonable it will continue serialization + * by deferring serialization to the Jsonable. + * @see Jsonable */ + ALLOW_JSONABLES, + /** Instead of aborting serialization on non-JSON values it will continue serialization by using reflection to + * best describe the value as a JsonObject. + * @deprecated 2.3.0 there is no passive way to accomplish this contract and so will be abandoned. */ + @Deprecated + ALLOW_UNDEFINEDS; + } + + /** The possible States of a JSON deserializer. */ + private static enum States{ + /** Post-parsing state. */ + DONE, + /** Pre-parsing state. */ + INITIAL, + /** Parsing error, ParsingException should be thrown. */ + PARSED_ERROR, + PARSING_ARRAY, + /** Parsing a key-value pair inside of an object. */ + PARSING_ENTRY, + PARSING_OBJECT; + } + + private Jsoner(){ + /* Keeping it classy. */ + } + + /** Deserializes a readable stream according to the RFC 4627 JSON specification. + * @param readableDeserializable representing content to be deserialized as JSON. + * @return either a boolean, null, Number, String, JsonObject, or JsonArray that best represents the deserializable. + * @throws DeserializationException if an unexpected token is encountered in the deserializable. To recover from a + * DeserializationException: fix the deserializable + * to no longer have an unexpected token and try again. */ + public static Object deserialize(final Reader readableDeserializable) throws DeserializationException{ + return Jsoner.deserialize(readableDeserializable, EnumSet.of(DeserializationOptions.ALLOW_JSON_ARRAYS, DeserializationOptions.ALLOW_JSON_OBJECTS, DeserializationOptions.ALLOW_JSON_DATA)).get(0); + } + + /** Deserialize a stream with all deserialized JSON values are wrapped in a JsonArray. + * @param deserializable representing content to be deserialized as JSON. + * @param flags representing the allowances and restrictions on deserialization. + * @return the allowable object best represented by the deserializable. + * @throws DeserializationException if a disallowed or unexpected token is encountered in the deserializable. To + * recover from a DeserializationException: fix the + * deserializable to no longer have a disallowed or unexpected token and try again. */ + private static JsonArray deserialize(final Reader deserializable, final Set<DeserializationOptions> flags) throws DeserializationException{ + final Yylex lexer = new Yylex(deserializable); + Yytoken token; + States currentState; + int returnCount = 1; + final LinkedList<States> stateStack = new LinkedList<>(); + final LinkedList<Object> valueStack = new LinkedList<>(); + stateStack.addLast(States.INITIAL); + //System.out.println("//////////DESERIALIZING//////////"); + do{ + /* Parse through the parsable string's tokens. */ + currentState = Jsoner.popNextState(stateStack); + token = Jsoner.lexNextToken(lexer); + switch(currentState){ + case DONE: + /* The parse has finished a JSON value. */ + if(!flags.contains(DeserializationOptions.ALLOW_CONCATENATED_JSON_VALUES) || Yytoken.Types.END.equals(token.getType())){ + /* Break if concatenated values are not allowed or if an END token is read. */ + break; + } + /* Increment the amount of returned JSON values and treat the token as if it were a fresh parse. */ + returnCount += 1; + /* Fall through to the case for the initial state. */ + //$FALL-THROUGH$ + case INITIAL: + /* The parse has just started. */ + switch(token.getType()){ + case DATUM: + /* A boolean, null, Number, or String could be detected. */ + if(flags.contains(DeserializationOptions.ALLOW_JSON_DATA)){ + valueStack.addLast(token.getValue()); + stateStack.addLast(States.DONE); + }else{ + throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.DISALLOWED_TOKEN, token); + } + break; + case LEFT_BRACE: + /* An object is detected. */ + if(flags.contains(DeserializationOptions.ALLOW_JSON_OBJECTS)){ + valueStack.addLast(new JsonObject()); + stateStack.addLast(States.PARSING_OBJECT); + }else{ + throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.DISALLOWED_TOKEN, token); + } + break; + case LEFT_SQUARE: + /* An array is detected. */ + if(flags.contains(DeserializationOptions.ALLOW_JSON_ARRAYS)){ + valueStack.addLast(new JsonArray()); + stateStack.addLast(States.PARSING_ARRAY); + }else{ + throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.DISALLOWED_TOKEN, token); + } + break; + default: + /* Neither a JSON array or object was detected. */ + throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token); + } + break; + case PARSED_ERROR: + /* The parse could be in this state due to the state stack not having a state to pop off. */ + throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token); + case PARSING_ARRAY: + switch(token.getType()){ + case COMMA: + /* The parse could detect a comma while parsing an array since it separates each element. */ + stateStack.addLast(currentState); + break; + case DATUM: + /* The parse found an element of the array. */ + JsonArray val = (JsonArray)valueStack.getLast(); + val.add(token.getValue()); + stateStack.addLast(currentState); + break; + case LEFT_BRACE: + /* The parse found an object in the array. */ + val = (JsonArray)valueStack.getLast(); + final JsonObject object = new JsonObject(); + val.add(object); + valueStack.addLast(object); + stateStack.addLast(currentState); + stateStack.addLast(States.PARSING_OBJECT); + break; + case LEFT_SQUARE: + /* The parse found another array in the array. */ + val = (JsonArray)valueStack.getLast(); + final JsonArray array = new JsonArray(); + val.add(array); + valueStack.addLast(array); + stateStack.addLast(currentState); + stateStack.addLast(States.PARSING_ARRAY); + break; + case RIGHT_SQUARE: + /* The parse found the end of the array. */ + if(valueStack.size() > returnCount){ + valueStack.removeLast(); + }else{ + /* The parse has been fully resolved. */ + stateStack.addLast(States.DONE); + } + break; + default: + /* Any other token is invalid in an array. */ + throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token); + } + break; + case PARSING_OBJECT: + /* The parse has detected the start of an object. */ + switch(token.getType()){ + case COMMA: + /* The parse could detect a comma while parsing an object since it separates each key value + * pair. Continue parsing the object. */ + stateStack.addLast(currentState); + break; + case DATUM: + /* The token ought to be a key. */ + if(token.getValue() instanceof String){ + /* JSON keys are always strings, strings are not always JSON keys but it is going to be + * treated as one. Continue parsing the object. */ + final String key = (String)token.getValue(); + valueStack.addLast(key); + stateStack.addLast(currentState); + stateStack.addLast(States.PARSING_ENTRY); + }else{ + /* Abort! JSON keys are always strings and it wasn't a string. */ + throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token); + } + break; + case RIGHT_BRACE: + /* The parse has found the end of the object. */ + if(valueStack.size() > returnCount){ + /* There are unresolved values remaining. */ + valueStack.removeLast(); + }else{ + /* The parse has been fully resolved. */ + stateStack.addLast(States.DONE); + } + break; + default: + /* The parse didn't detect the end of an object or a key. */ + throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token); + } + break; + case PARSING_ENTRY: + switch(token.getType()){ + /* Parsed pair keys can only happen while parsing objects. */ + case COLON: + /* The parse could detect a colon while parsing a key value pair since it separates the key + * and value from each other. Continue parsing the entry. */ + stateStack.addLast(currentState); + break; + case DATUM: + /* The parse has found a value for the parsed pair key. */ + String key = (String)valueStack.removeLast(); + JsonObject parent = (JsonObject)valueStack.getLast(); + parent.put(key, token.getValue()); + break; + case LEFT_BRACE: + /* The parse has found an object for the parsed pair key. */ + key = (String)valueStack.removeLast(); + parent = (JsonObject)valueStack.getLast(); + final JsonObject object = new JsonObject(); + parent.put(key, object); + valueStack.addLast(object); + stateStack.addLast(States.PARSING_OBJECT); + break; + case LEFT_SQUARE: + /* The parse has found an array for the parsed pair key. */ + key = (String)valueStack.removeLast(); + parent = (JsonObject)valueStack.getLast(); + final JsonArray array = new JsonArray(); + parent.put(key, array); + valueStack.addLast(array); + stateStack.addLast(States.PARSING_ARRAY); + break; + default: + /* The parse didn't find anything for the parsed pair key. */ + throw new DeserializationException(lexer.getPosition(), DeserializationException.Problems.UNEXPECTED_TOKEN, token); + } + break; + default: + break; + } + //System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + //System.out.println(currentState); + //System.out.println(token); + //System.out.println(valueStack); + //System.out.println(stateStack); + /* If we're not at the END and DONE then do the above again. */ + }while(!(States.DONE.equals(currentState) && Yytoken.Types.END.equals(token.getType()))); + //System.out.println("!!!!!!!!!!DESERIALIZED!!!!!!!!!!"); + return new JsonArray(valueStack); + } + + /** A convenience method that assumes a StringReader to deserialize a string. + * @param deserializable representing content to be deserialized as JSON. + * @return either a boolean, null, Number, String, JsonObject, or JsonArray that best represents the deserializable. + * @throws DeserializationException if an unexpected token is encountered in the deserializable. To recover from a + * DeserializationException: fix the deserializable + * to no longer have an unexpected token and try again. + * @see Jsoner#deserialize(Reader) + * @see StringReader */ + public static Object deserialize(final String deserializable) throws DeserializationException{ + Object returnable; + StringReader readableDeserializable = null; + try{ + readableDeserializable = new StringReader(deserializable); + returnable = Jsoner.deserialize(readableDeserializable); + }catch(final NullPointerException caught){ + /* They both have the same recovery scenario. + * See StringReader. + * If deserializable is null, it should be reasonable to expect null back. */ + returnable = null; + }finally{ + if(readableDeserializable != null){ + readableDeserializable.close(); + } + } + return returnable; + } + + /** A convenience method that assumes a JsonArray must be deserialized. + * @param deserializable representing content to be deserializable as a JsonArray. + * @param defaultValue representing what would be returned if deserializable isn't a JsonArray or an IOException, + * NullPointerException, or DeserializationException occurs during deserialization. + * @return a JsonArray that represents the deserializable, or the defaultValue if there isn't a JsonArray that + * represents deserializable. + * @see Jsoner#deserialize(Reader) */ + public static JsonArray deserialize(final String deserializable, final JsonArray defaultValue){ + StringReader readable = null; + JsonArray returnable; + try{ + readable = new StringReader(deserializable); + returnable = Jsoner.deserialize(readable, EnumSet.of(DeserializationOptions.ALLOW_JSON_ARRAYS)).<JsonArray> getCollection(0); + }catch(NullPointerException | DeserializationException caught){ + /* Don't care, just return the default value. */ + returnable = defaultValue; + }finally{ + if(readable != null){ + readable.close(); + } + } + return returnable; + } + + /** A convenience method that assumes a JsonObject must be deserialized. + * @param deserializable representing content to be deserializable as a JsonObject. + * @param defaultValue representing what would be returned if deserializable isn't a JsonObject or an IOException, + * NullPointerException, or DeserializationException occurs during deserialization. + * @return a JsonObject that represents the deserializable, or the defaultValue if there isn't a JsonObject that + * represents deserializable. + * @see Jsoner#deserialize(Reader) */ + public static JsonObject deserialize(final String deserializable, final JsonObject defaultValue){ + StringReader readable = null; + JsonObject returnable; + try{ + readable = new StringReader(deserializable); + returnable = Jsoner.deserialize(readable, EnumSet.of(DeserializationOptions.ALLOW_JSON_OBJECTS)).<JsonObject> getMap(0); + }catch(NullPointerException | DeserializationException caught){ + /* Don't care, just return the default value. */ + returnable = defaultValue; + }finally{ + if(readable != null){ + readable.close(); + } + } + return returnable; + } + + /** A convenience method that assumes multiple RFC 4627 JSON values (except numbers) have been concatenated together + * for deserilization which will be collectively returned in a JsonArray wrapper. + * There may be numbers included, they just must not be concatenated together as it is prone to + * NumberFormatExceptions (thus causing a DeserializationException) or the numbers no longer represent their + * respective values. + * Examples: + * "123null321" returns [123, null, 321] + * "nullnullnulltruefalse\"\"{}[]" returns [null, null, null, true, false, "", {}, []] + * "123" appended to "321" returns [123321] + * "12.3" appended to "3.21" throws DeserializationException(NumberFormatException) + * "123" appended to "-321" throws DeserializationException(NumberFormatException) + * "123e321" appended to "-1" throws DeserializationException(NumberFormatException) + * "null12.33.21null" throws DeserializationException(NumberFormatException) + * @param deserializable representing concatenated content to be deserialized as JSON in one reader. Its contents + * may + * not contain two numbers concatenated together. + * @return a JsonArray that contains each of the concatenated objects as its elements. Each concatenated element is + * either a boolean, null, Number, String, JsonArray, or JsonObject that best represents the concatenated + * content inside deserializable. + * @throws DeserializationException if an unexpected token is encountered in the deserializable. To recover from a + * DeserializationException: fix the deserializable to no longer have an unexpected token and try again. */ + public static JsonArray deserializeMany(final Reader deserializable) throws DeserializationException{ + return Jsoner.deserialize(deserializable, EnumSet.of(DeserializationOptions.ALLOW_JSON_ARRAYS, DeserializationOptions.ALLOW_JSON_OBJECTS, DeserializationOptions.ALLOW_JSON_DATA, DeserializationOptions.ALLOW_CONCATENATED_JSON_VALUES)); + } + + /** Escapes potentially confusing or important characters in the String provided. + * @param escapable an unescaped string. + * @return an escaped string for usage in JSON; An escaped string is one that has escaped all of the quotes ("), + * backslashes (\), return character (\r), new line character (\n), tab character (\t), + * backspace character (\b), form feed character (\f) and other control characters [u0000..u001F] or + * characters [u007F..u009F], [u2000..u20FF] with a + * backslash (\) which itself must be escaped by the backslash in a java string. */ + public static String escape(final String escapable){ + final StringBuilder builder = new StringBuilder(); + final int characters = escapable.length(); + for(int i = 0; i < characters; i++){ + final char character = escapable.charAt(i); + switch(character){ + case '"': + builder.append("\\\""); + break; + case '\\': + builder.append("\\\\"); + break; + case '\b': + builder.append("\\b"); + break; + case '\f': + builder.append("\\f"); + break; + case '\n': + builder.append("\\n"); + break; + case '\r': + builder.append("\\r"); + break; + case '\t': + builder.append("\\t"); + break; + case '/': + builder.append("\\/"); + break; + default: + /* The many characters that get replaced are benign to software but could be mistaken by people + * reading it for a JSON relevant character. */ + if(((character >= '\u0000') && (character <= '\u001F')) || ((character >= '\u007F') && (character <= '\u009F')) || ((character >= '\u2000') && (character <= '\u20FF'))){ + final String characterHexCode = Integer.toHexString(character); + builder.append("\\u"); + for(int k = 0; k < (4 - characterHexCode.length()); k++){ + builder.append("0"); + } + builder.append(characterHexCode.toUpperCase()); + }else{ + /* Character didn't need escaping. */ + builder.append(character); + } + } + } + return builder.toString(); + } + + /** Processes the lexer's reader for the next token. + * @param lexer represents a text processor being used in the deserialization process. + * @return a token representing a meaningful element encountered by the lexer. + * @throws DeserializationException if an unexpected character is encountered while processing the text. */ + private static Yytoken lexNextToken(final Yylex lexer) throws DeserializationException{ + Yytoken returnable; + /* Parse through the next token. */ + try{ + returnable = lexer.yylex(); + }catch(final IOException caught){ + throw new DeserializationException(-1, DeserializationException.Problems.UNEXPECTED_EXCEPTION, caught); + } + if(returnable == null){ + /* If there isn't another token, it must be the end. */ + returnable = new Yytoken(Yytoken.Types.END, null); + } + return returnable; + } + + /** Creates a new JsonKey that wraps the given string and value. This function should NOT be + * used in favor of existing constants and enumerations to make code easier to maintain. + * @param key represents the JsonKey as a String. + * @param value represents the value the JsonKey uses. + * @return a JsonKey that represents the provided key and value. */ + public static JsonKey mintJsonKey(final String key, final Object value){ + return new JsonKey(){ + @Override + public String getKey(){ + return key; + } + + @Override + public Object getValue(){ + return value; + } + }; + } + + /** Used for state transitions while deserializing. + * @param stateStack represents the deserialization states saved for future processing. + * @return a state for deserialization context so it knows how to consume the next token. */ + private static States popNextState(final LinkedList<States> stateStack){ + if(stateStack.size() > 0){ + return stateStack.removeLast(); + }else{ + return States.PARSED_ERROR; + } + } + + /** Formats the JSON string to be more easily human readable using tabs for indentation. + * @param printable representing a JSON formatted string with out extraneous characters, like one returned from + * Jsoner#serialize(Object). + * @return printable except it will have '\n' then '\t' characters inserted after '[', '{', ',' and before ']' '}' + * tokens in the JSON. It will return null if printable isn't a JSON string. */ + public static String prettyPrint(final String printable){ + return Jsoner.prettyPrint(printable, "\t"); + } + + /** Formats the JSON string to be more easily human readable using an arbitrary amount of spaces for indentation. + * @param printable representing a JSON formatted string with out extraneous characters, like one returned from + * Jsoner#serialize(Object). + * @param spaces representing the amount of spaces to use for indentation. Must be between 2 and 10. + * @return printable except it will have '\n' then space characters inserted after '[', '{', ',' and before ']' '}' + * tokens in the JSON. It will return null if printable isn't a JSON string. + * @throws IllegalArgumentException if spaces isn't between [2..10]. + * @see Jsoner#prettyPrint(String) + * @since 2.2.0 to allow pretty printing with spaces instead of tabs. */ + public static String prettyPrint(final String printable, final int spaces){ + if((spaces > 10) || (spaces < 2)){ + throw new IllegalArgumentException("Indentation with spaces must be between 2 and 10."); + } + final StringBuilder indentation = new StringBuilder(""); + for(int i = 0; i < spaces; i++){ + indentation.append(" "); + } + return Jsoner.prettyPrint(printable, indentation.toString()); + } + + /** Makes the JSON string more easily human readable using indentation of the caller's choice. + * @param printable representing a JSON formatted string with out extraneous characters, like one returned from + * Jsoner#serialize(Object). + * @param indentation representing the indentation used to format the JSON string. + * @return printable except it will have '\n' then indentation characters inserted after '[', '{', ',' and before + * ']' '}' + * tokens in the JSON. It will return null if printable isn't a JSON string. */ + private static String prettyPrint(final String printable, final String indentation){ + final Yylex lexer = new Yylex(new StringReader(printable)); + Yytoken lexed; + final StringBuilder returnable = new StringBuilder(); + int level = 0; + try{ + do{ + lexed = Jsoner.lexNextToken(lexer); + switch(lexed.getType()){ + case COLON: + returnable.append(":"); + break; + case COMMA: + returnable.append(lexed.getValue()); + returnable.append("\n"); + for(int i = 0; i < level; i++){ + returnable.append(indentation); + } + break; + case END: + break; + case LEFT_BRACE: + case LEFT_SQUARE: + returnable.append(lexed.getValue()); + returnable.append("\n"); + level++; + for(int i = 0; i < level; i++){ + returnable.append(indentation); + } + break; + case RIGHT_BRACE: + case RIGHT_SQUARE: + returnable.append("\n"); + level--; + for(int i = 0; i < level; i++){ + returnable.append(indentation); + } + returnable.append(lexed.getValue()); + break; + default: + if(lexed.getValue() instanceof String){ + returnable.append("\""); + returnable.append(Jsoner.escape((String)lexed.getValue())); + returnable.append("\""); + }else{ + returnable.append(lexed.getValue()); + } + break; + } + //System.out.println(lexed); + }while(!lexed.getType().equals(Yytoken.Types.END)); + }catch(final DeserializationException caught){ + /* This is according to the method's contract. */ + return null; + } + //System.out.println(printable); + //System.out.println(returnable); + //System.out.println(Jsoner.escape(returnable.toString())); + return returnable.toString(); + } + + /** A convenience method that assumes a StringWriter. + * @param jsonSerializable represents the object that should be serialized as a string in JSON format. + * @return a string, in JSON format, that represents the object provided. + * @throws IllegalArgumentException if the jsonSerializable isn't serializable in JSON. + * @see Jsoner#serialize(Object, Writer) + * @see StringWriter */ + public static String serialize(final Object jsonSerializable){ + final StringWriter writableDestination = new StringWriter(); + try{ + Jsoner.serialize(jsonSerializable, writableDestination); + }catch(final IOException caught){ + /* See StringWriter. */ + } + return writableDestination.toString(); + } + + /** Serializes values according to the RFC 4627 JSON specification. It will also trust the serialization provided by + * any Jsonables it serializes and serializes Enums that don't implement Jsonable as a string of their fully + * qualified name. + * @param jsonSerializable represents the object that should be serialized in JSON format. + * @param writableDestination represents where the resulting JSON text is written to. + * @throws IOException if the writableDestination encounters an I/O problem, like being closed while in use. + * @throws IllegalArgumentException if the jsonSerializable isn't serializable in JSON. */ + public static void serialize(final Object jsonSerializable, final Writer writableDestination) throws IOException{ + Jsoner.serialize(jsonSerializable, writableDestination, EnumSet.of(SerializationOptions.ALLOW_JSONABLES, SerializationOptions.ALLOW_FULLY_QUALIFIED_ENUMERATIONS)); + } + + /** Serialize values to JSON and write them to the provided writer based on behavior flags. + * @param jsonSerializable represents the object that should be serialized to a string in JSON format. + * @param writableDestination represents where the resulting JSON text is written to. + * @param replacement represents what is serialized instead of a non-JSON value when replacements are allowed. + * @param flags represents the allowances and restrictions on serialization. + * @throws IOException if the writableDestination encounters an I/O problem. + * @throws IllegalArgumentException if the jsonSerializable isn't serializable in JSON. + * @see SerializationOptions */ + private static void serialize(final Object jsonSerializable, final Writer writableDestination, final Set<SerializationOptions> flags) throws IOException{ + if(jsonSerializable == null){ + /* When a null is passed in the word null is supported in JSON. */ + writableDestination.write("null"); + }else if(((jsonSerializable instanceof Jsonable) && flags.contains(SerializationOptions.ALLOW_JSONABLES))){ + /* Writes the writable as defined by the writable. */ + writableDestination.write(((Jsonable)jsonSerializable).toJson()); + }else if((jsonSerializable instanceof Enum) && flags.contains(SerializationOptions.ALLOW_FULLY_QUALIFIED_ENUMERATIONS)){ + /* Writes the enum as a special case of string. All enums (unless they implement Jsonable) will be the + * string literal "${DECLARING_CLASS_NAME}.${ENUM_NAME}" as their value. */ + @SuppressWarnings("rawtypes") + final Enum e = (Enum)jsonSerializable; + writableDestination.write('"'); + writableDestination.write(e.getDeclaringClass().getName()); + writableDestination.write('.'); + writableDestination.write(e.name()); + writableDestination.write('"'); + }else if(jsonSerializable instanceof String){ + /* Make sure the string is properly escaped. */ + writableDestination.write('"'); + writableDestination.write(Jsoner.escape((String)jsonSerializable)); + writableDestination.write('"'); + }else if(jsonSerializable instanceof Double){ + if(((Double)jsonSerializable).isInfinite() || ((Double)jsonSerializable).isNaN()){ + /* Infinite and not a number are not supported by the JSON specification, so null is used instead. */ + writableDestination.write("null"); + }else{ + writableDestination.write(jsonSerializable.toString()); + } + }else if(jsonSerializable instanceof Float){ + if(((Float)jsonSerializable).isInfinite() || ((Float)jsonSerializable).isNaN()){ + /* Infinite and not a number are not supported by the JSON specification, so null is used instead. */ + writableDestination.write("null"); + }else{ + writableDestination.write(jsonSerializable.toString()); + } + }else if(jsonSerializable instanceof Number){ + writableDestination.write(jsonSerializable.toString()); + }else if(jsonSerializable instanceof Boolean){ + writableDestination.write(jsonSerializable.toString()); + }else if(jsonSerializable instanceof Map){ + /* Writes the map in JSON object format. */ + boolean isFirstEntry = true; + @SuppressWarnings("rawtypes") + final Iterator entries = ((Map)jsonSerializable).entrySet().iterator(); + writableDestination.write('{'); + while(entries.hasNext()){ + if(isFirstEntry){ + isFirstEntry = false; + }else{ + writableDestination.write(','); + } + @SuppressWarnings("rawtypes") + final Map.Entry entry = (Map.Entry)entries.next(); + Jsoner.serialize(entry.getKey(), writableDestination, flags); + writableDestination.write(':'); + Jsoner.serialize(entry.getValue(), writableDestination, flags); + } + writableDestination.write('}'); + }else if(jsonSerializable instanceof Collection){ + /* Writes the collection in JSON array format. */ + boolean isFirstElement = true; + @SuppressWarnings("rawtypes") + final Iterator elements = ((Collection)jsonSerializable).iterator(); + writableDestination.write('['); + while(elements.hasNext()){ + if(isFirstElement){ + isFirstElement = false; + }else{ + writableDestination.write(','); + } + Jsoner.serialize(elements.next(), writableDestination, flags); + } + writableDestination.write(']'); + }else if(jsonSerializable instanceof byte[]){ + /* Writes the array in JSON array format. */ + final byte[] writableArray = (byte[])jsonSerializable; + final int numberOfElements = writableArray.length; + writableDestination.write('['); + for(int i = 1; i <= numberOfElements; i++){ + if(i == numberOfElements){ + Jsoner.serialize(writableArray[i], writableDestination, flags); + }else{ + Jsoner.serialize(writableArray[i], writableDestination, flags); + writableDestination.write(','); + } + } + writableDestination.write(']'); + }else if(jsonSerializable instanceof short[]){ + /* Writes the array in JSON array format. */ + final short[] writableArray = (short[])jsonSerializable; + final int numberOfElements = writableArray.length; + writableDestination.write('['); + for(int i = 1; i <= numberOfElements; i++){ + if(i == numberOfElements){ + Jsoner.serialize(writableArray[i], writableDestination, flags); + }else{ + Jsoner.serialize(writableArray[i], writableDestination, flags); + writableDestination.write(','); + } + } + writableDestination.write(']'); + }else if(jsonSerializable instanceof int[]){ + /* Writes the array in JSON array format. */ + final int[] writableArray = (int[])jsonSerializable; + final int numberOfElements = writableArray.length; + writableDestination.write('['); + for(int i = 1; i <= numberOfElements; i++){ + if(i == numberOfElements){ + Jsoner.serialize(writableArray[i], writableDestination, flags); + }else{ + Jsoner.serialize(writableArray[i], writableDestination, flags); + writableDestination.write(','); + } + } + writableDestination.write(']'); + }else if(jsonSerializable instanceof long[]){ + /* Writes the array in JSON array format. */ + final long[] writableArray = (long[])jsonSerializable; + final int numberOfElements = writableArray.length; + writableDestination.write('['); + for(int i = 1; i <= numberOfElements; i++){ + if(i == numberOfElements){ + Jsoner.serialize(writableArray[i], writableDestination, flags); + }else{ + Jsoner.serialize(writableArray[i], writableDestination, flags); + writableDestination.write(','); + } + } + writableDestination.write(']'); + }else if(jsonSerializable instanceof float[]){ + /* Writes the array in JSON array format. */ + final float[] writableArray = (float[])jsonSerializable; + final int numberOfElements = writableArray.length; + writableDestination.write('['); + for(int i = 1; i <= numberOfElements; i++){ + if(i == numberOfElements){ + Jsoner.serialize(writableArray[i], writableDestination, flags); + }else{ + Jsoner.serialize(writableArray[i], writableDestination, flags); + writableDestination.write(','); + } + } + writableDestination.write(']'); + }else if(jsonSerializable instanceof double[]){ + /* Writes the array in JSON array format. */ + final double[] writableArray = (double[])jsonSerializable; + final int numberOfElements = writableArray.length; + writableDestination.write('['); + for(int i = 1; i <= numberOfElements; i++){ + if(i == numberOfElements){ + Jsoner.serialize(writableArray[i], writableDestination, flags); + }else{ + Jsoner.serialize(writableArray[i], writableDestination, flags); + writableDestination.write(','); + } + } + writableDestination.write(']'); + }else if(jsonSerializable instanceof boolean[]){ + /* Writes the array in JSON array format. */ + final boolean[] writableArray = (boolean[])jsonSerializable; + final int numberOfElements = writableArray.length; + writableDestination.write('['); + for(int i = 1; i <= numberOfElements; i++){ + if(i == numberOfElements){ + Jsoner.serialize(writableArray[i], writableDestination, flags); + }else{ + Jsoner.serialize(writableArray[i], writableDestination, flags); + writableDestination.write(','); + } + } + writableDestination.write(']'); + }else if(jsonSerializable instanceof char[]){ + /* Writes the array in JSON array format. */ + final char[] writableArray = (char[])jsonSerializable; + final int numberOfElements = writableArray.length; + writableDestination.write("[\""); + for(int i = 1; i <= numberOfElements; i++){ + if(i == numberOfElements){ + Jsoner.serialize(writableArray[i], writableDestination, flags); + }else{ + Jsoner.serialize(writableArray[i], writableDestination, flags); + writableDestination.write("\",\""); + } + } + writableDestination.write("\"]"); + }else if(jsonSerializable instanceof Object[]){ + /* Writes the array in JSON array format. */ + final Object[] writableArray = (Object[])jsonSerializable; + final int numberOfElements = writableArray.length; + writableDestination.write('['); + for(int i = 1; i <= numberOfElements; i++){ + if(i == numberOfElements){ + Jsoner.serialize(writableArray[i], writableDestination, flags); + }else{ + Jsoner.serialize(writableArray[i], writableDestination, flags); + writableDestination.write(","); + } + } + writableDestination.write(']'); + }else{ + /* It cannot by any measure be safely serialized according to specification. */ + if(flags.contains(SerializationOptions.ALLOW_INVALIDS)){ + /* Can be helpful for debugging how it isn't valid. */ + writableDestination.write(jsonSerializable.toString()); + }else{ + /* Notify the caller the cause of failure for the serialization. */ + throw new IllegalArgumentException("Encountered a: " + jsonSerializable.getClass().getName() + " as: " + jsonSerializable.toString() + " that isn't JSON serializable.\n Try:\n 1) Implementing the Jsonable interface for the object to return valid JSON. If it already does it probably has a bug.\n 2) If you cannot edit the source of the object or couple it with this library consider wrapping it in a class that does implement the Jsonable interface.\n 3) Otherwise convert it to a boolean, null, number, JsonArray, JsonObject, or String value before serializing it.\n 4) If you feel it should have serialized you could use a more tolerant serialization for debugging purposes."); + } + } + //System.out.println(writableDestination.toString()); + } + + /** Serializes like the first version of this library. + * It has been adapted to use Jsonable for serializing custom objects, but otherwise works like the old JSON string + * serializer. It + * will allow non-JSON values in its output like the old one. It can be helpful for last resort log statements and + * debugging errors in self generated JSON. Anything serialized using this method isn't guaranteed to be + * deserializable. + * @param jsonSerializable represents the object that should be serialized in JSON format. + * @param writableDestination represents where the resulting JSON text is written to. + * @throws IOException if the writableDestination encounters an I/O problem, like being closed while in use. */ + public static void serializeCarelessly(final Object jsonSerializable, final Writer writableDestination) throws IOException{ + Jsoner.serialize(jsonSerializable, writableDestination, EnumSet.of(SerializationOptions.ALLOW_JSONABLES, SerializationOptions.ALLOW_INVALIDS)); + } + + /** Serializes JSON values and only JSON values according to the RFC 4627 JSON specification. + * @param jsonSerializable represents the object that should be serialized in JSON format. + * @param writableDestination represents where the resulting JSON text is written to. + * @throws IOException if the writableDestination encounters an I/O problem, like being closed while in use. + * @throws IllegalArgumentException if the jsonSerializable isn't serializable in raw JSON. */ + public static void serializeStrictly(final Object jsonSerializable, final Writer writableDestination) throws IOException{ + Jsoner.serialize(jsonSerializable, writableDestination, EnumSet.noneOf(SerializationOptions.class)); + } +} diff --git a/core/java/src/org/json/simple/parser/Yylex.java b/core/java/src/org/json/simple/Yylex.java similarity index 88% rename from core/java/src/org/json/simple/parser/Yylex.java rename to core/java/src/org/json/simple/Yylex.java index 11be091922f1fed28ca182dbc8fba23596153dfe..db438aa562341c0a2f35c14e91cd78c03dc90637 100644 --- a/core/java/src/org/json/simple/parser/Yylex.java +++ b/core/java/src/org/json/simple/Yylex.java @@ -1,688 +1,695 @@ -/* The following code was generated by JFlex 1.4.2 */ - -package org.json.simple.parser; - -class Yylex { - - /** This character denotes the end of file */ - public static final int YYEOF = -1; - - /** initial size of the lookahead buffer */ - private static final int ZZ_BUFFERSIZE = 16384; - - /** lexical states */ - public static final int YYINITIAL = 0; - public static final int STRING_BEGIN = 2; - - /** - * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l - * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l - * at the beginning of a line - * l is of the form l = 2*k, k a non negative integer - */ - private static final int ZZ_LEXSTATE[] = { - 0, 0, 1, 1 - }; - - /** - * Translates characters to character classes - */ - private static final String ZZ_CMAP_PACKED = - "\11\0\1\7\1\7\2\0\1\7\22\0\1\7\1\0\1\11\10\0"+ - "\1\6\1\31\1\2\1\4\1\12\12\3\1\32\6\0\4\1\1\5"+ - "\1\1\24\0\1\27\1\10\1\30\3\0\1\22\1\13\2\1\1\21"+ - "\1\14\5\0\1\23\1\0\1\15\3\0\1\16\1\24\1\17\1\20"+ - "\5\0\1\25\1\0\1\26\uff82\0"; - - /** - * Translates characters to character classes - */ - private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); - - /** - * Translates DFA states to action switch labels. - */ - private static final int [] ZZ_ACTION = zzUnpackAction(); - - private static final String ZZ_ACTION_PACKED_0 = - "\2\0\2\1\1\2\1\3\1\4\3\1\1\5\1\6"+ - "\1\7\1\10\1\11\1\12\1\13\1\14\1\15\5\0"+ - "\1\14\1\16\1\17\1\20\1\21\1\22\1\23\1\24"+ - "\1\0\1\25\1\0\1\25\4\0\1\26\1\27\2\0"+ - "\1\30"; - - private static int [] zzUnpackAction() { - int [] result = new int[45]; - int offset = 0; - offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackAction(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int count = packed.charAt(i++); - int value = packed.charAt(i++); - do result[j++] = value; while (--count > 0); - } - return j; - } - - - /** - * Translates a state to a row index in the transition table - */ - private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); - - private static final String ZZ_ROWMAP_PACKED_0 = - "\0\0\0\33\0\66\0\121\0\154\0\207\0\66\0\242"+ - "\0\275\0\330\0\66\0\66\0\66\0\66\0\66\0\66"+ - "\0\363\0\u010e\0\66\0\u0129\0\u0144\0\u015f\0\u017a\0\u0195"+ - "\0\66\0\66\0\66\0\66\0\66\0\66\0\66\0\66"+ - "\0\u01b0\0\u01cb\0\u01e6\0\u01e6\0\u0201\0\u021c\0\u0237\0\u0252"+ - "\0\66\0\66\0\u026d\0\u0288\0\66"; - - private static int [] zzUnpackRowMap() { - int [] result = new int[45]; - int offset = 0; - offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackRowMap(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int high = packed.charAt(i++) << 16; - result[j++] = high | packed.charAt(i++); - } - return j; - } - - /** - * The transition table of the DFA - */ - private static final int ZZ_TRANS [] = { - 2, 2, 3, 4, 2, 2, 2, 5, 2, 6, - 2, 2, 7, 8, 2, 9, 2, 2, 2, 2, - 2, 10, 11, 12, 13, 14, 15, 16, 16, 16, - 16, 16, 16, 16, 16, 17, 18, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 4, 19, 20, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 20, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 21, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 16, 16, 16, 16, 16, 16, 16, - 16, -1, -1, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - -1, -1, -1, -1, -1, -1, -1, -1, 24, 25, - 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 34, 35, -1, -1, - 34, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 37, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 38, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 39, -1, 39, -1, 39, -1, -1, - -1, -1, -1, 39, 39, -1, -1, -1, -1, 39, - 39, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 33, -1, 20, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 20, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 38, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 40, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 41, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 42, -1, 42, -1, 42, - -1, -1, -1, -1, -1, 42, 42, -1, -1, -1, - -1, 42, 42, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 43, -1, 43, -1, 43, -1, -1, -1, - -1, -1, 43, 43, -1, -1, -1, -1, 43, 43, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, - -1, 44, -1, 44, -1, -1, -1, -1, -1, 44, - 44, -1, -1, -1, -1, 44, 44, -1, -1, -1, - -1, -1, -1, -1, -1, - }; - - /* error codes */ - private static final int ZZ_UNKNOWN_ERROR = 0; - private static final int ZZ_NO_MATCH = 1; - private static final int ZZ_PUSHBACK_2BIG = 2; - - /* error messages for the codes above */ - private static final String ZZ_ERROR_MSG[] = { - "Unkown internal scanner error", - "Error: could not match input", - "Error: pushback value was too large" - }; - - /** - * ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code> - */ - private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); - - private static final String ZZ_ATTRIBUTE_PACKED_0 = - "\2\0\1\11\3\1\1\11\3\1\6\11\2\1\1\11"+ - "\5\0\10\11\1\0\1\1\1\0\1\1\4\0\2\11"+ - "\2\0\1\11"; - - private static int [] zzUnpackAttribute() { - int [] result = new int[45]; - int offset = 0; - offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackAttribute(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int count = packed.charAt(i++); - int value = packed.charAt(i++); - do result[j++] = value; while (--count > 0); - } - return j; - } - - /** the input device */ - private java.io.Reader zzReader; - - /** the current state of the DFA */ - private int zzState; - - /** the current lexical state */ - private int zzLexicalState = YYINITIAL; - - /** this buffer contains the current text to be matched and is - the source of the yytext() string */ - private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; - - /** the textposition at the last accepting state */ - private int zzMarkedPos; - - /** the current text position in the buffer */ - private int zzCurrentPos; - - /** startRead marks the beginning of the yytext() string in the buffer */ - private int zzStartRead; - - /** endRead marks the last character in the buffer, that has been read - from input */ - private int zzEndRead; - - /** number of newlines encountered up to the start of the matched text */ - private int yyline; - - /** the number of characters up to the start of the matched text */ - private int yychar; - - /** - * the number of characters from the last newline up to the start of the - * matched text - */ - private int yycolumn; - - /** - * zzAtBOL == true <=> the scanner is currently at the beginning of a line - */ - private boolean zzAtBOL = true; - - /** zzAtEOF == true <=> the scanner is at the EOF */ - private boolean zzAtEOF; - - /* user code: */ -private StringBuffer sb=new StringBuffer(); +/* The following code was generated by JFlex 1.4.3 on 8/30/16 5:50 PM */ + +package org.json.simple; + + +/** + * This class is a scanner generated by + * <a href="http://www.jflex.de/">JFlex</a> 1.4.3 + * on 8/30/16 5:50 PM from the specification file + * <tt>/home/davinloegering/cliftonlabs/workspace/json-simple/src/main/lex/jsonstrict.lex</tt> + */ +class Yylex { + + /** This character denotes the end of file */ + public static final int YYEOF = -1; + + /** initial size of the lookahead buffer */ + private static final int ZZ_BUFFERSIZE = 16384; + + /** lexical states */ + public static final int YYINITIAL = 0; + public static final int STRING_BEGIN = 2; + + /** + * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private static final int ZZ_LEXSTATE[] = { + 0, 0, 1, 1 + }; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = + "\11\0\1\7\1\7\2\0\1\7\22\0\1\7\1\0\1\11\10\0"+ + "\1\6\1\31\1\2\1\4\1\12\12\3\1\32\6\0\4\1\1\5"+ + "\1\1\24\0\1\27\1\10\1\30\3\0\1\22\1\13\2\1\1\21"+ + "\1\14\5\0\1\23\1\0\1\15\3\0\1\16\1\24\1\17\1\20"+ + "\5\0\1\25\1\0\1\26\uff82\0"; + + /** + * Translates characters to character classes + */ + private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int [] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = + "\2\0\2\1\1\2\1\3\1\4\3\1\1\5\1\6"+ + "\1\7\1\10\1\11\1\12\1\13\1\14\1\15\5\0"+ + "\1\14\1\16\1\17\1\20\1\21\1\22\1\23\1\24"+ + "\1\0\1\2\1\0\1\2\4\0\1\25\1\26\2\0"+ + "\1\27"; + + private static int [] zzUnpackAction() { + int [] result = new int[45]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /** + * Translates a state to a row index in the transition table + */ + private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 = + "\0\0\0\33\0\66\0\121\0\154\0\207\0\66\0\242"+ + "\0\275\0\330\0\66\0\66\0\66\0\66\0\66\0\66"+ + "\0\363\0\u010e\0\66\0\u0129\0\u0144\0\u015f\0\u017a\0\u0195"+ + "\0\66\0\66\0\66\0\66\0\66\0\66\0\66\0\66"+ + "\0\u01b0\0\u01cb\0\u01e6\0\u01e6\0\u0201\0\u021c\0\u0237\0\u0252"+ + "\0\66\0\66\0\u026d\0\u0288\0\66"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[45]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int ZZ_TRANS [] = { + 2, 2, 3, 4, 2, 2, 2, 5, 2, 6, + 2, 2, 7, 8, 2, 9, 2, 2, 2, 2, + 2, 10, 11, 12, 13, 14, 15, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 4, 19, 20, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 20, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 21, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 16, 16, 16, 16, 16, 16, 16, + 16, -1, -1, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + -1, -1, -1, -1, -1, -1, -1, -1, 24, 25, + 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 34, 35, -1, -1, + 34, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 37, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 38, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 39, -1, 39, -1, 39, -1, -1, + -1, -1, -1, 39, 39, -1, -1, -1, -1, 39, + 39, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 33, -1, 20, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 20, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 38, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 40, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 41, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 42, -1, 42, -1, 42, + -1, -1, -1, -1, -1, 42, 42, -1, -1, -1, + -1, 42, 42, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 43, -1, 43, -1, 43, -1, -1, -1, + -1, -1, 43, 43, -1, -1, -1, -1, 43, 43, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, + -1, 44, -1, 44, -1, -1, -1, -1, -1, 44, + 44, -1, -1, -1, -1, 44, 44, -1, -1, -1, + -1, -1, -1, -1, -1, + }; + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code> + */ + private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\2\0\1\11\3\1\1\11\3\1\6\11\2\1\1\11"+ + "\5\0\10\11\1\0\1\1\1\0\1\1\4\0\2\11"+ + "\2\0\1\11"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[45]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + /** the input device */ + private java.io.Reader zzReader; + + /** the current state of the DFA */ + private int zzState; + + /** the current lexical state */ + private int zzLexicalState = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; + + /** the textposition at the last accepting state */ + private int zzMarkedPos; + + /** the current text position in the buffer */ + private int zzCurrentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int zzStartRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int zzEndRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + + /** zzAtEOF == true <=> the scanner is at the EOF */ + private boolean zzAtEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean zzEOFDone; + + /* user code: */ +private StringBuilder sb=new StringBuilder(); int getPosition(){ return yychar; } - - - /** - * Creates a new scanner - * There is also a java.io.InputStream version of this constructor. - * - * @param in the java.io.Reader to read input from. - */ - Yylex(java.io.Reader in) { - this.zzReader = in; - } - - /** - * Creates a new scanner. - * There is also java.io.Reader version of this constructor. - * - * @param in the java.io.Inputstream to read input from. - */ - Yylex(java.io.InputStream in) { - this(new java.io.InputStreamReader(in)); - } - - /** - * Unpacks the compressed character translation table. - * - * @param packed the packed character translation table - * @return the unpacked character translation table - */ - private static char [] zzUnpackCMap(String packed) { - char [] map = new char[0x10000]; - int i = 0; /* index in packed string */ - int j = 0; /* index in unpacked array */ - while (i < 90) { - int count = packed.charAt(i++); - char value = packed.charAt(i++); - do map[j++] = value; while (--count > 0); - } - return map; - } - - - /** - * Refills the input buffer. - * - * @return <code>false</code>, iff there was new input. - * - * @exception java.io.IOException if any I/O-Error occurs - */ - private boolean zzRefill() throws java.io.IOException { - - /* first: make room (if you can) */ - if (zzStartRead > 0) { - System.arraycopy(zzBuffer, zzStartRead, - zzBuffer, 0, - zzEndRead-zzStartRead); - - /* translate stored positions */ - zzEndRead-= zzStartRead; - zzCurrentPos-= zzStartRead; - zzMarkedPos-= zzStartRead; - zzStartRead = 0; - } - - /* is the buffer big enough? */ - if (zzCurrentPos >= zzBuffer.length) { - /* if not: blow it up */ - char newBuffer[] = new char[zzCurrentPos*2]; - System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); - zzBuffer = newBuffer; - } - - /* finally: fill the buffer with new input */ - int numRead = zzReader.read(zzBuffer, zzEndRead, - zzBuffer.length-zzEndRead); - - if (numRead > 0) { - zzEndRead+= numRead; - return false; - } - // unlikely but not impossible: read 0 characters, but not at end of stream - if (numRead == 0) { - int c = zzReader.read(); - if (c == -1) { - return true; - } else { - zzBuffer[zzEndRead++] = (char) c; - return false; - } - } - - // numRead < 0 - return true; - } - - - /** - * Closes the input stream. - */ - public final void yyclose() throws java.io.IOException { - zzAtEOF = true; /* indicate end of file */ - zzEndRead = zzStartRead; /* invalidate buffer */ - - if (zzReader != null) - zzReader.close(); - } - - - /** - * Resets the scanner to read from a new input stream. - * Does not close the old reader. - * - * All internal variables are reset, the old input stream - * <b>cannot</b> be reused (internal buffer is discarded and lost). - * Lexical state is set to <code>ZZ_INITIAL</code>. - * - * @param reader the new input stream - */ - public final void yyreset(java.io.Reader reader) { - zzReader = reader; - zzAtBOL = true; - zzAtEOF = false; - zzEndRead = zzStartRead = 0; - zzCurrentPos = zzMarkedPos = 0; - yyline = yychar = yycolumn = 0; - zzLexicalState = YYINITIAL; - } - - - /** - * Returns the current lexical state. - */ - public final int yystate() { - return zzLexicalState; - } - - - /** - * Enters a new lexical state - * - * @param newState the new lexical state - */ - public final void yybegin(int newState) { - zzLexicalState = newState; - } - - - /** - * Returns the text matched by the current regular expression. - */ - public final String yytext() { - return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); - } - - - /** - * Returns the character at position <code>pos</code> from the - * matched text. - * - * It is equivalent to yytext().charAt(pos), but faster - * - * @param pos the position of the character to fetch. - * A value from 0 to yylength()-1. - * - * @return the character at position pos - */ - public final char yycharat(int pos) { - return zzBuffer[zzStartRead+pos]; - } - - - /** - * Returns the length of the matched text region. - */ - public final int yylength() { - return zzMarkedPos-zzStartRead; - } - - - /** - * Reports an error that occured while scanning. - * - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method - * will only be called with things that "Can't Possibly Happen". - * If this method is called, something is seriously wrong - * (e.g. a JFlex bug producing a faulty scanner etc.). - * - * Usual syntax/scanner level error handling should be done - * in error fallback rules. - * - * @param errorCode the code of the errormessage to display - */ - private void zzScanError(int errorCode) { - String message; - try { - message = ZZ_ERROR_MSG[errorCode]; - } - catch (ArrayIndexOutOfBoundsException e) { - message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; - } - - throw new Error(message); - } - - - /** - * Pushes the specified amount of characters back into the input stream. - * - * They will be read again by then next call of the scanning method - * - * @param number the number of characters to be read again. - * This number must not be greater than yylength()! - */ - public void yypushback(int number) { - if ( number > yylength() ) - zzScanError(ZZ_PUSHBACK_2BIG); - - zzMarkedPos -= number; - } - - - /** - * Resumes scanning until the next regular expression is matched, - * the end of input is encountered or an I/O-Error occurs. - * - * @return the next token - * @exception java.io.IOException if any I/O-Error occurs - */ - public Yytoken yylex() throws java.io.IOException, ParseException { - int zzInput; - int zzAction; - - // cached fields: - int zzCurrentPosL; - int zzMarkedPosL; - int zzEndReadL = zzEndRead; - char [] zzBufferL = zzBuffer; - char [] zzCMapL = ZZ_CMAP; - - int [] zzTransL = ZZ_TRANS; - int [] zzRowMapL = ZZ_ROWMAP; - int [] zzAttrL = ZZ_ATTRIBUTE; - - while (true) { - zzMarkedPosL = zzMarkedPos; - - yychar+= zzMarkedPosL-zzStartRead; - - zzAction = -1; - - zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; - - zzState = ZZ_LEXSTATE[zzLexicalState]; - - - zzForAction: { - while (true) { - - if (zzCurrentPosL < zzEndReadL) - zzInput = zzBufferL[zzCurrentPosL++]; - else if (zzAtEOF) { - zzInput = YYEOF; - break zzForAction; - } - else { - // store back cached positions - zzCurrentPos = zzCurrentPosL; - zzMarkedPos = zzMarkedPosL; - boolean eof = zzRefill(); - // get translated positions and possibly new buffer - zzCurrentPosL = zzCurrentPos; - zzMarkedPosL = zzMarkedPos; - zzBufferL = zzBuffer; - zzEndReadL = zzEndRead; - if (eof) { - zzInput = YYEOF; - break zzForAction; - } - else { - zzInput = zzBufferL[zzCurrentPosL++]; - } - } - int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; - if (zzNext == -1) break zzForAction; - zzState = zzNext; - - int zzAttributes = zzAttrL[zzState]; - if ( (zzAttributes & 1) == 1 ) { - zzAction = zzState; - zzMarkedPosL = zzCurrentPosL; - if ( (zzAttributes & 8) == 8 ) break zzForAction; - } - - } - } - - // store back cached position - zzMarkedPos = zzMarkedPosL; - - switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { - case 11: - { sb.append(yytext()); - } - case 25: break; - case 4: - { sb.delete(0, sb.length());yybegin(STRING_BEGIN); - } - case 26: break; - case 16: - { sb.append('\b'); - } - case 27: break; - case 6: - { return new Yytoken(Yytoken.TYPE_RIGHT_BRACE,null); - } - case 28: break; - case 23: - { Boolean val=Boolean.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE, val); - } - case 29: break; - case 22: - { return new Yytoken(Yytoken.TYPE_VALUE, null); - } - case 30: break; - case 13: - { yybegin(YYINITIAL);return new Yytoken(Yytoken.TYPE_VALUE, sb.toString()); - } - case 31: break; - case 12: - { sb.append('\\'); - } - case 32: break; - case 21: - { Double val=Double.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE, val); - } - case 33: break; - case 1: - { throw new ParseException(yychar, ParseException.ERROR_UNEXPECTED_CHAR, new Character(yycharat(0))); - } - case 34: break; - case 8: - { return new Yytoken(Yytoken.TYPE_RIGHT_SQUARE,null); - } - case 35: break; - case 19: - { sb.append('\r'); - } - case 36: break; - case 15: - { sb.append('/'); - } - case 37: break; - case 10: - { return new Yytoken(Yytoken.TYPE_COLON,null); - } - case 38: break; - case 14: - { sb.append('"'); - } - case 39: break; - case 5: - { return new Yytoken(Yytoken.TYPE_LEFT_BRACE,null); - } - case 40: break; - case 17: - { sb.append('\f'); - } - case 41: break; - case 24: + + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ + Yylex(java.io.Reader in) { + this.zzReader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + Yylex(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] zzUnpackCMap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 90) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Refills the input buffer. + * + * @return <code>false</code>, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + + /* first: make room (if you can) */ + if (zzStartRead > 0) { + System.arraycopy(zzBuffer, zzStartRead, + zzBuffer, 0, + zzEndRead-zzStartRead); + + /* translate stored positions */ + zzEndRead-= zzStartRead; + zzCurrentPos-= zzStartRead; + zzMarkedPos-= zzStartRead; + zzStartRead = 0; + } + + /* is the buffer big enough? */ + if (zzCurrentPos >= zzBuffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[zzCurrentPos*2]; + System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); + zzBuffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = zzReader.read(zzBuffer, zzEndRead, + zzBuffer.length-zzEndRead); + + if (numRead > 0) { + zzEndRead+= numRead; + return false; + } + // unlikely but not impossible: read 0 characters, but not at end of stream + if (numRead == 0) { + int c = zzReader.read(); + if (c == -1) { + return true; + } else { + zzBuffer[zzEndRead++] = (char) c; + return false; + } + } + + // numRead < 0 + return true; + } + + + /** + * Closes the input stream. + */ + public final void yyclose() throws java.io.IOException { + zzAtEOF = true; /* indicate end of file */ + zzEndRead = zzStartRead; /* invalidate buffer */ + + if (zzReader != null) + zzReader.close(); + } + + + /** + * Resets the scanner to read from a new input stream. + * Does not close the old reader. + * + * All internal variables are reset, the old input stream + * <b>cannot</b> be reused (internal buffer is discarded and lost). + * Lexical state is set to <tt>ZZ_INITIAL</tt>. + * + * @param reader the new input stream + */ + public final void yyreset(java.io.Reader reader) { + zzReader = reader; + zzAtBOL = true; + zzAtEOF = false; + zzEOFDone = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final String yytext() { + return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); + } + + + /** + * Returns the character at position <tt>pos</tt> from the + * matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBuffer[zzStartRead+pos]; + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos-zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + */ + public Yytoken yylex() throws java.io.IOException, DeserializationException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char [] zzBufferL = zzBuffer; + char [] zzCMapL = ZZ_CMAP; + + int [] zzTransL = ZZ_TRANS; + int [] zzRowMapL = ZZ_ROWMAP; + int [] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + yychar+= zzMarkedPosL-zzStartRead; + + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = ZZ_LEXSTATE[zzLexicalState]; + + + zzForAction: { + while (true) { + + if (zzCurrentPosL < zzEndReadL) + zzInput = zzBufferL[zzCurrentPosL++]; + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } + else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } + else { + zzInput = zzBufferL[zzCurrentPosL++]; + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + int zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ( (zzAttributes & 8) == 8 ) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 4: + { sb = null; sb = new StringBuilder(); yybegin(STRING_BEGIN); + } + case 24: break; + case 11: + { sb.append(yytext()); + } + case 25: break; + case 5: + { return new Yytoken(Yytoken.Types.LEFT_BRACE, null); + } + case 26: break; + case 16: + { sb.append('\b'); + } + case 27: break; + case 23: { try{ - int ch=Integer.parseInt(yytext().substring(2),16); - sb.append((char)ch); - } - catch(Exception e){ - throw new ParseException(yychar, ParseException.ERROR_UNEXPECTED_EXCEPTION, e); - } - } - case 42: break; - case 20: - { sb.append('\t'); - } - case 43: break; - case 7: - { return new Yytoken(Yytoken.TYPE_LEFT_SQUARE,null); - } - case 44: break; - case 2: - { Long val=Long.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE, val); - } - case 45: break; - case 18: - { sb.append('\n'); - } - case 46: break; - case 9: - { return new Yytoken(Yytoken.TYPE_COMMA,null); - } - case 47: break; - case 3: - { - } - case 48: break; - default: - if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { - zzAtEOF = true; - return null; - } - else { - zzScanError(ZZ_NO_MATCH); - } - } - } - } - - -} + int ch=Integer.parseInt(yytext().substring(2),16); + sb.append((char)ch); + }catch(Exception e){ + /* The lexer is broken if it can build a 4 byte character code and fail to append the character. */ + throw new DeserializationException(yychar, DeserializationException.Problems.UNEXPECTED_EXCEPTION, e); + } + } + case 28: break; + case 22: + { Boolean val=Boolean.valueOf(yytext()); return new Yytoken(Yytoken.Types.DATUM, val); + } + case 29: break; + case 12: + { sb.append('\\'); + } + case 30: break; + case 10: + { return new Yytoken(Yytoken.Types.COLON, null); + } + case 31: break; + case 9: + { return new Yytoken(Yytoken.Types.COMMA, null); + } + case 32: break; + case 21: + { return new Yytoken(Yytoken.Types.DATUM, null); + } + case 33: break; + case 19: + { sb.append('\r'); + } + case 34: break; + case 15: + { sb.append('/'); + } + case 35: break; + case 2: + { java.math.BigDecimal val= new java.math.BigDecimal(yytext()); return new Yytoken(Yytoken.Types.DATUM, val); + } + case 36: break; + case 14: + { sb.append('"'); + } + case 37: break; + case 8: + { return new Yytoken(Yytoken.Types.RIGHT_SQUARE, null); + } + case 38: break; + case 17: + { sb.append('\f'); + } + case 39: break; + case 1: + { throw new DeserializationException(yychar, DeserializationException.Problems.UNEXPECTED_CHARACTER, new Character(yycharat(0))); + } + case 40: break; + case 6: + { return new Yytoken(Yytoken.Types.RIGHT_BRACE, null); + } + case 41: break; + case 20: + { sb.append('\t'); + } + case 42: break; + case 7: + { return new Yytoken(Yytoken.Types.LEFT_SQUARE, null); + } + case 43: break; + case 18: + { sb.append('\n'); + } + case 44: break; + case 13: + { yybegin(YYINITIAL);return new Yytoken(Yytoken.Types.DATUM, sb.toString()); + } + case 45: break; + case 3: + { + } + case 46: break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + return null; + } + else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/core/java/src/org/json/simple/Yytoken.java b/core/java/src/org/json/simple/Yytoken.java new file mode 100644 index 0000000000000000000000000000000000000000..72e503c02cad4d7d1baa8297c676998d77cc7fdd --- /dev/null +++ b/core/java/src/org/json/simple/Yytoken.java @@ -0,0 +1,92 @@ +/* Copyright 2016 Clifton Labs + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ +package org.json.simple; + +/** Represents structural entities in JSON. + * @since 2.0.0 */ +class Yytoken{ + /** Represents the different kinds of tokens. */ + enum Types{ + /** Tokens of this type will always have a value of ":" */ + COLON, + /** Tokens of this type will always have a value of "," */ + COMMA, + /** Tokens of this type will always have a value that is a boolean, null, number, or string. */ + DATUM, + /** Tokens of this type will always have a value of "" */ + END, + /** Tokens of this type will always have a value of "{" */ + LEFT_BRACE, + /** Tokens of this type will always have a value of "[" */ + LEFT_SQUARE, + /** Tokens of this type will always have a value of "}" */ + RIGHT_BRACE, + /** Tokens of this type will always have a value of "]" */ + RIGHT_SQUARE; + } + + private final Types type; + private final Object value; + + /** @param type represents the kind of token the instantiated token will be. + * @param value represents the value the token is associated with, will be ignored unless type is equal to + * Types.DATUM. + * @see Types */ + Yytoken(final Types type, final Object value){ + /* Sanity check. Make sure the value is ignored for the proper value unless it is a datum token. */ + switch(type){ + case COLON: + this.value = ":"; + break; + case COMMA: + this.value = ","; + break; + case END: + this.value = ""; + break; + case LEFT_BRACE: + this.value = "{"; + break; + case LEFT_SQUARE: + this.value = "["; + break; + case RIGHT_BRACE: + this.value = "}"; + break; + case RIGHT_SQUARE: + this.value = "]"; + break; + default: + this.value = value; + break; + } + this.type = type; + } + + /** @return which of the Types the token is. + * @see Types */ + Types getType(){ + return this.type; + } + + /** @return what the token is. + * @see Types */ + Object getValue(){ + return this.value; + } + + @Override + public String toString(){ + final StringBuffer sb = new StringBuffer(); + sb.append(this.type.toString()).append("(").append(this.value).append(")"); + return sb.toString(); + } +} diff --git a/core/java/src/org/json/simple/package.html b/core/java/src/org/json/simple/package.html index 0c028c5722fc2a66bc034ef0c90247ec9279feee..bd8e1ac5d0d864436dcd6f25ecdd6111677e4faf 100644 --- a/core/java/src/org/json/simple/package.html +++ b/core/java/src/org/json/simple/package.html @@ -1,9 +1,10 @@ <html> <body> <p> -This is json-simple release 1.1.1 2012-02-19 +This is json-simple release 2.3.0 2017-10-04 retrieved from <a href="https://github.com/cliftonlabs/json-simple">github</a>. </p><p> +Only the new 2.0 API is included. Unmodified, except for javadoc fixes. Apache 2. </p> diff --git a/core/java/src/org/json/simple/parser/ContainerFactory.java b/core/java/src/org/json/simple/parser/ContainerFactory.java deleted file mode 100644 index 5ff4ce1727ca1ddcc64ac02ab5a2c0a17c43a4d1..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/parser/ContainerFactory.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.json.simple.parser; - -import java.util.List; -import java.util.Map; - -/** - * Container factory for creating containers for JSON object and JSON array. - * - * @see org.json.simple.parser.JSONParser#parse(java.io.Reader, ContainerFactory) - * - * @author FangYidong fangyidong@yahoo.com.cn - */ -public interface ContainerFactory { - /** - * @return A Map instance to store JSON object, or null if you want to use org.json.simple.JSONObject. - */ - Map createObjectContainer(); - - /** - * @return A List instance to store JSON array, or null if you want to use org.json.simple.JSONArray. - */ - List creatArrayContainer(); -} diff --git a/core/java/src/org/json/simple/parser/ContentHandler.java b/core/java/src/org/json/simple/parser/ContentHandler.java deleted file mode 100644 index 36dcfd7bf8de2cabb768034581efa10cd83d492d..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/parser/ContentHandler.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.json.simple.parser; - -import java.io.IOException; - -/** - * A simplified and stoppable SAX-like content handler for stream processing of JSON text. - * - * @see org.xml.sax.ContentHandler - * @see org.json.simple.parser.JSONParser#parse(java.io.Reader, ContentHandler, boolean) - * - * @author FangYidong fangyidong@yahoo.com.cn - */ -public interface ContentHandler { - /** - * Receive notification of the beginning of JSON processing. - * The parser will invoke this method only once. - * - * @throws ParseException - * - JSONParser will stop and throw the same exception to the caller when receiving this exception. - */ - void startJSON() throws ParseException, IOException; - - /** - * Receive notification of the end of JSON processing. - * - * @throws ParseException - */ - void endJSON() throws ParseException, IOException; - - /** - * Receive notification of the beginning of a JSON object. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * - JSONParser will stop and throw the same exception to the caller when receiving this exception. - * @see #endJSON - */ - boolean startObject() throws ParseException, IOException; - - /** - * Receive notification of the end of a JSON object. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * - * @see #startObject - */ - boolean endObject() throws ParseException, IOException; - - /** - * Receive notification of the beginning of a JSON object entry. - * - * @param key - Key of a JSON object entry. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * - * @see #endObjectEntry - */ - boolean startObjectEntry(String key) throws ParseException, IOException; - - /** - * Receive notification of the end of the value of previous object entry. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * - * @see #startObjectEntry - */ - boolean endObjectEntry() throws ParseException, IOException; - - /** - * Receive notification of the beginning of a JSON array. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * - * @see #endArray - */ - boolean startArray() throws ParseException, IOException; - - /** - * Receive notification of the end of a JSON array. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * - * @see #startArray - */ - boolean endArray() throws ParseException, IOException; - - /** - * Receive notification of the JSON primitive values: - * java.lang.String, - * java.lang.Number, - * java.lang.Boolean - * null - * - * @param value - Instance of the following: - * java.lang.String, - * java.lang.Number, - * java.lang.Boolean - * null - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - */ - boolean primitive(Object value) throws ParseException, IOException; - -} diff --git a/core/java/src/org/json/simple/parser/JSONParser.java b/core/java/src/org/json/simple/parser/JSONParser.java deleted file mode 100644 index a32d81cc00860322cca7ed559ea6313ef13ad314..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/parser/JSONParser.java +++ /dev/null @@ -1,531 +0,0 @@ -/* - * $Id: JSONParser.java,v 1.1 2006/04/15 14:10:48 platform Exp $ - * Created on 2006-4-15 - */ -package org.json.simple.parser; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; - - -/** - * Parser for JSON text. Please note that JSONParser is NOT thread-safe. - * - * @author FangYidong fangyidong@yahoo.com.cn - */ -public class JSONParser { - public static final int S_INIT=0; - public static final int S_IN_FINISHED_VALUE=1;//string,number,boolean,null,object,array - public static final int S_IN_OBJECT=2; - public static final int S_IN_ARRAY=3; - public static final int S_PASSED_PAIR_KEY=4; - public static final int S_IN_PAIR_VALUE=5; - public static final int S_END=6; - public static final int S_IN_ERROR=-1; - - private LinkedList handlerStatusStack; - private Yylex lexer = new Yylex((Reader)null); - private Yytoken token = null; - private int status = S_INIT; - - private int peekStatus(LinkedList statusStack){ - if(statusStack.size()==0) - return -1; - Integer status=(Integer)statusStack.getFirst(); - return status.intValue(); - } - - /** - * Reset the parser to the initial state without resetting the underlying reader. - * - */ - public void reset(){ - token = null; - status = S_INIT; - handlerStatusStack = null; - } - - /** - * Reset the parser to the initial state with a new character reader. - * - * @param in - The new character reader. - */ - public void reset(Reader in){ - lexer.yyreset(in); - reset(); - } - - /** - * @return The position of the beginning of the current token. - */ - public int getPosition(){ - return lexer.getPosition(); - } - - public Object parse(String s) throws ParseException{ - return parse(s, (ContainerFactory)null); - } - - public Object parse(String s, ContainerFactory containerFactory) throws ParseException{ - StringReader in=new StringReader(s); - try{ - return parse(in, containerFactory); - } - catch(IOException ie){ - /* - * Actually it will never happen. - */ - throw new ParseException(-1, ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); - } - } - - public Object parse(Reader in) throws IOException, ParseException{ - return parse(in, (ContainerFactory)null); - } - - /** - * Parse JSON text into java object from the input source. - * - * @param in - * @param containerFactory - Use this factory to createyour own JSON object and JSON array containers. - * @return Instance of the following: - * org.json.simple.JSONObject, - * org.json.simple.JSONArray, - * java.lang.String, - * java.lang.Number, - * java.lang.Boolean, - * null - * - * @throws IOException - * @throws ParseException - */ - public Object parse(Reader in, ContainerFactory containerFactory) throws IOException, ParseException{ - reset(in); - LinkedList statusStack = new LinkedList(); - LinkedList valueStack = new LinkedList(); - - try{ - do{ - nextToken(); - switch(status){ - case S_INIT: - switch(token.type){ - case Yytoken.TYPE_VALUE: - status=S_IN_FINISHED_VALUE; - statusStack.addFirst(new Integer(status)); - valueStack.addFirst(token.value); - break; - case Yytoken.TYPE_LEFT_BRACE: - status=S_IN_OBJECT; - statusStack.addFirst(new Integer(status)); - valueStack.addFirst(createObjectContainer(containerFactory)); - break; - case Yytoken.TYPE_LEFT_SQUARE: - status=S_IN_ARRAY; - statusStack.addFirst(new Integer(status)); - valueStack.addFirst(createArrayContainer(containerFactory)); - break; - default: - status=S_IN_ERROR; - }//inner switch - break; - - case S_IN_FINISHED_VALUE: - if(token.type==Yytoken.TYPE_EOF) - return valueStack.removeFirst(); - else - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - - case S_IN_OBJECT: - switch(token.type){ - case Yytoken.TYPE_COMMA: - break; - case Yytoken.TYPE_VALUE: - if(token.value instanceof String){ - String key=(String)token.value; - valueStack.addFirst(key); - status=S_PASSED_PAIR_KEY; - statusStack.addFirst(new Integer(status)); - } - else{ - status=S_IN_ERROR; - } - break; - case Yytoken.TYPE_RIGHT_BRACE: - if(valueStack.size()>1){ - statusStack.removeFirst(); - valueStack.removeFirst(); - status=peekStatus(statusStack); - } - else{ - status=S_IN_FINISHED_VALUE; - } - break; - default: - status=S_IN_ERROR; - break; - }//inner switch - break; - - case S_PASSED_PAIR_KEY: - switch(token.type){ - case Yytoken.TYPE_COLON: - break; - case Yytoken.TYPE_VALUE: - statusStack.removeFirst(); - String key=(String)valueStack.removeFirst(); - Map parent=(Map)valueStack.getFirst(); - parent.put(key,token.value); - status=peekStatus(statusStack); - break; - case Yytoken.TYPE_LEFT_SQUARE: - statusStack.removeFirst(); - key=(String)valueStack.removeFirst(); - parent=(Map)valueStack.getFirst(); - List newArray=createArrayContainer(containerFactory); - parent.put(key,newArray); - status=S_IN_ARRAY; - statusStack.addFirst(new Integer(status)); - valueStack.addFirst(newArray); - break; - case Yytoken.TYPE_LEFT_BRACE: - statusStack.removeFirst(); - key=(String)valueStack.removeFirst(); - parent=(Map)valueStack.getFirst(); - Map newObject=createObjectContainer(containerFactory); - parent.put(key,newObject); - status=S_IN_OBJECT; - statusStack.addFirst(new Integer(status)); - valueStack.addFirst(newObject); - break; - default: - status=S_IN_ERROR; - } - break; - - case S_IN_ARRAY: - switch(token.type){ - case Yytoken.TYPE_COMMA: - break; - case Yytoken.TYPE_VALUE: - List val=(List)valueStack.getFirst(); - val.add(token.value); - break; - case Yytoken.TYPE_RIGHT_SQUARE: - if(valueStack.size()>1){ - statusStack.removeFirst(); - valueStack.removeFirst(); - status=peekStatus(statusStack); - } - else{ - status=S_IN_FINISHED_VALUE; - } - break; - case Yytoken.TYPE_LEFT_BRACE: - val=(List)valueStack.getFirst(); - Map newObject=createObjectContainer(containerFactory); - val.add(newObject); - status=S_IN_OBJECT; - statusStack.addFirst(new Integer(status)); - valueStack.addFirst(newObject); - break; - case Yytoken.TYPE_LEFT_SQUARE: - val=(List)valueStack.getFirst(); - List newArray=createArrayContainer(containerFactory); - val.add(newArray); - status=S_IN_ARRAY; - statusStack.addFirst(new Integer(status)); - valueStack.addFirst(newArray); - break; - default: - status=S_IN_ERROR; - }//inner switch - break; - case S_IN_ERROR: - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - }//switch - if(status==S_IN_ERROR){ - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } - }while(token.type!=Yytoken.TYPE_EOF); - } - catch(IOException ie){ - throw ie; - } - - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } - - private void nextToken() throws ParseException, IOException{ - token = lexer.yylex(); - if(token == null) - token = new Yytoken(Yytoken.TYPE_EOF, null); - } - - private Map createObjectContainer(ContainerFactory containerFactory){ - if(containerFactory == null) - return new JSONObject(); - Map m = containerFactory.createObjectContainer(); - - if(m == null) - return new JSONObject(); - return m; - } - - private List createArrayContainer(ContainerFactory containerFactory){ - if(containerFactory == null) - return new JSONArray(); - List l = containerFactory.creatArrayContainer(); - - if(l == null) - return new JSONArray(); - return l; - } - - public void parse(String s, ContentHandler contentHandler) throws ParseException{ - parse(s, contentHandler, false); - } - - public void parse(String s, ContentHandler contentHandler, boolean isResume) throws ParseException{ - StringReader in=new StringReader(s); - try{ - parse(in, contentHandler, isResume); - } - catch(IOException ie){ - /* - * Actually it will never happen. - */ - throw new ParseException(-1, ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); - } - } - - public void parse(Reader in, ContentHandler contentHandler) throws IOException, ParseException{ - parse(in, contentHandler, false); - } - - /** - * Stream processing of JSON text. - * - * @see ContentHandler - * - * @param in - * @param contentHandler - * @param isResume - Indicates if it continues previous parsing operation. - * If set to true, resume parsing the old stream, and parameter 'in' will be ignored. - * If this method is called for the first time in this instance, isResume will be ignored. - * - * @throws IOException - * @throws ParseException - */ - public void parse(Reader in, ContentHandler contentHandler, boolean isResume) throws IOException, ParseException{ - if(!isResume){ - reset(in); - handlerStatusStack = new LinkedList(); - } - else{ - if(handlerStatusStack == null){ - isResume = false; - reset(in); - handlerStatusStack = new LinkedList(); - } - } - - LinkedList statusStack = handlerStatusStack; - - try{ - do{ - switch(status){ - case S_INIT: - contentHandler.startJSON(); - nextToken(); - switch(token.type){ - case Yytoken.TYPE_VALUE: - status=S_IN_FINISHED_VALUE; - statusStack.addFirst(new Integer(status)); - if(!contentHandler.primitive(token.value)) - return; - break; - case Yytoken.TYPE_LEFT_BRACE: - status=S_IN_OBJECT; - statusStack.addFirst(new Integer(status)); - if(!contentHandler.startObject()) - return; - break; - case Yytoken.TYPE_LEFT_SQUARE: - status=S_IN_ARRAY; - statusStack.addFirst(new Integer(status)); - if(!contentHandler.startArray()) - return; - break; - default: - status=S_IN_ERROR; - }//inner switch - break; - - case S_IN_FINISHED_VALUE: - nextToken(); - if(token.type==Yytoken.TYPE_EOF){ - contentHandler.endJSON(); - status = S_END; - return; - } - else{ - status = S_IN_ERROR; - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } - - case S_IN_OBJECT: - nextToken(); - switch(token.type){ - case Yytoken.TYPE_COMMA: - break; - case Yytoken.TYPE_VALUE: - if(token.value instanceof String){ - String key=(String)token.value; - status=S_PASSED_PAIR_KEY; - statusStack.addFirst(new Integer(status)); - if(!contentHandler.startObjectEntry(key)) - return; - } - else{ - status=S_IN_ERROR; - } - break; - case Yytoken.TYPE_RIGHT_BRACE: - if(statusStack.size()>1){ - statusStack.removeFirst(); - status=peekStatus(statusStack); - } - else{ - status=S_IN_FINISHED_VALUE; - } - if(!contentHandler.endObject()) - return; - break; - default: - status=S_IN_ERROR; - break; - }//inner switch - break; - - case S_PASSED_PAIR_KEY: - nextToken(); - switch(token.type){ - case Yytoken.TYPE_COLON: - break; - case Yytoken.TYPE_VALUE: - statusStack.removeFirst(); - status=peekStatus(statusStack); - if(!contentHandler.primitive(token.value)) - return; - if(!contentHandler.endObjectEntry()) - return; - break; - case Yytoken.TYPE_LEFT_SQUARE: - statusStack.removeFirst(); - statusStack.addFirst(new Integer(S_IN_PAIR_VALUE)); - status=S_IN_ARRAY; - statusStack.addFirst(new Integer(status)); - if(!contentHandler.startArray()) - return; - break; - case Yytoken.TYPE_LEFT_BRACE: - statusStack.removeFirst(); - statusStack.addFirst(new Integer(S_IN_PAIR_VALUE)); - status=S_IN_OBJECT; - statusStack.addFirst(new Integer(status)); - if(!contentHandler.startObject()) - return; - break; - default: - status=S_IN_ERROR; - } - break; - - case S_IN_PAIR_VALUE: - /* - * S_IN_PAIR_VALUE is just a marker to indicate the end of an object entry, it doesn't proccess any token, - * therefore delay consuming token until next round. - */ - statusStack.removeFirst(); - status = peekStatus(statusStack); - if(!contentHandler.endObjectEntry()) - return; - break; - - case S_IN_ARRAY: - nextToken(); - switch(token.type){ - case Yytoken.TYPE_COMMA: - break; - case Yytoken.TYPE_VALUE: - if(!contentHandler.primitive(token.value)) - return; - break; - case Yytoken.TYPE_RIGHT_SQUARE: - if(statusStack.size()>1){ - statusStack.removeFirst(); - status=peekStatus(statusStack); - } - else{ - status=S_IN_FINISHED_VALUE; - } - if(!contentHandler.endArray()) - return; - break; - case Yytoken.TYPE_LEFT_BRACE: - status=S_IN_OBJECT; - statusStack.addFirst(new Integer(status)); - if(!contentHandler.startObject()) - return; - break; - case Yytoken.TYPE_LEFT_SQUARE: - status=S_IN_ARRAY; - statusStack.addFirst(new Integer(status)); - if(!contentHandler.startArray()) - return; - break; - default: - status=S_IN_ERROR; - }//inner switch - break; - - case S_END: - return; - - case S_IN_ERROR: - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - }//switch - if(status==S_IN_ERROR){ - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } - }while(token.type!=Yytoken.TYPE_EOF); - } - catch(IOException ie){ - status = S_IN_ERROR; - throw ie; - } - catch(ParseException pe){ - status = S_IN_ERROR; - throw pe; - } - catch(RuntimeException re){ - status = S_IN_ERROR; - throw re; - } - catch(Error e){ - status = S_IN_ERROR; - throw e; - } - - status = S_IN_ERROR; - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } -} diff --git a/core/java/src/org/json/simple/parser/ParseException.java b/core/java/src/org/json/simple/parser/ParseException.java deleted file mode 100644 index 39f95f61608a4557e50b73b12b13ed1f68410097..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/parser/ParseException.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.json.simple.parser; - -/** - * ParseException explains why and where the error occurs in source JSON text. - * - * @author FangYidong fangyidong@yahoo.com.cn - * - */ -public class ParseException extends Exception { - private static final long serialVersionUID = -7880698968187728548L; - - public static final int ERROR_UNEXPECTED_CHAR = 0; - public static final int ERROR_UNEXPECTED_TOKEN = 1; - public static final int ERROR_UNEXPECTED_EXCEPTION = 2; - - private int errorType; - private Object unexpectedObject; - private int position; - - public ParseException(int errorType){ - this(-1, errorType, null); - } - - public ParseException(int errorType, Object unexpectedObject){ - this(-1, errorType, unexpectedObject); - } - - public ParseException(int position, int errorType, Object unexpectedObject){ - this.position = position; - this.errorType = errorType; - this.unexpectedObject = unexpectedObject; - } - - public int getErrorType() { - return errorType; - } - - public void setErrorType(int errorType) { - this.errorType = errorType; - } - - /** - * @see org.json.simple.parser.JSONParser#getPosition() - * - * @return The character position (starting with 0) of the input where the error occurs. - */ - public int getPosition() { - return position; - } - - public void setPosition(int position) { - this.position = position; - } - - /** - * @see org.json.simple.parser.Yytoken - * - * @return One of the following base on the value of errorType: - * ERROR_UNEXPECTED_CHAR java.lang.Character - * ERROR_UNEXPECTED_TOKEN org.json.simple.parser.Yytoken - * ERROR_UNEXPECTED_EXCEPTION java.lang.Exception - */ - public Object getUnexpectedObject() { - return unexpectedObject; - } - - public void setUnexpectedObject(Object unexpectedObject) { - this.unexpectedObject = unexpectedObject; - } - - public String toString(){ - StringBuffer sb = new StringBuffer(); - - switch(errorType){ - case ERROR_UNEXPECTED_CHAR: - sb.append("Unexpected character (").append(unexpectedObject).append(") at position ").append(position).append("."); - break; - case ERROR_UNEXPECTED_TOKEN: - sb.append("Unexpected token ").append(unexpectedObject).append(" at position ").append(position).append("."); - break; - case ERROR_UNEXPECTED_EXCEPTION: - sb.append("Unexpected exception at position ").append(position).append(": ").append(unexpectedObject); - break; - default: - sb.append("Unkown error at position ").append(position).append("."); - break; - } - return sb.toString(); - } -} diff --git a/core/java/src/org/json/simple/parser/Yytoken.java b/core/java/src/org/json/simple/parser/Yytoken.java deleted file mode 100644 index c8b3ca7e2604b8929888551aefa52d4ca838bb36..0000000000000000000000000000000000000000 --- a/core/java/src/org/json/simple/parser/Yytoken.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * $Id: Yytoken.java,v 1.1 2006/04/15 14:10:48 platform Exp $ - * Created on 2006-4-15 - */ -package org.json.simple.parser; - -/** - * @author FangYidong fangyidong@yahoo.com.cn - */ -public class Yytoken { - public static final int TYPE_VALUE=0;//JSON primitive value: string,number,boolean,null - public static final int TYPE_LEFT_BRACE=1; - public static final int TYPE_RIGHT_BRACE=2; - public static final int TYPE_LEFT_SQUARE=3; - public static final int TYPE_RIGHT_SQUARE=4; - public static final int TYPE_COMMA=5; - public static final int TYPE_COLON=6; - public static final int TYPE_EOF=-1;//end of file - - public int type=0; - public Object value=null; - - public Yytoken(int type,Object value){ - this.type=type; - this.value=value; - } - - public String toString(){ - StringBuffer sb = new StringBuffer(); - switch(type){ - case TYPE_VALUE: - sb.append("VALUE(").append(value).append(")"); - break; - case TYPE_LEFT_BRACE: - sb.append("LEFT BRACE({)"); - break; - case TYPE_RIGHT_BRACE: - sb.append("RIGHT BRACE(})"); - break; - case TYPE_LEFT_SQUARE: - sb.append("LEFT SQUARE([)"); - break; - case TYPE_RIGHT_SQUARE: - sb.append("RIGHT SQUARE(])"); - break; - case TYPE_COMMA: - sb.append("COMMA(,)"); - break; - case TYPE_COLON: - sb.append("COLON(:)"); - break; - case TYPE_EOF: - sb.append("END OF FILE"); - break; - } - return sb.toString(); - } -} diff --git a/debian-alt/bionic/control b/debian-alt/bionic/control index ce9ca8117421c21bb7870906c1844cafb32695d4..46e0af8319670cef30e42be6910c4529b198a0f7 100644 --- a/debian-alt/bionic/control +++ b/debian-alt/bionic/control @@ -16,7 +16,7 @@ Build-Depends: debhelper (>= 9.20160709) ,bash-completion ,gettext ,libgetopt-java - ,libjson-simple-java (<< 3) + ,libjson-simple-java (>= 2),libjson-simple-java (<< 3) ,libgmp-dev (>= 2:5.0.5) ,libservice-wrapper-java ,po-debconf @@ -78,7 +78,7 @@ Depends: ${misc:Depends}, ${java:Depends}, ${shlibs:Depends}, geoip-database, gettext-base, libgetopt-java, - libjson-simple-java (<< 3), + libjson-simple-java (>= 2),libjson-simple-java (<< 3), libjetty9-java (>= 9.4), libtaglibs-standard-jstlel-java, libtomcat8-java, diff --git a/debian-alt/xenial/control b/debian-alt/xenial/control index 03b2552cb6c0fa5ee9b4696b6b57f158e531cfb9..d7bc542ef20abea838b40a4183f8d13135501b47 100644 --- a/debian-alt/xenial/control +++ b/debian-alt/xenial/control @@ -17,7 +17,6 @@ Build-Depends: debhelper (>= 7.0.50~) ,bash-completion ,gettext ,libgetopt-java - ,libjson-simple-java (<< 3) ,libgmp-dev (>= 2:5.0.2) ,libservice-wrapper-java ,hardening-wrapper @@ -83,7 +82,6 @@ Depends: ${misc:Depends}, ${java:Depends}, ${shlibs:Depends}, geoip-database, gettext-base, libgetopt-java, - libjson-simple-java (<< 3), famfamfam-flag-png Replaces: i2p ( << 0.8.6-5) Breaks: i2p (<< 0.8.6-5) diff --git a/debian/control b/debian/control index 3ab225c38980d177a258f9f7cf61360afdeef56c..1cca4bbc5a5c1fe914076d7efe453c8a76843c42 100644 --- a/debian/control +++ b/debian/control @@ -18,7 +18,7 @@ Build-Depends: debhelper (>= 9.20160709) ,bash-completion ,gettext ,libgetopt-java - ,libjson-simple-java (<< 3) + ,libjson-simple-java (>= 2),libjson-simple-java (<< 3) ,libgmp-dev (>= 2:5.0.5) ,libservice-wrapper-java ,po-debconf @@ -82,7 +82,7 @@ Depends: ${misc:Depends}, ${java:Depends}, ${shlibs:Depends}, geoip-database, gettext-base, libgetopt-java, - libjson-simple-java (<< 3), + libjson-simple-java (>= 2),libjson-simple-java (<< 3), libjetty9-java (>= 9.4), libtaglibs-standard-jstlel-java, libtomcat9-java,