X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Forg%2Fjson%2FJSONWriter.java;h=0a623702cf01bac7848f633598feae2912083304;hb=57738a1f3c19b1c3a00bd3ac5108f8cd0af32f99;hp=f520c0c8fed64d1c7784b1e61bd70aa78b800a7c;hpb=e7338a61f3ce96dadf44ac80b2b32cc5ba4b94c8;p=jalview.git diff --git a/src/org/json/JSONWriter.java b/src/org/json/JSONWriter.java index f520c0c..0a62370 100644 --- a/src/org/json/JSONWriter.java +++ b/src/org/json/JSONWriter.java @@ -30,389 +30,470 @@ SOFTWARE. */ /** - * JSONWriter provides a quick and convenient way of producing JSON text. - * The texts produced strictly conform to JSON syntax rules. No whitespace is - * added, so the results are ready for transmission or storage. Each instance of + * JSONWriter provides a quick and convenient way of producing JSON text. The + * texts produced strictly conform to JSON syntax rules. No whitespace is added, + * so the results are ready for transmission or storage. Each instance of * JSONWriter can produce one JSON text. *

* A JSONWriter instance provides a value method for appending - * values to the - * text, and a key - * method for adding keys before values in objects. There are array - * and endArray methods that make and bound array values, and - * object and endObject methods which make and bound - * object values. All of these methods return the JSONWriter instance, - * permitting a cascade style. For example,

- * new JSONWriter(myWriter)
- *     .object()
- *         .key("JSON")
- *         .value("Hello, World!")
- *     .endObject();
which writes
- * {"JSON":"Hello, World!"}
+ * values to the text, and a key method for adding keys before + * values in objects. There are array and endArray + * methods that make and bound array values, and object and + * endObject methods which make and bound object values. All of + * these methods return the JSONWriter instance, permitting a cascade style. For + * example, + * + *
+ * new JSONWriter(myWriter).object().key("JSON").value("Hello, World!")
+ *         .endObject();
+ * 
+ * + * which writes + * + *
+ * {"JSON":"Hello, World!"}
+ * 
*

* The first method called must be array or object. * There are no methods for adding commas or colons. JSONWriter adds them for * you. Objects and arrays can be nested up to 200 levels deep. *

* This can sometimes be easier than using a JSONObject to build a string. + * * @author JSON.org * @version 2016-08-08 */ -public class JSONWriter { - private static final int maxdepth = 200; +public class JSONWriter +{ + private static final int maxdepth = 200; - /** - * The comma flag determines if a comma should be output before the next - * value. - */ - private boolean comma; + /** + * The comma flag determines if a comma should be output before the next + * value. + */ + private boolean comma; - /** - * The current mode. Values: - * 'a' (array), - * 'd' (done), - * 'i' (initial), - * 'k' (key), - * 'o' (object). - */ - protected char mode; + /** + * The current mode. Values: 'a' (array), 'd' (done), 'i' (initial), 'k' + * (key), 'o' (object). + */ + protected char mode; - /** - * The object/array stack. - */ - private final JSONObject stack[]; + /** + * The object/array stack. + */ + private final JSONObject stack[]; - /** - * The stack top index. A value of 0 indicates that the stack is empty. - */ - private int top; + /** + * The stack top index. A value of 0 indicates that the stack is empty. + */ + private int top; - /** - * The writer that will receive the output. - */ - protected Appendable writer; + /** + * The writer that will receive the output. + */ + protected Appendable writer; - /** - * Make a fresh JSONWriter. It can be used to build one JSON text. - */ - public JSONWriter(Appendable w) { - this.comma = false; - this.mode = 'i'; - this.stack = new JSONObject[maxdepth]; - this.top = 0; - this.writer = w; - } + /** + * Make a fresh JSONWriter. It can be used to build one JSON text. + */ + public JSONWriter(Appendable w) + { + this.comma = false; + this.mode = 'i'; + this.stack = new JSONObject[maxdepth]; + this.top = 0; + this.writer = w; + } - /** - * Append a value. - * @param string A string value. - * @return this - * @throws JSONException If the value is out of sequence. - */ - private JSONWriter append(String string) throws JSONException { - if (string == null) { - throw new JSONException("Null pointer"); - } - if (this.mode == 'o' || this.mode == 'a') { - try { - if (this.comma && this.mode == 'a') { - this.writer.append(','); - } - this.writer.append(string); - } catch (IOException e) { - // Android as of API 25 does not support this exception constructor - // however we won't worry about it. If an exception is happening here - // it will just throw a "Method not found" exception instead. - throw new JSONException(e); - } - if (this.mode == 'o') { - this.mode = 'k'; - } - this.comma = true; - return this; - } - throw new JSONException("Value out of sequence."); + /** + * Append a value. + * + * @param string + * A string value. + * @return this + * @throws JSONException + * If the value is out of sequence. + */ + private JSONWriter append(String string) throws JSONException + { + if (string == null) + { + throw new JSONException("Null pointer"); } - - /** - * Begin appending a new array. All values until the balancing - * endArray will be appended to this array. The - * endArray method must be called to mark the array's end. - * @return this - * @throws JSONException If the nesting is too deep, or if the object is - * started in the wrong place (for example as a key or after the end of the - * outermost array or object). - */ - public JSONWriter array() throws JSONException { - if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { - this.push(null); - this.append("["); - this.comma = false; - return this; + if (this.mode == 'o' || this.mode == 'a') + { + try + { + if (this.comma && this.mode == 'a') + { + this.writer.append(','); } - throw new JSONException("Misplaced array."); + this.writer.append(string); + } catch (IOException e) + { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. + throw new JSONException(e); + } + if (this.mode == 'o') + { + this.mode = 'k'; + } + this.comma = true; + return this; } + throw new JSONException("Value out of sequence."); + } - /** - * End something. - * @param m Mode - * @param c Closing character - * @return this - * @throws JSONException If unbalanced. - */ - private JSONWriter end(char m, char c) throws JSONException { - if (this.mode != m) { - throw new JSONException(m == 'a' - ? "Misplaced endArray." - : "Misplaced endObject."); - } - this.pop(m); - try { - this.writer.append(c); - } catch (IOException e) { - // Android as of API 25 does not support this exception constructor - // however we won't worry about it. If an exception is happening here - // it will just throw a "Method not found" exception instead. - throw new JSONException(e); - } - this.comma = true; - return this; + /** + * Begin appending a new array. All values until the balancing + * endArray will be appended to this array. The + * endArray method must be called to mark the array's end. + * + * @return this + * @throws JSONException + * If the nesting is too deep, or if the object is started in the + * wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter array() throws JSONException + { + if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') + { + this.push(null); + this.append("["); + this.comma = false; + return this; } + throw new JSONException("Misplaced array."); + } - /** - * End an array. This method most be called to balance calls to - * array. - * @return this - * @throws JSONException If incorrectly nested. - */ - public JSONWriter endArray() throws JSONException { - return this.end('a', ']'); + /** + * End something. + * + * @param m + * Mode + * @param c + * Closing character + * @return this + * @throws JSONException + * If unbalanced. + */ + private JSONWriter end(char m, char c) throws JSONException + { + if (this.mode != m) + { + throw new JSONException( + m == 'a' ? "Misplaced endArray." : "Misplaced endObject."); } - - /** - * End an object. This method most be called to balance calls to - * object. - * @return this - * @throws JSONException If incorrectly nested. - */ - public JSONWriter endObject() throws JSONException { - return this.end('k', '}'); + this.pop(m); + try + { + this.writer.append(c); + } catch (IOException e) + { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. + throw new JSONException(e); } + this.comma = true; + return this; + } - /** - * Append a key. The key will be associated with the next value. In an - * object, every value must be preceded by a key. - * @param string A key string. - * @return this - * @throws JSONException If the key is out of place. For example, keys - * do not belong in arrays or if the key is null. - */ - public JSONWriter key(String string) throws JSONException { - if (string == null) { - throw new JSONException("Null key."); - } - if (this.mode == 'k') { - try { - JSONObject topObject = this.stack[this.top - 1]; - // don't use the built in putOnce method to maintain Android support - if(topObject.has(string)) { - throw new JSONException("Duplicate key \"" + string + "\""); - } - topObject.put(string, true); - if (this.comma) { - this.writer.append(','); - } - this.writer.append(JSONObject.quote(string)); - this.writer.append(':'); - this.comma = false; - this.mode = 'o'; - return this; - } catch (IOException e) { - // Android as of API 25 does not support this exception constructor - // however we won't worry about it. If an exception is happening here - // it will just throw a "Method not found" exception instead. - throw new JSONException(e); - } - } - throw new JSONException("Misplaced key."); - } + /** + * End an array. This method most be called to balance calls to + * array. + * + * @return this + * @throws JSONException + * If incorrectly nested. + */ + public JSONWriter endArray() throws JSONException + { + return this.end('a', ']'); + } + /** + * End an object. This method most be called to balance calls to + * object. + * + * @return this + * @throws JSONException + * If incorrectly nested. + */ + public JSONWriter endObject() throws JSONException + { + return this.end('k', '}'); + } - /** - * Begin appending a new object. All keys and values until the balancing - * endObject will be appended to this object. The - * endObject method must be called to mark the object's end. - * @return this - * @throws JSONException If the nesting is too deep, or if the object is - * started in the wrong place (for example as a key or after the end of the - * outermost array or object). - */ - public JSONWriter object() throws JSONException { - if (this.mode == 'i') { - this.mode = 'o'; + /** + * Append a key. The key will be associated with the next value. In an object, + * every value must be preceded by a key. + * + * @param string + * A key string. + * @return this + * @throws JSONException + * If the key is out of place. For example, keys do not belong in + * arrays or if the key is null. + */ + public JSONWriter key(String string) throws JSONException + { + if (string == null) + { + throw new JSONException("Null key."); + } + if (this.mode == 'k') + { + try + { + JSONObject topObject = this.stack[this.top - 1]; + // don't use the built in putOnce method to maintain Android support + if (topObject.has(string)) + { + throw new JSONException("Duplicate key \"" + string + "\""); } - if (this.mode == 'o' || this.mode == 'a') { - this.append("{"); - this.push(new JSONObject()); - this.comma = false; - return this; + topObject.put(string, true); + if (this.comma) + { + this.writer.append(','); } - throw new JSONException("Misplaced object."); + this.writer.append(JSONObject.quote(string)); + this.writer.append(':'); + this.comma = false; + this.mode = 'o'; + return this; + } catch (IOException e) + { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. + throw new JSONException(e); + } + } + throw new JSONException("Misplaced key."); + } + /** + * Begin appending a new object. All keys and values until the balancing + * endObject will be appended to this object. The + * endObject method must be called to mark the object's end. + * + * @return this + * @throws JSONException + * If the nesting is too deep, or if the object is started in the + * wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter object() throws JSONException + { + if (this.mode == 'i') + { + this.mode = 'o'; + } + if (this.mode == 'o' || this.mode == 'a') + { + this.append("{"); + this.push(new JSONObject()); + this.comma = false; + return this; } + throw new JSONException("Misplaced object."); + } - /** - * Pop an array or object scope. - * @param c The scope to close. - * @throws JSONException If nesting is wrong. - */ - private void pop(char c) throws JSONException { - if (this.top <= 0) { - throw new JSONException("Nesting error."); - } - char m = this.stack[this.top - 1] == null ? 'a' : 'k'; - if (m != c) { - throw new JSONException("Nesting error."); - } - this.top -= 1; - this.mode = this.top == 0 - ? 'd' - : this.stack[this.top - 1] == null - ? 'a' - : 'k'; + /** + * Pop an array or object scope. + * + * @param c + * The scope to close. + * @throws JSONException + * If nesting is wrong. + */ + private void pop(char c) throws JSONException + { + if (this.top <= 0) + { + throw new JSONException("Nesting error."); } - - /** - * Push an array or object scope. - * @param jo The scope to open. - * @throws JSONException If nesting is too deep. - */ - private void push(JSONObject jo) throws JSONException { - if (this.top >= maxdepth) { - throw new JSONException("Nesting too deep."); - } - this.stack[this.top] = jo; - this.mode = jo == null ? 'a' : 'k'; - this.top += 1; + char m = this.stack[this.top - 1] == null ? 'a' : 'k'; + if (m != c) + { + throw new JSONException("Nesting error."); } + this.top -= 1; + this.mode = this.top == 0 ? 'd' + : this.stack[this.top - 1] == null ? 'a' : 'k'; + } - /** - * Make a JSON text of an Object value. If the object has an - * value.toJSONString() method, then that method will be used to produce the - * JSON text. The method is required to produce a strictly conforming text. - * If the object does not contain a toJSONString method (which is the most - * common case), then a text will be produced by other means. If the value - * is an array or Collection, then a JSONArray will be made from it and its - * toJSONString method will be called. If the value is a MAP, then a - * JSONObject will be made from it and its toJSONString method will be - * called. Otherwise, the value's toString method will be called, and the - * result will be quoted. - * - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @param value - * The value to be serialized. - * @return a printable, displayable, transmittable representation of the - * object, beginning with { (left - * brace) and ending with } (right - * brace). - * @throws JSONException - * If the value is or contains an invalid number. - */ - public static String valueToString(Object value) throws JSONException { - if (value == null || value.equals(null)) { - return "null"; - } - if (value instanceof JSONString) { - Object object; - try { - object = ((JSONString) value).toJSONString(); - } catch (Exception e) { - throw new JSONException(e); - } - if (object instanceof String) { - return (String) object; - } - throw new JSONException("Bad value from toJSONString: " + object); - } - if (value instanceof Number) { - // not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex - final String numberAsString = JSONObject.numberToString((Number) value); - try { - // Use the BigDecimal constructor for it's parser to validate the format. - @SuppressWarnings("unused") - BigDecimal unused = new BigDecimal(numberAsString); - // Close enough to a JSON number that we will return it unquoted - return numberAsString; - } catch (NumberFormatException ex){ - // The Number value is not a valid JSON number. - // Instead we will quote it as a string - return JSONObject.quote(numberAsString); - } - } - if (value instanceof Boolean || value instanceof JSONObject - || value instanceof JSONArray) { - return value.toString(); - } - if (value instanceof Map) { - Map map = (Map) value; - return new JSONObject(map).toString(); - } - if (value instanceof Collection) { - Collection coll = (Collection) value; - return new JSONArray(coll).toString(); - } - if (value.getClass().isArray()) { - return new JSONArray(value).toString(); - } - if(value instanceof Enum){ - return JSONObject.quote(((Enum)value).name()); - } - return JSONObject.quote(value.toString()); + /** + * Push an array or object scope. + * + * @param jo + * The scope to open. + * @throws JSONException + * If nesting is too deep. + */ + private void push(JSONObject jo) throws JSONException + { + if (this.top >= maxdepth) + { + throw new JSONException("Nesting too deep."); } + this.stack[this.top] = jo; + this.mode = jo == null ? 'a' : 'k'; + this.top += 1; + } - /** - * Append either the value true or the value - * false. - * @param b A boolean. - * @return this - * @throws JSONException - */ - public JSONWriter value(boolean b) throws JSONException { - return this.append(b ? "true" : "false"); + /** + * Make a JSON text of an Object value. If the object has an + * value.toJSONString() method, then that method will be used to produce the + * JSON text. The method is required to produce a strictly conforming text. If + * the object does not contain a toJSONString method (which is the most common + * case), then a text will be produced by other means. If the value is an + * array or Collection, then a JSONArray will be made from it and its + * toJSONString method will be called. If the value is a MAP, then a + * JSONObject will be made from it and its toJSONString method will be called. + * Otherwise, the value's toString method will be called, and the result will + * be quoted. + * + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param value + * The value to be serialized. + * @return a printable, displayable, transmittable representation of the + * object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException + * If the value is or contains an invalid number. + */ + public static String valueToString(Object value) throws JSONException + { + if (value == null || value.equals(null)) + { + return "null"; } - - /** - * Append a double value. - * @param d A double. - * @return this - * @throws JSONException If the number is not finite. - */ - public JSONWriter value(double d) throws JSONException { - return this.value(Double.valueOf(d)); + if (value instanceof JSONString) + { + Object object; + try + { + object = ((JSONString) value).toJSONString(); + } catch (Exception e) + { + throw new JSONException(e); + } + if (object instanceof String) + { + return (String) object; + } + throw new JSONException("Bad value from toJSONString: " + object); } - - /** - * Append a long value. - * @param l A long. - * @return this - * @throws JSONException - */ - public JSONWriter value(long l) throws JSONException { - return this.append(Long.toString(l)); + if (value instanceof Number) + { + // not all Numbers may match actual JSON Numbers. i.e. Fractions or + // Complex + final String numberAsString = JSONObject + .numberToString((Number) value); + try + { + // Use the BigDecimal constructor for it's parser to validate the + // format. + @SuppressWarnings("unused") + BigDecimal unused = new BigDecimal(numberAsString); + // Close enough to a JSON number that we will return it unquoted + return numberAsString; + } catch (NumberFormatException ex) + { + // The Number value is not a valid JSON number. + // Instead we will quote it as a string + return JSONObject.quote(numberAsString); + } + } + if (value instanceof Boolean || value instanceof JSONObject + || value instanceof JSONArray) + { + return value.toString(); + } + if (value instanceof Map) + { + Map map = (Map) value; + return new JSONObject(map).toString(); + } + if (value instanceof Collection) + { + Collection coll = (Collection) value; + return new JSONArray(coll).toString(); + } + if (value.getClass().isArray()) + { + return new JSONArray(value).toString(); } + if (value instanceof Enum) + { + return JSONObject.quote(((Enum) value).name()); + } + return JSONObject.quote(value.toString()); + } + /** + * Append either the value true or the value false. + * + * @param b + * A boolean. + * @return this + * @throws JSONException + */ + public JSONWriter value(boolean b) throws JSONException + { + return this.append(b ? "true" : "false"); + } - /** - * Append an object value. - * @param object The object to append. It can be null, or a Boolean, Number, - * String, JSONObject, or JSONArray, or an object that implements JSONString. - * @return this - * @throws JSONException If the value is out of sequence. - */ - public JSONWriter value(Object object) throws JSONException { - return this.append(valueToString(object)); - } + /** + * Append a double value. + * + * @param d + * A double. + * @return this + * @throws JSONException + * If the number is not finite. + */ + public JSONWriter value(double d) throws JSONException + { + return this.value(Double.valueOf(d)); + } + + /** + * Append a long value. + * + * @param l + * A long. + * @return this + * @throws JSONException + */ + public JSONWriter value(long l) throws JSONException + { + return this.append(Long.toString(l)); + } + + /** + * Append an object value. + * + * @param object + * The object to append. It can be null, or a Boolean, Number, + * String, JSONObject, or JSONArray, or an object that implements + * JSONString. + * @return this + * @throws JSONException + * If the value is out of sequence. + */ + public JSONWriter value(Object object) throws JSONException + { + return this.append(valueToString(object)); + } }