Compare commits
No commits in common. "9b6872b6e52e28b1b99aa328c2109436de58622f" and "34f327e6d070568256b314479be158589d391891" have entirely different histories.
9b6872b6e5
...
34f327e6d0
30 changed files with 3504 additions and 486 deletions
4
CDL.java
4
CDL.java
|
|
@ -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 2014-05-03
|
* @version 2012-11-13
|
||||||
*/
|
*/
|
||||||
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) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
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(',');
|
||||||
|
|
|
||||||
338
Cookie.java
338
Cookie.java
|
|
@ -1,169 +1,169 @@
|
||||||
package org.json;
|
package org.json;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (c) 2002 JSON.org
|
Copyright (c) 2002 JSON.org
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
The Software shall be used for Good, not Evil.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
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 2014-05-03
|
* @version 2010-12-24
|
||||||
*/
|
*/
|
||||||
public class Cookie {
|
public class Cookie {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a copy of a string in which the characters '+', '%', '=', ';'
|
* Produce a copy of a string in which the characters '+', '%', '=', ';'
|
||||||
* and control characters are replaced with "%hh". This is a gentle form
|
* and control characters are replaced with "%hh". This is a gentle form
|
||||||
* of URL encoding, attempting to cause as little distortion to the
|
* of URL encoding, attempting to cause as little distortion to the
|
||||||
* string as possible. The characters '=' and ';' are meta characters in
|
* string as possible. The characters '=' and ';' are meta characters in
|
||||||
* cookies. By convention, they are escaped using the URL-encoding. This is
|
* cookies. By convention, they are escaped using the URL-encoding. This is
|
||||||
* only a convention, not a standard. Often, cookies are expected to have
|
* only a convention, not a standard. Often, cookies are expected to have
|
||||||
* encoded values. We encode '=' and ';' because we must. We encode '%' and
|
* encoded values. We encode '=' and ';' because we must. We encode '%' and
|
||||||
* '+' because they are meta characters in URL encoding.
|
* '+' because they are meta characters in URL encoding.
|
||||||
* @param string The source string.
|
* @param string The source string.
|
||||||
* @return The escaped result.
|
* @return The escaped result.
|
||||||
*/
|
*/
|
||||||
public static String escape(String string) {
|
public static String escape(String string) {
|
||||||
char c;
|
char c;
|
||||||
String s = string.trim();
|
String s = string.trim();
|
||||||
int length = s.length();
|
StringBuffer sb = new StringBuffer();
|
||||||
StringBuilder sb = new StringBuilder(length);
|
int length = s.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 == ';') {
|
||||||
sb.append('%');
|
sb.append('%');
|
||||||
sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16));
|
sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16));
|
||||||
sb.append(Character.forDigit((char)(c & 0x0f), 16));
|
sb.append(Character.forDigit((char)(c & 0x0f), 16));
|
||||||
} else {
|
} else {
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a cookie specification string into a JSONObject. The string
|
* Convert a cookie specification string into a JSONObject. The string
|
||||||
* will contain a name value pair separated by '='. The name and the value
|
* will contain a name value pair separated by '='. The name and the value
|
||||||
* will be unescaped, possibly converting '+' and '%' sequences. The
|
* will be unescaped, possibly converting '+' and '%' sequences. The
|
||||||
* cookie properties may follow, separated by ';', also represented as
|
* cookie properties may follow, separated by ';', also represented as
|
||||||
* name=value (except the secure property, which does not have a value).
|
* 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
|
* 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
|
* stored under the key "value". This method does not do checking or
|
||||||
* validation of the parameters. It only converts the cookie string into
|
* validation of the parameters. It only converts the cookie string into
|
||||||
* a JSONObject.
|
* a JSONObject.
|
||||||
* @param string The cookie specification string.
|
* @param string The cookie specification string.
|
||||||
* @return A JSONObject containing "name", "value", and possibly other
|
* @return A JSONObject containing "name", "value", and possibly other
|
||||||
* members.
|
* members.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||||
String name;
|
String name;
|
||||||
JSONObject jo = new JSONObject();
|
JSONObject jo = new JSONObject();
|
||||||
Object value;
|
Object value;
|
||||||
JSONTokener x = new JSONTokener(string);
|
JSONTokener x = new JSONTokener(string);
|
||||||
jo.put("name", x.nextTo('='));
|
jo.put("name", x.nextTo('='));
|
||||||
x.next('=');
|
x.next('=');
|
||||||
jo.put("value", x.nextTo(';'));
|
jo.put("value", x.nextTo(';'));
|
||||||
x.next();
|
x.next();
|
||||||
while (x.more()) {
|
while (x.more()) {
|
||||||
name = unescape(x.nextTo("=;"));
|
name = unescape(x.nextTo("=;"));
|
||||||
if (x.next() != '=') {
|
if (x.next() != '=') {
|
||||||
if (name.equals("secure")) {
|
if (name.equals("secure")) {
|
||||||
value = Boolean.TRUE;
|
value = Boolean.TRUE;
|
||||||
} else {
|
} else {
|
||||||
throw x.syntaxError("Missing '=' in cookie parameter.");
|
throw x.syntaxError("Missing '=' in cookie parameter.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value = unescape(x.nextTo(';'));
|
value = unescape(x.nextTo(';'));
|
||||||
x.next();
|
x.next();
|
||||||
}
|
}
|
||||||
jo.put(name, value);
|
jo.put(name, value);
|
||||||
}
|
}
|
||||||
return jo;
|
return jo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a JSONObject into a cookie specification string. The JSONObject
|
* Convert a JSONObject into a cookie specification string. The JSONObject
|
||||||
* must contain "name" and "value" members.
|
* must contain "name" and "value" members.
|
||||||
* If the JSONObject contains "expires", "domain", "path", or "secure"
|
* If the JSONObject contains "expires", "domain", "path", or "secure"
|
||||||
* members, they will be appended to the cookie specification string.
|
* members, they will be appended to the cookie specification string.
|
||||||
* All other members are ignored.
|
* All other members are ignored.
|
||||||
* @param jo A JSONObject
|
* @param jo A JSONObject
|
||||||
* @return A cookie specification string
|
* @return A cookie specification string
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static String toString(JSONObject jo) throws JSONException {
|
public static String toString(JSONObject jo) throws JSONException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
|
|
||||||
sb.append(escape(jo.getString("name")));
|
sb.append(escape(jo.getString("name")));
|
||||||
sb.append("=");
|
sb.append("=");
|
||||||
sb.append(escape(jo.getString("value")));
|
sb.append(escape(jo.getString("value")));
|
||||||
if (jo.has("expires")) {
|
if (jo.has("expires")) {
|
||||||
sb.append(";expires=");
|
sb.append(";expires=");
|
||||||
sb.append(jo.getString("expires"));
|
sb.append(jo.getString("expires"));
|
||||||
}
|
}
|
||||||
if (jo.has("domain")) {
|
if (jo.has("domain")) {
|
||||||
sb.append(";domain=");
|
sb.append(";domain=");
|
||||||
sb.append(escape(jo.getString("domain")));
|
sb.append(escape(jo.getString("domain")));
|
||||||
}
|
}
|
||||||
if (jo.has("path")) {
|
if (jo.has("path")) {
|
||||||
sb.append(";path=");
|
sb.append(";path=");
|
||||||
sb.append(escape(jo.getString("path")));
|
sb.append(escape(jo.getString("path")));
|
||||||
}
|
}
|
||||||
if (jo.optBoolean("secure")) {
|
if (jo.optBoolean("secure")) {
|
||||||
sb.append(";secure");
|
sb.append(";secure");
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert <code>%</code><i>hh</i> sequences to single characters, and
|
* Convert <code>%</code><i>hh</i> sequences to single characters, and
|
||||||
* convert plus to space.
|
* convert plus to space.
|
||||||
* @param string A string that may contain
|
* @param string A string that may contain
|
||||||
* <code>+</code> <small>(plus)</small> and
|
* <code>+</code> <small>(plus)</small> and
|
||||||
* <code>%</code><i>hh</i> sequences.
|
* <code>%</code><i>hh</i> sequences.
|
||||||
* @return The unescaped string.
|
* @return The unescaped string.
|
||||||
*/
|
*/
|
||||||
public static String unescape(String string) {
|
public static String unescape(String string) {
|
||||||
int length = string.length();
|
int length = string.length();
|
||||||
StringBuilder sb = new StringBuilder(length);
|
StringBuffer sb = new StringBuffer();
|
||||||
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 == '+') {
|
||||||
c = ' ';
|
c = ' ';
|
||||||
} else if (c == '%' && i + 2 < length) {
|
} else if (c == '%' && i + 2 < length) {
|
||||||
int d = JSONTokener.dehexchar(string.charAt(i + 1));
|
int d = JSONTokener.dehexchar(string.charAt(i + 1));
|
||||||
int e = JSONTokener.dehexchar(string.charAt(i + 2));
|
int e = JSONTokener.dehexchar(string.charAt(i + 2));
|
||||||
if (d >= 0 && e >= 0) {
|
if (d >= 0 && e >= 0) {
|
||||||
c = (char)(d * 16 + e);
|
c = (char)(d * 16 + e);
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
179
CookieList.java
179
CookieList.java
|
|
@ -1,89 +1,90 @@
|
||||||
package org.json;
|
package org.json;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (c) 2002 JSON.org
|
Copyright (c) 2002 JSON.org
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
The Software shall be used for Good, not Evil.
|
The Software shall be used for Good, not Evil.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Iterator;
|
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 2014-05-03
|
* @version 2010-12-24
|
||||||
*/
|
*/
|
||||||
public class CookieList {
|
public class CookieList {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a cookie list into a JSONObject. A cookie list is a sequence
|
* 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 '='.
|
* of name/value pairs. The names are separated from the values by '='.
|
||||||
* The pairs are separated by ';'. The names and the values
|
* The pairs are separated by ';'. The names and the values
|
||||||
* will be unescaped, possibly converting '+' and '%' sequences.
|
* will be unescaped, possibly converting '+' and '%' sequences.
|
||||||
*
|
*
|
||||||
* To add a cookie to a cooklist,
|
* To add a cookie to a cooklist,
|
||||||
* cookielistJSONObject.put(cookieJSONObject.getString("name"),
|
* cookielistJSONObject.put(cookieJSONObject.getString("name"),
|
||||||
* cookieJSONObject.getString("value"));
|
* cookieJSONObject.getString("value"));
|
||||||
* @param string A cookie list string
|
* @param string A cookie list string
|
||||||
* @return A JSONObject
|
* @return A JSONObject
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||||
JSONObject jo = new JSONObject();
|
JSONObject jo = new JSONObject();
|
||||||
JSONTokener x = new JSONTokener(string);
|
JSONTokener x = new JSONTokener(string);
|
||||||
while (x.more()) {
|
while (x.more()) {
|
||||||
String name = Cookie.unescape(x.nextTo('='));
|
String name = Cookie.unescape(x.nextTo('='));
|
||||||
x.next('=');
|
x.next('=');
|
||||||
jo.put(name, Cookie.unescape(x.nextTo(';')));
|
jo.put(name, Cookie.unescape(x.nextTo(';')));
|
||||||
x.next();
|
x.next();
|
||||||
}
|
}
|
||||||
return jo;
|
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 '='.
|
* Convert a JSONObject into a cookie list. A cookie list is a sequence
|
||||||
* The pairs are separated by ';'. The characters '%', '+', '=', and ';'
|
* of name/value pairs. The names are separated from the values by '='.
|
||||||
* in the names and values are replaced by "%hh".
|
* The pairs are separated by ';'. The characters '%', '+', '=', and ';'
|
||||||
* @param jo A JSONObject
|
* in the names and values are replaced by "%hh".
|
||||||
* @return A cookie list string
|
* @param jo A JSONObject
|
||||||
* @throws JSONException
|
* @return A cookie list string
|
||||||
*/
|
* @throws JSONException
|
||||||
public static String toString(JSONObject jo) throws JSONException {
|
*/
|
||||||
boolean b = false;
|
public static String toString(JSONObject jo) throws JSONException {
|
||||||
Iterator<String> keys = jo.keys();
|
boolean b = false;
|
||||||
String string;
|
Iterator keys = jo.keys();
|
||||||
StringBuilder sb = new StringBuilder();
|
String string;
|
||||||
while (keys.hasNext()) {
|
StringBuffer sb = new StringBuffer();
|
||||||
string = keys.next();
|
while (keys.hasNext()) {
|
||||||
if (!jo.isNull(string)) {
|
string = keys.next().toString();
|
||||||
if (b) {
|
if (!jo.isNull(string)) {
|
||||||
sb.append(';');
|
if (b) {
|
||||||
}
|
sb.append(';');
|
||||||
sb.append(Cookie.escape(string));
|
}
|
||||||
sb.append("=");
|
sb.append(Cookie.escape(string));
|
||||||
sb.append(Cookie.escape(jo.getString(string)));
|
sb.append("=");
|
||||||
b = true;
|
sb.append(Cookie.escape(jo.getString(string)));
|
||||||
}
|
b = true;
|
||||||
}
|
}
|
||||||
return sb.toString();
|
}
|
||||||
}
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
10
HTTP.java
10
HTTP.java
|
|
@ -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 2014-05-03
|
* @version 2010-12-24
|
||||||
*/
|
*/
|
||||||
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<String> keys = jo.keys();
|
Iterator keys = jo.keys();
|
||||||
String string;
|
String string;
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
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();
|
string = keys.next().toString();
|
||||||
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)) {
|
||||||
|
|
|
||||||
|
|
@ -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 2014-05-03
|
* @version 2012-11-13
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
do {
|
do {
|
||||||
c = next();
|
c = next();
|
||||||
} while (Character.isWhitespace(c));
|
} while (Character.isWhitespace(c));
|
||||||
|
|
|
||||||
|
|
@ -75,20 +75,20 @@ import java.util.Map;
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2014-05-03
|
* @version 2013-04-18
|
||||||
*/
|
*/
|
||||||
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<Object> myArrayList;
|
private final ArrayList myArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an empty JSONArray.
|
* Construct an empty JSONArray.
|
||||||
*/
|
*/
|
||||||
public JSONArray() {
|
public JSONArray() {
|
||||||
this.myArrayList = new ArrayList<Object>();
|
this.myArrayList = new ArrayList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -150,10 +150,10 @@ public class JSONArray {
|
||||||
* @param collection
|
* @param collection
|
||||||
* A Collection.
|
* A Collection.
|
||||||
*/
|
*/
|
||||||
public JSONArray(Collection<Object> collection) {
|
public JSONArray(Collection collection) {
|
||||||
this.myArrayList = new ArrayList<Object>();
|
this.myArrayList = new ArrayList();
|
||||||
if (collection != null) {
|
if (collection != null) {
|
||||||
Iterator<Object> iter = collection.iterator();
|
Iterator 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();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
|
|
||||||
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<Object> value) {
|
public JSONArray put(Collection 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<String, Object> value) {
|
public JSONArray put(Map 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<Object> value) throws JSONException {
|
public JSONArray put(int index, Collection 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<String, Object> value) throws JSONException {
|
public JSONArray put(int index, Map value) throws JSONException {
|
||||||
this.put(index, new JSONObject(value));
|
this.put(index, new JSONObject(value));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
@ -813,42 +813,9 @@ public class JSONArray {
|
||||||
* was no value.
|
* was no value.
|
||||||
*/
|
*/
|
||||||
public Object remove(int index) {
|
public Object remove(int index) {
|
||||||
return index >= 0 && index < this.length()
|
Object o = this.opt(index);
|
||||||
? this.myArrayList.remove(index)
|
this.myArrayList.remove(index);
|
||||||
: null;
|
return o;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if two JSONArrays are similar.
|
|
||||||
* They must contain similar sequences.
|
|
||||||
*
|
|
||||||
* @param other The other JSONArray
|
|
||||||
* @return true if they are equal
|
|
||||||
*/
|
|
||||||
public boolean similar(Object other) {
|
|
||||||
if (!(other instanceof JSONArray)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int len = this.length();
|
|
||||||
if (len != ((JSONArray)other).length()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < len; i += 1) {
|
|
||||||
Object valueThis = this.get(i);
|
|
||||||
Object valueOther = ((JSONArray)other).get(i);
|
|
||||||
if (valueThis instanceof JSONObject) {
|
|
||||||
if (!((JSONObject)valueThis).similar(valueOther)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (valueThis instanceof JSONArray) {
|
|
||||||
if (!((JSONArray)valueThis).similar(valueOther)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (!valueThis.equals(valueOther)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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 2014-05-03
|
* @version 2013-02-10
|
||||||
*/
|
*/
|
||||||
public class JSONException extends RuntimeException {
|
public class JSONException extends RuntimeException {
|
||||||
private static final long serialVersionUID = 0;
|
private static final long serialVersionUID = 0;
|
||||||
|
|
@ -22,7 +22,6 @@ 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());
|
||||||
|
|
@ -33,10 +32,9 @@ 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.
|
||||||
*
|
*
|
||||||
* @return 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.
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
public Throwable getCause() {
|
public Throwable getCause() {
|
||||||
return this.cause;
|
return this.cause;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
JSONML.java
48
JSONML.java
|
|
@ -33,7 +33,7 @@ import java.util.Iterator;
|
||||||
* the JsonML transform.
|
* the JsonML transform.
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2014-05-03
|
* @version 2012-03-28
|
||||||
*/
|
*/
|
||||||
public class JSONML {
|
public class JSONML {
|
||||||
|
|
||||||
|
|
@ -53,12 +53,12 @@ public class JSONML {
|
||||||
) throws JSONException {
|
) throws JSONException {
|
||||||
String attribute;
|
String attribute;
|
||||||
char c;
|
char c;
|
||||||
String closeTag = null;
|
String closeTag = null;
|
||||||
int i;
|
int i;
|
||||||
JSONArray newja = null;
|
JSONArray newja = null;
|
||||||
JSONObject newjo = null;
|
JSONObject newjo = null;
|
||||||
Object token;
|
Object token;
|
||||||
String tagName = null;
|
String tagName = null;
|
||||||
|
|
||||||
// Test for and skip past these forms:
|
// Test for and skip past these forms:
|
||||||
// <!-- ... -->
|
// <!-- ... -->
|
||||||
|
|
@ -312,15 +312,15 @@ public class JSONML {
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static String toString(JSONArray ja) throws JSONException {
|
public static String toString(JSONArray ja) throws JSONException {
|
||||||
int i;
|
int i;
|
||||||
JSONObject jo;
|
JSONObject jo;
|
||||||
String key;
|
String key;
|
||||||
Iterator<String> keys;
|
Iterator keys;
|
||||||
int length;
|
int length;
|
||||||
Object object;
|
Object object;
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
String tagName;
|
String tagName;
|
||||||
String value;
|
String value;
|
||||||
|
|
||||||
// Emit <tagName
|
// Emit <tagName
|
||||||
|
|
||||||
|
|
@ -339,7 +339,7 @@ public class JSONML {
|
||||||
|
|
||||||
keys = jo.keys();
|
keys = jo.keys();
|
||||||
while (keys.hasNext()) {
|
while (keys.hasNext()) {
|
||||||
key = keys.next();
|
key = keys.next().toString();
|
||||||
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,15 +394,15 @@ public class JSONML {
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static String toString(JSONObject jo) throws JSONException {
|
public static String toString(JSONObject jo) throws JSONException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
int i;
|
int i;
|
||||||
JSONArray ja;
|
JSONArray ja;
|
||||||
String key;
|
String key;
|
||||||
Iterator<String> keys;
|
Iterator keys;
|
||||||
int length;
|
int length;
|
||||||
Object object;
|
Object object;
|
||||||
String tagName;
|
String tagName;
|
||||||
String value;
|
String value;
|
||||||
|
|
||||||
//Emit <tagName
|
//Emit <tagName
|
||||||
|
|
||||||
|
|
@ -419,7 +419,7 @@ public class JSONML {
|
||||||
|
|
||||||
keys = jo.keys();
|
keys = jo.keys();
|
||||||
while (keys.hasNext()) {
|
while (keys.hasNext()) {
|
||||||
key = keys.next();
|
key = keys.next().toString();
|
||||||
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);
|
||||||
|
|
|
||||||
114
JSONObject.java
114
JSONObject.java
|
|
@ -36,7 +36,6 @@ 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;
|
||||||
|
|
||||||
|
|
@ -91,7 +90,7 @@ import java.util.Set;
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2014-05-03
|
* @version 2013-06-17
|
||||||
*/
|
*/
|
||||||
public class JSONObject {
|
public class JSONObject {
|
||||||
/**
|
/**
|
||||||
|
|
@ -107,7 +106,6 @@ public class JSONObject {
|
||||||
*
|
*
|
||||||
* @return NULL.
|
* @return NULL.
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
protected final Object clone() {
|
protected final Object clone() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +118,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +135,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<String, Object> map;
|
private final Map map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It is sometimes more convenient and less ambiguous to have a
|
* It is sometimes more convenient and less ambiguous to have a
|
||||||
|
|
@ -152,7 +149,7 @@ public class JSONObject {
|
||||||
* Construct an empty JSONObject.
|
* Construct an empty JSONObject.
|
||||||
*/
|
*/
|
||||||
public JSONObject() {
|
public JSONObject() {
|
||||||
this.map = new HashMap<String, Object>();
|
this.map = new HashMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -242,15 +239,15 @@ public class JSONObject {
|
||||||
* the JSONObject.
|
* the JSONObject.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public JSONObject(Map<String, Object> map) {
|
public JSONObject(Map map) {
|
||||||
this.map = new HashMap<String, Object>();
|
this.map = new HashMap();
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
Iterator<Entry<String, Object>> i = map.entrySet().iterator();
|
Iterator i = map.entrySet().iterator();
|
||||||
while (i.hasNext()) {
|
while (i.hasNext()) {
|
||||||
Entry<String, Object> entry = i.next();
|
Map.Entry e = (Map.Entry) i.next();
|
||||||
Object value = entry.getValue();
|
Object value = e.getValue();
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
this.map.put(entry.getKey(), wrap(value));
|
this.map.put(e.getKey(), wrap(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -341,10 +338,10 @@ public class JSONObject {
|
||||||
|
|
||||||
// Iterate through the keys in the bundle.
|
// Iterate through the keys in the bundle.
|
||||||
|
|
||||||
Enumeration<String> keys = bundle.getKeys();
|
Enumeration keys = bundle.getKeys();
|
||||||
while (keys.hasMoreElements()) {
|
while (keys.hasMoreElements()) {
|
||||||
Object key = keys.nextElement();
|
Object key = keys.nextElement();
|
||||||
if (key != null) {
|
if (key instanceof String) {
|
||||||
|
|
||||||
// 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
|
||||||
|
|
@ -612,11 +609,11 @@ public class JSONObject {
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Iterator<String> iterator = jo.keys();
|
Iterator 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] = iterator.next();
|
names[i] = (String) iterator.next();
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
return names;
|
return names;
|
||||||
|
|
@ -689,13 +686,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 + 1);
|
this.put(key, ((Integer) value).intValue() + 1);
|
||||||
} else if (value instanceof Long) {
|
} else if (value instanceof Long) {
|
||||||
this.put(key, (Long) value + 1);
|
this.put(key, ((Long) value).longValue() + 1);
|
||||||
} else if (value instanceof Double) {
|
} else if (value instanceof Double) {
|
||||||
this.put(key, (Double) value + 1);
|
this.put(key, ((Double) value).doubleValue() + 1);
|
||||||
} else if (value instanceof Float) {
|
} else if (value instanceof Float) {
|
||||||
this.put(key, (Float) value + 1);
|
this.put(key, ((Float) value).floatValue() + 1);
|
||||||
} else {
|
} else {
|
||||||
throw new JSONException("Unable to increment [" + quote(key) + "].");
|
throw new JSONException("Unable to increment [" + quote(key) + "].");
|
||||||
}
|
}
|
||||||
|
|
@ -720,7 +717,7 @@ public class JSONObject {
|
||||||
*
|
*
|
||||||
* @return An iterator of the keys.
|
* @return An iterator of the keys.
|
||||||
*/
|
*/
|
||||||
public Iterator<String> keys() {
|
public Iterator keys() {
|
||||||
return this.keySet().iterator();
|
return this.keySet().iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -729,7 +726,7 @@ public class JSONObject {
|
||||||
*
|
*
|
||||||
* @return A keySet.
|
* @return A keySet.
|
||||||
*/
|
*/
|
||||||
public Set<String> keySet() {
|
public Set keySet() {
|
||||||
return this.map.keySet();
|
return this.map.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -751,7 +748,7 @@ public class JSONObject {
|
||||||
*/
|
*/
|
||||||
public JSONArray names() {
|
public JSONArray names() {
|
||||||
JSONArray ja = new JSONArray();
|
JSONArray ja = new JSONArray();
|
||||||
Iterator<String> keys = this.keys();
|
Iterator keys = this.keys();
|
||||||
while (keys.hasNext()) {
|
while (keys.hasNext()) {
|
||||||
ja.put(keys.next());
|
ja.put(keys.next());
|
||||||
}
|
}
|
||||||
|
|
@ -1053,7 +1050,7 @@ public class JSONObject {
|
||||||
* @return this.
|
* @return this.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public JSONObject put(String key, Collection<Object> value) throws JSONException {
|
public JSONObject put(String key, Collection value) throws JSONException {
|
||||||
this.put(key, new JSONArray(value));
|
this.put(key, new JSONArray(value));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
@ -1117,7 +1114,7 @@ public class JSONObject {
|
||||||
* @return this.
|
* @return this.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public JSONObject put(String key, Map<String, Object> value) throws JSONException {
|
public JSONObject put(String key, Map value) throws JSONException {
|
||||||
this.put(key, new JSONObject(value));
|
this.put(key, new JSONObject(value));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
@ -1154,9 +1151,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 string
|
* @param key
|
||||||
* @param value object
|
* @param value
|
||||||
* @return this.
|
* @return his.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
* if the key is a duplicate
|
* if the key is a duplicate
|
||||||
*/
|
*/
|
||||||
|
|
@ -1284,46 +1281,6 @@ public class JSONObject {
|
||||||
return this.map.remove(key);
|
return this.map.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if two JSONObjects are similar.
|
|
||||||
* They must contain the same set of names which must be associated with
|
|
||||||
* similar values.
|
|
||||||
*
|
|
||||||
* @param other The other JSONObject
|
|
||||||
* @return true if they are equal
|
|
||||||
*/
|
|
||||||
public boolean similar(Object other) {
|
|
||||||
try {
|
|
||||||
if (!(other instanceof JSONObject)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Set<String> set = this.keySet();
|
|
||||||
if (!set.equals(((JSONObject)other).keySet())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Iterator<String> iterator = set.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
String name = iterator.next();
|
|
||||||
Object valueThis = this.get(name);
|
|
||||||
Object valueOther = ((JSONObject)other).get(name);
|
|
||||||
if (valueThis instanceof JSONObject) {
|
|
||||||
if (!((JSONObject)valueThis).similar(valueOther)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (valueThis instanceof JSONArray) {
|
|
||||||
if (!((JSONArray)valueThis).similar(valueOther)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (!valueThis.equals(valueOther)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (Throwable exception) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to convert a string into a number, boolean, or null. If the string
|
* Try to convert a string into a number, boolean, or null. If the string
|
||||||
* can't be converted, return the string.
|
* can't be converted, return the string.
|
||||||
|
|
@ -1364,8 +1321,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 == myLong.intValue()) {
|
if (myLong.longValue() == myLong.intValue()) {
|
||||||
return myLong.intValue();
|
return new Integer(myLong.intValue());
|
||||||
} else {
|
} else {
|
||||||
return myLong;
|
return myLong;
|
||||||
}
|
}
|
||||||
|
|
@ -1512,10 +1469,10 @@ public class JSONObject {
|
||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
if (value instanceof Map) {
|
if (value instanceof Map) {
|
||||||
return new JSONObject((Map<String, Object>)value).toString();
|
return new JSONObject((Map) value).toString();
|
||||||
}
|
}
|
||||||
if (value instanceof Collection) {
|
if (value instanceof Collection) {
|
||||||
return new JSONArray((Collection<Object>) value).toString();
|
return new JSONArray((Collection) value).toString();
|
||||||
}
|
}
|
||||||
if (value.getClass().isArray()) {
|
if (value.getClass().isArray()) {
|
||||||
return new JSONArray(value).toString();
|
return new JSONArray(value).toString();
|
||||||
|
|
@ -1551,13 +1508,13 @@ public class JSONObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object instanceof Collection) {
|
if (object instanceof Collection) {
|
||||||
return new JSONArray((Collection<Object>) object);
|
return new JSONArray((Collection) 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<String, Object>) object);
|
return new JSONObject((Map) object);
|
||||||
}
|
}
|
||||||
Package objectPackage = object.getClass().getPackage();
|
Package objectPackage = object.getClass().getPackage();
|
||||||
String objectPackageName = objectPackage != null ? objectPackage
|
String objectPackageName = objectPackage != null ? objectPackage
|
||||||
|
|
@ -1595,9 +1552,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<String, Object>) value).write(writer, indentFactor, indent);
|
new JSONObject((Map) value).write(writer, indentFactor, indent);
|
||||||
} else if (value instanceof Collection) {
|
} else if (value instanceof Collection) {
|
||||||
new JSONArray((Collection<Object>) value).write(writer, indentFactor,
|
new JSONArray((Collection) 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);
|
||||||
|
|
@ -1639,7 +1596,7 @@ public class JSONObject {
|
||||||
try {
|
try {
|
||||||
boolean commanate = false;
|
boolean commanate = false;
|
||||||
final int length = this.length();
|
final int length = this.length();
|
||||||
Iterator<String> keys = this.keys();
|
Iterator keys = this.keys();
|
||||||
writer.write('{');
|
writer.write('{');
|
||||||
|
|
||||||
if (length == 1) {
|
if (length == 1) {
|
||||||
|
|
@ -1666,7 +1623,8 @@ public class JSONObject {
|
||||||
if (indentFactor > 0) {
|
if (indentFactor > 0) {
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
}
|
}
|
||||||
writeValue(writer, this.map.get(key), indentFactor, newindent);
|
writeValue(writer, this.map.get(key), indentFactor,
|
||||||
|
newindent);
|
||||||
commanate = true;
|
commanate = true;
|
||||||
}
|
}
|
||||||
if (indentFactor > 0) {
|
if (indentFactor > 0) {
|
||||||
|
|
|
||||||
|
|
@ -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 2014-05-03
|
* @version 2012-02-16
|
||||||
*/
|
*/
|
||||||
public class JSONTokener {
|
public class JSONTokener {
|
||||||
|
|
||||||
|
|
@ -69,7 +69,6 @@ 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));
|
||||||
|
|
@ -251,7 +250,7 @@ public class JSONTokener {
|
||||||
*/
|
*/
|
||||||
public String nextString(char quote) throws JSONException {
|
public String nextString(char quote) throws JSONException {
|
||||||
char c;
|
char c;
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
c = this.next();
|
c = this.next();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|
@ -307,7 +306,7 @@ public class JSONTokener {
|
||||||
* @return A string.
|
* @return A string.
|
||||||
*/
|
*/
|
||||||
public String nextTo(char delimiter) throws JSONException {
|
public String nextTo(char delimiter) throws JSONException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
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') {
|
||||||
|
|
@ -329,7 +328,7 @@ public class JSONTokener {
|
||||||
*/
|
*/
|
||||||
public String nextTo(String delimiters) throws JSONException {
|
public String nextTo(String delimiters) throws JSONException {
|
||||||
char c;
|
char c;
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
c = this.next();
|
c = this.next();
|
||||||
if (delimiters.indexOf(c) >= 0 || c == 0 ||
|
if (delimiters.indexOf(c) >= 0 || c == 0 ||
|
||||||
|
|
@ -376,7 +375,7 @@ public class JSONTokener {
|
||||||
* formatting character.
|
* formatting character.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
c = this.next();
|
c = this.next();
|
||||||
|
|
@ -415,9 +414,10 @@ public class JSONTokener {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
} while (c != to);
|
} while (c != to);
|
||||||
} catch (IOException exception) {
|
} catch (IOException exc) {
|
||||||
throw new JSONException(exception);
|
throw new JSONException(exc);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.back();
|
this.back();
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,7 @@ public class JSONWriter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push an array or object scope.
|
* Push an array or object scope.
|
||||||
* @param jo The scope to open.
|
* @param c 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 {
|
||||||
|
|
|
||||||
373
Kim.java
Normal file
373
Kim.java
Normal file
|
|
@ -0,0 +1,373 @@
|
||||||
|
package org.json;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kim makes immutable eight bit Unicode strings. If the MSB of a byte is set,
|
||||||
|
* then the next byte is a continuation byte. The last byte of a character
|
||||||
|
* never has the MSB reset. Every byte that is not the last byte has the MSB
|
||||||
|
* set. Kim stands for "Keep it minimal". A Unicode character is never longer
|
||||||
|
* than 3 bytes. Every byte contributes 7 bits to the character. ASCII is
|
||||||
|
* unmodified.
|
||||||
|
*
|
||||||
|
* Kim UTF-8
|
||||||
|
* one byte U+007F U+007F
|
||||||
|
* two bytes U+3FFF U+07FF
|
||||||
|
* three bytes U+10FFF U+FFFF
|
||||||
|
* four bytes U+10FFFF
|
||||||
|
*
|
||||||
|
* Characters in the ranges U+0800..U+3FFF and U+10000..U+10FFFF will be one
|
||||||
|
* byte smaller when encoded in Kim compared to UTF-8.
|
||||||
|
*
|
||||||
|
* Kim is beneficial when using scripts such as Old South Arabian, Aramaic,
|
||||||
|
* Avestan, Balinese, Batak, Bopomofo, Buginese, Buhid, Carian, Cherokee,
|
||||||
|
* Coptic, Cyrillic, Deseret, Egyptian Hieroglyphs, Ethiopic, Georgian,
|
||||||
|
* Glagolitic, Gothic, Hangul Jamo, Hanunoo, Hiragana, Kanbun, Kaithi,
|
||||||
|
* Kannada, Katakana, Kharoshthi, Khmer, Lao, Lepcha, Limbu, Lycian, Lydian,
|
||||||
|
* Malayalam, Mandaic, Meroitic, Miao, Mongolian, Myanmar, New Tai Lue,
|
||||||
|
* Ol Chiki, Old Turkic, Oriya, Osmanya, Pahlavi, Parthian, Phags-Pa,
|
||||||
|
* Phoenician, Samaritan, Sharada, Sinhala, Sora Sompeng, Tagalog, Tagbanwa,
|
||||||
|
* Takri, Tai Le, Tai Tham, Tamil, Telugu, Thai, Tibetan, Tifinagh, UCAS.
|
||||||
|
*
|
||||||
|
* A kim object can be constructed from an ordinary UTF-16 string, or from a
|
||||||
|
* byte array. A kim object can produce a UTF-16 string.
|
||||||
|
*
|
||||||
|
* As with UTF-8, it is possible to detect character boundaries within a byte
|
||||||
|
* sequence. UTF-8 is one of the world's great inventions. While Kim is more
|
||||||
|
* efficient, it is not clear that it is worth the expense of transition.
|
||||||
|
*
|
||||||
|
* @version 2013-04-18
|
||||||
|
*/
|
||||||
|
public class Kim {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The byte array containing the kim's content.
|
||||||
|
*/
|
||||||
|
private byte[] bytes = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The kim's hashcode, conforming to Java's hashcode conventions.
|
||||||
|
*/
|
||||||
|
private int hashcode = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of bytes in the kim. The number of bytes can be as much as
|
||||||
|
* three times the number of characters.
|
||||||
|
*/
|
||||||
|
public int length = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The memoization of toString().
|
||||||
|
*/
|
||||||
|
private String string = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a kim from a portion of a byte array.
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* A byte array.
|
||||||
|
* @param from
|
||||||
|
* The index of the first byte.
|
||||||
|
* @param thru
|
||||||
|
* The index of the last byte plus one.
|
||||||
|
*/
|
||||||
|
public Kim(byte[] bytes, int from, int thru) {
|
||||||
|
|
||||||
|
// As the bytes are copied into the new kim, a hashcode is computed using a
|
||||||
|
// modified Fletcher code.
|
||||||
|
|
||||||
|
int sum = 1;
|
||||||
|
int value;
|
||||||
|
this.hashcode = 0;
|
||||||
|
this.length = thru - from;
|
||||||
|
if (this.length > 0) {
|
||||||
|
this.bytes = new byte[this.length];
|
||||||
|
for (int at = 0; at < this.length; at += 1) {
|
||||||
|
value = (int) bytes[at + from] & 0xFF;
|
||||||
|
sum += value;
|
||||||
|
this.hashcode += sum;
|
||||||
|
this.bytes[at] = (byte) value;
|
||||||
|
}
|
||||||
|
this.hashcode += sum << 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a kim from a byte array.
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* The byte array.
|
||||||
|
* @param length
|
||||||
|
* The number of bytes.
|
||||||
|
*/
|
||||||
|
public Kim(byte[] bytes, int length) {
|
||||||
|
this(bytes, 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a new kim from a substring of an existing kim. The coordinates are
|
||||||
|
* in byte units, not character units.
|
||||||
|
*
|
||||||
|
* @param kim
|
||||||
|
* The source of bytes.
|
||||||
|
* @param from
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a kim from a string.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
* The string.
|
||||||
|
* @throws JSONException
|
||||||
|
* if surrogate pair mismatch.
|
||||||
|
*/
|
||||||
|
public Kim(String string) throws JSONException {
|
||||||
|
int stringLength = string.length();
|
||||||
|
this.hashcode = 0;
|
||||||
|
this.length = 0;
|
||||||
|
|
||||||
|
// First pass: Determine the length of the kim, allowing for the UTF-16
|
||||||
|
// to UTF-32 conversion, and then the UTF-32 to Kim conversion.
|
||||||
|
|
||||||
|
if (stringLength > 0) {
|
||||||
|
for (int i = 0; i < stringLength; i += 1) {
|
||||||
|
int c = string.charAt(i);
|
||||||
|
if (c <= 0x7F) {
|
||||||
|
this.length += 1;
|
||||||
|
} else if (c <= 0x3FFF) {
|
||||||
|
this.length += 2;
|
||||||
|
} else {
|
||||||
|
if (c >= 0xD800 && c <= 0xDFFF) {
|
||||||
|
i += 1;
|
||||||
|
int d = string.charAt(i);
|
||||||
|
if (c > 0xDBFF || d < 0xDC00 || d > 0xDFFF) {
|
||||||
|
throw new JSONException("Bad UTF16");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.length += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass: Allocate a byte array and fill that array with the conversion
|
||||||
|
// while computing the hashcode.
|
||||||
|
|
||||||
|
this.bytes = new byte[length];
|
||||||
|
int at = 0;
|
||||||
|
int b;
|
||||||
|
int sum = 1;
|
||||||
|
for (int i = 0; i < stringLength; i += 1) {
|
||||||
|
int character = string.charAt(i);
|
||||||
|
if (character <= 0x7F) {
|
||||||
|
bytes[at] = (byte) character;
|
||||||
|
sum += character;
|
||||||
|
this.hashcode += sum;
|
||||||
|
at += 1;
|
||||||
|
} else if (character <= 0x3FFF) {
|
||||||
|
b = 0x80 | (character >>> 7);
|
||||||
|
bytes[at] = (byte) b;
|
||||||
|
sum += b;
|
||||||
|
this.hashcode += sum;
|
||||||
|
at += 1;
|
||||||
|
b = character & 0x7F;
|
||||||
|
bytes[at] = (byte) b;
|
||||||
|
sum += b;
|
||||||
|
this.hashcode += sum;
|
||||||
|
at += 1;
|
||||||
|
} else {
|
||||||
|
if (character >= 0xD800 && character <= 0xDBFF) {
|
||||||
|
i += 1;
|
||||||
|
character = (((character & 0x3FF) << 10) | (string
|
||||||
|
.charAt(i) & 0x3FF)) + 65536;
|
||||||
|
}
|
||||||
|
b = 0x80 | (character >>> 14);
|
||||||
|
bytes[at] = (byte) b;
|
||||||
|
sum += b;
|
||||||
|
this.hashcode += sum;
|
||||||
|
at += 1;
|
||||||
|
b = 0x80 | ((character >>> 7) & 0xFF);
|
||||||
|
bytes[at] = (byte) b;
|
||||||
|
sum += b;
|
||||||
|
this.hashcode += sum;
|
||||||
|
at += 1;
|
||||||
|
b = character & 0x7F;
|
||||||
|
bytes[at] = (byte) b;
|
||||||
|
sum += b;
|
||||||
|
this.hashcode += sum;
|
||||||
|
at += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.hashcode += sum << 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the character at the specified index. The index refers to byte
|
||||||
|
* values and ranges from 0 to length - 1. The index of the next character
|
||||||
|
* is at index + Kim.characterSize(kim.characterAt(index)).
|
||||||
|
*
|
||||||
|
* @param at
|
||||||
|
* the index of the char value. The first character is at 0.
|
||||||
|
* @returns a Unicode character between 0 and 0x10FFFF.
|
||||||
|
* @throws JSONException
|
||||||
|
* if at does not point to a valid character.
|
||||||
|
*/
|
||||||
|
public int characterAt(int at) throws JSONException {
|
||||||
|
int c = get(at);
|
||||||
|
if ((c & 0x80) == 0) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
int character;
|
||||||
|
int c1 = get(at + 1);
|
||||||
|
if ((c1 & 0x80) == 0) {
|
||||||
|
character = ((c & 0x7F) << 7) | c1;
|
||||||
|
if (character > 0x7F) {
|
||||||
|
return character;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int c2 = get(at + 2);
|
||||||
|
character = ((c & 0x7F) << 14) | ((c1 & 0x7F) << 7) | c2;
|
||||||
|
if ((c2 & 0x80) == 0 && character > 0x3FFF && character <= 0x10FFFF
|
||||||
|
&& (character < 0xD800 || character > 0xDFFF)) {
|
||||||
|
return character;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new JSONException("Bad character at " + at);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bytes needed to contain the character in Kim
|
||||||
|
* format.
|
||||||
|
*
|
||||||
|
* @param character
|
||||||
|
* a Unicode character between 0 and 0x10FFFF.
|
||||||
|
* @return 1, 2, or 3
|
||||||
|
* @throws JSONException
|
||||||
|
* if the character is not representable in a kim.
|
||||||
|
*/
|
||||||
|
public static int characterSize(int character) throws JSONException {
|
||||||
|
if (character < 0 || character > 0x10FFFF) {
|
||||||
|
throw new JSONException("Bad character " + character);
|
||||||
|
}
|
||||||
|
return character <= 0x7F ? 1 : character <= 0x3FFF ? 2 : 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the contents of this kim to a byte array.
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* A byte array of sufficient size.
|
||||||
|
* @param at
|
||||||
|
* The position within the byte array to take the byes.
|
||||||
|
* @return The position immediately after the copy.
|
||||||
|
*/
|
||||||
|
public int copy(byte[] bytes, int at) {
|
||||||
|
System.arraycopy(this.bytes, 0, bytes, at, this.length);
|
||||||
|
return at + this.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Two kim objects containing exactly the same bytes in the same order are
|
||||||
|
* equal to each other.
|
||||||
|
*
|
||||||
|
* @param obj
|
||||||
|
* the other kim with which to compare.
|
||||||
|
* @returns true if this and obj are both kim objects containing identical
|
||||||
|
* byte sequences.
|
||||||
|
*/
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof Kim)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Kim that = (Kim) obj;
|
||||||
|
if (this == that) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.hashcode != that.hashcode) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return java.util.Arrays.equals(this.bytes, that.bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a byte from a kim.
|
||||||
|
* @param at
|
||||||
|
* The position of the byte. The first byte is at 0.
|
||||||
|
* @return The byte.
|
||||||
|
* @throws JSONException
|
||||||
|
* if there is no byte at that position.
|
||||||
|
*/
|
||||||
|
public int get(int at) throws JSONException {
|
||||||
|
if (at < 0 || at > this.length) {
|
||||||
|
throw new JSONException("Bad character at " + at);
|
||||||
|
}
|
||||||
|
return ((int) this.bytes[at]) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hash code value for the kim.
|
||||||
|
*/
|
||||||
|
public int hashCode() {
|
||||||
|
return this.hashcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce a UTF-16 String from this kim. The number of codepoints in the
|
||||||
|
* string will not be greater than the number of bytes in the kim, although
|
||||||
|
* it could be less.
|
||||||
|
*
|
||||||
|
* @return The string. A kim memoizes its string representation.
|
||||||
|
* @throws JSONException
|
||||||
|
* if the kim is not valid.
|
||||||
|
*/
|
||||||
|
public String toString() throws JSONException {
|
||||||
|
if (this.string == null) {
|
||||||
|
int c;
|
||||||
|
int length = 0;
|
||||||
|
char chars[] = new char[this.length];
|
||||||
|
for (int at = 0; at < this.length; at += characterSize(c)) {
|
||||||
|
c = this.characterAt(at);
|
||||||
|
if (c < 0x10000) {
|
||||||
|
chars[length] = (char) c;
|
||||||
|
length += 1;
|
||||||
|
} else {
|
||||||
|
chars[length] = (char) (0xD800 | ((c - 0x10000) >>> 10));
|
||||||
|
length += 1;
|
||||||
|
chars[length] = (char) (0xDC00 | (c & 0x03FF));
|
||||||
|
length += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.string = new String(chars, 0, length);
|
||||||
|
}
|
||||||
|
return this.string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 2014-05-03
|
* @version 2013-05-23
|
||||||
*/
|
*/
|
||||||
public class Property {
|
public class Property {
|
||||||
/**
|
/**
|
||||||
|
|
@ -50,6 +50,7 @@ public class Property {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return jo;
|
return jo;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -61,12 +62,13 @@ 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<String> keys = jo.keys();
|
Iterator keys = jo.keys();
|
||||||
|
|
||||||
while (keys.hasNext()) {
|
while (keys.hasNext()) {
|
||||||
String name = keys.next();
|
String name = keys.next().toString();
|
||||||
properties.put(name, jo.getString(name));
|
properties.put(name, jo.getString(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
9
README
9
README
|
|
@ -1,14 +1,9 @@
|
||||||
JSON in Java [package org.json]
|
JSON in Java [package org.json]
|
||||||
|
|
||||||
This package needs a new owner. I have not used it in over a decade, and I do
|
|
||||||
not have time to maintain programs that I do not use.
|
|
||||||
|
|
||||||
If you think you can give this package a good home, please contact me.
|
|
||||||
|
|
||||||
Douglas Crockford
|
Douglas Crockford
|
||||||
douglas@crockford.com
|
douglas@crockford.com
|
||||||
|
|
||||||
2015-02-06
|
2011-02-02
|
||||||
|
|
||||||
|
|
||||||
JSON is a light-weight, language independent, data interchange format.
|
JSON is a light-weight, language independent, data interchange format.
|
||||||
|
|
@ -26,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.8.
|
The package compiles on Java 1.2 thru Java 1.4.
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
|
|
||||||
90
XML.java
90
XML.java
|
|
@ -26,40 +26,41 @@ 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 2014-05-03
|
* @version 2012-10-26
|
||||||
*/
|
*/
|
||||||
public class XML {
|
public class XML {
|
||||||
|
|
||||||
/** The Character '&'. */
|
/** The Character '&'. */
|
||||||
public static final Character AMP = '&';
|
public static final Character AMP = new Character('&');
|
||||||
|
|
||||||
/** The Character '''. */
|
/** The Character '''. */
|
||||||
public static final Character APOS = '\'';
|
public static final Character APOS = new Character('\'');
|
||||||
|
|
||||||
/** The Character '!'. */
|
/** The Character '!'. */
|
||||||
public static final Character BANG = '!';
|
public static final Character BANG = new Character('!');
|
||||||
|
|
||||||
/** The Character '='. */
|
/** The Character '='. */
|
||||||
public static final Character EQ = '=';
|
public static final Character EQ = new Character('=');
|
||||||
|
|
||||||
/** The Character '>'. */
|
/** The Character '>'. */
|
||||||
public static final Character GT = '>';
|
public static final Character GT = new Character('>');
|
||||||
|
|
||||||
/** The Character '<'. */
|
/** The Character '<'. */
|
||||||
public static final Character LT = '<';
|
public static final Character LT = new Character('<');
|
||||||
|
|
||||||
/** The Character '?'. */
|
/** The Character '?'. */
|
||||||
public static final Character QUEST = '?';
|
public static final Character QUEST = new Character('?');
|
||||||
|
|
||||||
/** The Character '"'. */
|
/** The Character '"'. */
|
||||||
public static final Character QUOT = '"';
|
public static final Character QUOT = new Character('"');
|
||||||
|
|
||||||
/** The Character '/'. */
|
/** The Character '/'. */
|
||||||
public static final Character SLASH = '/';
|
public static final Character SLASH = new Character('/');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace special characters with XML escapes:
|
* Replace special characters with XML escapes:
|
||||||
|
|
@ -73,7 +74,7 @@ public class XML {
|
||||||
* @return The escaped string.
|
* @return The escaped string.
|
||||||
*/
|
*/
|
||||||
public static String escape(String string) {
|
public static String escape(String string) {
|
||||||
StringBuilder sb = new StringBuilder(string.length());
|
StringBuffer sb = new StringBuffer();
|
||||||
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) {
|
||||||
|
|
@ -102,7 +103,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 A string.
|
* @param string
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static void noSpace(String string) throws JSONException {
|
public static void noSpace(String string) throws JSONException {
|
||||||
|
|
@ -300,6 +301,9 @@ public class XML {
|
||||||
* @return A simple JSON value.
|
* @return A simple JSON value.
|
||||||
*/
|
*/
|
||||||
public static Object stringToValue(String string) {
|
public static Object stringToValue(String string) {
|
||||||
|
if ("".equals(string)) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
if ("true".equalsIgnoreCase(string)) {
|
if ("true".equalsIgnoreCase(string)) {
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
}
|
}
|
||||||
|
|
@ -309,26 +313,36 @@ public class XML {
|
||||||
if ("null".equalsIgnoreCase(string)) {
|
if ("null".equalsIgnoreCase(string)) {
|
||||||
return JSONObject.NULL;
|
return JSONObject.NULL;
|
||||||
}
|
}
|
||||||
|
if ("0".equals(string)) {
|
||||||
|
return new Integer(0);
|
||||||
|
}
|
||||||
|
|
||||||
// If it might be a number, try converting it, first as a Long, and then as a
|
// If it might be a number, try converting it. If that doesn't work,
|
||||||
// Double. If that doesn't work, return the string.
|
// return the string.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
char initial = string.charAt(0);
|
char initial = string.charAt(0);
|
||||||
if (initial == '-' || (initial >= '0' && initial <= '9')) {
|
boolean negative = false;
|
||||||
Long value = new Long(string);
|
if (initial == '-') {
|
||||||
if (value.toString().equals(string)) {
|
initial = string.charAt(1);
|
||||||
return value;
|
negative = true;
|
||||||
|
}
|
||||||
|
if (initial == '0' && string.charAt(negative ? 2 : 1) == '0') {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
if ((initial >= '0' && initial <= '9')) {
|
||||||
|
if (string.indexOf('.') >= 0) {
|
||||||
|
return Double.valueOf(string);
|
||||||
|
} else if (string.indexOf('e') < 0 && string.indexOf('E') < 0) {
|
||||||
|
Long myLong = new Long(string);
|
||||||
|
if (myLong.longValue() == myLong.intValue()) {
|
||||||
|
return new Integer(myLong.intValue());
|
||||||
|
} else {
|
||||||
|
return myLong;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
try {
|
|
||||||
Double value = new Double(string);
|
|
||||||
if (value.toString().equals(string)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
} catch (Exception ignoreAlso) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
@ -378,15 +392,15 @@ public class XML {
|
||||||
*/
|
*/
|
||||||
public static String toString(Object object, String tagName)
|
public static String toString(Object object, String tagName)
|
||||||
throws JSONException {
|
throws JSONException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
int i;
|
int i;
|
||||||
JSONArray ja;
|
JSONArray ja;
|
||||||
JSONObject jo;
|
JSONObject jo;
|
||||||
String key;
|
String key;
|
||||||
Iterator<String> keys;
|
Iterator keys;
|
||||||
int length;
|
int length;
|
||||||
String string;
|
String string;
|
||||||
Object value;
|
Object value;
|
||||||
if (object instanceof JSONObject) {
|
if (object instanceof JSONObject) {
|
||||||
|
|
||||||
// Emit <tagName>
|
// Emit <tagName>
|
||||||
|
|
@ -402,12 +416,16 @@ public class XML {
|
||||||
jo = (JSONObject)object;
|
jo = (JSONObject)object;
|
||||||
keys = jo.keys();
|
keys = jo.keys();
|
||||||
while (keys.hasNext()) {
|
while (keys.hasNext()) {
|
||||||
key = keys.next();
|
key = keys.next().toString();
|
||||||
value = jo.opt(key);
|
value = jo.opt(key);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
value = "";
|
value = "";
|
||||||
}
|
}
|
||||||
string = value instanceof String ? (String)value : null;
|
if (value instanceof String) {
|
||||||
|
string = (String)value;
|
||||||
|
} else {
|
||||||
|
string = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Emit content in body
|
// Emit content in body
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 2014-05-03
|
* @version 2012-11-13
|
||||||
*/
|
*/
|
||||||
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<String, Character> entity;
|
public static final java.util.HashMap entity;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
entity = new java.util.HashMap<String, Character>(8);
|
entity = new java.util.HashMap(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;
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
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;
|
||||||
StringBuilder sb;
|
StringBuffer 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 StringBuilder();
|
sb = new StringBuffer();
|
||||||
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 {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuffer sb = new StringBuffer();
|
||||||
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;
|
||||||
StringBuilder sb;
|
StringBuffer 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 StringBuilder();
|
sb = new StringBuffer();
|
||||||
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 StringBuilder();
|
sb = new StringBuffer();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
c = next();
|
c = next();
|
||||||
|
|
|
||||||
169
zip/BitInputStream.java
Normal file
169
zip/BitInputStream.java
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013 JSON.org
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
The Software shall be used for Good, not Evil.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a big endian bit reader. It reads its bits from an InputStream.
|
||||||
|
*
|
||||||
|
* @version 2013-04-18
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
private int available = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Up to a byte's worth of unread bits.
|
||||||
|
*/
|
||||||
|
private int unread = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source of the bits.
|
||||||
|
*/
|
||||||
|
private InputStream in;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of bits read so far. This is used in padding.
|
||||||
|
*/
|
||||||
|
private long nrBits = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a BitReader from an InputStream. The BitReader will take bytes from
|
||||||
|
* the InputStream and unpack them into bits.
|
||||||
|
*
|
||||||
|
* @param in
|
||||||
|
* An InputStream.
|
||||||
|
*/
|
||||||
|
public BitInputStream(InputStream 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.
|
||||||
|
*
|
||||||
|
* @return true if it is a 1 bit.
|
||||||
|
*/
|
||||||
|
public boolean bit() throws IOException {
|
||||||
|
return read(1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of bits that have been read from this BitInputStream.
|
||||||
|
* This includes pad bits that have been skipped, but might not include
|
||||||
|
* bytes that have been read from the underlying InputStream that have not
|
||||||
|
* yet been delivered as bits.
|
||||||
|
*
|
||||||
|
* @return The number of bits read so far.
|
||||||
|
*/
|
||||||
|
public long nrBits() {
|
||||||
|
return this.nrBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* @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);
|
||||||
|
boolean result = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < padding; i += 1) {
|
||||||
|
if (bit()) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read some bits.
|
||||||
|
*
|
||||||
|
* @param width
|
||||||
|
* The number of bits to read. (0..32)
|
||||||
|
* @throws IOException
|
||||||
|
* @return the bits
|
||||||
|
*/
|
||||||
|
public int read(int width) throws IOException {
|
||||||
|
if (width == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (width < 0 || width > 32) {
|
||||||
|
throw new IOException("Bad read width.");
|
||||||
|
}
|
||||||
|
int result = 0;
|
||||||
|
while (width > 0) {
|
||||||
|
if (this.available == 0) {
|
||||||
|
this.unread = this.in.read();
|
||||||
|
if (this.unread < 0) {
|
||||||
|
throw new IOException("Attempt to read past end.");
|
||||||
|
}
|
||||||
|
this.available = 8;
|
||||||
|
}
|
||||||
|
int take = width;
|
||||||
|
if (take > this.available) {
|
||||||
|
take = this.available;
|
||||||
|
}
|
||||||
|
result |= ((this.unread >>> (this.available - take)) & mask[take])
|
||||||
|
<< (width - take);
|
||||||
|
this.nrBits += take;
|
||||||
|
this.available -= take;
|
||||||
|
width -= take;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
154
zip/BitOutputStream.java
Normal file
154
zip/BitOutputStream.java
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013 JSON.org
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
The Software shall be used for Good, not Evil.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a big endian bit writer. It writes its bits to an OutputStream.
|
||||||
|
*
|
||||||
|
* @version 2013-04-18
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BitOutputStream implements BitWriter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of bits written.
|
||||||
|
*/
|
||||||
|
private long nrBits = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The destination of the bits.
|
||||||
|
*/
|
||||||
|
private OutputStream out;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holder of bits not yet written.
|
||||||
|
*/
|
||||||
|
private int unwritten;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of unused bits in this.unwritten.
|
||||||
|
*/
|
||||||
|
private int vacant = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use an OutputStream to produce a BitWriter. The BitWriter will send its
|
||||||
|
* bits to the OutputStream as each byte is filled.
|
||||||
|
*
|
||||||
|
* @param out
|
||||||
|
* An Output Stream
|
||||||
|
*/
|
||||||
|
public BitOutputStream(OutputStream out) {
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bits that have been written to this
|
||||||
|
* bitOutputStream. This may include bits that have not yet been written
|
||||||
|
* to the underlying outputStream.
|
||||||
|
*/
|
||||||
|
public long nrBits() {
|
||||||
|
return this.nrBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a 1 bit.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void one() throws IOException {
|
||||||
|
write(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pad the rest of the block with zeroes 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
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
while (padding > 0) {
|
||||||
|
this.write(0, 8);
|
||||||
|
padding -= 8;
|
||||||
|
}
|
||||||
|
this.out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write some bits. Up to 32 bits can be written at a time.
|
||||||
|
*
|
||||||
|
* @param bits
|
||||||
|
* The bits to be written.
|
||||||
|
* @param width
|
||||||
|
* The number of bits to write. (0..32)
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void write(int bits, int width) throws IOException {
|
||||||
|
if (bits == 0 && width == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (width <= 0 || width > 32) {
|
||||||
|
throw new IOException("Bad write width.");
|
||||||
|
}
|
||||||
|
while (width > 0) {
|
||||||
|
int actual = width;
|
||||||
|
if (actual > this.vacant) {
|
||||||
|
actual = this.vacant;
|
||||||
|
}
|
||||||
|
this.unwritten |= ((bits >>> (width - actual)) &
|
||||||
|
BitInputStream.mask[actual]) << (this.vacant - actual);
|
||||||
|
width -= actual;
|
||||||
|
nrBits += actual;
|
||||||
|
this.vacant -= actual;
|
||||||
|
if (this.vacant == 0) {
|
||||||
|
this.out.write(this.unwritten);
|
||||||
|
this.unwritten = 0;
|
||||||
|
this.vacant = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a 0 bit.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void zero() throws IOException {
|
||||||
|
write(0, 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
41
zip/BitReader.java
Normal file
41
zip/BitReader.java
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface BitReader {
|
||||||
|
/**
|
||||||
|
* Read one bit.
|
||||||
|
*
|
||||||
|
* @return true if it is a 1 bit.
|
||||||
|
*/
|
||||||
|
public boolean bit() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bits that have been read from this bitreader.
|
||||||
|
*
|
||||||
|
* @return The number of bits read so far.
|
||||||
|
*/
|
||||||
|
public long nrBits();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that the rest of the block has been padded with zeroes.
|
||||||
|
*
|
||||||
|
* @param factor
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read some bits.
|
||||||
|
*
|
||||||
|
* @param width
|
||||||
|
* The number of bits to read. (0..32)
|
||||||
|
* @throws IOException
|
||||||
|
* @return the bits
|
||||||
|
*/
|
||||||
|
public int read(int width) throws IOException;
|
||||||
|
}
|
||||||
51
zip/BitWriter.java
Normal file
51
zip/BitWriter.java
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A bitwriter is a an interface that allows for doing output at the bit level.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void one() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pad the rest of the block with zeros and flush.
|
||||||
|
*
|
||||||
|
* @param factor
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write some bits. Up to 32 bits can be written at a time.
|
||||||
|
*
|
||||||
|
* @param bits
|
||||||
|
* The bits to be written.
|
||||||
|
* @param width
|
||||||
|
* The number of bits to write. (0..32)
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void write(int bits, int width) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a 0 bit.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void zero() throws IOException;
|
||||||
|
}
|
||||||
575
zip/Compressor.java
Normal file
575
zip/Compressor.java
Normal file
|
|
@ -0,0 +1,575 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.json.Kim;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSONzip is a compression scheme for JSON text.
|
||||||
|
*
|
||||||
|
* @author JSON.org
|
||||||
|
* @version 2013-04-18
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compressor 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 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compressor outputs to a BitWriter.
|
||||||
|
*/
|
||||||
|
final BitWriter bitwriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new compressor. It may be used for an entire session or
|
||||||
|
* subsession.
|
||||||
|
*
|
||||||
|
* @param bitwriter
|
||||||
|
* The BitWriter this Compressor will output to. Don't forget to
|
||||||
|
* flush.
|
||||||
|
*/
|
||||||
|
public Compressor(BitWriter bitwriter) {
|
||||||
|
super();
|
||||||
|
this.bitwriter = bitwriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a 4 bit code for a character in a JSON number. The digits '0' to
|
||||||
|
* '9' get the codes 0 to 9. '.' is 10, '-' is 11, '+' is 12, and 'E' or
|
||||||
|
* 'e' is 13.
|
||||||
|
*
|
||||||
|
* @param digit
|
||||||
|
* An ASCII character from a JSIN number.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static int bcd(char digit) {
|
||||||
|
if (digit >= '0' && digit <= '9') {
|
||||||
|
return digit - '0';
|
||||||
|
}
|
||||||
|
switch (digit) {
|
||||||
|
case '.':
|
||||||
|
return 10;
|
||||||
|
case '-':
|
||||||
|
return 11;
|
||||||
|
case '+':
|
||||||
|
return 12;
|
||||||
|
default:
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish the final byte and flush the bitwriter. This does the same thing
|
||||||
|
* as pad(8).
|
||||||
|
*
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
public void flush() throws JSONException {
|
||||||
|
pad(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output a one bit.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void one() throws JSONException {
|
||||||
|
if (probe) {
|
||||||
|
log(1);
|
||||||
|
}
|
||||||
|
write(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pad the output to fill an allotment of bits.
|
||||||
|
*
|
||||||
|
* @param factor
|
||||||
|
* 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 {
|
||||||
|
try {
|
||||||
|
this.bitwriter.pad(factor);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new JSONException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a number, using the number of bits necessary to hold the number.
|
||||||
|
*
|
||||||
|
* @param integer
|
||||||
|
* The value to be encoded.
|
||||||
|
* @param width
|
||||||
|
* The number of bits to encode the value, between 0 and 32.
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private void write(int integer, int width) throws JSONException {
|
||||||
|
try {
|
||||||
|
this.bitwriter.write(integer, width);
|
||||||
|
if (probe) {
|
||||||
|
log(integer, width);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new JSONException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an integer with Huffman encoding. The bit pattern that is written
|
||||||
|
* will be determined by the Huffman encoder.
|
||||||
|
*
|
||||||
|
* @param integer
|
||||||
|
* The value to be written.
|
||||||
|
* @param huff
|
||||||
|
* The Huffman encoder.
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private void write(int integer, Huff huff) throws JSONException {
|
||||||
|
huff.write(integer, this.bitwriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write each of the bytes in a kim with Huffman encoding.
|
||||||
|
*
|
||||||
|
* @param kim
|
||||||
|
* A kim containing the bytes to be written.
|
||||||
|
* @param huff
|
||||||
|
* The Huffman encoder.
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an integer, using the number of bits necessary to hold the number
|
||||||
|
* as determined by its keep, and increment its usage count in the keep.
|
||||||
|
*
|
||||||
|
* @param integer
|
||||||
|
* The value to be encoded.
|
||||||
|
* @param keep
|
||||||
|
* The Keep that the integer is one of.
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private void writeAndTick(int integer, Keep keep) throws JSONException {
|
||||||
|
int width = keep.bitsize();
|
||||||
|
keep.tick(integer);
|
||||||
|
if (probe) {
|
||||||
|
log("\"" + keep.value(integer) + "\"");
|
||||||
|
}
|
||||||
|
write(integer, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a JSON Array.
|
||||||
|
*
|
||||||
|
* @param jsonarray
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private void writeArray(JSONArray jsonarray) throws JSONException {
|
||||||
|
|
||||||
|
// JSONzip has three encodings for arrays:
|
||||||
|
// The array is empty (zipEmptyArray).
|
||||||
|
// First value in the array is a string (zipArrayString).
|
||||||
|
// First value in the array is not a string (zipArrayValue).
|
||||||
|
|
||||||
|
boolean stringy = false;
|
||||||
|
int length = jsonarray.length();
|
||||||
|
if (length == 0) {
|
||||||
|
write(zipEmptyArray, 3);
|
||||||
|
} else {
|
||||||
|
Object value = jsonarray.get(0);
|
||||||
|
if (value == null) {
|
||||||
|
value = JSONObject.NULL;
|
||||||
|
}
|
||||||
|
if (value instanceof String) {
|
||||||
|
stringy = true;
|
||||||
|
write(zipArrayString, 3);
|
||||||
|
writeString((String) value);
|
||||||
|
} else {
|
||||||
|
write(zipArrayValue, 3);
|
||||||
|
writeValue(value);
|
||||||
|
}
|
||||||
|
for (int i = 1; i < length; i += 1) {
|
||||||
|
if (probe) {
|
||||||
|
log();
|
||||||
|
}
|
||||||
|
value = jsonarray.get(i);
|
||||||
|
if (value == null) {
|
||||||
|
value = JSONObject.NULL;
|
||||||
|
}
|
||||||
|
if (value instanceof String != stringy) {
|
||||||
|
zero();
|
||||||
|
}
|
||||||
|
one();
|
||||||
|
if (value instanceof String) {
|
||||||
|
writeString((String) value);
|
||||||
|
} else {
|
||||||
|
writeValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zero();
|
||||||
|
zero();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a JSON value.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* One of these types: JSONObject, JSONArray (or Map or
|
||||||
|
* Collection or array), Number (or Integer or Long or Double),
|
||||||
|
* or String, or Boolean, or JSONObject.NULL, or null.
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private void writeJSON(Object value) throws JSONException {
|
||||||
|
if (JSONObject.NULL.equals(value)) {
|
||||||
|
write(zipNull, 3);
|
||||||
|
} else if (Boolean.FALSE.equals(value)) {
|
||||||
|
write(zipFalse, 3);
|
||||||
|
} else if (Boolean.TRUE.equals(value)) {
|
||||||
|
write(zipTrue, 3);
|
||||||
|
} else {
|
||||||
|
if (value instanceof Map) {
|
||||||
|
value = new JSONObject((Map) value);
|
||||||
|
} else if (value instanceof Collection) {
|
||||||
|
value = new JSONArray((Collection) value);
|
||||||
|
} else if (value.getClass().isArray()) {
|
||||||
|
value = new JSONArray(value);
|
||||||
|
}
|
||||||
|
if (value instanceof JSONObject) {
|
||||||
|
writeObject((JSONObject) value);
|
||||||
|
} else if (value instanceof JSONArray) {
|
||||||
|
writeArray((JSONArray) value);
|
||||||
|
} else {
|
||||||
|
throw new JSONException("Unrecognized object");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private void writeName(String name) throws JSONException {
|
||||||
|
|
||||||
|
// If this name has already been registered, then emit its integer and
|
||||||
|
// increment its usage count.
|
||||||
|
|
||||||
|
Kim kim = new Kim(name);
|
||||||
|
int integer = this.namekeep.find(kim);
|
||||||
|
if (integer != none) {
|
||||||
|
one();
|
||||||
|
writeAndTick(integer, this.namekeep);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Otherwise, emit the string with Huffman encoding, and register it.
|
||||||
|
|
||||||
|
zero();
|
||||||
|
write(kim, this.namehuff);
|
||||||
|
write(end, namehuff);
|
||||||
|
this.namekeep.register(kim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a JSON object.
|
||||||
|
*
|
||||||
|
* @param jsonobject
|
||||||
|
* @return
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private void writeObject(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();
|
||||||
|
while (keys.hasNext()) {
|
||||||
|
if (probe) {
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
Object key = keys.next();
|
||||||
|
if (key instanceof String) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
write(zipObject, 3);
|
||||||
|
} else {
|
||||||
|
one();
|
||||||
|
}
|
||||||
|
writeName((String) key);
|
||||||
|
Object value = jsonobject.get((String) key);
|
||||||
|
if (value instanceof String) {
|
||||||
|
zero();
|
||||||
|
writeString((String) value);
|
||||||
|
} else {
|
||||||
|
one();
|
||||||
|
writeValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (first) {
|
||||||
|
write(zipEmptyObject, 3);
|
||||||
|
} else {
|
||||||
|
zero();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a string.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private void writeString(String string) throws JSONException {
|
||||||
|
|
||||||
|
// Special case for empty strings.
|
||||||
|
|
||||||
|
if (string.length() == 0) {
|
||||||
|
zero();
|
||||||
|
zero();
|
||||||
|
write(end, this.substringhuff);
|
||||||
|
zero();
|
||||||
|
} else {
|
||||||
|
Kim kim = new Kim(string);
|
||||||
|
|
||||||
|
// Look for the string in the strings keep. If it is found, emit its
|
||||||
|
// integer and count that as a use.
|
||||||
|
|
||||||
|
int integer = this.stringkeep.find(kim);
|
||||||
|
if (integer != none) {
|
||||||
|
one();
|
||||||
|
writeAndTick(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.
|
||||||
|
|
||||||
|
writeSubstring(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.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* One of these types: Boolean, Number, etc.
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private void writeValue(Object value) throws JSONException {
|
||||||
|
if (value instanceof Number) {
|
||||||
|
String string = JSONObject.numberToString((Number) value);
|
||||||
|
int integer = this.values.find(string);
|
||||||
|
if (integer != none) {
|
||||||
|
write(2, 2);
|
||||||
|
writeAndTick(integer, this.values);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof Integer || value instanceof Long) {
|
||||||
|
long longer = ((Number) value).longValue();
|
||||||
|
if (longer >= 0 && longer < int14) {
|
||||||
|
write(0, 2);
|
||||||
|
if (longer < int4) {
|
||||||
|
zero();
|
||||||
|
write((int) longer, 4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
one();
|
||||||
|
if (longer < int7) {
|
||||||
|
zero();
|
||||||
|
write((int) longer, 7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
one();
|
||||||
|
write((int) longer, 14);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write(1, 2);
|
||||||
|
for (int i = 0; i < string.length(); i += 1) {
|
||||||
|
write(bcd(string.charAt(i)), 4);
|
||||||
|
}
|
||||||
|
write(endOfNumber, 4);
|
||||||
|
this.values.register(string);
|
||||||
|
} else {
|
||||||
|
write(3, 2);
|
||||||
|
writeJSON(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output a zero bit.
|
||||||
|
*
|
||||||
|
* @throws JSONException
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void zero() throws JSONException {
|
||||||
|
if (probe) {
|
||||||
|
log(0);
|
||||||
|
}
|
||||||
|
write(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a JSONObject.
|
||||||
|
*
|
||||||
|
* @param jsonobject
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
public void zip(JSONObject jsonobject) throws JSONException {
|
||||||
|
begin();
|
||||||
|
writeJSON(jsonobject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a JSONArray.
|
||||||
|
*
|
||||||
|
* @param jsonarray
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
public void zip(JSONArray jsonarray) throws JSONException {
|
||||||
|
begin();
|
||||||
|
writeJSON(jsonarray);
|
||||||
|
}
|
||||||
|
}
|
||||||
325
zip/Decompressor.java
Normal file
325
zip/Decompressor.java
Normal file
|
|
@ -0,0 +1,325 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.json.Kim;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2012 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSONzip is a compression scheme for JSON text.
|
||||||
|
*
|
||||||
|
* @author JSON.org
|
||||||
|
* @version 2013-04-18
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Decompressor extends JSONzip {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A decompressor reads bits from a BitReader.
|
||||||
|
*/
|
||||||
|
BitReader bitreader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new compressor. It may be used for an entire session or
|
||||||
|
* subsession.
|
||||||
|
*
|
||||||
|
* @param bitreader
|
||||||
|
* The bitreader that this decompressor will read from.
|
||||||
|
*/
|
||||||
|
public Decompressor(BitReader bitreader) {
|
||||||
|
super();
|
||||||
|
this.bitreader = bitreader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read one bit.
|
||||||
|
*
|
||||||
|
* @return true if 1, false if 0.
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private boolean bit() throws JSONException {
|
||||||
|
boolean value;
|
||||||
|
try {
|
||||||
|
value = this.bitreader.bit();
|
||||||
|
if (probe) {
|
||||||
|
log(value ? 1 : 0);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new JSONException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read enough bits to obtain an integer from the keep, and increase that
|
||||||
|
* integer's weight.
|
||||||
|
*
|
||||||
|
* @param keep
|
||||||
|
* @param bitreader
|
||||||
|
* @return
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private Object getAndTick(Keep keep, BitReader bitreader)
|
||||||
|
throws JSONException {
|
||||||
|
try {
|
||||||
|
int width = keep.bitsize();
|
||||||
|
int integer = bitreader.read(width);
|
||||||
|
Object value = keep.value(integer);
|
||||||
|
if (JSONzip.probe) {
|
||||||
|
JSONzip.log("\"" + value + "\"");
|
||||||
|
JSONzip.log(integer, width);
|
||||||
|
}
|
||||||
|
if (integer >= keep.length) {
|
||||||
|
throw new JSONException("Deep error.");
|
||||||
|
}
|
||||||
|
keep.tick(integer);
|
||||||
|
return value;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new JSONException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @return true if all of the padding bits were zero.
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
public boolean pad(int factor) throws JSONException {
|
||||||
|
try {
|
||||||
|
return this.bitreader.pad(factor);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new JSONException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an integer, specifying its width in bits.
|
||||||
|
*
|
||||||
|
* @param width
|
||||||
|
* 0 to 32.
|
||||||
|
* @return An unsigned integer.
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private int read(int width) throws JSONException {
|
||||||
|
try {
|
||||||
|
int value = this.bitreader.read(width);
|
||||||
|
if (probe) {
|
||||||
|
log(value, width);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new JSONException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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());
|
||||||
|
while (true) {
|
||||||
|
if (probe) {
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
if (!bit()) {
|
||||||
|
if (!bit()) {
|
||||||
|
return jsonarray;
|
||||||
|
}
|
||||||
|
jsonarray.put(stringy ? readValue() : readString());
|
||||||
|
} else {
|
||||||
|
jsonarray.put(stringy ? readString() : readValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a JSON value. The type of value is determined by the next 3 bits.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private Object readJSON() throws JSONException {
|
||||||
|
switch (read(3)) {
|
||||||
|
case zipObject:
|
||||||
|
return readObject();
|
||||||
|
case zipArrayString:
|
||||||
|
return readArray(true);
|
||||||
|
case zipArrayValue:
|
||||||
|
return readArray(false);
|
||||||
|
case zipEmptyObject:
|
||||||
|
return new JSONObject();
|
||||||
|
case zipEmptyArray:
|
||||||
|
return new JSONArray();
|
||||||
|
case zipTrue:
|
||||||
|
return Boolean.TRUE;
|
||||||
|
case zipFalse:
|
||||||
|
return Boolean.FALSE;
|
||||||
|
default:
|
||||||
|
return JSONObject.NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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("\n");
|
||||||
|
}
|
||||||
|
String name = readName();
|
||||||
|
jsonobject.put(name, !bit() ? readString() : 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:
|
||||||
|
return new Integer(read(!bit() ? 4 : !bit() ? 7 : 14));
|
||||||
|
case 1:
|
||||||
|
byte[] bytes = new byte[256];
|
||||||
|
int length = 0;
|
||||||
|
while (true) {
|
||||||
|
int c = read(4);
|
||||||
|
if (c == endOfNumber) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bytes[length] = bcd[c];
|
||||||
|
length += 1;
|
||||||
|
}
|
||||||
|
Object value;
|
||||||
|
try {
|
||||||
|
value = JSONObject.stringToValue(new String(bytes, 0, length,
|
||||||
|
"US-ASCII"));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new JSONException(e);
|
||||||
|
}
|
||||||
|
this.values.register(value);
|
||||||
|
return value;
|
||||||
|
case 2:
|
||||||
|
return getAndTick(this.values, this.bitreader);
|
||||||
|
case 3:
|
||||||
|
return readJSON();
|
||||||
|
default:
|
||||||
|
throw new JSONException("Impossible.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object unzip() throws JSONException {
|
||||||
|
begin();
|
||||||
|
return readJSON();
|
||||||
|
}
|
||||||
|
}
|
||||||
406
zip/Huff.java
Normal file
406
zip/Huff.java
Normal file
|
|
@ -0,0 +1,406 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSONzip is a compression scheme for JSON text.
|
||||||
|
* @author JSON.org
|
||||||
|
* @version 2013-04-18
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Huffman encoder/decoder. It operates over a domain of integers, which may
|
||||||
|
* map to characters or other symbols. Symbols that are used frequently are
|
||||||
|
* given shorter codes than symbols that are used infrequently. This usually
|
||||||
|
* produces shorter messages.
|
||||||
|
*
|
||||||
|
* Initially, all of the symbols are given the same weight. The weight of a
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public class Huff implements None, PostMortem {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of symbols known to the encoder.
|
||||||
|
*/
|
||||||
|
private final int domain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array that maps symbol values to symbols.
|
||||||
|
*/
|
||||||
|
private final Symbol[] symbols;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The root of the decoding table, and the terminal of the encoding table.
|
||||||
|
*/
|
||||||
|
private Symbol table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Have any weights changed since the table was last generated?
|
||||||
|
*/
|
||||||
|
private boolean upToDate = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of bits in the last symbol. This is used in tracing.
|
||||||
|
*/
|
||||||
|
private int width;
|
||||||
|
|
||||||
|
private static class Symbol implements PostMortem {
|
||||||
|
public Symbol back;
|
||||||
|
public Symbol next;
|
||||||
|
public Symbol zero;
|
||||||
|
public Symbol one;
|
||||||
|
public final int integer;
|
||||||
|
public long weight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a symbol representing a character or other value.
|
||||||
|
*
|
||||||
|
* @param integer
|
||||||
|
* The symbol's number
|
||||||
|
*/
|
||||||
|
public Symbol(int integer) {
|
||||||
|
this.integer = integer;
|
||||||
|
this.weight = 0;
|
||||||
|
this.next = null;
|
||||||
|
this.back = null;
|
||||||
|
this.one = null;
|
||||||
|
this.zero = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean postMortem(PostMortem pm) {
|
||||||
|
boolean result = true;
|
||||||
|
Symbol that = (Symbol) pm;
|
||||||
|
|
||||||
|
if (this.integer != that.integer || this.weight != that.weight) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((this.back != null) != (that.back != null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Symbol zero = this.zero;
|
||||||
|
Symbol one = this.one;
|
||||||
|
if (zero == null) {
|
||||||
|
if (that.zero != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = zero.postMortem(that.zero);
|
||||||
|
}
|
||||||
|
if (one == null) {
|
||||||
|
if (that.one != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = one.postMortem(that.one);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a Huffman encoder/decoder.
|
||||||
|
*
|
||||||
|
* @param domain
|
||||||
|
* The number of values known to the object.
|
||||||
|
*/
|
||||||
|
public Huff(int domain) {
|
||||||
|
this.domain = domain;
|
||||||
|
int length = domain * 2 - 1;
|
||||||
|
this.symbols = new Symbol[length];
|
||||||
|
|
||||||
|
// Make the leaf symbols.
|
||||||
|
|
||||||
|
for (int i = 0; i < domain; i += 1) {
|
||||||
|
symbols[i] = new Symbol(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SMake the links.
|
||||||
|
|
||||||
|
for (int i = domain; i < length; i += 1) {
|
||||||
|
symbols[i] = new Symbol(none);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
// Phase One: Sort the symbols by weight into a linked list.
|
||||||
|
|
||||||
|
Symbol head = this.symbols[0];
|
||||||
|
Symbol next;
|
||||||
|
Symbol previous = head;
|
||||||
|
Symbol symbol;
|
||||||
|
|
||||||
|
this.table = null;
|
||||||
|
head.next = null;
|
||||||
|
for (int i = 1; i < this.domain; i += 1) {
|
||||||
|
symbol = symbols[i];
|
||||||
|
|
||||||
|
// If this symbol weights less than the head, then it becomes the new head.
|
||||||
|
|
||||||
|
if (symbol.weight < head.weight) {
|
||||||
|
symbol.next = head;
|
||||||
|
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.
|
||||||
|
|
||||||
|
if (symbol.weight < previous.weight) {
|
||||||
|
previous = head;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a connected pair (previous and next) where the symbol weighs the same
|
||||||
|
// or more than previous but less than the next. Link the symbol between them.
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
next = previous.next;
|
||||||
|
if (next == null || symbol.weight < next.weight) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previous = next;
|
||||||
|
}
|
||||||
|
symbol.next = next;
|
||||||
|
previous.next = symbol;
|
||||||
|
previous = symbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase Two: Make new symbols from the two lightest symbols until only one
|
||||||
|
// symbol remains. The final symbol becomes the root of the table binary tree.
|
||||||
|
|
||||||
|
int avail = this.domain;
|
||||||
|
Symbol first;
|
||||||
|
Symbol second;
|
||||||
|
previous = head;
|
||||||
|
while (true) {
|
||||||
|
first = head;
|
||||||
|
second = first.next;
|
||||||
|
head = second.next;
|
||||||
|
symbol = this.symbols[avail];
|
||||||
|
avail += 1;
|
||||||
|
symbol.weight = first.weight + second.weight;
|
||||||
|
symbol.zero = first;
|
||||||
|
symbol.one = second;
|
||||||
|
symbol.back = null;
|
||||||
|
first.back = symbol;
|
||||||
|
second.back = symbol;
|
||||||
|
if (head == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the new symbol back into the sorted list.
|
||||||
|
|
||||||
|
if (symbol.weight < head.weight) {
|
||||||
|
symbol.next = head;
|
||||||
|
head = symbol;
|
||||||
|
previous = head;
|
||||||
|
} else {
|
||||||
|
while (true) {
|
||||||
|
next = previous.next;
|
||||||
|
if (next == null || symbol.weight < next.weight) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previous = next;
|
||||||
|
}
|
||||||
|
symbol.next = next;
|
||||||
|
previous.next = symbol;
|
||||||
|
previous = symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last remaining symbol is the root of the table.
|
||||||
|
|
||||||
|
this.table = symbol;
|
||||||
|
this.upToDate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean postMortem(int integer) {
|
||||||
|
int[] bits = new int[this.domain];
|
||||||
|
Symbol symbol = this.symbols[integer];
|
||||||
|
if (symbol.integer != integer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
Symbol back = symbol.back;
|
||||||
|
if (back == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (back.zero == symbol) {
|
||||||
|
bits[i] = 0;
|
||||||
|
} else if (back.one == symbol) {
|
||||||
|
bits[i] = 1;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
symbol = back;
|
||||||
|
}
|
||||||
|
if (symbol != this.table) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.width = 0;
|
||||||
|
symbol = this.table;
|
||||||
|
while (symbol.integer == none) {
|
||||||
|
i -= 1;
|
||||||
|
symbol = bits[i] != 0 ? symbol.one : symbol.zero;
|
||||||
|
}
|
||||||
|
return symbol.integer == integer && i == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two Huffman tables.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
|
||||||
|
for (int integer = 0; integer < this.domain; integer += 1) {
|
||||||
|
if (!postMortem(integer)) {
|
||||||
|
JSONzip.log("\nBad huff ");
|
||||||
|
JSONzip.logchar(integer, integer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.table.postMortem(((Huff) pm).table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read bits until a symbol can be identified. The weight of the read
|
||||||
|
* symbol will be incremented.
|
||||||
|
*
|
||||||
|
* @param bitreader
|
||||||
|
* The source of bits.
|
||||||
|
* @return The integer value of the symbol.
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
public int read(BitReader bitreader) throws JSONException {
|
||||||
|
try {
|
||||||
|
this.width = 0;
|
||||||
|
Symbol symbol = this.table;
|
||||||
|
while (symbol.integer == none) {
|
||||||
|
this.width += 1;
|
||||||
|
symbol = bitreader.bit() ? symbol.one : symbol.zero;
|
||||||
|
}
|
||||||
|
tick(symbol.integer);
|
||||||
|
if (JSONzip.probe) {
|
||||||
|
JSONzip.logchar(symbol.integer, this.width);
|
||||||
|
}
|
||||||
|
return symbol.integer;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new JSONException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase by 1 the weight associated with a value.
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recur from a symbol back, emitting bits. We recur before emitting to
|
||||||
|
* make the bits come out in the right order.
|
||||||
|
*
|
||||||
|
* @param symbol
|
||||||
|
* The symbol to write.
|
||||||
|
* @param bitwriter
|
||||||
|
* The bitwriter to write it to.
|
||||||
|
* @throws JSONException
|
||||||
|
*/
|
||||||
|
private void write(Symbol symbol, BitWriter bitwriter)
|
||||||
|
throws JSONException {
|
||||||
|
try {
|
||||||
|
Symbol back = symbol.back;
|
||||||
|
if (back != null) {
|
||||||
|
this.width += 1;
|
||||||
|
write(back, bitwriter);
|
||||||
|
if (back.zero == symbol) {
|
||||||
|
bitwriter.zero();
|
||||||
|
} else {
|
||||||
|
bitwriter.one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new JSONException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the bits corresponding to a symbol. The weight of the symbol will
|
||||||
|
* be incremented.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* 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 {
|
||||||
|
this.width = 0;
|
||||||
|
write(this.symbols[value], bitwriter);
|
||||||
|
tick(value);
|
||||||
|
if (JSONzip.probe) {
|
||||||
|
JSONzip.logchar(value, this.width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
281
zip/JSONzip.java
Normal file
281
zip/JSONzip.java
Normal file
|
|
@ -0,0 +1,281 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSONzip is a binary-encoded JSON dialect. It is designed to compress the
|
||||||
|
* messages in a session. It 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
|
||||||
|
* substantially reduced. It uses a character encoding called Kim (Keep it
|
||||||
|
* minimal) that is smaller than UTF-8 for most East European, African, and
|
||||||
|
* Asian scripts.
|
||||||
|
*
|
||||||
|
* JSONzip tends to reduce most content by about half. If there is a lot of
|
||||||
|
* recurring information, the reduction can be much more dramatic.
|
||||||
|
*
|
||||||
|
* FOR EVALUATION PURPOSES ONLY. THIS PACKAGE HAS NOT YET BEEN TESTED
|
||||||
|
* ADEQUATELY FOR PRODUCTION USE.
|
||||||
|
*
|
||||||
|
* @author JSON.org
|
||||||
|
* @version 2013-04-18
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
public static final byte[] bcd = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '-', '+', 'E'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of integers that can be encoded in 4 bits.
|
||||||
|
*/
|
||||||
|
public static final long int4 = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of integers that can be encoded in 7 bits.
|
||||||
|
*/
|
||||||
|
public static final long int7 = 128;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of integers that can be encoded in 14 bits.
|
||||||
|
*/
|
||||||
|
public static final long int14 = 16384;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end of string code.
|
||||||
|
*/
|
||||||
|
public static final int end = 256;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end of number code.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
public static final int zipEmptyObject = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value code for an empty array.
|
||||||
|
*/
|
||||||
|
public static final int zipEmptyArray = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value code for true.
|
||||||
|
*/
|
||||||
|
public static final int zipTrue = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value code for false.
|
||||||
|
*/
|
||||||
|
public static final int zipFalse = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value code for null.
|
||||||
|
*/
|
||||||
|
public static final int zipNull = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value code for a non-empty object.
|
||||||
|
*/
|
||||||
|
public static final int zipObject = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value code for an array with a string as its first element.
|
||||||
|
*/
|
||||||
|
public static final int zipArrayString = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value code for an array with a non-string value as its first element.
|
||||||
|
*/
|
||||||
|
public static final int zipArrayValue = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Huffman encoder for names.
|
||||||
|
*/
|
||||||
|
protected final Huff namehuff;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A place to keep the names (keys).
|
||||||
|
*/
|
||||||
|
protected final MapKeep namekeep;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A place to keep the strings.
|
||||||
|
*/
|
||||||
|
protected final MapKeep stringkeep;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Huffman encoder for string values.
|
||||||
|
*/
|
||||||
|
protected final Huff substringhuff;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A place to keep the strings.
|
||||||
|
*/
|
||||||
|
protected final TrieKeep substringkeep;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A place to keep the values.
|
||||||
|
*/
|
||||||
|
protected final MapKeep values;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected void begin() {
|
||||||
|
this.namehuff.generate();
|
||||||
|
this.substringhuff.generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an end-of-line to the console.
|
||||||
|
*/
|
||||||
|
static void log() {
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an integer to the console.
|
||||||
|
*
|
||||||
|
* @param integer
|
||||||
|
*/
|
||||||
|
static void log(int integer) {
|
||||||
|
log(integer + " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write two integers, separated by ':' to the console.
|
||||||
|
*
|
||||||
|
* @param integer
|
||||||
|
* @param width
|
||||||
|
*/
|
||||||
|
static void log(int integer, int width) {
|
||||||
|
log(integer + ":" + width + " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a string to the console.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
*/
|
||||||
|
static void log(String string) {
|
||||||
|
System.out.print(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a character or its code to the console.
|
||||||
|
*
|
||||||
|
* @param integer
|
||||||
|
* @param width
|
||||||
|
*/
|
||||||
|
static void logchar(int integer, int width) {
|
||||||
|
if (integer > ' ' && integer <= '}') {
|
||||||
|
log("'" + (char) integer + "':" + width + " ");
|
||||||
|
} else {
|
||||||
|
log(integer, width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used for testing the implementation of JSONzip. It is not
|
||||||
|
* suitable for any other purpose. It is used to compare a Compressor and a
|
||||||
|
* Decompressor, verifying that the data structures that were built during
|
||||||
|
* zipping and unzipping were the same.
|
||||||
|
*
|
||||||
|
* @return true if the structures match.
|
||||||
|
*/
|
||||||
|
public boolean postMortem(PostMortem pm) {
|
||||||
|
JSONzip that = (JSONzip) pm;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
84
zip/Keep.java
Normal file
84
zip/Keep.java
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013 JSON.org
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
The Software shall be used for Good, not Evil.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A keep is a data structure that associates strings (or substrings) with
|
||||||
|
* numbers. This allows the sending of small integers instead of strings.
|
||||||
|
*
|
||||||
|
* @author JSON.org
|
||||||
|
* @version 2013-04-18
|
||||||
|
*/
|
||||||
|
abstract class Keep implements None, PostMortem {
|
||||||
|
protected int capacity;
|
||||||
|
protected int length;
|
||||||
|
protected int power;
|
||||||
|
protected long[] uses;
|
||||||
|
|
||||||
|
public Keep(int bits) {
|
||||||
|
this.capacity = JSONzip.twos[bits];
|
||||||
|
this.length = 0;
|
||||||
|
this.power = 0;
|
||||||
|
this.uses = new long[this.capacity];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When an item ages, its use count is reduced by at least half.
|
||||||
|
*
|
||||||
|
* @param use
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of bits required to contain an integer based on the
|
||||||
|
* current length of the keep. As the keep fills up, the number of bits
|
||||||
|
* required to identify one of its items goes up.
|
||||||
|
*/
|
||||||
|
public int bitsize() {
|
||||||
|
while (JSONzip.twos[this.power] < this.length) {
|
||||||
|
this.power += 1;
|
||||||
|
}
|
||||||
|
return this.power;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase the usage count on an integer value.
|
||||||
|
*/
|
||||||
|
public void tick(int integer) {
|
||||||
|
this.uses[integer] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value associated with an integer.
|
||||||
|
* @param integer The number of an item in the keep.
|
||||||
|
* @return The value.
|
||||||
|
*/
|
||||||
|
abstract public Object value(int integer);
|
||||||
|
}
|
||||||
160
zip/MapKeep.java
Normal file
160
zip/MapKeep.java
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.json.Kim;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013 JSON.org
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
The Software shall be used for Good, not Evil.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A keep is an associative data structure that maintains usage counts of each
|
||||||
|
* of the associations in its keeping. When the keep becomes full, it purges
|
||||||
|
* little used associations, and ages the survivors. Each key is assigned an
|
||||||
|
* integer value. When the keep is compacted, each key can be given a new
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
class MapKeep extends Keep {
|
||||||
|
private Object[] list;
|
||||||
|
private HashMap map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Keep.
|
||||||
|
* @param bits
|
||||||
|
* The capacity of the keep expressed in the number of bits
|
||||||
|
* required to hold an integer.
|
||||||
|
*/
|
||||||
|
public MapKeep(int bits) {
|
||||||
|
super(bits);
|
||||||
|
this.list = new Object[this.capacity];
|
||||||
|
this.map = new HashMap(this.capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.uses[from]);
|
||||||
|
if (usage > 0) {
|
||||||
|
this.uses[to] = usage;
|
||||||
|
this.list[to] = key;
|
||||||
|
this.map.put(key, new Integer(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) {
|
||||||
|
MapKeep that = (MapKeep) 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 = ((Kim) 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.uses[i] + "\n "
|
||||||
|
+ that.uses[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, new Integer(this.length));
|
||||||
|
this.uses[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.
|
||||||
|
*/
|
||||||
|
public Object value(int integer) {
|
||||||
|
return this.list[integer];
|
||||||
|
}
|
||||||
|
}
|
||||||
15
zip/None.java
Normal file
15
zip/None.java
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* None is an interface that makes the constant <i>none</i> (short for
|
||||||
|
* negative one or long for -1) available to any class that implements it.
|
||||||
|
* The none value is used to stand for an integer that is not an integer,
|
||||||
|
* such as the negative result of a search.
|
||||||
|
*/
|
||||||
|
public interface None {
|
||||||
|
/**
|
||||||
|
* Negative One.
|
||||||
|
*/
|
||||||
|
public static final int none = -1;
|
||||||
|
|
||||||
|
}
|
||||||
47
zip/PostMortem.java
Normal file
47
zip/PostMortem.java
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013 JSON.org
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
The Software shall be used for Good, not Evil.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PostMortem interface allows for testing the internal state of JSONzip
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* PostMortem allows for testing of deep structures without breaking
|
||||||
|
* encapsulation.
|
||||||
|
*/
|
||||||
|
public interface PostMortem {
|
||||||
|
/**
|
||||||
|
* Determine if two objects are equivalent.
|
||||||
|
*
|
||||||
|
* @param pm
|
||||||
|
* Another object of the same type.
|
||||||
|
* @return true if they match.
|
||||||
|
*/
|
||||||
|
public boolean postMortem(PostMortem pm);
|
||||||
|
}
|
||||||
2
zip/README
Normal file
2
zip/README
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
FOR EVALUATION PURPOSES ONLY. THIS PACKAGE HAS NOT BEEN TESTED ADEQUATELY FOR
|
||||||
|
PRODUCTION USE.
|
||||||
396
zip/TrieKeep.java
Normal file
396
zip/TrieKeep.java
Normal file
|
|
@ -0,0 +1,396 @@
|
||||||
|
package org.json.zip;
|
||||||
|
|
||||||
|
import org.json.Kim;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2013 JSON.org
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
The Software shall be used for Good, not Evil.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A TrieKeep is a Keep that implements a Trie.
|
||||||
|
*/
|
||||||
|
class TrieKeep extends Keep {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The trie is made of nodes.
|
||||||
|
*/
|
||||||
|
class Node implements PostMortem {
|
||||||
|
private int integer;
|
||||||
|
private Node[] next;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each non-leaf node contains links to up to 256 next nodes. Each node
|
||||||
|
* has an integer value.
|
||||||
|
*/
|
||||||
|
public Node() {
|
||||||
|
this.integer = none;
|
||||||
|
this.next = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get one of a node's 256 links. If it is a leaf node, it returns
|
||||||
|
* null.
|
||||||
|
*
|
||||||
|
* @param cell
|
||||||
|
* A integer between 0 and 255.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Node get(int cell) {
|
||||||
|
return this.next == null ? null : this.next[cell];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get one of a node's 256 links. If it is a leap node, it returns
|
||||||
|
* null. The argument is treated as an unsigned integer.
|
||||||
|
*
|
||||||
|
* @param cell
|
||||||
|
* A byte.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Node get(byte cell) {
|
||||||
|
return get(((int) cell) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two nodes. Their lengths must be equal. Their links must
|
||||||
|
* also compare.
|
||||||
|
*/
|
||||||
|
public boolean postMortem(PostMortem pm) {
|
||||||
|
Node that = (Node) pm;
|
||||||
|
if (that == null) {
|
||||||
|
JSONzip.log("\nMisalign");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.integer != that.integer) {
|
||||||
|
JSONzip.log("\nInteger " + this.integer + " <> " +
|
||||||
|
that.integer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.next == null) {
|
||||||
|
if (that.next == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
JSONzip.log("\nNext is null " + this.integer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 256; i += 1) {
|
||||||
|
Node node = this.next[i];
|
||||||
|
if (node != null) {
|
||||||
|
if (!node.postMortem(that.next[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (that.next[i] != null) {
|
||||||
|
JSONzip.log("\nMisalign " + i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a node's link to another node.
|
||||||
|
*
|
||||||
|
* @param cell
|
||||||
|
* An integer between 0 and 255.
|
||||||
|
* @param node
|
||||||
|
* The new value for the cell.
|
||||||
|
*/
|
||||||
|
public void set(int cell, Node node) {
|
||||||
|
if (this.next == null) {
|
||||||
|
this.next = new Node[256];
|
||||||
|
}
|
||||||
|
if (JSONzip.probe) {
|
||||||
|
if (node == null || this.next[cell] != null) {
|
||||||
|
JSONzip.log("\nUnexpected set.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.next[cell] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a node's link to another node.
|
||||||
|
*
|
||||||
|
* @param cell
|
||||||
|
* A byte.
|
||||||
|
* @param node
|
||||||
|
* The new value for the cell.
|
||||||
|
*/
|
||||||
|
public void set(byte cell, Node node) {
|
||||||
|
set(((int) cell) & 0xFF, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get one of a node's 256 links. It will not return null. If there is
|
||||||
|
* no link, then a link is manufactured.
|
||||||
|
*
|
||||||
|
* @param cell
|
||||||
|
* A integer between 0 and 255.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Node vet(int cell) {
|
||||||
|
Node node = get(cell);
|
||||||
|
if (node == null) {
|
||||||
|
node = new Node();
|
||||||
|
set(cell, node);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get one of a node's 256 links. It will not return null. If there is
|
||||||
|
* no link, then a link is manufactured.
|
||||||
|
*
|
||||||
|
* @param cell
|
||||||
|
* A byte.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Node vet(byte cell) {
|
||||||
|
return vet(((int) cell) & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] froms;
|
||||||
|
private int[] thrus;
|
||||||
|
private Node root;
|
||||||
|
private Kim[] kims;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Keep of kims.
|
||||||
|
*
|
||||||
|
* @param bits
|
||||||
|
* The log2 of the capacity of the Keep. For example, if bits is
|
||||||
|
* 12, then the keep's capacity will be 4096.
|
||||||
|
*/
|
||||||
|
public TrieKeep(int bits) {
|
||||||
|
super(bits);
|
||||||
|
this.froms = new int[this.capacity];
|
||||||
|
this.thrus = new int[this.capacity];
|
||||||
|
this.kims = new Kim[this.capacity];
|
||||||
|
this.root = new Node();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the kim associated with an integer.
|
||||||
|
*
|
||||||
|
* @param integer
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Kim kim(int integer) {
|
||||||
|
Kim kim = this.kims[integer];
|
||||||
|
int from = this.froms[integer];
|
||||||
|
int thru = this.thrus[integer];
|
||||||
|
if (from != 0 || thru != kim.length) {
|
||||||
|
kim = new Kim(kim, from, thru);
|
||||||
|
this.froms[integer] = 0;
|
||||||
|
this.thrus[integer] = kim.length;
|
||||||
|
this.kims[integer] = kim;
|
||||||
|
}
|
||||||
|
return kim;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the length of the Kim associated with an integer. This is sometimes
|
||||||
|
* much faster than get(integer).length.
|
||||||
|
*
|
||||||
|
* @param integer
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int length(int integer) {
|
||||||
|
return this.thrus[integer] - this.froms[integer];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 match(Kim kim, int from, int thru) {
|
||||||
|
Node node = this.root;
|
||||||
|
int best = none;
|
||||||
|
for (int at = from; at < thru; at += 1) {
|
||||||
|
node = node.get(kim.get(at));
|
||||||
|
if (node == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (node.integer != none) {
|
||||||
|
best = node.integer;
|
||||||
|
}
|
||||||
|
from += 1;
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean postMortem(PostMortem pm) {
|
||||||
|
boolean result = true;
|
||||||
|
TrieKeep that = (TrieKeep) pm;
|
||||||
|
if (this.length != that.length) {
|
||||||
|
JSONzip.log("\nLength " + this.length + " <> " + that.length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.capacity != that.capacity) {
|
||||||
|
JSONzip.log("\nCapacity " + this.capacity + " <> " +
|
||||||
|
that.capacity);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < this.length; i += 1) {
|
||||||
|
Kim thiskim = this.kim(i);
|
||||||
|
Kim thatkim = that.kim(i);
|
||||||
|
if (!thiskim.equals(thatkim)) {
|
||||||
|
JSONzip.log("\n[" + i + "] " + thiskim + " <> " + thatkim);
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result && this.root.postMortem(that.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerMany(Kim kim) {
|
||||||
|
int length = kim.length;
|
||||||
|
int limit = this.capacity - this.length;
|
||||||
|
if (limit > JSONzip.substringLimit) {
|
||||||
|
limit = JSONzip.substringLimit;
|
||||||
|
}
|
||||||
|
int until = length - (JSONzip.minSubstringLength - 1);
|
||||||
|
for (int from = 0; from < until; from += 1) {
|
||||||
|
int len = length - from;
|
||||||
|
if (len > JSONzip.maxSubstringLength) {
|
||||||
|
len = JSONzip.maxSubstringLength;
|
||||||
|
}
|
||||||
|
len += from;
|
||||||
|
Node node = this.root;
|
||||||
|
for (int at = from; at < len; at += 1) {
|
||||||
|
Node next = node.vet(kim.get(at));
|
||||||
|
if (next.integer == none
|
||||||
|
&& at - from >= (JSONzip.minSubstringLength - 1)) {
|
||||||
|
next.integer = this.length;
|
||||||
|
this.uses[this.length] = 1;
|
||||||
|
this.kims[this.length] = kim;
|
||||||
|
this.froms[this.length] = from;
|
||||||
|
this.thrus[this.length] = at + 1;
|
||||||
|
if (JSONzip.probe) {
|
||||||
|
try {
|
||||||
|
JSONzip.log("<<" + this.length + " "
|
||||||
|
+ new Kim(kim, from, at + 1) + ">> ");
|
||||||
|
} catch (Throwable ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.length += 1;
|
||||||
|
limit -= 1;
|
||||||
|
if (limit <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerOne(Kim kim) {
|
||||||
|
int integer = registerOne(kim, 0, kim.length);
|
||||||
|
if (integer != none) {
|
||||||
|
this.kims[integer] = kim;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int registerOne(Kim kim, int from, int thru) {
|
||||||
|
if (this.length < this.capacity) {
|
||||||
|
Node node = this.root;
|
||||||
|
for (int at = from; at < thru; at += 1) {
|
||||||
|
node = node.vet(kim.get(at));
|
||||||
|
}
|
||||||
|
if (node.integer == none) {
|
||||||
|
int integer = this.length;
|
||||||
|
node.integer = integer;
|
||||||
|
this.uses[integer] = 1;
|
||||||
|
this.kims[integer] = kim;
|
||||||
|
this.froms[integer] = from;
|
||||||
|
this.thrus[integer] = thru;
|
||||||
|
if (JSONzip.probe) {
|
||||||
|
try {
|
||||||
|
JSONzip.log("<<" + integer + " " + new Kim(kim, from, thru) + ">> ");
|
||||||
|
} catch (Throwable ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.length += 1;
|
||||||
|
return integer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserve space in the keep, compacting if necessary. A keep may contain
|
||||||
|
* at most -capacity- elements. The keep contents can be reduced by
|
||||||
|
* deleting all elements with low use counts, rebuilding the trie with the
|
||||||
|
* survivors.
|
||||||
|
*/
|
||||||
|
public void reserve() {
|
||||||
|
if (this.capacity - this.length < JSONzip.substringLimit) {
|
||||||
|
int from = 0;
|
||||||
|
int to = 0;
|
||||||
|
this.root = new Node();
|
||||||
|
while (from < this.capacity) {
|
||||||
|
if (this.uses[from] > 1) {
|
||||||
|
Kim kim = this.kims[from];
|
||||||
|
int thru = this.thrus[from];
|
||||||
|
Node node = this.root;
|
||||||
|
for (int at = this.froms[from]; at < thru; at += 1) {
|
||||||
|
Node next = node.vet(kim.get(at));
|
||||||
|
node = next;
|
||||||
|
}
|
||||||
|
node.integer = to;
|
||||||
|
this.uses[to] = age(this.uses[from]);
|
||||||
|
this.froms[to] = this.froms[from];
|
||||||
|
this.thrus[to] = thru;
|
||||||
|
this.kims[to] = kim;
|
||||||
|
to += 1;
|
||||||
|
}
|
||||||
|
from += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is possible, but highly unlikely, that too many items survive.
|
||||||
|
// If that happens, clear the keep.
|
||||||
|
|
||||||
|
if (this.capacity - to < JSONzip.substringLimit) {
|
||||||
|
this.power = 0;
|
||||||
|
this.root = new Node();
|
||||||
|
to = 0;
|
||||||
|
}
|
||||||
|
this.length = to;
|
||||||
|
while (to < this.capacity) {
|
||||||
|
this.uses[to] = 0;
|
||||||
|
this.kims[to] = null;
|
||||||
|
this.froms[to] = 0;
|
||||||
|
this.thrus[to] = 0;
|
||||||
|
to += 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object value(int integer) {
|
||||||
|
return kim(integer);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue