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

View file

@ -28,7 +28,7 @@ SOFTWARE.
* Convert a web browser cookie specification to a JSONObject and back. * Convert a web browser cookie specification to a JSONObject and back.
* JSON and Cookies are both notations for name/value pairs. * JSON and Cookies are both notations for name/value pairs.
* @author JSON.org * @author JSON.org
* @version 2010-12-24 * @version 2014-05-03
*/ */
public class Cookie { public class Cookie {
@ -47,8 +47,8 @@ public class Cookie {
public static String escape(String string) { public static String escape(String string) {
char c; char c;
String s = string.trim(); String s = string.trim();
StringBuffer sb = new StringBuffer();
int length = s.length(); int length = s.length();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i += 1) { for (int i = 0; i < length; i += 1) {
c = s.charAt(i); c = s.charAt(i);
if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') { if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
@ -116,7 +116,7 @@ public class Cookie {
* @throws JSONException * @throws JSONException
*/ */
public static String toString(JSONObject jo) throws JSONException { public static String toString(JSONObject jo) throws JSONException {
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
sb.append(escape(jo.getString("name"))); sb.append(escape(jo.getString("name")));
sb.append("="); sb.append("=");
@ -149,7 +149,7 @@ public class Cookie {
*/ */
public static String unescape(String string) { public static String unescape(String string) {
int length = string.length(); int length = string.length();
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
char c = string.charAt(i); char c = string.charAt(i);
if (c == '+') { if (c == '+') {

View file

@ -29,7 +29,7 @@ import java.util.Iterator;
/** /**
* Convert a web browser cookie list string to a JSONObject and back. * Convert a web browser cookie list string to a JSONObject and back.
* @author JSON.org * @author JSON.org
* @version 2010-12-24 * @version 2014-05-03
*/ */
public class CookieList { public class CookieList {
@ -58,7 +58,6 @@ public class CookieList {
return jo; return jo;
} }
/** /**
* Convert a JSONObject into a cookie list. A cookie list is a sequence * 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 '='. * of name/value pairs. The names are separated from the values by '='.
@ -70,11 +69,11 @@ public class CookieList {
*/ */
public static String toString(JSONObject jo) throws JSONException { public static String toString(JSONObject jo) throws JSONException {
boolean b = false; boolean b = false;
Iterator keys = jo.keys(); Iterator<String> keys = jo.keys();
String string; String string;
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
while (keys.hasNext()) { while (keys.hasNext()) {
string = keys.next().toString(); string = keys.next();
if (!jo.isNull(string)) { if (!jo.isNull(string)) {
if (b) { if (b) {
sb.append(';'); sb.append(';');

View file

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

View file

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

View file

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

View file

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

View file

@ -33,7 +33,7 @@ import java.util.Iterator;
* the JsonML transform. * the JsonML transform.
* *
* @author JSON.org * @author JSON.org
* @version 2012-03-28 * @version 2014-05-03
*/ */
public class JSONML { public class JSONML {
@ -315,10 +315,10 @@ public class JSONML {
int i; int i;
JSONObject jo; JSONObject jo;
String key; String key;
Iterator keys; Iterator<String> keys;
int length; int length;
Object object; Object object;
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
String tagName; String tagName;
String value; String value;
@ -339,7 +339,7 @@ public class JSONML {
keys = jo.keys(); keys = jo.keys();
while (keys.hasNext()) { while (keys.hasNext()) {
key = keys.next().toString(); key = keys.next();
XML.noSpace(key); XML.noSpace(key);
value = jo.optString(key); value = jo.optString(key);
if (value != null) { if (value != null) {
@ -355,7 +355,7 @@ public class JSONML {
i = 1; i = 1;
} }
//Emit content in body // Emit content in body
length = ja.length(); length = ja.length();
if (i >= length) { if (i >= length) {
@ -394,11 +394,11 @@ public class JSONML {
* @throws JSONException * @throws JSONException
*/ */
public static String toString(JSONObject jo) throws JSONException { public static String toString(JSONObject jo) throws JSONException {
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
int i; int i;
JSONArray ja; JSONArray ja;
String key; String key;
Iterator keys; Iterator<String> keys;
int length; int length;
Object object; Object object;
String tagName; String tagName;
@ -419,7 +419,7 @@ public class JSONML {
keys = jo.keys(); keys = jo.keys();
while (keys.hasNext()) { while (keys.hasNext()) {
key = keys.next().toString(); key = keys.next();
if (!"tagName".equals(key) && !"childNodes".equals(key)) { if (!"tagName".equals(key) && !"childNodes".equals(key)) {
XML.noSpace(key); XML.noSpace(key);
value = jo.optString(key); value = jo.optString(key);

View file

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

View file

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

View file

@ -269,7 +269,7 @@ public class JSONWriter {
/** /**
* Push an array or object scope. * 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. * @throws JSONException If nesting is too deep.
*/ */
private void push(JSONObject jo) throws JSONException { private void push(JSONObject jo) throws JSONException {

View file

@ -137,7 +137,6 @@ public class Kim {
* The point at which to take bytes. * The point at which to take bytes.
* @param thru * @param thru
* The point at which to stop taking bytes. * The point at which to stop taking bytes.
* @return the substring
*/ */
public Kim(Kim kim, int from, int thru) { public Kim(Kim kim, int from, int thru) {
this(kim.bytes, from, 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. * Converts a Property file data into JSONObject and back.
* @author JSON.org * @author JSON.org
* @version 2013-05-23 * @version 2014-05-03
*/ */
public class Property { public class Property {
/** /**
@ -50,7 +50,6 @@ public class Property {
} }
} }
return jo; return jo;
} }
/** /**
@ -62,10 +61,9 @@ public class Property {
public static Properties toProperties(JSONObject jo) throws JSONException { public static Properties toProperties(JSONObject jo) throws JSONException {
Properties properties = new Properties(); Properties properties = new Properties();
if (jo != null) { if (jo != null) {
Iterator keys = jo.keys(); Iterator<String> keys = jo.keys();
while (keys.hasNext()) { while (keys.hasNext()) {
String name = keys.next().toString(); String name = keys.next();
properties.put(name, jo.getString(name)); properties.put(name, jo.getString(name));
} }
} }

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 not evil." If your conscience cannot live with that, then choose a different
package. 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 JSONObject.java: The JSONObject can parse text from a String or a JSONTokener

View file

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

View file

@ -28,7 +28,7 @@ SOFTWARE.
* The XMLTokener extends the JSONTokener to provide additional methods * The XMLTokener extends the JSONTokener to provide additional methods
* for the parsing of XML texts. * for the parsing of XML texts.
* @author JSON.org * @author JSON.org
* @version 2012-11-13 * @version 2014-05-03
*/ */
public class XMLTokener extends JSONTokener { 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 /** The table of entity values. It initially contains Character values for
* amp, apos, gt, lt, quot. * amp, apos, gt, lt, quot.
*/ */
public static final java.util.HashMap entity; public static final java.util.HashMap<String, Character> entity;
static { static {
entity = new java.util.HashMap(8); entity = new java.util.HashMap<String, Character>(8);
entity.put("amp", XML.AMP); entity.put("amp", XML.AMP);
entity.put("apos", XML.APOS); entity.put("apos", XML.APOS);
entity.put("gt", XML.GT); entity.put("gt", XML.GT);
@ -63,7 +63,7 @@ public class XMLTokener extends JSONTokener {
public String nextCDATA() throws JSONException { public String nextCDATA() throws JSONException {
char c; char c;
int i; int i;
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
for (;;) { for (;;) {
c = next(); c = next();
if (end()) { if (end()) {
@ -91,7 +91,7 @@ public class XMLTokener extends JSONTokener {
*/ */
public Object nextContent() throws JSONException { public Object nextContent() throws JSONException {
char c; char c;
StringBuffer sb; StringBuilder sb;
do { do {
c = next(); c = next();
} while (Character.isWhitespace(c)); } while (Character.isWhitespace(c));
@ -101,7 +101,7 @@ public class XMLTokener extends JSONTokener {
if (c == '<') { if (c == '<') {
return XML.LT; return XML.LT;
} }
sb = new StringBuffer(); sb = new StringBuilder();
for (;;) { for (;;) {
if (c == '<' || c == 0) { if (c == '<' || c == 0) {
back(); back();
@ -125,7 +125,7 @@ public class XMLTokener extends JSONTokener {
* @throws JSONException If missing ';' in XML entity. * @throws JSONException If missing ';' in XML entity.
*/ */
public Object nextEntity(char ampersand) throws JSONException { public Object nextEntity(char ampersand) throws JSONException {
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
for (;;) { for (;;) {
char c = next(); char c = next();
if (Character.isLetterOrDigit(c) || c == '#') { if (Character.isLetterOrDigit(c) || c == '#') {
@ -219,7 +219,7 @@ public class XMLTokener extends JSONTokener {
public Object nextToken() throws JSONException { public Object nextToken() throws JSONException {
char c; char c;
char q; char q;
StringBuffer sb; StringBuilder sb;
do { do {
c = next(); c = next();
} while (Character.isWhitespace(c)); } while (Character.isWhitespace(c));
@ -244,7 +244,7 @@ public class XMLTokener extends JSONTokener {
case '"': case '"':
case '\'': case '\'':
q = c; q = c;
sb = new StringBuffer(); sb = new StringBuilder();
for (;;) { for (;;) {
c = next(); c = next();
if (c == 0) { if (c == 0) {
@ -263,7 +263,7 @@ public class XMLTokener extends JSONTokener {
// Name // Name
sb = new StringBuffer(); sb = new StringBuilder();
for (;;) { for (;;) {
sb.append(c); sb.append(c);
c = next(); 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. * 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 { 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. * The number of bits remaining in the current byte.
*/ */
@ -70,23 +65,6 @@ public class BitInputStream implements BitReader {
this.in = in; 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. * Read one bit.
* *
@ -111,21 +89,27 @@ public class BitInputStream implements BitReader {
/** /**
* Check that the rest of the block has been padded with zeroes. * Check that the rest of the block has been padded with zeroes.
* *
* @param factor * @param width
* The size of the block to pad. This will typically be 8, 16, * The size of the block to pad in bits.
* 32, 64, 128, 256, etc. * 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 * @return true if the block was zero padded, or false if the the padding
* contains any one bits. * contains any one bits.
* @throws IOException * @throws IOException
*/ */
public boolean pad(int factor) throws IOException { public boolean pad(int width) throws IOException {
int padding = factor - (int) (this.nrBits % factor);
boolean result = true; boolean result = true;
int gap = (int)this.nrBits % width;
for (int i = 0; i < padding; i += 1) { if (gap < 0) {
gap += width;
}
if (gap != 0) {
int padding = width - gap;
while (padding > 0) {
if (bit()) { if (bit()) {
result = false; result = false;
} }
padding -= 1;
}
} }
return result; return result;
} }
@ -158,8 +142,8 @@ public class BitInputStream implements BitReader {
if (take > this.available) { if (take > this.available) {
take = this.available; take = this.available;
} }
result |= ((this.unread >>> (this.available - take)) & mask[take]) result |= ((this.unread >>> (this.available - take)) &
<< (width - take); ((1 << take) - 1)) << (width - take);
this.nrBits += take; this.nrBits += take;
this.available -= take; this.available -= take;
width -= 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. * 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 { 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. * unfinished byte. The underlying OutputStream will be flushed.
* *
* @param factor * @param width
* The size of the block to pad. This will typically be 8, 16, * The size of the block to pad in bits.
* 32, 64, 128, 256, etc. * This will typically be 8, 16, 32, 64, 128, 256, etc.
* @return this
* @throws IOException * @throws IOException
*/ */
public void pad(int factor) throws IOException { public void pad(int width) throws IOException {
int padding = factor - (int) (nrBits % factor); int gap = (int)this.nrBits % width;
int excess = padding & 7; if (gap < 0) {
if (excess > 0) { gap += width;
this.write(0, excess);
padding -= excess;
} }
if (gap != 0) {
int padding = width - gap;
while (padding > 0) { while (padding > 0) {
this.write(0, 8); this.zero();
padding -= 8; padding -= 1;
}
} }
this.out.flush(); this.out.flush();
} }
@ -130,7 +130,7 @@ public class BitOutputStream implements BitWriter {
actual = this.vacant; actual = this.vacant;
} }
this.unwritten |= ((bits >>> (width - actual)) & this.unwritten |= ((bits >>> (width - actual)) &
BitInputStream.mask[actual]) << (this.vacant - actual); ((1 << actual) - 1)) << (this.vacant - actual);
width -= actual; width -= actual;
nrBits += actual; nrBits += actual;
this.vacant -= actual; this.vacant -= actual;

View file

@ -3,6 +3,7 @@ package org.json.zip;
import java.io.IOException; import java.io.IOException;
public interface BitReader { public interface BitReader {
/** /**
* Read one bit. * Read one bit.
* *
@ -18,16 +19,16 @@ public interface BitReader {
public long nrBits(); 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 * The size in bits of the block to pad. This will typically be
* 8, 16, 32, 64, 128, 256, etc. * 8, 16, 32, 64, 128, 256, etc.
* @return true if the block was zero padded, or false if the the padding * @return true if the block was zero padded, or false if the the padding
* contained any one bits. * contained any one bits.
* @throws IOException * @throws IOException
*/ */
public boolean pad(int factor) throws IOException; public boolean pad(int width) throws IOException;
/** /**
* Read some bits. * 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. * Most IO interfaces only allow for writing at the byte level or higher.
*/ */
public interface BitWriter { public interface BitWriter {
/**
* Returns the number of bits that have been written to this bitwriter.
*/
public long nrBits();
/** /**
* Write a 1 bit. * Write a 1 bit.
@ -22,14 +18,12 @@ public interface BitWriter {
/** /**
* Pad the rest of the block with zeros and flush. * 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 * The size in bits of the block to pad. This will typically be
* 8, 16, 32, 64, 128, 256, etc. * 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 * @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. * 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. * JSONzip is a compression scheme for JSON text.
* @author JSON.org * @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 * 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 * 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. * 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 { public class Huff implements None, PostMortem {
@ -60,6 +63,11 @@ public class Huff implements None, PostMortem {
*/ */
private Symbol table; 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? * 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) { if (this.integer != that.integer || this.weight != that.weight) {
return false; return false;
} }
if ((this.back != null) != (that.back != null)) { if ((this.back == null) != (that.back == null)) {
return false; return false;
} }
Symbol zero = this.zero; Symbol zero = this.zero;
@ -132,6 +140,7 @@ public class Huff implements None, PostMortem {
*/ */
public Huff(int domain) { public Huff(int domain) {
this.domain = domain; this.domain = domain;
this.toLearn = 1000000;
int length = domain * 2 - 1; int length = domain * 2 - 1;
this.symbols = new Symbol[length]; this.symbols = new Symbol[length];
@ -141,7 +150,7 @@ public class Huff implements None, PostMortem {
symbols[i] = new Symbol(i); symbols[i] = new Symbol(i);
} }
// SMake the links. // Make the links.
for (int i = domain; i < length; i += 1) { for (int i = domain; i < length; i += 1) {
symbols[i] = new Symbol(none); 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 * Generate the encoding/decoding table. The table determines the bit
* sequences used by the read and write methods. * sequences used by the read and write methods.
*
* @return this
*/ */
public void generate() { public void generate() {
if (!this.upToDate) { if (!this.upToDate) {
@ -176,8 +183,8 @@ public class Huff implements None, PostMortem {
head = symbol; head = symbol;
} else { } else {
// To save time, we will start the search from the previous symbol instead // We will start the search from the previous symbol instead of the head unless
// of the head unless the current symbol weights less than the previous symbol. // the current symbol weights less than the previous symbol.
if (symbol.weight < previous.weight) { if (symbol.weight < previous.weight) {
previous = head; previous = head;
@ -290,7 +297,7 @@ public class Huff implements None, PostMortem {
public boolean postMortem(PostMortem pm) { public boolean postMortem(PostMortem pm) {
// Go through every integer in the domain, generating its bit sequence, and // 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) { for (int integer = 0; integer < this.domain; integer += 1) {
if (!postMortem(integer)) { if (!postMortem(integer)) {
@ -330,30 +337,17 @@ 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 * @param value
* The number of the symbol to tick * The number of the symbol to tick
* @return this
*/ */
public void tick(int value) { public void tick(int value) {
if (this.toLearn > 0) {
this.toLearn -= 1;
this.symbols[value].weight += 1; this.symbols[value].weight += 1;
this.upToDate = false; 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);
}
} }
/** /**
@ -392,7 +386,6 @@ public class Huff implements None, PostMortem {
* The number of the symbol to write * The number of the symbol to write
* @param bitwriter * @param bitwriter
* The destination of the bits. * The destination of the bits.
* @return this
* @throws JSONException * @throws JSONException
*/ */
public void write(int value, BitWriter bitwriter) 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 * JSONzip is a binary-encoded JSON dialect. It is designed to compress the
* messages in a session in bandwidth constrained applications, such as mobile. * messages in a session in bandwidth constrained applications, such as mobile.
* *
* JSONzip is adaptive, so with each message seen, it should * JSONzip is adaptive, so with each message seen, it should improve its
* improve its compression. It minimizes JSON's overhead, reducing punctuation * compression. It minimizes JSON's overhead, reducing punctuation
* to a small number of bits. It uses Huffman encoding to reduce the average * 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 * size of characters. It uses caches (or Keeps) to keep recently seen strings
* and values, so repetitive content (such as object keys) can be * and values, so repetitive content (such as object keys) can be
@ -44,17 +44,9 @@ package org.json.zip;
* ADEQUATELY FOR PRODUCTION USE. * ADEQUATELY FOR PRODUCTION USE.
* *
* @author JSON.org * @author JSON.org
* @version 2014-04-28 * @version 2014-05-03
*/ */
public abstract class JSONzip implements None, PostMortem { 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. * 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; 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. * The package supports tracing for debugging.
*/ */
public static final boolean probe = false; 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. * The value code for an empty object.
*/ */
@ -155,62 +130,55 @@ public abstract class JSONzip implements None, PostMortem {
protected final Huff namehuff; 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. * 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. * A place to keep the strings.
*/ */
protected final TrieKeep substringkeep; protected final Keep stringkeep;
/** /**
* A place to keep the values. * A place to keep the values.
*/ */
protected final MapKeep values; protected final Keep valuekeep;
/** /**
* Initialize the data structures. * Initialize the data structures.
*/ */
protected JSONzip() { protected JSONzip() {
this.namehuff = new Huff(end + 1); this.namehuff = new Huff(end + 1);
this.namekeep = new MapKeep(9); this.namehuffext = new Huff(end + 1);
this.stringkeep = new MapKeep(11); this.namekeep = new Keep(9);
this.substringhuff = new Huff(end + 1); this.stringhuff = new Huff(end + 1);
this.substringkeep = new TrieKeep(12); this.stringhuffext = new Huff(end + 1);
this.values = new MapKeep(10); this.stringkeep = new Keep(11);
this.valuekeep = new Keep(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);
} }
/** /**
* * Generate the Huffman tables.
*/ */
protected void begin() { protected void generate() {
this.namehuff.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. * Write an integer to the console.
* *
* @param integer * @param integer The integer to write to the log.
*/ */
static void log(int integer) { static void log(int integer) {
log(integer + " "); log(integer + " ");
@ -233,8 +201,8 @@ public abstract class JSONzip implements None, PostMortem {
* Write two integers, separated by ':' to the console. * Write two integers, separated by ':' to the console.
* The second integer is suppressed if it is 1. * The second integer is suppressed if it is 1.
* *
* @param integer * @param integer The integer to write to the log.
* @param width * @param width The width of the integer in bits.
*/ */
static void log(int integer, int width) { static void log(int integer, int width) {
if (width == 1) { if (width == 1) {
@ -247,7 +215,7 @@ public abstract class JSONzip implements None, PostMortem {
/** /**
* Write a string to the console. * Write a string to the console.
* *
* @param string * @param string The string to be written to the log.
*/ */
static void log(String string) { static void log(String string) {
System.out.print(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. * Write a character or its code to the console.
* *
* @param integer * @param integer The charcode to be written to the log.
* @param width * @param width The width of the charcode in bits.
*/ */
static void logchar(int integer, int width) { static void logchar(int integer, int width) {
if (integer > ' ' && integer <= '}') { if (integer > ' ' && integer <= '}') {
@ -280,8 +248,7 @@ public abstract class JSONzip implements None, PostMortem {
return this.namehuff.postMortem(that.namehuff) return this.namehuff.postMortem(that.namehuff)
&& this.namekeep.postMortem(that.namekeep) && this.namekeep.postMortem(that.namekeep)
&& this.stringkeep.postMortem(that.stringkeep) && this.stringkeep.postMortem(that.stringkeep)
&& this.substringhuff.postMortem(that.substringhuff) && this.stringhuff.postMortem(that.stringhuff)
&& this.substringkeep.postMortem(that.substringkeep) && this.valuekeep.postMortem(that.valuekeep);
&& this.values.postMortem(that.values);
} }
} }

View file

@ -1,5 +1,8 @@
package org.json.zip; package org.json.zip;
import java.util.HashMap;
import org.json.Kim;
/* /*
Copyright (c) 2013 JSON.org Copyright (c) 2013 JSON.org
@ -30,30 +33,34 @@ package org.json.zip;
* numbers. This allows the sending of small integers instead of strings. * numbers. This allows the sending of small integers instead of strings.
* *
* @author JSON.org * @author JSON.org
* @version 2013-04-18 * @version 2013-05-03
*/ */
abstract class Keep implements None, PostMortem { class Keep implements None, PostMortem {
protected int capacity; private int capacity;
protected int length; protected int length;
protected int power; private Object[] list;
protected long[] uses; private HashMap<Object, Integer> map;
private int power;
private long[] ticks;
public Keep(int bits) { public Keep(int bits) {
this.capacity = JSONzip.twos[bits]; this.capacity = 1 << bits;
this.length = 0; this.length = 0;
this.power = 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. * When an item ages, its use count is reduced by at least half.
* *
* @param use * @param ticks
* The current use count of an item. * The current use count of an item.
* @return The new use count for that item. * @return The new use count for that item.
*/ */
public static long age(long use) { public static long age(long ticks) {
return use >= 32 ? 16 : use / 2; 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. * required to identify one of its items goes up.
*/ */
public int bitsize() { public int bitsize() {
while (JSONzip.twos[this.power] < this.length) { while (1 << this.power < this.length) {
this.power += 1; this.power += 1;
} }
return this.power; return this.power;
@ -72,13 +79,113 @@ abstract class Keep implements None, PostMortem {
* Increase the usage count on an integer value. * Increase the usage count on an integer value.
*/ */
public void tick(int integer) { 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. * @param integer The number of an item in the keep.
* @return The value. * @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 * processors. Testing that JSONzip can compress an object and reproduce a
* corresponding object is not sufficient. Complete testing requires that the * corresponding object is not sufficient. Complete testing requires that the
* same internal data structures were constructed on both ends. If those * same internal data structures were constructed on both ends. If those
* structures are not equivalent, then it is likely that the implementations * structures are not exactly equivalent, then it is likely that the
* are not correct, even if convention tests are passed. * implementations are not correct, even if conventional tests are passed.
* *
* PostMortem allows for testing of deep structures without breaking * PostMortem allows for testing of deep structures without breaking
* encapsulation. * encapsulation.

View file

@ -1,7 +1,5 @@
package org.json.zip; package org.json.zip;
import java.io.UnsupportedEncodingException;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; 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 * @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; 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. * subsession.
* *
* @param bitreader * @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(); super();
this.bitreader = bitreader; 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 * Read enough bits to obtain an integer from the keep, and increase that
* integer's weight. * integer's weight.
* *
* @param keep * @param keep The keep providing the context.
* @param bitreader * @param bitreader The bitreader that is the source of bits.
* @return * @return The value associated with the number.
* @throws JSONException * @throws JSONException
*/ */
private Object getAndTick(Keep keep, BitReader bitreader) 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 * The pad method skips the bits that padded a stream to fit some
* allocation. pad(8) will skip over the remainder of a byte. * 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. * @return true if all of the padding bits were zero.
* @throws JSONException * @throws JSONException
*/ */
public boolean pad(int factor) throws JSONException { public boolean pad(int width) throws JSONException {
try { try {
return this.bitreader.pad(factor); return this.bitreader.pad(width);
} catch (Throwable e) { } catch (Throwable e) {
throw new JSONException(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. * Read a JSONArray.
* *
* @param stringy * @param stringy
* true if the first element is a string. * true if the first element is a string.
* @return
* @throws JSONException * @throws JSONException
*/ */
private JSONArray readArray(boolean stringy) throws JSONException { private JSONArray readArray(boolean stringy) throws JSONException {
JSONArray jsonarray = new JSONArray(); JSONArray jsonarray = new JSONArray();
jsonarray.put(stringy ? readString() : readValue()); jsonarray.put(stringy
? read(this.stringhuff, this.stringhuffext, this.stringkeep)
: readValue());
while (true) { while (true) {
if (probe) { if (probe) {
log("\n"); log();
} }
if (!bit()) { if (!bit()) {
if (!bit()) { if (!bit()) {
return jsonarray; return jsonarray;
} }
jsonarray.put(stringy ? readValue() : readString()); jsonarray.put(stringy
? readValue()
: read(this.stringhuff, this.stringhuffext,
this.stringkeep));
} else { } 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. * Read a JSON value. The type of value is determined by the next 3 bits.
* *
* @return * @return The read value.
* @throws JSONException * @throws JSONException
*/ */
private Object readJSON() 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 { private JSONObject readObject() throws JSONException {
JSONObject jsonobject = new JSONObject(); JSONObject jsonobject = new JSONObject();
while (true) { while (true) {
if (probe) { if (probe) {
log(); log();
} }
String name = readName(); String name = read(this.namehuff, this.namehuffext, this.namekeep);
jsonobject.put(name, !bit() ? readString() : readValue()); if (jsonobject.opt(name) != null) {
throw new JSONException("Duplicate key.");
}
jsonobject.put(name, !bit()
? read(this.stringhuff, this.stringhuffext, this.stringkeep)
: readValue());
if (!bit()) { if (!bit()) {
return jsonobject; 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 { private Object readValue() throws JSONException {
switch (read(2)) { switch (read(2)) {
case 0: case 0:
@ -298,7 +273,7 @@ public class Decompressor extends JSONzip {
integer += int7; integer += int7;
break; break;
} }
return new Integer(integer); return integer;
case 1: case 1:
byte[] bytes = new byte[256]; byte[] bytes = new byte[256];
int length = 0; int length = 0;
@ -314,13 +289,13 @@ public class Decompressor extends JSONzip {
try { try {
value = JSONObject.stringToValue(new String(bytes, 0, length, value = JSONObject.stringToValue(new String(bytes, 0, length,
"US-ASCII")); "US-ASCII"));
} catch (UnsupportedEncodingException e) { } catch (java.io.UnsupportedEncodingException e) {
throw new JSONException(e); throw new JSONException(e);
} }
this.values.register(value); this.valuekeep.register(value);
return value; return value;
case 2: case 2:
return getAndTick(this.values, this.bitreader); return getAndTick(this.valuekeep, this.bitreader);
case 3: case 3:
return readJSON(); return readJSON();
default: default:
@ -328,8 +303,8 @@ public class Decompressor extends JSONzip {
} }
} }
public Object unzip() throws JSONException { public Object decode() throws JSONException {
begin(); generate();
return readJSON(); return readJSON();
} }
} }

View file

@ -1,6 +1,5 @@
package org.json.zip; package org.json.zip;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; 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 * @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 * zip method that takes a JSONObject or JSONArray and delivers a stream of
* bits to a BitWriter. * bits to a BitWriter.
* *
* FOR EVALUATION PURPOSES ONLY. THIS PACKAGE HAS NOT BEEN TESTED ADEQUATELY * FOR EVALUATION PURPOSES ONLY. THIS PACKAGE HAS NOT BEEN TESTED ADEQUATELY
* FOR PRODUCTION USE. * 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; 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. * subsession.
* *
* @param bitwriter * @param bitwriter
* The BitWriter this Compressor will output to. Don't forget to * The BitWriter this encoder will output to.
* flush. * Don't forget to flush.
*/ */
public Compressor(BitWriter bitwriter) { public Zipper(BitWriter bitwriter) {
super(); super();
this.bitwriter = bitwriter; this.bitwriter = bitwriter;
} }
@ -76,7 +75,7 @@ public class Compressor extends JSONzip {
* *
* @param digit * @param digit
* An ASCII character from a JSON number. * An ASCII character from a JSON number.
* @return * @return The number code.
*/ */
private static int bcd(char digit) { private static int bcd(char digit) {
if (digit >= '0' && digit <= '9') { if (digit >= '0' && digit <= '9') {
@ -107,7 +106,7 @@ public class Compressor extends JSONzip {
/** /**
* Output a one bit. * Output a one bit.
* *
* @throws IOException * @throws JSONException
*/ */
private void one() throws JSONException { private void one() throws JSONException {
write(1, 1); write(1, 1);
@ -116,15 +115,15 @@ public class Compressor extends JSONzip {
/** /**
* Pad the output to fill an allotment of bits. * 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 * 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 * flush the current byte. If you don't pad, then some of the
* last bits might not be sent to the Output Stream. * last bits might not be sent to the Output Stream.
* @throws JSONException * @throws JSONException
*/ */
public void pad(int factor) throws JSONException { public void pad(int width) throws JSONException {
try { try {
this.bitwriter.pad(factor); this.bitwriter.pad(width);
} catch (Throwable e) { } catch (Throwable e) {
throw new JSONException(e); throw new JSONException(e);
} }
@ -171,29 +170,19 @@ public class Compressor extends JSONzip {
* A kim containing the bytes to be written. * A kim containing the bytes to be written.
* @param huff * @param huff
* The Huffman encoder. * The Huffman encoder.
* @param ext
* The Huffman encoder for the extended bytes.
* @throws JSONException * @throws JSONException
*/ */
private void write(Kim kim, Huff huff) throws JSONException { private void write(Kim kim, Huff huff, Huff ext) throws JSONException {
write(kim, 0, kim.length, huff); 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);
} }
/**
* 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);
} }
} }
@ -207,7 +196,7 @@ public class Compressor extends JSONzip {
* The Keep that the integer is one of. * The Keep that the integer is one of.
* @throws JSONException * @throws JSONException
*/ */
private void writeAndTick(int integer, Keep keep) throws JSONException { private void write(int integer, Keep keep) throws JSONException {
int width = keep.bitsize(); int width = keep.bitsize();
keep.tick(integer); keep.tick(integer);
if (probe) { if (probe) {
@ -219,10 +208,10 @@ public class Compressor extends JSONzip {
/** /**
* Write a JSON Array. * Write a JSON Array.
* *
* @param jsonarray * @param jsonarray The JSONArray to write.
* @throws JSONException * @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: // JSONzip has three encodings for arrays:
// The array is empty (zipEmptyArray). // The array is empty (zipEmptyArray).
@ -295,9 +284,9 @@ public class Compressor extends JSONzip {
value = new JSONArray(value); value = new JSONArray(value);
} }
if (value instanceof JSONObject) { if (value instanceof JSONObject) {
writeObject((JSONObject) value); write((JSONObject) value);
} else if (value instanceof JSONArray) { } else if (value instanceof JSONArray) {
writeArray((JSONArray) value); write((JSONArray) value);
} else { } else {
throw new JSONException("Unrecognized object"); 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 * 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. * Huffman encoder because they are expected to be a more restricted set.
* *
* @param name * @param name The name string.
* @throws JSONException * @throws JSONException
*/ */
private void writeName(String name) throws JSONException { private void writeName(String name) throws JSONException {
@ -320,13 +309,13 @@ public class Compressor extends JSONzip {
int integer = this.namekeep.find(kim); int integer = this.namekeep.find(kim);
if (integer != none) { if (integer != none) {
one(); one();
writeAndTick(integer, this.namekeep); write(integer, this.namekeep);
} else { } else {
// Otherwise, emit the string with Huffman encoding, and register it. // Otherwise, emit the string with Huffman encoding, and register it.
zero(); zero();
write(kim, this.namehuff); write(kim, this.namehuff, this.namehuffext);
write(end, namehuff); write(end, namehuff);
this.namekeep.register(kim); this.namekeep.register(kim);
} }
@ -335,17 +324,16 @@ public class Compressor extends JSONzip {
/** /**
* Write a JSON object. * Write a JSON object.
* *
* @param jsonobject * @param jsonobject The JSONObject to be written.
* @return
* @throws JSONException * @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 // JSONzip has two encodings for objects: Empty Objects (zipEmptyObject) and
// non-empty objects (zipObject). // non-empty objects (zipObject).
boolean first = true; boolean first = true;
Iterator keys = jsonobject.keys(); Iterator<String> keys = jsonobject.keys();
while (keys.hasNext()) { while (keys.hasNext()) {
if (probe) { if (probe) {
log(); log();
@ -379,7 +367,7 @@ public class Compressor extends JSONzip {
/** /**
* Write a string. * Write a string.
* *
* @param string * @param string The string to write.
* @throws JSONException * @throws JSONException
*/ */
private void writeString(String string) throws JSONException { private void writeString(String string) throws JSONException {
@ -388,9 +376,7 @@ public class Compressor extends JSONzip {
if (string.length() == 0) { if (string.length() == 0) {
zero(); zero();
zero(); write(end, this.stringhuff);
write(end, this.substringhuff);
zero();
} else { } else {
Kim kim = new Kim(string); Kim kim = new Kim(string);
@ -400,92 +386,20 @@ public class Compressor extends JSONzip {
int integer = this.stringkeep.find(kim); int integer = this.stringkeep.find(kim);
if (integer != none) { if (integer != none) {
one(); one();
writeAndTick(integer, this.stringkeep); write(integer, this.stringkeep);
} else { } else {
// But if it is not found, emit the string's substrings. Register the string // But if it is not found, emit the string's characters. Register the string
// so that the next lookup will succeed. // so that a later lookup can succeed.
writeSubstring(kim); zero();
write(kim, this.stringhuff, this.stringhuffext);
write(end, this.stringhuff);
this.stringkeep.register(kim); 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. * Write a value.
* *
@ -496,10 +410,10 @@ public class Compressor extends JSONzip {
private void writeValue(Object value) throws JSONException { private void writeValue(Object value) throws JSONException {
if (value instanceof Number) { if (value instanceof Number) {
String string = JSONObject.numberToString((Number) value); String string = JSONObject.numberToString((Number) value);
int integer = this.values.find(string); int integer = this.valuekeep.find(string);
if (integer != none) { if (integer != none) {
write(2, 2); write(2, 2);
writeAndTick(integer, this.values); write(integer, this.valuekeep);
return; return;
} }
if (value instanceof Integer || value instanceof Long) { if (value instanceof Integer || value instanceof Long) {
@ -527,7 +441,7 @@ public class Compressor extends JSONzip {
write(bcd(string.charAt(i)), 4); write(bcd(string.charAt(i)), 4);
} }
write(endOfNumber, 4); write(endOfNumber, 4);
this.values.register(string); this.valuekeep.register(string);
} else { } else {
write(3, 2); write(3, 2);
writeJSON(value); writeJSON(value);
@ -538,32 +452,30 @@ public class Compressor extends JSONzip {
* Output a zero bit. * Output a zero bit.
* *
* @throws JSONException * @throws JSONException
*
* @throws IOException
*/ */
private void zero() throws JSONException { private void zero() throws JSONException {
write(0, 1); write(0, 1);
} }
/** /**
* Compress a JSONObject. * Encode a JSONObject.
* *
* @param jsonobject * @param jsonobject The JSONObject.
* @throws JSONException * @throws JSONException
*/ */
public void zip(JSONObject jsonobject) throws JSONException { public void encode(JSONObject jsonobject) throws JSONException {
begin(); generate();
writeJSON(jsonobject); writeJSON(jsonobject);
} }
/** /**
* Compress a JSONArray. * Encode a JSONArray.
* *
* @param jsonarray * @param jsonarray The JSONArray.
* @throws JSONException * @throws JSONException
*/ */
public void zip(JSONArray jsonarray) throws JSONException { public void encode(JSONArray jsonarray) throws JSONException {
begin(); generate();
writeJSON(jsonarray); writeJSON(jsonarray);
} }
} }