1
0
Fork 0
mirror of https://github.com/ethauvin/JSON-java.git synced 2025-06-17 07:50:52 -07:00

Java 1.8.

This commit is contained in:
Douglas Crockford 2014-05-05 15:09:32 -07:00
parent 48d31b7f5c
commit a9a0762383
26 changed files with 747 additions and 819 deletions

View file

@ -41,7 +41,7 @@ SOFTWARE.
* The names for the elements in the JSONObjects can be taken from the names
* in the first row.
* @author JSON.org
* @version 2012-11-13
* @version 2014-05-03
*/
public class CDL {
@ -142,7 +142,7 @@ public class CDL {
* @return A string ending in NEWLINE.
*/
public static String rowToString(JSONArray ja) {
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ja.length(); i += 1) {
if (i > 0) {
sb.append(',');

View file

@ -1,169 +1,169 @@
package org.json;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* Convert a web browser cookie specification to a JSONObject and back.
* JSON and Cookies are both notations for name/value pairs.
* @author JSON.org
* @version 2010-12-24
*/
public class Cookie {
/**
* Produce a copy of a string in which the characters '+', '%', '=', ';'
* and control characters are replaced with "%hh". This is a gentle form
* of URL encoding, attempting to cause as little distortion to the
* string as possible. The characters '=' and ';' are meta characters in
* cookies. By convention, they are escaped using the URL-encoding. This is
* only a convention, not a standard. Often, cookies are expected to have
* encoded values. We encode '=' and ';' because we must. We encode '%' and
* '+' because they are meta characters in URL encoding.
* @param string The source string.
* @return The escaped result.
*/
public static String escape(String string) {
char c;
String s = string.trim();
StringBuffer sb = new StringBuffer();
int length = s.length();
for (int i = 0; i < length; i += 1) {
c = s.charAt(i);
if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
sb.append('%');
sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16));
sb.append(Character.forDigit((char)(c & 0x0f), 16));
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* Convert a cookie specification string into a JSONObject. The string
* will contain a name value pair separated by '='. The name and the value
* will be unescaped, possibly converting '+' and '%' sequences. The
* cookie properties may follow, separated by ';', also represented as
* name=value (except the secure property, which does not have a value).
* The name will be stored under the key "name", and the value will be
* stored under the key "value". This method does not do checking or
* validation of the parameters. It only converts the cookie string into
* a JSONObject.
* @param string The cookie specification string.
* @return A JSONObject containing "name", "value", and possibly other
* members.
* @throws JSONException
*/
public static JSONObject toJSONObject(String string) throws JSONException {
String name;
JSONObject jo = new JSONObject();
Object value;
JSONTokener x = new JSONTokener(string);
jo.put("name", x.nextTo('='));
x.next('=');
jo.put("value", x.nextTo(';'));
x.next();
while (x.more()) {
name = unescape(x.nextTo("=;"));
if (x.next() != '=') {
if (name.equals("secure")) {
value = Boolean.TRUE;
} else {
throw x.syntaxError("Missing '=' in cookie parameter.");
}
} else {
value = unescape(x.nextTo(';'));
x.next();
}
jo.put(name, value);
}
return jo;
}
/**
* Convert a JSONObject into a cookie specification string. The JSONObject
* must contain "name" and "value" members.
* If the JSONObject contains "expires", "domain", "path", or "secure"
* members, they will be appended to the cookie specification string.
* All other members are ignored.
* @param jo A JSONObject
* @return A cookie specification string
* @throws JSONException
*/
public static String toString(JSONObject jo) throws JSONException {
StringBuffer sb = new StringBuffer();
sb.append(escape(jo.getString("name")));
sb.append("=");
sb.append(escape(jo.getString("value")));
if (jo.has("expires")) {
sb.append(";expires=");
sb.append(jo.getString("expires"));
}
if (jo.has("domain")) {
sb.append(";domain=");
sb.append(escape(jo.getString("domain")));
}
if (jo.has("path")) {
sb.append(";path=");
sb.append(escape(jo.getString("path")));
}
if (jo.optBoolean("secure")) {
sb.append(";secure");
}
return sb.toString();
}
/**
* Convert <code>%</code><i>hh</i> sequences to single characters, and
* convert plus to space.
* @param string A string that may contain
* <code>+</code>&nbsp;<small>(plus)</small> and
* <code>%</code><i>hh</i> sequences.
* @return The unescaped string.
*/
public static String unescape(String string) {
int length = string.length();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; ++i) {
char c = string.charAt(i);
if (c == '+') {
c = ' ';
} else if (c == '%' && i + 2 < length) {
int d = JSONTokener.dehexchar(string.charAt(i + 1));
int e = JSONTokener.dehexchar(string.charAt(i + 2));
if (d >= 0 && e >= 0) {
c = (char)(d * 16 + e);
i += 2;
}
}
sb.append(c);
}
return sb.toString();
}
}
package org.json;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* Convert a web browser cookie specification to a JSONObject and back.
* JSON and Cookies are both notations for name/value pairs.
* @author JSON.org
* @version 2014-05-03
*/
public class Cookie {
/**
* Produce a copy of a string in which the characters '+', '%', '=', ';'
* and control characters are replaced with "%hh". This is a gentle form
* of URL encoding, attempting to cause as little distortion to the
* string as possible. The characters '=' and ';' are meta characters in
* cookies. By convention, they are escaped using the URL-encoding. This is
* only a convention, not a standard. Often, cookies are expected to have
* encoded values. We encode '=' and ';' because we must. We encode '%' and
* '+' because they are meta characters in URL encoding.
* @param string The source string.
* @return The escaped result.
*/
public static String escape(String string) {
char c;
String s = string.trim();
int length = s.length();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i += 1) {
c = s.charAt(i);
if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
sb.append('%');
sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16));
sb.append(Character.forDigit((char)(c & 0x0f), 16));
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* Convert a cookie specification string into a JSONObject. The string
* will contain a name value pair separated by '='. The name and the value
* will be unescaped, possibly converting '+' and '%' sequences. The
* cookie properties may follow, separated by ';', also represented as
* name=value (except the secure property, which does not have a value).
* The name will be stored under the key "name", and the value will be
* stored under the key "value". This method does not do checking or
* validation of the parameters. It only converts the cookie string into
* a JSONObject.
* @param string The cookie specification string.
* @return A JSONObject containing "name", "value", and possibly other
* members.
* @throws JSONException
*/
public static JSONObject toJSONObject(String string) throws JSONException {
String name;
JSONObject jo = new JSONObject();
Object value;
JSONTokener x = new JSONTokener(string);
jo.put("name", x.nextTo('='));
x.next('=');
jo.put("value", x.nextTo(';'));
x.next();
while (x.more()) {
name = unescape(x.nextTo("=;"));
if (x.next() != '=') {
if (name.equals("secure")) {
value = Boolean.TRUE;
} else {
throw x.syntaxError("Missing '=' in cookie parameter.");
}
} else {
value = unescape(x.nextTo(';'));
x.next();
}
jo.put(name, value);
}
return jo;
}
/**
* Convert a JSONObject into a cookie specification string. The JSONObject
* must contain "name" and "value" members.
* If the JSONObject contains "expires", "domain", "path", or "secure"
* members, they will be appended to the cookie specification string.
* All other members are ignored.
* @param jo A JSONObject
* @return A cookie specification string
* @throws JSONException
*/
public static String toString(JSONObject jo) throws JSONException {
StringBuilder sb = new StringBuilder();
sb.append(escape(jo.getString("name")));
sb.append("=");
sb.append(escape(jo.getString("value")));
if (jo.has("expires")) {
sb.append(";expires=");
sb.append(jo.getString("expires"));
}
if (jo.has("domain")) {
sb.append(";domain=");
sb.append(escape(jo.getString("domain")));
}
if (jo.has("path")) {
sb.append(";path=");
sb.append(escape(jo.getString("path")));
}
if (jo.optBoolean("secure")) {
sb.append(";secure");
}
return sb.toString();
}
/**
* Convert <code>%</code><i>hh</i> sequences to single characters, and
* convert plus to space.
* @param string A string that may contain
* <code>+</code>&nbsp;<small>(plus)</small> and
* <code>%</code><i>hh</i> sequences.
* @return The unescaped string.
*/
public static String unescape(String string) {
int length = string.length();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; ++i) {
char c = string.charAt(i);
if (c == '+') {
c = ' ';
} else if (c == '%' && i + 2 < length) {
int d = JSONTokener.dehexchar(string.charAt(i + 1));
int e = JSONTokener.dehexchar(string.charAt(i + 2));
if (d >= 0 && e >= 0) {
c = (char)(d * 16 + e);
i += 2;
}
}
sb.append(c);
}
return sb.toString();
}
}

View file

@ -1,90 +1,89 @@
package org.json;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import java.util.Iterator;
/**
* Convert a web browser cookie list string to a JSONObject and back.
* @author JSON.org
* @version 2010-12-24
*/
public class CookieList {
/**
* Convert a cookie list into a JSONObject. A cookie list is a sequence
* of name/value pairs. The names are separated from the values by '='.
* The pairs are separated by ';'. The names and the values
* will be unescaped, possibly converting '+' and '%' sequences.
*
* To add a cookie to a cooklist,
* cookielistJSONObject.put(cookieJSONObject.getString("name"),
* cookieJSONObject.getString("value"));
* @param string A cookie list string
* @return A JSONObject
* @throws JSONException
*/
public static JSONObject toJSONObject(String string) throws JSONException {
JSONObject jo = new JSONObject();
JSONTokener x = new JSONTokener(string);
while (x.more()) {
String name = Cookie.unescape(x.nextTo('='));
x.next('=');
jo.put(name, Cookie.unescape(x.nextTo(';')));
x.next();
}
return jo;
}
/**
* Convert a JSONObject into a cookie list. A cookie list is a sequence
* of name/value pairs. The names are separated from the values by '='.
* The pairs are separated by ';'. The characters '%', '+', '=', and ';'
* in the names and values are replaced by "%hh".
* @param jo A JSONObject
* @return A cookie list string
* @throws JSONException
*/
public static String toString(JSONObject jo) throws JSONException {
boolean b = false;
Iterator keys = jo.keys();
String string;
StringBuffer sb = new StringBuffer();
while (keys.hasNext()) {
string = keys.next().toString();
if (!jo.isNull(string)) {
if (b) {
sb.append(';');
}
sb.append(Cookie.escape(string));
sb.append("=");
sb.append(Cookie.escape(jo.getString(string)));
b = true;
}
}
return sb.toString();
}
}
package org.json;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import java.util.Iterator;
/**
* Convert a web browser cookie list string to a JSONObject and back.
* @author JSON.org
* @version 2014-05-03
*/
public class CookieList {
/**
* Convert a cookie list into a JSONObject. A cookie list is a sequence
* of name/value pairs. The names are separated from the values by '='.
* The pairs are separated by ';'. The names and the values
* will be unescaped, possibly converting '+' and '%' sequences.
*
* To add a cookie to a cooklist,
* cookielistJSONObject.put(cookieJSONObject.getString("name"),
* cookieJSONObject.getString("value"));
* @param string A cookie list string
* @return A JSONObject
* @throws JSONException
*/
public static JSONObject toJSONObject(String string) throws JSONException {
JSONObject jo = new JSONObject();
JSONTokener x = new JSONTokener(string);
while (x.more()) {
String name = Cookie.unescape(x.nextTo('='));
x.next('=');
jo.put(name, Cookie.unescape(x.nextTo(';')));
x.next();
}
return jo;
}
/**
* Convert a JSONObject into a cookie list. A cookie list is a sequence
* of name/value pairs. The names are separated from the values by '='.
* The pairs are separated by ';'. The characters '%', '+', '=', and ';'
* in the names and values are replaced by "%hh".
* @param jo A JSONObject
* @return A cookie list string
* @throws JSONException
*/
public static String toString(JSONObject jo) throws JSONException {
boolean b = false;
Iterator<String> keys = jo.keys();
String string;
StringBuilder sb = new StringBuilder();
while (keys.hasNext()) {
string = keys.next();
if (!jo.isNull(string)) {
if (b) {
sb.append(';');
}
sb.append(Cookie.escape(string));
sb.append("=");
sb.append(Cookie.escape(jo.getString(string)));
b = true;
}
}
return sb.toString();
}
}

View file

@ -29,7 +29,7 @@ import java.util.Iterator;
/**
* Convert an HTTP header to a JSONObject and back.
* @author JSON.org
* @version 2010-12-24
* @version 2014-05-03
*/
public class HTTP {
@ -125,9 +125,9 @@ public class HTTP {
* information.
*/
public static String toString(JSONObject jo) throws JSONException {
Iterator keys = jo.keys();
String string;
StringBuffer sb = new StringBuffer();
Iterator<String> keys = jo.keys();
String string;
StringBuilder sb = new StringBuilder();
if (jo.has("Status-Code") && jo.has("Reason-Phrase")) {
sb.append(jo.getString("HTTP-Version"));
sb.append(' ');
@ -147,7 +147,7 @@ public class HTTP {
}
sb.append(CRLF);
while (keys.hasNext()) {
string = keys.next().toString();
string = keys.next();
if (!"HTTP-Version".equals(string) && !"Status-Code".equals(string) &&
!"Reason-Phrase".equals(string) && !"Method".equals(string) &&
!"Request-URI".equals(string) && !jo.isNull(string)) {

View file

@ -28,7 +28,7 @@ SOFTWARE.
* The HTTPTokener extends the JSONTokener to provide additional methods
* for the parsing of HTTP headers.
* @author JSON.org
* @version 2012-11-13
* @version 2014-05-03
*/
public class HTTPTokener extends JSONTokener {
@ -49,7 +49,7 @@ public class HTTPTokener extends JSONTokener {
public String nextToken() throws JSONException {
char c;
char q;
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
do {
c = next();
} while (Character.isWhitespace(c));

View file

@ -75,20 +75,20 @@ import java.util.Map;
* </ul>
*
* @author JSON.org
* @version 2014-04-21
* @version 2014-05-03
*/
public class JSONArray {
/**
* The arrayList where the JSONArray's properties are kept.
*/
private final ArrayList myArrayList;
private final ArrayList<Object> myArrayList;
/**
* Construct an empty JSONArray.
*/
public JSONArray() {
this.myArrayList = new ArrayList();
this.myArrayList = new ArrayList<Object>();
}
/**
@ -150,10 +150,10 @@ public class JSONArray {
* @param collection
* A Collection.
*/
public JSONArray(Collection collection) {
this.myArrayList = new ArrayList();
public JSONArray(Collection<Object> collection) {
this.myArrayList = new ArrayList<Object>();
if (collection != null) {
Iterator iter = collection.iterator();
Iterator<Object> iter = collection.iterator();
while (iter.hasNext()) {
this.myArrayList.add(JSONObject.wrap(iter.next()));
}
@ -357,7 +357,7 @@ public class JSONArray {
*/
public String join(String separator) throws JSONException {
int len = this.length();
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i += 1) {
if (i > 0) {
@ -593,7 +593,7 @@ public class JSONArray {
* A Collection value.
* @return this.
*/
public JSONArray put(Collection value) {
public JSONArray put(Collection<Object> value) {
this.put(new JSONArray(value));
return this;
}
@ -646,7 +646,7 @@ public class JSONArray {
* A Map value.
* @return this.
*/
public JSONArray put(Map value) {
public JSONArray put(Map<String, Object> value) {
this.put(new JSONObject(value));
return this;
}
@ -695,7 +695,7 @@ public class JSONArray {
* @throws JSONException
* If the index is negative or if the value is not finite.
*/
public JSONArray put(int index, Collection value) throws JSONException {
public JSONArray put(int index, Collection<Object> value) throws JSONException {
this.put(index, new JSONArray(value));
return this;
}
@ -767,7 +767,7 @@ public class JSONArray {
* If the index is negative or if the the value is an invalid
* number.
*/
public JSONArray put(int index, Map value) throws JSONException {
public JSONArray put(int index, Map<String, Object> value) throws JSONException {
this.put(index, new JSONObject(value));
return this;
}

View file

@ -4,7 +4,7 @@ package org.json;
* The JSONException is thrown by the JSON.org classes when things are amiss.
*
* @author JSON.org
* @version 2013-02-10
* @version 2014-05-03
*/
public class JSONException extends RuntimeException {
private static final long serialVersionUID = 0;
@ -22,6 +22,7 @@ public class JSONException extends RuntimeException {
/**
* Constructs a new JSONException with the specified cause.
* @param cause The cause.
*/
public JSONException(Throwable cause) {
super(cause.getMessage());
@ -32,9 +33,10 @@ public class JSONException extends RuntimeException {
* Returns the cause of this exception or null if the cause is nonexistent
* or unknown.
*
* @returns the cause of this exception or null if the cause is nonexistent
* @return the cause of this exception or null if the cause is nonexistent
* or unknown.
*/
@Override
public Throwable getCause() {
return this.cause;
}

View file

@ -33,7 +33,7 @@ import java.util.Iterator;
* the JsonML transform.
*
* @author JSON.org
* @version 2012-03-28
* @version 2014-05-03
*/
public class JSONML {
@ -53,12 +53,12 @@ public class JSONML {
) throws JSONException {
String attribute;
char c;
String closeTag = null;
String closeTag = null;
int i;
JSONArray newja = null;
JSONObject newjo = null;
Object token;
String tagName = null;
String tagName = null;
// Test for and skip past these forms:
// <!-- ... -->
@ -312,15 +312,15 @@ public class JSONML {
* @throws JSONException
*/
public static String toString(JSONArray ja) throws JSONException {
int i;
JSONObject jo;
String key;
Iterator keys;
int length;
Object object;
StringBuffer sb = new StringBuffer();
String tagName;
String value;
int i;
JSONObject jo;
String key;
Iterator<String> keys;
int length;
Object object;
StringBuilder sb = new StringBuilder();
String tagName;
String value;
// Emit <tagName
@ -339,7 +339,7 @@ public class JSONML {
keys = jo.keys();
while (keys.hasNext()) {
key = keys.next().toString();
key = keys.next();
XML.noSpace(key);
value = jo.optString(key);
if (value != null) {
@ -355,7 +355,7 @@ public class JSONML {
i = 1;
}
//Emit content in body
// Emit content in body
length = ja.length();
if (i >= length) {
@ -394,15 +394,15 @@ public class JSONML {
* @throws JSONException
*/
public static String toString(JSONObject jo) throws JSONException {
StringBuffer sb = new StringBuffer();
int i;
JSONArray ja;
String key;
Iterator keys;
int length;
Object object;
String tagName;
String value;
StringBuilder sb = new StringBuilder();
int i;
JSONArray ja;
String key;
Iterator<String> keys;
int length;
Object object;
String tagName;
String value;
//Emit <tagName
@ -419,7 +419,7 @@ public class JSONML {
keys = jo.keys();
while (keys.hasNext()) {
key = keys.next().toString();
key = keys.next();
if (!"tagName".equals(key) && !"childNodes".equals(key)) {
XML.noSpace(key);
value = jo.optString(key);

View file

@ -36,6 +36,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.Set;
@ -90,7 +91,7 @@ import java.util.Set;
* </ul>
*
* @author JSON.org
* @version 2014-04-21
* @version 2014-05-03
*/
public class JSONObject {
/**
@ -106,6 +107,7 @@ public class JSONObject {
*
* @return NULL.
*/
@Override
protected final Object clone() {
return this;
}
@ -118,6 +120,7 @@ public class JSONObject {
* @return true if the object parameter is the JSONObject.NULL object or
* null.
*/
@Override
public boolean equals(Object object) {
return object == null || object == this;
}
@ -135,7 +138,7 @@ public class JSONObject {
/**
* The map where the JSONObject's properties are kept.
*/
private final Map map;
private final Map<String, Object> map;
/**
* It is sometimes more convenient and less ambiguous to have a
@ -149,7 +152,7 @@ public class JSONObject {
* Construct an empty JSONObject.
*/
public JSONObject() {
this.map = new HashMap();
this.map = new HashMap<String, Object>();
}
/**
@ -239,15 +242,15 @@ public class JSONObject {
* the JSONObject.
* @throws JSONException
*/
public JSONObject(Map map) {
this.map = new HashMap();
public JSONObject(Map<String, Object> map) {
this.map = new HashMap<String, Object>();
if (map != null) {
Iterator i = map.entrySet().iterator();
Iterator<Entry<String, Object>> i = map.entrySet().iterator();
while (i.hasNext()) {
Map.Entry e = (Map.Entry) i.next();
Object value = e.getValue();
Entry<String, Object> entry = i.next();
Object value = entry.getValue();
if (value != null) {
this.map.put(e.getKey(), wrap(value));
this.map.put(entry.getKey(), wrap(value));
}
}
}
@ -338,10 +341,10 @@ public class JSONObject {
// Iterate through the keys in the bundle.
Enumeration keys = bundle.getKeys();
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
if (key instanceof String) {
if (key != null) {
// Go through the path, ensuring that there is a nested JSONObject for each
// segment except the last. Add the value using the last segment's name into
@ -609,11 +612,11 @@ public class JSONObject {
if (length == 0) {
return null;
}
Iterator iterator = jo.keys();
Iterator<String> iterator = jo.keys();
String[] names = new String[length];
int i = 0;
while (iterator.hasNext()) {
names[i] = (String) iterator.next();
names[i] = iterator.next();
i += 1;
}
return names;
@ -686,13 +689,13 @@ public class JSONObject {
if (value == null) {
this.put(key, 1);
} else if (value instanceof Integer) {
this.put(key, ((Integer) value).intValue() + 1);
this.put(key, (Integer) value + 1);
} else if (value instanceof Long) {
this.put(key, ((Long) value).longValue() + 1);
this.put(key, (Long) value + 1);
} else if (value instanceof Double) {
this.put(key, ((Double) value).doubleValue() + 1);
this.put(key, (Double) value + 1);
} else if (value instanceof Float) {
this.put(key, ((Float) value).floatValue() + 1);
this.put(key, (Float) value + 1);
} else {
throw new JSONException("Unable to increment [" + quote(key) + "].");
}
@ -717,7 +720,7 @@ public class JSONObject {
*
* @return An iterator of the keys.
*/
public Iterator keys() {
public Iterator<String> keys() {
return this.keySet().iterator();
}
@ -726,7 +729,7 @@ public class JSONObject {
*
* @return A keySet.
*/
public Set keySet() {
public Set<String> keySet() {
return this.map.keySet();
}
@ -748,7 +751,7 @@ public class JSONObject {
*/
public JSONArray names() {
JSONArray ja = new JSONArray();
Iterator keys = this.keys();
Iterator<String> keys = this.keys();
while (keys.hasNext()) {
ja.put(keys.next());
}
@ -1050,7 +1053,7 @@ public class JSONObject {
* @return this.
* @throws JSONException
*/
public JSONObject put(String key, Collection value) throws JSONException {
public JSONObject put(String key, Collection<Object> value) throws JSONException {
this.put(key, new JSONArray(value));
return this;
}
@ -1114,7 +1117,7 @@ public class JSONObject {
* @return this.
* @throws JSONException
*/
public JSONObject put(String key, Map value) throws JSONException {
public JSONObject put(String key, Map<String, Object> value) throws JSONException {
this.put(key, new JSONObject(value));
return this;
}
@ -1151,9 +1154,9 @@ public class JSONObject {
* are both non-null, and only if there is not already a member with that
* name.
*
* @param key
* @param value
* @return his.
* @param key string
* @param value object
* @return this.
* @throws JSONException
* if the key is a duplicate
*/
@ -1294,13 +1297,13 @@ public class JSONObject {
if (!(other instanceof JSONObject)) {
return false;
}
Set set = this.keySet();
Set<String> set = this.keySet();
if (!set.equals(((JSONObject)other).keySet())) {
return false;
}
Iterator iterator = set.iterator();
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String name = (String)iterator.next();
String name = iterator.next();
Object valueThis = this.get(name);
Object valueOther = ((JSONObject)other).get(name);
if (valueThis instanceof JSONObject) {
@ -1361,8 +1364,8 @@ public class JSONObject {
} else {
Long myLong = new Long(string);
if (string.equals(myLong.toString())) {
if (myLong.longValue() == myLong.intValue()) {
return new Integer(myLong.intValue());
if (myLong == myLong.intValue()) {
return myLong.intValue();
} else {
return myLong;
}
@ -1509,10 +1512,10 @@ public class JSONObject {
return value.toString();
}
if (value instanceof Map) {
return new JSONObject((Map) value).toString();
return new JSONObject((Map<String, Object>)value).toString();
}
if (value instanceof Collection) {
return new JSONArray((Collection) value).toString();
return new JSONArray((Collection<Object>) value).toString();
}
if (value.getClass().isArray()) {
return new JSONArray(value).toString();
@ -1548,13 +1551,13 @@ public class JSONObject {
}
if (object instanceof Collection) {
return new JSONArray((Collection) object);
return new JSONArray((Collection<Object>) object);
}
if (object.getClass().isArray()) {
return new JSONArray(object);
}
if (object instanceof Map) {
return new JSONObject((Map) object);
return new JSONObject((Map<String, Object>) object);
}
Package objectPackage = object.getClass().getPackage();
String objectPackageName = objectPackage != null ? objectPackage
@ -1592,9 +1595,9 @@ public class JSONObject {
} else if (value instanceof JSONArray) {
((JSONArray) value).write(writer, indentFactor, indent);
} else if (value instanceof Map) {
new JSONObject((Map) value).write(writer, indentFactor, indent);
new JSONObject((Map<String, Object>) value).write(writer, indentFactor, indent);
} else if (value instanceof Collection) {
new JSONArray((Collection) value).write(writer, indentFactor,
new JSONArray((Collection<Object>) value).write(writer, indentFactor,
indent);
} else if (value.getClass().isArray()) {
new JSONArray(value).write(writer, indentFactor, indent);
@ -1636,7 +1639,7 @@ public class JSONObject {
try {
boolean commanate = false;
final int length = this.length();
Iterator keys = this.keys();
Iterator<String> keys = this.keys();
writer.write('{');
if (length == 1) {
@ -1663,8 +1666,7 @@ public class JSONObject {
if (indentFactor > 0) {
writer.write(' ');
}
writeValue(writer, this.map.get(key), indentFactor,
newindent);
writeValue(writer, this.map.get(key), indentFactor, newindent);
commanate = true;
}
if (indentFactor > 0) {

View file

@ -36,7 +36,7 @@ SOFTWARE.
* it. It is used by the JSONObject and JSONArray constructors to parse
* JSON source strings.
* @author JSON.org
* @version 2012-02-16
* @version 2014-05-03
*/
public class JSONTokener {
@ -69,6 +69,7 @@ public class JSONTokener {
/**
* Construct a JSONTokener from an InputStream.
* @param inputStream The source.
*/
public JSONTokener(InputStream inputStream) throws JSONException {
this(new InputStreamReader(inputStream));
@ -250,7 +251,7 @@ public class JSONTokener {
*/
public String nextString(char quote) throws JSONException {
char c;
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
for (;;) {
c = this.next();
switch (c) {
@ -306,7 +307,7 @@ public class JSONTokener {
* @return A string.
*/
public String nextTo(char delimiter) throws JSONException {
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
for (;;) {
char c = this.next();
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
@ -328,7 +329,7 @@ public class JSONTokener {
*/
public String nextTo(String delimiters) throws JSONException {
char c;
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
for (;;) {
c = this.next();
if (delimiters.indexOf(c) >= 0 || c == 0 ||
@ -375,7 +376,7 @@ public class JSONTokener {
* formatting character.
*/
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = this.next();
@ -414,10 +415,9 @@ public class JSONTokener {
return c;
}
} while (c != to);
} catch (IOException exc) {
throw new JSONException(exc);
} catch (IOException exception) {
throw new JSONException(exception);
}
this.back();
return c;
}

View file

@ -269,7 +269,7 @@ public class JSONWriter {
/**
* Push an array or object scope.
* @param c The scope to open.
* @param jo The scope to open.
* @throws JSONException If nesting is too deep.
*/
private void push(JSONObject jo) throws JSONException {

View file

@ -137,7 +137,6 @@ public class Kim {
* The point at which to take bytes.
* @param thru
* The point at which to stop taking bytes.
* @return the substring
*/
public Kim(Kim kim, int from, int thru) {
this(kim.bytes, from, thru);

View file

@ -31,7 +31,7 @@ import java.util.Properties;
/**
* Converts a Property file data into JSONObject and back.
* @author JSON.org
* @version 2013-05-23
* @version 2014-05-03
*/
public class Property {
/**
@ -50,7 +50,6 @@ public class Property {
}
}
return jo;
}
/**
@ -62,13 +61,12 @@ public class Property {
public static Properties toProperties(JSONObject jo) throws JSONException {
Properties properties = new Properties();
if (jo != null) {
Iterator keys = jo.keys();
Iterator<String> keys = jo.keys();
while (keys.hasNext()) {
String name = keys.next().toString();
String name = keys.next();
properties.put(name, jo.getString(name));
}
}
return properties;
}
}
}

2
README
View file

@ -21,7 +21,7 @@ The license includes this restriction: "The software shall be used for good,
not evil." If your conscience cannot live with that, then choose a different
package.
The package compiles on Java 1.2 thru Java 1.4.
The package compiles on Java 1.8.
JSONObject.java: The JSONObject can parse text from a String or a JSONTokener

View file

@ -26,41 +26,40 @@ SOFTWARE.
import java.util.Iterator;
/**
* This provides static methods to convert an XML text into a JSONObject,
* and to covert a JSONObject into an XML text.
* @author JSON.org
* @version 2013-11-12
* @version 2014-05-03
*/
public class XML {
/** The Character '&amp;'. */
public static final Character AMP = new Character('&');
public static final Character AMP = '&';
/** The Character '''. */
public static final Character APOS = new Character('\'');
public static final Character APOS = '\'';
/** The Character '!'. */
public static final Character BANG = new Character('!');
public static final Character BANG = '!';
/** The Character '='. */
public static final Character EQ = new Character('=');
public static final Character EQ = '=';
/** The Character '>'. */
public static final Character GT = new Character('>');
public static final Character GT = '>';
/** The Character '&lt;'. */
public static final Character LT = new Character('<');
public static final Character LT = '<';
/** The Character '?'. */
public static final Character QUEST = new Character('?');
public static final Character QUEST = '?';
/** The Character '"'. */
public static final Character QUOT = new Character('"');
public static final Character QUOT = '"';
/** The Character '/'. */
public static final Character SLASH = new Character('/');
public static final Character SLASH = '/';
/**
* Replace special characters with XML escapes:
@ -74,7 +73,7 @@ public class XML {
* @return The escaped string.
*/
public static String escape(String string) {
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder(string.length());
for (int i = 0, length = string.length(); i < length; i++) {
char c = string.charAt(i);
switch (c) {
@ -103,7 +102,7 @@ public class XML {
/**
* Throw an exception if the string contains whitespace.
* Whitespace is not allowed in tagNames and attributes.
* @param string
* @param string A string.
* @throws JSONException
*/
public static void noSpace(String string) throws JSONException {
@ -379,15 +378,15 @@ public class XML {
*/
public static String toString(Object object, String tagName)
throws JSONException {
StringBuffer sb = new StringBuffer();
int i;
JSONArray ja;
JSONObject jo;
String key;
Iterator keys;
int length;
String string;
Object value;
StringBuilder sb = new StringBuilder();
int i;
JSONArray ja;
JSONObject jo;
String key;
Iterator<String> keys;
int length;
String string;
Object value;
if (object instanceof JSONObject) {
// Emit <tagName>
@ -403,16 +402,12 @@ public class XML {
jo = (JSONObject)object;
keys = jo.keys();
while (keys.hasNext()) {
key = keys.next().toString();
key = keys.next();
value = jo.opt(key);
if (value == null) {
value = "";
}
if (value instanceof String) {
string = (String)value;
} else {
string = null;
}
string = value instanceof String ? (String)value : null;
// Emit content in body

View file

@ -28,7 +28,7 @@ SOFTWARE.
* The XMLTokener extends the JSONTokener to provide additional methods
* for the parsing of XML texts.
* @author JSON.org
* @version 2012-11-13
* @version 2014-05-03
*/
public class XMLTokener extends JSONTokener {
@ -36,10 +36,10 @@ public class XMLTokener extends JSONTokener {
/** The table of entity values. It initially contains Character values for
* amp, apos, gt, lt, quot.
*/
public static final java.util.HashMap entity;
public static final java.util.HashMap<String, Character> entity;
static {
entity = new java.util.HashMap(8);
entity = new java.util.HashMap<String, Character>(8);
entity.put("amp", XML.AMP);
entity.put("apos", XML.APOS);
entity.put("gt", XML.GT);
@ -63,7 +63,7 @@ public class XMLTokener extends JSONTokener {
public String nextCDATA() throws JSONException {
char c;
int i;
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
for (;;) {
c = next();
if (end()) {
@ -91,7 +91,7 @@ public class XMLTokener extends JSONTokener {
*/
public Object nextContent() throws JSONException {
char c;
StringBuffer sb;
StringBuilder sb;
do {
c = next();
} while (Character.isWhitespace(c));
@ -101,7 +101,7 @@ public class XMLTokener extends JSONTokener {
if (c == '<') {
return XML.LT;
}
sb = new StringBuffer();
sb = new StringBuilder();
for (;;) {
if (c == '<' || c == 0) {
back();
@ -125,7 +125,7 @@ public class XMLTokener extends JSONTokener {
* @throws JSONException If missing ';' in XML entity.
*/
public Object nextEntity(char ampersand) throws JSONException {
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
for (;;) {
char c = next();
if (Character.isLetterOrDigit(c) || c == '#') {
@ -219,7 +219,7 @@ public class XMLTokener extends JSONTokener {
public Object nextToken() throws JSONException {
char c;
char q;
StringBuffer sb;
StringBuilder sb;
do {
c = next();
} while (Character.isWhitespace(c));
@ -244,7 +244,7 @@ public class XMLTokener extends JSONTokener {
case '"':
case '\'':
q = c;
sb = new StringBuffer();
sb = new StringBuilder();
for (;;) {
c = next();
if (c == 0) {
@ -263,7 +263,7 @@ public class XMLTokener extends JSONTokener {
// Name
sb = new StringBuffer();
sb = new StringBuilder();
for (;;) {
sb.append(c);
c = next();

View file

@ -30,15 +30,10 @@ import java.io.InputStream;
/**
* This is a big endian bit reader. It reads its bits from an InputStream.
*
* @version 2013-04-18
* @version 2013-05-03
*
*/
public class BitInputStream implements BitReader {
/**
* 2^n - 1
*/
static final int[] mask = { 0, 1, 3, 7, 15, 31, 63, 127, 255 };
/**
* The number of bits remaining in the current byte.
*/
@ -70,23 +65,6 @@ public class BitInputStream implements BitReader {
this.in = in;
}
/**
* Make a BitReader. The first byte is passed in explicitly, the remaining
* bytes are obtained from the InputStream. This makes it possible to look
* at the first byte of a stream before deciding that it should be read as
* bits.
*
* @param in
* An InputStream
* @param firstByte
* The first byte, which was probably read from in.
*/
public BitInputStream(InputStream in, int firstByte) {
this.in = in;
this.unread = firstByte;
this.available = 8;
}
/**
* Read one bit.
*
@ -111,20 +89,26 @@ public class BitInputStream implements BitReader {
/**
* Check that the rest of the block has been padded with zeroes.
*
* @param factor
* The size of the block to pad. This will typically be 8, 16,
* 32, 64, 128, 256, etc.
* @param width
* The size of the block to pad in bits.
* This will typically be 8, 16, 32, 64, 128, 256, etc.
* @return true if the block was zero padded, or false if the the padding
* contains any one bits.
* @throws IOException
*/
public boolean pad(int factor) throws IOException {
int padding = factor - (int) (this.nrBits % factor);
public boolean pad(int width) throws IOException {
boolean result = true;
for (int i = 0; i < padding; i += 1) {
if (bit()) {
result = false;
int gap = (int)this.nrBits % width;
if (gap < 0) {
gap += width;
}
if (gap != 0) {
int padding = width - gap;
while (padding > 0) {
if (bit()) {
result = false;
}
padding -= 1;
}
}
return result;
@ -158,8 +142,8 @@ public class BitInputStream implements BitReader {
if (take > this.available) {
take = this.available;
}
result |= ((this.unread >>> (this.available - take)) & mask[take])
<< (width - take);
result |= ((this.unread >>> (this.available - take)) &
((1 << take) - 1)) << (width - take);
this.nrBits += take;
this.available -= take;
width -= take;

View file

@ -30,7 +30,7 @@ import java.io.OutputStream;
/**
* This is a big endian bit writer. It writes its bits to an OutputStream.
*
* @version 2013-04-18
* @version 2013-05-03
*
*/
public class BitOutputStream implements BitWriter {
@ -85,25 +85,25 @@ public class BitOutputStream implements BitWriter {
}
/**
* Pad the rest of the block with zeroes and flush. pad(8) flushes the last
* Pad the rest of the block with zeros and flush. pad(8) flushes the last
* unfinished byte. The underlying OutputStream will be flushed.
*
* @param factor
* The size of the block to pad. This will typically be 8, 16,
* 32, 64, 128, 256, etc.
* @return this
* @param width
* The size of the block to pad in bits.
* This will typically be 8, 16, 32, 64, 128, 256, etc.
* @throws IOException
*/
public void pad(int factor) throws IOException {
int padding = factor - (int) (nrBits % factor);
int excess = padding & 7;
if (excess > 0) {
this.write(0, excess);
padding -= excess;
public void pad(int width) throws IOException {
int gap = (int)this.nrBits % width;
if (gap < 0) {
gap += width;
}
while (padding > 0) {
this.write(0, 8);
padding -= 8;
if (gap != 0) {
int padding = width - gap;
while (padding > 0) {
this.zero();
padding -= 1;
}
}
this.out.flush();
}
@ -130,7 +130,7 @@ public class BitOutputStream implements BitWriter {
actual = this.vacant;
}
this.unwritten |= ((bits >>> (width - actual)) &
BitInputStream.mask[actual]) << (this.vacant - actual);
((1 << actual) - 1)) << (this.vacant - actual);
width -= actual;
nrBits += actual;
this.vacant -= actual;

View file

@ -3,6 +3,7 @@ package org.json.zip;
import java.io.IOException;
public interface BitReader {
/**
* Read one bit.
*
@ -18,16 +19,16 @@ public interface BitReader {
public long nrBits();
/**
* Check that the rest of the block has been padded with zeroes.
* Check that the rest of the block has been padded with zeros.
*
* @param factor
* @param width
* The size in bits of the block to pad. This will typically be
* 8, 16, 32, 64, 128, 256, etc.
* @return true if the block was zero padded, or false if the the padding
* contained any one bits.
* @throws IOException
*/
public boolean pad(int factor) throws IOException;
public boolean pad(int width) throws IOException;
/**
* Read some bits.

View file

@ -7,10 +7,6 @@ import java.io.IOException;
* Most IO interfaces only allow for writing at the byte level or higher.
*/
public interface BitWriter {
/**
* Returns the number of bits that have been written to this bitwriter.
*/
public long nrBits();
/**
* Write a 1 bit.
@ -22,14 +18,12 @@ public interface BitWriter {
/**
* Pad the rest of the block with zeros and flush.
*
* @param factor
* @param width
* The size in bits of the block to pad. This will typically be
* 8, 16, 32, 64, 128, 256, etc.
* @return true if the block was zero padded, or false if the the padding
* contains any one bits.
* @throws IOException
*/
public void pad(int factor) throws IOException;
public void pad(int width) throws IOException;
/**
* Write some bits. Up to 32 bits can be written at a time.

View file

@ -29,7 +29,7 @@ import org.json.JSONException;
/**
* JSONzip is a compression scheme for JSON text.
* @author JSON.org
* @version 2013-04-18
* @version 2014-05-03
*/
/**
@ -42,6 +42,9 @@ import org.json.JSONException;
* symbol is incremented by the tick method. The generate method is used to
* generate the encoding table. The table must be generated before encoding or
* decoding. You may regenerate the table with the latest weights at any time.
*
* After a million ticks, it is assumed that the distribution is well
* understood and that no more regeneration will be required.
*/
public class Huff implements None, PostMortem {
@ -60,6 +63,11 @@ public class Huff implements None, PostMortem {
*/
private Symbol table;
/**
* The number of characters left to learn to adapt the coding table.
*/
private int toLearn;
/**
* Have any weights changed since the table was last generated?
*/
@ -100,7 +108,7 @@ public class Huff implements None, PostMortem {
if (this.integer != that.integer || this.weight != that.weight) {
return false;
}
if ((this.back != null) != (that.back != null)) {
if ((this.back == null) != (that.back == null)) {
return false;
}
Symbol zero = this.zero;
@ -132,6 +140,7 @@ public class Huff implements None, PostMortem {
*/
public Huff(int domain) {
this.domain = domain;
this.toLearn = 1000000;
int length = domain * 2 - 1;
this.symbols = new Symbol[length];
@ -141,7 +150,7 @@ public class Huff implements None, PostMortem {
symbols[i] = new Symbol(i);
}
// SMake the links.
// Make the links.
for (int i = domain; i < length; i += 1) {
symbols[i] = new Symbol(none);
@ -151,8 +160,6 @@ public class Huff implements None, PostMortem {
/**
* Generate the encoding/decoding table. The table determines the bit
* sequences used by the read and write methods.
*
* @return this
*/
public void generate() {
if (!this.upToDate) {
@ -176,8 +183,8 @@ public class Huff implements None, PostMortem {
head = symbol;
} else {
// To save time, we will start the search from the previous symbol instead
// of the head unless the current symbol weights less than the previous symbol.
// We will start the search from the previous symbol instead of the head unless
// the current symbol weights less than the previous symbol.
if (symbol.weight < previous.weight) {
previous = head;
@ -290,7 +297,7 @@ public class Huff implements None, PostMortem {
public boolean postMortem(PostMortem pm) {
// Go through every integer in the domain, generating its bit sequence, and
// then proving that that bit sequence produces the same integer.
// then prove that that bit sequence produces the same integer.
for (int integer = 0; integer < this.domain; integer += 1) {
if (!postMortem(integer)) {
@ -330,29 +337,16 @@ public class Huff implements None, PostMortem {
}
/**
* Increase by 1 the weight associated with a value.
* Increase the weight associated with a value by 1.
*
* @param value
* The number of the symbol to tick
* @return this
*/
public void tick(int value) {
this.symbols[value].weight += 1;
this.upToDate = false;
}
/**
* Increase by 1 the weight associated with a range of values.
*
* @param from
* The first symbol to tick
* @param to
* The last symbol to tick
* @return this
*/
public void tick(int from, int to) {
for (int value = from; value <= to; value += 1) {
tick(value);
if (this.toLearn > 0) {
this.toLearn -= 1;
this.symbols[value].weight += 1;
this.upToDate = false;
}
}
@ -392,7 +386,6 @@ public class Huff implements None, PostMortem {
* The number of the symbol to write
* @param bitwriter
* The destination of the bits.
* @return this
* @throws JSONException
*/
public void write(int value, BitWriter bitwriter) throws JSONException {

View file

@ -28,8 +28,8 @@ package org.json.zip;
* JSONzip is a binary-encoded JSON dialect. It is designed to compress the
* messages in a session in bandwidth constrained applications, such as mobile.
*
* JSONzip is adaptive, so with each message seen, it should
* improve its compression. It minimizes JSON's overhead, reducing punctuation
* JSONzip is adaptive, so with each message seen, it should improve its
* compression. It minimizes JSON's overhead, reducing punctuation
* to a small number of bits. It uses Huffman encoding to reduce the average
* size of characters. It uses caches (or Keeps) to keep recently seen strings
* and values, so repetitive content (such as object keys) can be
@ -44,17 +44,9 @@ package org.json.zip;
* ADEQUATELY FOR PRODUCTION USE.
*
* @author JSON.org
* @version 2014-04-28
* @version 2014-05-03
*/
public abstract class JSONzip implements None, PostMortem {
/**
* Powers of 2.
*/
public static final int[] twos = {
1, 2, 4, 8, 16, 32, 64, 128, 256, 512,
1024, 2048, 4096, 8192, 16384, 32768, 65536
};
/**
* The characters in JSON numbers can be reduced to 4 bits each.
*/
@ -87,28 +79,11 @@ public abstract class JSONzip implements None, PostMortem {
*/
public static final int endOfNumber = bcd.length;
/**
* The maximum substring length when registering many. The registration of
* one substring may be longer.
*/
public static final int maxSubstringLength = 10;
/**
* The minimum substring length.
*/
public static final int minSubstringLength = 3;
/**
* The package supports tracing for debugging.
*/
public static final boolean probe = false;
/**
* The maximum number of substrings added to the substrings keep per
* string.
*/
public static final int substringLimit = 40;
/**
* The value code for an empty object.
*/
@ -155,62 +130,55 @@ public abstract class JSONzip implements None, PostMortem {
protected final Huff namehuff;
/**
* A place to keep the names (keys).
* A Huffman encoder for names extended bytes.
*/
protected final MapKeep namekeep;
protected final Huff namehuffext;
/**
* A place to keep the strings.
* A place to keep the names (keys).
*/
protected final MapKeep stringkeep;
protected final Keep namekeep;
/**
* A Huffman encoder for string values.
*/
protected final Huff substringhuff;
protected final Huff stringhuff;
/**
* A Huffman encoder for string values extended bytes.
*/
protected final Huff stringhuffext;
/**
* A place to keep the strings.
*/
protected final TrieKeep substringkeep;
protected final Keep stringkeep;
/**
* A place to keep the values.
*/
protected final MapKeep values;
protected final Keep valuekeep;
/**
* Initialize the data structures.
*/
protected JSONzip() {
this.namehuff = new Huff(end + 1);
this.namekeep = new MapKeep(9);
this.stringkeep = new MapKeep(11);
this.substringhuff = new Huff(end + 1);
this.substringkeep = new TrieKeep(12);
this.values = new MapKeep(10);
// Increase the weights of the ASCII letters, digits, and special characters
// because they are highly likely to occur more frequently. The weight of each
// character will increase as it is used. The Huffman encoder will tend to
// use fewer bits to encode heavier characters.
this.namehuff.tick(' ', '}');
this.namehuff.tick('a', 'z');
this.namehuff.tick(end);
this.namehuff.tick(end);
this.substringhuff.tick(' ', '}');
this.substringhuff.tick('a', 'z');
this.substringhuff.tick(end);
this.substringhuff.tick(end);
this.namehuffext = new Huff(end + 1);
this.namekeep = new Keep(9);
this.stringhuff = new Huff(end + 1);
this.stringhuffext = new Huff(end + 1);
this.stringkeep = new Keep(11);
this.valuekeep = new Keep(10);
}
/**
*
* Generate the Huffman tables.
*/
protected void begin() {
protected void generate() {
this.namehuff.generate();
this.substringhuff.generate();
this.stringhuff.generate();
this.stringhuffext.generate();
}
/**
@ -223,7 +191,7 @@ public abstract class JSONzip implements None, PostMortem {
/**
* Write an integer to the console.
*
* @param integer
* @param integer The integer to write to the log.
*/
static void log(int integer) {
log(integer + " ");
@ -233,8 +201,8 @@ public abstract class JSONzip implements None, PostMortem {
* Write two integers, separated by ':' to the console.
* The second integer is suppressed if it is 1.
*
* @param integer
* @param width
* @param integer The integer to write to the log.
* @param width The width of the integer in bits.
*/
static void log(int integer, int width) {
if (width == 1) {
@ -247,7 +215,7 @@ public abstract class JSONzip implements None, PostMortem {
/**
* Write a string to the console.
*
* @param string
* @param string The string to be written to the log.
*/
static void log(String string) {
System.out.print(string);
@ -256,8 +224,8 @@ public abstract class JSONzip implements None, PostMortem {
/**
* Write a character or its code to the console.
*
* @param integer
* @param width
* @param integer The charcode to be written to the log.
* @param width The width of the charcode in bits.
*/
static void logchar(int integer, int width) {
if (integer > ' ' && integer <= '}') {
@ -280,8 +248,7 @@ public abstract class JSONzip implements None, PostMortem {
return this.namehuff.postMortem(that.namehuff)
&& this.namekeep.postMortem(that.namekeep)
&& this.stringkeep.postMortem(that.stringkeep)
&& this.substringhuff.postMortem(that.substringhuff)
&& this.substringkeep.postMortem(that.substringkeep)
&& this.values.postMortem(that.values);
&& this.stringhuff.postMortem(that.stringhuff)
&& this.valuekeep.postMortem(that.valuekeep);
}
}

View file

@ -1,5 +1,8 @@
package org.json.zip;
import java.util.HashMap;
import org.json.Kim;
/*
Copyright (c) 2013 JSON.org
@ -30,30 +33,34 @@ package org.json.zip;
* numbers. This allows the sending of small integers instead of strings.
*
* @author JSON.org
* @version 2013-04-18
* @version 2013-05-03
*/
abstract class Keep implements None, PostMortem {
protected int capacity;
class Keep implements None, PostMortem {
private int capacity;
protected int length;
protected int power;
protected long[] uses;
private Object[] list;
private HashMap<Object, Integer> map;
private int power;
private long[] ticks;
public Keep(int bits) {
this.capacity = JSONzip.twos[bits];
this.capacity = 1 << bits;
this.length = 0;
this.power = 0;
this.uses = new long[this.capacity];
}
this.ticks = new long[this.capacity];
this.list = new Object[this.capacity];
this.map = new HashMap<Object, Integer>(this.capacity);
}
/**
* When an item ages, its use count is reduced by at least half.
*
* @param use
* @param ticks
* The current use count of an item.
* @return The new use count for that item.
*/
public static long age(long use) {
return use >= 32 ? 16 : use / 2;
public static long age(long ticks) {
return ticks >= 32 ? 16 : ticks / 2;
}
/**
@ -62,7 +69,7 @@ abstract class Keep implements None, PostMortem {
* required to identify one of its items goes up.
*/
public int bitsize() {
while (JSONzip.twos[this.power] < this.length) {
while (1 << this.power < this.length) {
this.power += 1;
}
return this.power;
@ -72,13 +79,113 @@ abstract class Keep implements None, PostMortem {
* Increase the usage count on an integer value.
*/
public void tick(int integer) {
this.uses[integer] += 1;
this.ticks[integer] += 1;
}
/**
* Get the value associated with an integer.
* Compact the keep. A keep may contain at most this.capacity elements.
* The keep contents can be reduced by deleting all elements with low use
* counts, and by reducing the use counts of the survivors.
*/
private void compact() {
int from = 0;
int to = 0;
while (from < this.capacity) {
Object key = this.list[from];
long usage = age(this.ticks[from]);
if (usage > 0) {
this.ticks[to] = usage;
this.list[to] = key;
this.map.put(key, to);
to += 1;
} else {
this.map.remove(key);
}
from += 1;
}
if (to < this.capacity) {
this.length = to;
} else {
this.map.clear();
this.length = 0;
}
this.power = 0;
}
/**
* Find the integer value associated with this key, or nothing if this key
* is not in the keep.
*
* @param key
* An object.
* @return An integer
*/
public int find(Object key) {
Object o = this.map.get(key);
return o instanceof Integer ? ((Integer) o).intValue() : none;
}
public boolean postMortem(PostMortem pm) {
Keep that = (Keep) pm;
if (this.length != that.length) {
JSONzip.log(this.length + " <> " + that.length);
return false;
}
for (int i = 0; i < this.length; i += 1) {
boolean b;
if (this.list[i] instanceof Kim) {
b = this.list[i].equals(that.list[i]);
} else {
Object o = this.list[i];
Object q = that.list[i];
if (o instanceof Number) {
o = o.toString();
}
if (q instanceof Number) {
q = q.toString();
}
b = o.equals(q);
}
if (!b) {
JSONzip.log("\n[" + i + "]\n " + this.list[i] + "\n "
+ that.list[i] + "\n " + this.ticks[i] + "\n "
+ that.ticks[i]);
return false;
}
}
return true;
}
/**
* Register a value in the keep. Compact the keep if it is full. The next
* time this value is encountered, its integer can be sent instead.
* @param value A value.
*/
public void register(Object value) {
if (JSONzip.probe) {
int integer = find(value);
if (integer >= 0) {
JSONzip.log("\nDuplicate key " + value);
}
}
if (this.length >= this.capacity) {
compact();
}
this.list[this.length] = value;
this.map.put(value, this.length);
this.ticks[this.length] = 1;
if (JSONzip.probe) {
JSONzip.log("<" + this.length + " " + value + "> ");
}
this.length += 1;
}
/**
* Return the value associated with the integer.
* @param integer The number of an item in the keep.
* @return The value.
*/
abstract public Object value(int integer);
public Object value(int integer) {
return this.list[integer];
}
}

View file

@ -29,8 +29,8 @@ package org.json.zip;
* processors. Testing that JSONzip can compress an object and reproduce a
* corresponding object is not sufficient. Complete testing requires that the
* same internal data structures were constructed on both ends. If those
* structures are not equivalent, then it is likely that the implementations
* are not correct, even if convention tests are passed.
* structures are not exactly equivalent, then it is likely that the
* implementations are not correct, even if conventional tests are passed.
*
* PostMortem allows for testing of deep structures without breaking
* encapsulation.

View file

@ -1,7 +1,5 @@
package org.json.zip;
import java.io.UnsupportedEncodingException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -32,27 +30,27 @@ import org.json.Kim;
*/
/**
* JSONzip is a compression scheme for JSON text.
* JSONzip is a binary compression scheme for JSON text.
*
* @author JSON.org
* @version 2014-04-28
* @version 2014-05-03
*/
public class Decompressor extends JSONzip {
public class Unzipper extends JSONzip {
/**
* A decompressor reads bits from a BitReader.
* A decoder reads bits from a BitReader.
*/
BitReader bitreader;
/**
* Create a new compressor. It may be used for an entire session or
* Create a new unzipper. It may be used for an entire session or
* subsession.
*
* @param bitreader
* The bitreader that this decompressor will read from.
* The bitreader that this decoder will read from.
*/
public Decompressor(BitReader bitreader) {
public Unzipper(BitReader bitreader) {
super();
this.bitreader = bitreader;
}
@ -81,9 +79,9 @@ public class Decompressor extends JSONzip {
* Read enough bits to obtain an integer from the keep, and increase that
* integer's weight.
*
* @param keep
* @param bitreader
* @return
* @param keep The keep providing the context.
* @param bitreader The bitreader that is the source of bits.
* @return The value associated with the number.
* @throws JSONException
*/
private Object getAndTick(Keep keep, BitReader bitreader)
@ -110,13 +108,13 @@ public class Decompressor extends JSONzip {
* The pad method skips the bits that padded a stream to fit some
* allocation. pad(8) will skip over the remainder of a byte.
*
* @param factor
* @param width The width of the pad field in bits.
* @return true if all of the padding bits were zero.
* @throws JSONException
*/
public boolean pad(int factor) throws JSONException {
public boolean pad(int width) throws JSONException {
try {
return this.bitreader.pad(factor);
return this.bitreader.pad(width);
} catch (Throwable e) {
throw new JSONException(e);
}
@ -142,28 +140,76 @@ public class Decompressor extends JSONzip {
}
}
/**
* Read Huffman encoded characters into a keep.
* @param huff A Huffman decoder.
* @param ext A Huffman decoder for the extended bytes.
* @param keep The keep that will receive the kim.
* @return The string that was read.
* @throws JSONException
*/
private String read(Huff huff, Huff ext, Keep keep) throws JSONException {
Kim kim;
int at = 0;
int allocation = 256;
byte[] bytes = new byte[allocation];
if (bit()) {
return getAndTick(keep, this.bitreader).toString();
}
while (true) {
if (at >= allocation) {
allocation *= 2;
bytes = java.util.Arrays.copyOf(bytes, allocation);
}
int c = huff.read(this.bitreader);
if (c == end) {
break;
}
while ((c & 128) == 128) {
bytes[at] = (byte) c;
at += 1;
c = ext.read(this.bitreader);
}
bytes[at] = (byte) c;
at += 1;
}
if (at == 0) {
return "";
}
kim = new Kim(bytes, at);
keep.register(kim);
return kim.toString();
}
/**
* Read a JSONArray.
*
* @param stringy
* true if the first element is a string.
* @return
* @throws JSONException
*/
private JSONArray readArray(boolean stringy) throws JSONException {
JSONArray jsonarray = new JSONArray();
jsonarray.put(stringy ? readString() : readValue());
jsonarray.put(stringy
? read(this.stringhuff, this.stringhuffext, this.stringkeep)
: readValue());
while (true) {
if (probe) {
log("\n");
log();
}
if (!bit()) {
if (!bit()) {
return jsonarray;
}
jsonarray.put(stringy ? readValue() : readString());
jsonarray.put(stringy
? readValue()
: read(this.stringhuff, this.stringhuffext,
this.stringkeep));
} else {
jsonarray.put(stringy ? readString() : readValue());
jsonarray.put(stringy
? read(this.stringhuff, this.stringhuffext,
this.stringkeep)
: readValue());
}
}
}
@ -171,7 +217,7 @@ public class Decompressor extends JSONzip {
/**
* Read a JSON value. The type of value is determined by the next 3 bits.
*
* @return
* @return The read value.
* @throws JSONException
*/
private Object readJSON() throws JSONException {
@ -195,96 +241,25 @@ public class Decompressor extends JSONzip {
}
}
private String readName() throws JSONException {
byte[] bytes = new byte[65536];
int length = 0;
if (!bit()) {
while (true) {
int c = this.namehuff.read(this.bitreader);
if (c == end) {
break;
}
bytes[length] = (byte) c;
length += 1;
}
if (length == 0) {
return "";
}
Kim kim = new Kim(bytes, length);
this.namekeep.register(kim);
return kim.toString();
}
return getAndTick(this.namekeep, this.bitreader).toString();
}
private JSONObject readObject() throws JSONException {
JSONObject jsonobject = new JSONObject();
while (true) {
if (probe) {
log();
}
String name = readName();
jsonobject.put(name, !bit() ? readString() : readValue());
String name = read(this.namehuff, this.namehuffext, this.namekeep);
if (jsonobject.opt(name) != null) {
throw new JSONException("Duplicate key.");
}
jsonobject.put(name, !bit()
? read(this.stringhuff, this.stringhuffext, this.stringkeep)
: readValue());
if (!bit()) {
return jsonobject;
}
}
}
private String readString() throws JSONException {
Kim kim;
int from = 0;
int thru = 0;
int previousFrom = none;
int previousThru = 0;
if (bit()) {
return getAndTick(this.stringkeep, this.bitreader).toString();
}
byte[] bytes = new byte[65536];
boolean one = bit();
this.substringkeep.reserve();
while (true) {
if (one) {
from = thru;
kim = (Kim) getAndTick(this.substringkeep, this.bitreader);
thru = kim.copy(bytes, from);
if (previousFrom != none) {
this.substringkeep.registerOne(new Kim(bytes, previousFrom,
previousThru + 1));
}
previousFrom = from;
previousThru = thru;
one = bit();
} else {
from = none;
while (true) {
int c = this.substringhuff.read(this.bitreader);
if (c == end) {
break;
}
bytes[thru] = (byte) c;
thru += 1;
if (previousFrom != none) {
this.substringkeep.registerOne(new Kim(bytes,
previousFrom, previousThru + 1));
previousFrom = none;
}
}
if (!bit()) {
break;
}
one = true;
}
}
if (thru == 0) {
return "";
}
kim = new Kim(bytes, thru);
this.stringkeep.register(kim);
this.substringkeep.registerMany(kim);
return kim.toString();
}
private Object readValue() throws JSONException {
switch (read(2)) {
case 0:
@ -298,7 +273,7 @@ public class Decompressor extends JSONzip {
integer += int7;
break;
}
return new Integer(integer);
return integer;
case 1:
byte[] bytes = new byte[256];
int length = 0;
@ -314,13 +289,13 @@ public class Decompressor extends JSONzip {
try {
value = JSONObject.stringToValue(new String(bytes, 0, length,
"US-ASCII"));
} catch (UnsupportedEncodingException e) {
} catch (java.io.UnsupportedEncodingException e) {
throw new JSONException(e);
}
this.values.register(value);
this.valuekeep.register(value);
return value;
case 2:
return getAndTick(this.values, this.bitreader);
return getAndTick(this.valuekeep, this.bitreader);
case 3:
return readJSON();
default:
@ -328,8 +303,8 @@ public class Decompressor extends JSONzip {
}
}
public Object unzip() throws JSONException {
begin();
public Object decode() throws JSONException {
generate();
return readJSON();
}
}

View file

@ -1,6 +1,5 @@
package org.json.zip;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
@ -35,36 +34,36 @@ import org.json.Kim;
*/
/**
* JSONzip is a compression scheme for JSON text.
* JSONzip is a binary compression scheme for JSON text.
*
* @author JSON.org
* @version 2014-04-28
* @version 2014-05-03
*/
/**
* A compressor implements the compression behavior of JSONzip. It provides a
* An encoder implements the compression behavior of JSONzip. It provides a
* zip method that takes a JSONObject or JSONArray and delivers a stream of
* bits to a BitWriter.
*
* FOR EVALUATION PURPOSES ONLY. THIS PACKAGE HAS NOT BEEN TESTED ADEQUATELY
* FOR PRODUCTION USE.
*/
public class Compressor extends JSONzip {
public class Zipper extends JSONzip {
/**
* A compressor outputs to a BitWriter.
* An encoder outputs to a BitWriter.
*/
final BitWriter bitwriter;
/**
* Create a new compressor. It may be used for an entire session or
* Create a new encoder. It may be used for an entire session or
* subsession.
*
* @param bitwriter
* The BitWriter this Compressor will output to. Don't forget to
* flush.
* The BitWriter this encoder will output to.
* Don't forget to flush.
*/
public Compressor(BitWriter bitwriter) {
public Zipper(BitWriter bitwriter) {
super();
this.bitwriter = bitwriter;
}
@ -76,7 +75,7 @@ public class Compressor extends JSONzip {
*
* @param digit
* An ASCII character from a JSON number.
* @return
* @return The number code.
*/
private static int bcd(char digit) {
if (digit >= '0' && digit <= '9') {
@ -107,7 +106,7 @@ public class Compressor extends JSONzip {
/**
* Output a one bit.
*
* @throws IOException
* @throws JSONException
*/
private void one() throws JSONException {
write(1, 1);
@ -116,15 +115,15 @@ public class Compressor extends JSONzip {
/**
* Pad the output to fill an allotment of bits.
*
* @param factor
* @param width
* The size of the bit allotment. A value of 8 will complete and
* flush the current byte. If you don't pad, then some of the
* last bits might not be sent to the Output Stream.
* @throws JSONException
*/
public void pad(int factor) throws JSONException {
public void pad(int width) throws JSONException {
try {
this.bitwriter.pad(factor);
this.bitwriter.pad(width);
} catch (Throwable e) {
throw new JSONException(e);
}
@ -171,29 +170,19 @@ public class Compressor extends JSONzip {
* A kim containing the bytes to be written.
* @param huff
* The Huffman encoder.
* @param ext
* The Huffman encoder for the extended bytes.
* @throws JSONException
*/
private void write(Kim kim, Huff huff) throws JSONException {
write(kim, 0, kim.length, huff);
}
/**
* Write a range of bytes from a Kim with Huffman encoding.
*
* @param kim
* A Kim containing the bytes to be written.
* @param from
* The index of the first byte to write.
* @param thru
* The index after the last byte to write.
* @param huff
* The Huffman encoder.
* @throws JSONException
*/
private void write(Kim kim, int from, int thru, Huff huff)
throws JSONException {
for (int at = from; at < thru; at += 1) {
write(kim.get(at), huff);
private void write(Kim kim, Huff huff, Huff ext) throws JSONException {
for (int at = 0; at < kim.length; at += 1) {
int c = kim.get(at);
write(c, huff);
while ((c & 128) == 128) {
at += 1;
c = kim.get(at);
write(c, ext);
}
}
}
@ -207,7 +196,7 @@ public class Compressor extends JSONzip {
* The Keep that the integer is one of.
* @throws JSONException
*/
private void writeAndTick(int integer, Keep keep) throws JSONException {
private void write(int integer, Keep keep) throws JSONException {
int width = keep.bitsize();
keep.tick(integer);
if (probe) {
@ -219,10 +208,10 @@ public class Compressor extends JSONzip {
/**
* Write a JSON Array.
*
* @param jsonarray
* @throws JSONException
* @param jsonarray The JSONArray to write.
* @throws JSONException If the write fails.
*/
private void writeArray(JSONArray jsonarray) throws JSONException {
private void write(JSONArray jsonarray) throws JSONException {
// JSONzip has three encodings for arrays:
// The array is empty (zipEmptyArray).
@ -295,9 +284,9 @@ public class Compressor extends JSONzip {
value = new JSONArray(value);
}
if (value instanceof JSONObject) {
writeObject((JSONObject) value);
write((JSONObject) value);
} else if (value instanceof JSONArray) {
writeArray((JSONArray) value);
write((JSONArray) value);
} else {
throw new JSONException("Unrecognized object");
}
@ -308,7 +297,7 @@ public class Compressor extends JSONzip {
* Write the name of an object property. Names have their own Keep and
* Huffman encoder because they are expected to be a more restricted set.
*
* @param name
* @param name The name string.
* @throws JSONException
*/
private void writeName(String name) throws JSONException {
@ -320,13 +309,13 @@ public class Compressor extends JSONzip {
int integer = this.namekeep.find(kim);
if (integer != none) {
one();
writeAndTick(integer, this.namekeep);
write(integer, this.namekeep);
} else {
// Otherwise, emit the string with Huffman encoding, and register it.
zero();
write(kim, this.namehuff);
write(kim, this.namehuff, this.namehuffext);
write(end, namehuff);
this.namekeep.register(kim);
}
@ -335,17 +324,16 @@ public class Compressor extends JSONzip {
/**
* Write a JSON object.
*
* @param jsonobject
* @return
* @param jsonobject The JSONObject to be written.
* @throws JSONException
*/
private void writeObject(JSONObject jsonobject) throws JSONException {
private void write(JSONObject jsonobject) throws JSONException {
// JSONzip has two encodings for objects: Empty Objects (zipEmptyObject) and
// non-empty objects (zipObject).
boolean first = true;
Iterator keys = jsonobject.keys();
Iterator<String> keys = jsonobject.keys();
while (keys.hasNext()) {
if (probe) {
log();
@ -379,7 +367,7 @@ public class Compressor extends JSONzip {
/**
* Write a string.
*
* @param string
* @param string The string to write.
* @throws JSONException
*/
private void writeString(String string) throws JSONException {
@ -388,9 +376,7 @@ public class Compressor extends JSONzip {
if (string.length() == 0) {
zero();
zero();
write(end, this.substringhuff);
zero();
write(end, this.stringhuff);
} else {
Kim kim = new Kim(string);
@ -400,92 +386,20 @@ public class Compressor extends JSONzip {
int integer = this.stringkeep.find(kim);
if (integer != none) {
one();
writeAndTick(integer, this.stringkeep);
write(integer, this.stringkeep);
} else {
// But if it is not found, emit the string's substrings. Register the string
// so that the next lookup will succeed.
// But if it is not found, emit the string's characters. Register the string
// so that a later lookup can succeed.
writeSubstring(kim);
zero();
write(kim, this.stringhuff, this.stringhuffext);
write(end, this.stringhuff);
this.stringkeep.register(kim);
}
}
}
/**
* Write a string, attempting to match registered substrings.
*
* @param kim
* @throws JSONException
*/
private void writeSubstring(Kim kim) throws JSONException {
this.substringkeep.reserve();
zero();
int from = 0;
int thru = kim.length;
int until = thru - JSONzip.minSubstringLength;
int previousFrom = none;
int previousThru = 0;
// Find a substring from the substring keep.
while (true) {
int at;
int integer = none;
for (at = from; at <= until; at += 1) {
integer = this.substringkeep.match(kim, at, thru);
if (integer != none) {
break;
}
}
if (integer == none) {
break;
}
// If a substring is found, emit any characters that were before the matched
// substring. Then emit the substring's integer and loop back to match the
// remainder with another substring.
if (from != at) {
zero();
write(kim, from, at, this.substringhuff);
write(end, this.substringhuff);
if (previousFrom != none) {
this.substringkeep.registerOne(kim, previousFrom,
previousThru);
previousFrom = none;
}
}
one();
writeAndTick(integer, this.substringkeep);
from = at + this.substringkeep.length(integer);
if (previousFrom != none) {
this.substringkeep.registerOne(kim, previousFrom,
previousThru);
previousFrom = none;
}
previousFrom = at;
previousThru = from + 1;
}
// If a substring is not found, then emit the remaining characters.
zero();
if (from < thru) {
write(kim, from, thru, this.substringhuff);
if (previousFrom != none) {
this.substringkeep.registerOne(kim, previousFrom, previousThru);
}
}
write(end, this.substringhuff);
zero();
// Register the string's substrings in the trie in hopes of future substring
// matching.
substringkeep.registerMany(kim);
}
/**
* Write a value.
*
@ -496,10 +410,10 @@ public class Compressor extends JSONzip {
private void writeValue(Object value) throws JSONException {
if (value instanceof Number) {
String string = JSONObject.numberToString((Number) value);
int integer = this.values.find(string);
int integer = this.valuekeep.find(string);
if (integer != none) {
write(2, 2);
writeAndTick(integer, this.values);
write(integer, this.valuekeep);
return;
}
if (value instanceof Integer || value instanceof Long) {
@ -527,7 +441,7 @@ public class Compressor extends JSONzip {
write(bcd(string.charAt(i)), 4);
}
write(endOfNumber, 4);
this.values.register(string);
this.valuekeep.register(string);
} else {
write(3, 2);
writeJSON(value);
@ -538,32 +452,30 @@ public class Compressor extends JSONzip {
* Output a zero bit.
*
* @throws JSONException
*
* @throws IOException
*/
private void zero() throws JSONException {
write(0, 1);
}
/**
* Compress a JSONObject.
* Encode a JSONObject.
*
* @param jsonobject
* @param jsonobject The JSONObject.
* @throws JSONException
*/
public void zip(JSONObject jsonobject) throws JSONException {
begin();
public void encode(JSONObject jsonobject) throws JSONException {
generate();
writeJSON(jsonobject);
}
/**
* Compress a JSONArray.
* Encode a JSONArray.
*
* @param jsonarray
* @param jsonarray The JSONArray.
* @throws JSONException
*/
public void zip(JSONArray jsonarray) throws JSONException {
begin();
public void encode(JSONArray jsonarray) throws JSONException {
generate();
writeJSON(jsonarray);
}
}