Added exception handling in the main loop for the web server to avoid the main loop thread getting killed off due to an unexpected/unhandled exception.
Moved code for JSON handling and metadata/metadata container into the Common project from the Foundation project.
This commit is contained in:
22
Common/src/com/common/metadata/IMetadata.java
Normal file
22
Common/src/com/common/metadata/IMetadata.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.common.metadata;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import com.common.io.IExternalizable;
|
||||
|
||||
/**
|
||||
* This interface is used by the EntityMetadata and future metadata types that can be passed around to specify what data is required in different situations.
|
||||
*/
|
||||
public interface IMetadata extends IExternalizable, Externalizable {
|
||||
/**
|
||||
* The class of object for which this metadata can be used.
|
||||
* @return The metadata type mapping.
|
||||
*/
|
||||
public Class getType();
|
||||
}//IMetadata//
|
||||
100
Common/src/com/common/metadata/MetadataContainer.java
Normal file
100
Common/src/com/common/metadata/MetadataContainer.java
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.common.metadata;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.io.IExternalizable;
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
import com.common.util.LiteHashMap;
|
||||
|
||||
/*
|
||||
* This container is used to pass information about what to include or exclude when performing such operations as cloning models.
|
||||
*/
|
||||
public class MetadataContainer implements IExternalizable, Externalizable {
|
||||
/** Tells the user of the metadata to not include any reflections. */
|
||||
public static final int FLAG_EXCLUDE_REFLECTIONS = 1;
|
||||
|
||||
private LiteHashMap metadataByTypeMap = new LiteHashMap(10, Comparator.getLogicalComparator(), null);
|
||||
private int flags = 0;
|
||||
/**
|
||||
* MetadataContainer constructor.
|
||||
*/
|
||||
public MetadataContainer() {
|
||||
super();
|
||||
}//MetadataContainer()//
|
||||
/**
|
||||
* MetadataContainer constructor.
|
||||
* @param flags One or more of the flag identifiers defined by this class.
|
||||
*/
|
||||
public MetadataContainer(int flags) {
|
||||
super();
|
||||
this.flags = flags;
|
||||
}//MetadataContainer()//
|
||||
/**
|
||||
* MetadataContainer constructor.
|
||||
* @param metadata The set of metadata instances, one for each type for which metadata is provided. The type must match exactly with the object whose metadata is being searched for (ie: type hierarchy is not considered).
|
||||
*/
|
||||
public MetadataContainer(IMetadata[] metadata) {
|
||||
super();
|
||||
|
||||
for(int index = 0; index < metadata.length; index++) {
|
||||
metadataByTypeMap.put(metadata[index].getType(), metadata[index]);
|
||||
}//for//
|
||||
}//MetadataContainer()//
|
||||
/**
|
||||
* Gets the metadata for the given type.
|
||||
* @param type The type whose metadata is to be retreived.
|
||||
* @return The metadata for that exact type, or null if none was specified.
|
||||
*/
|
||||
public IMetadata getMetadata(Class type) {
|
||||
return (IMetadata) metadataByTypeMap.get(type);
|
||||
}//getMetadata()//
|
||||
/**
|
||||
* Determines whether reflections should be excluded based on the flags passed to this metadata container.
|
||||
* @return Whether reflections should be excluded.
|
||||
*/
|
||||
public boolean excludeReflections() {
|
||||
return (flags & FLAG_EXCLUDE_REFLECTIONS) > 0;
|
||||
}//excludeReflections()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(metadataByTypeMap);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(metadataByTypeMap);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
/*byte version = */in.readByte();
|
||||
metadataByTypeMap = (LiteHashMap) in.readObject();
|
||||
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
/*byte version = */in.readByte();
|
||||
metadataByTypeMap = (LiteHashMap) in.readObject();
|
||||
}//readExternal()//
|
||||
}//MetadataContainer//
|
||||
18
Common/src/com/common/util/json/IJsonAware.java
Normal file
18
Common/src/com/common/util/json/IJsonAware.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.common.util.json;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
import com.common.metadata.MetadataContainer;
|
||||
import com.common.util.LiteHashSet;
|
||||
|
||||
/**
|
||||
* Classes implement this if they know how to stream to/from JSON formatted text.
|
||||
*/
|
||||
public interface IJsonAware {
|
||||
/**
|
||||
* Converts the entity into a JSON string.
|
||||
* Includes all attributes that are not cyclical.
|
||||
* @param context The context for the JSON streaming operation.
|
||||
*/
|
||||
public void toJson(IJsonContext context);
|
||||
}//IJsonAware//
|
||||
35
Common/src/com/common/util/json/IJsonContext.java
Normal file
35
Common/src/com/common/util/json/IJsonContext.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.common.util.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import com.common.metadata.MetadataContainer;
|
||||
import com.common.util.LiteHashSet;
|
||||
|
||||
public interface IJsonContext {
|
||||
/**
|
||||
* @return The container containing metadata on what to include in the JSON.
|
||||
*/
|
||||
public MetadataContainer getMetadataContainer();
|
||||
/**
|
||||
* @return Writes a new line character to the writer and indents as required. New line and indent characters are optional and may be ignored (not outputted) based on the user's preferences when the context was setup.
|
||||
*/
|
||||
public void writeNewLine();
|
||||
/**
|
||||
* @return The set of included objects, used to prevent recursion.
|
||||
*/
|
||||
public LiteHashSet getIncluded();
|
||||
/**
|
||||
* @return The writer used to write the output.
|
||||
*/
|
||||
public StringWriter getWriter();
|
||||
/**
|
||||
* @return Whether the output should be compacted as much as possible.
|
||||
*/
|
||||
public boolean isCompact();
|
||||
/**
|
||||
* Writes the given object to the JSON output.
|
||||
* @param value The value to write.
|
||||
*/
|
||||
public void toJson(Object value) throws IOException;
|
||||
}//IJsonContext//
|
||||
846
Common/src/com/common/util/json/JSONArray.java
Normal file
846
Common/src/com/common/util/json/JSONArray.java
Normal file
@@ -0,0 +1,846 @@
|
||||
package com.common.util.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A JSONArray is an ordered sequence of values. Its external text form is a
|
||||
* string wrapped in square brackets with commas separating the values. The
|
||||
* internal form is an object having <code>get</code> and <code>opt</code>
|
||||
* methods for accessing the values by index, and <code>put</code> methods for
|
||||
* adding or replacing values. The values can be any of these types:
|
||||
* <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
|
||||
* <code>Number</code>, <code>String</code>, or the
|
||||
* <code>JSONObject.NULL object</code>.
|
||||
* <p>
|
||||
* The constructor can convert a JSON text into a Java object. The
|
||||
* <code>toString</code> method converts to JSON text.
|
||||
* <p>
|
||||
* A <code>get</code> method returns a value if one can be found, and throws an
|
||||
* exception if one cannot be found. An <code>opt</code> method returns a
|
||||
* default value instead of throwing an exception, and so is useful for
|
||||
* obtaining optional values.
|
||||
* <p>
|
||||
* The generic <code>get()</code> and <code>opt()</code> methods return an
|
||||
* object which you can cast or query for type. There are also typed
|
||||
* <code>get</code> and <code>opt</code> methods that do type checking and type
|
||||
* coercion for you.
|
||||
* <p>
|
||||
* The texts produced by the <code>toString</code> methods strictly conform to
|
||||
* JSON syntax rules. The constructors are more forgiving in the texts they will
|
||||
* accept:
|
||||
* <ul>
|
||||
* <li>An extra <code>,</code> <small>(comma)</small> may appear just
|
||||
* before the closing bracket.</li>
|
||||
* <li>The <code>null</code> value will be inserted when there is <code>,</code>
|
||||
* <small>(comma)</small> elision.</li>
|
||||
* <li>Strings may be quoted with <code>'</code> <small>(single
|
||||
* quote)</small>.</li>
|
||||
* <li>Strings do not need to be quoted at all if they do not begin with a quote
|
||||
* or single quote, and if they do not contain leading or trailing spaces, and
|
||||
* if they do not contain any of these characters:
|
||||
* <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
|
||||
* if they are not the reserved words <code>true</code>, <code>false</code>, or
|
||||
* <code>null</code>.</li>
|
||||
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
|
||||
* well as by <code>,</code> <small>(comma)</small>.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2012-04-20
|
||||
*/
|
||||
public class JSONArray {
|
||||
/** The arrayList where the JSONArray's properties are kept. */
|
||||
private final ArrayList myArrayList;
|
||||
/**
|
||||
* Construct an empty JSONArray.
|
||||
*/
|
||||
public JSONArray() {
|
||||
this.myArrayList = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a JSONTokener.
|
||||
* @param x A JSONTokener
|
||||
* @throws JSONException If there is a syntax error.
|
||||
*/
|
||||
public JSONArray(JSONTokener x) throws JSONException {
|
||||
this();
|
||||
if(x.nextClean() != '[') {
|
||||
throw x.syntaxError("A JSONArray text must start with '['");
|
||||
}
|
||||
if(x.nextClean() != ']') {
|
||||
x.back();
|
||||
for(;;) {
|
||||
if(x.nextClean() == ',') {
|
||||
x.back();
|
||||
this.myArrayList.add(JSONObject.NULL);
|
||||
}
|
||||
else {
|
||||
x.back();
|
||||
this.myArrayList.add(x.nextValue());
|
||||
}
|
||||
switch(x.nextClean()) {
|
||||
case ';':
|
||||
case ',':
|
||||
if(x.nextClean() == ']') {
|
||||
return;
|
||||
}
|
||||
x.back();
|
||||
break;
|
||||
case ']':
|
||||
return;
|
||||
default:
|
||||
throw x.syntaxError("Expected a ',' or ']'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a source JSON text.
|
||||
* @param source A string that begins with
|
||||
* <code>[</code> <small>(left bracket)</small>
|
||||
* and ends with <code>]</code> <small>(right bracket)</small>.
|
||||
* @throws JSONException If there is a syntax error.
|
||||
*/
|
||||
public JSONArray(String source) throws JSONException {
|
||||
this(new JSONTokener(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a Collection.
|
||||
* @param collection A Collection.
|
||||
*/
|
||||
public JSONArray(Collection collection) {
|
||||
this.myArrayList = new ArrayList();
|
||||
if(collection != null) {
|
||||
Iterator iter = collection.iterator();
|
||||
while(iter.hasNext()) {
|
||||
this.myArrayList.add(JSONObject.wrap(iter.next()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from an array
|
||||
* @throws JSONException If not an array.
|
||||
*/
|
||||
public JSONArray(Object array) throws JSONException {
|
||||
this();
|
||||
if(array.getClass().isArray()) {
|
||||
int length = Array.getLength(array);
|
||||
for(int i = 0; i < length; i += 1) {
|
||||
this.put(JSONObject.wrap(Array.get(array, i)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new JSONException("JSONArray initial value should be a string or collection or array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object value associated with an index.
|
||||
* @param index
|
||||
* The index must be between 0 and length() - 1.
|
||||
* @return An object value.
|
||||
* @throws JSONException If there is no value for the index.
|
||||
*/
|
||||
public Object get(int index) throws JSONException {
|
||||
Object object = this.opt(index);
|
||||
if(object == null) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the boolean value associated with an index.
|
||||
* The string values "true" and "false" are converted to boolean.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The truth.
|
||||
* @throws JSONException If there is no value for the index or if the
|
||||
* value is not convertible to boolean.
|
||||
*/
|
||||
public boolean getBoolean(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if(object.equals(Boolean.FALSE) || (object instanceof String && ((String) object).equalsIgnoreCase("false"))) {
|
||||
return false;
|
||||
}
|
||||
else if(object.equals(Boolean.TRUE) || (object instanceof String && ((String) object).equalsIgnoreCase("true"))) {
|
||||
return true;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a boolean.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the double value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot
|
||||
* be converted to a number.
|
||||
*/
|
||||
public double getDouble(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number ? ((Number) object).doubleValue() : Double.parseDouble((String) object);
|
||||
}
|
||||
catch(Exception e) {
|
||||
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the int value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value is not a number.
|
||||
*/
|
||||
public int getInt(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number ? ((Number) object).intValue() : Integer.parseInt((String) object);
|
||||
}
|
||||
catch(Exception e) {
|
||||
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSONArray associated with an index.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A JSONArray value.
|
||||
* @throws JSONException If there is no value for the index. or if the
|
||||
* value is not a JSONArray
|
||||
*/
|
||||
public JSONArray getJSONArray(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if(object instanceof JSONArray) {
|
||||
return (JSONArray) object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSONObject associated with an index.
|
||||
* @param index subscript
|
||||
* @return A JSONObject value.
|
||||
* @throws JSONException If there is no value for the index or if the
|
||||
* value is not a JSONObject
|
||||
*/
|
||||
public JSONObject getJSONObject(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if(object instanceof JSONObject) {
|
||||
return (JSONObject) object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the long value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot
|
||||
* be converted to a number.
|
||||
*/
|
||||
public long getLong(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
try {
|
||||
return object instanceof Number ? ((Number) object).longValue() : Long.parseLong((String) object);
|
||||
}
|
||||
catch(Exception e) {
|
||||
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string associated with an index.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A string value.
|
||||
* @throws JSONException If there is no string value for the index.
|
||||
*/
|
||||
public String getString(int index) throws JSONException {
|
||||
Object object = this.get(index);
|
||||
if(object instanceof String) {
|
||||
return (String) object;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] not a string.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the value is null.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return true if the value at the index is null, or if there is no value.
|
||||
*/
|
||||
public boolean isNull(int index) {
|
||||
return JSONObject.NULL.equals(this.opt(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a string from the contents of this JSONArray. The
|
||||
* <code>separator</code> string is inserted between each element.
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
* @param separator A string that will be inserted between the elements.
|
||||
* @return a string.
|
||||
* @throws JSONException If the array contains an invalid number.
|
||||
*/
|
||||
public String join(String separator) throws JSONException {
|
||||
int len = this.length();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
for(int i = 0; i < len; i += 1) {
|
||||
if(i > 0) {
|
||||
sb.append(separator);
|
||||
}
|
||||
sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of elements in the JSONArray, included nulls.
|
||||
*
|
||||
* @return The length (or size).
|
||||
*/
|
||||
public int length() {
|
||||
return this.myArrayList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional object value associated with an index.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return An object value, or null if there is no
|
||||
* object at that index.
|
||||
*/
|
||||
public Object opt(int index) {
|
||||
return (index < 0 || index >= this.length()) ? null : this.myArrayList.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional boolean value associated with an index.
|
||||
* It returns false if there is no value at that index,
|
||||
* or if the value is not Boolean.TRUE or the String "true".
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The truth.
|
||||
*/
|
||||
public boolean optBoolean(int index) {
|
||||
return this.optBoolean(index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional boolean value associated with an index.
|
||||
* It returns the defaultValue if there is no value at that index or if
|
||||
* it is not a Boolean or the String "true" or "false" (case insensitive).
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue A boolean default.
|
||||
* @return The truth.
|
||||
*/
|
||||
public boolean optBoolean(int index, boolean defaultValue) {
|
||||
try {
|
||||
return this.getBoolean(index);
|
||||
}
|
||||
catch(Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional double value associated with an index.
|
||||
* NaN is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public double optDouble(int index) {
|
||||
return this.optDouble(index, Double.NaN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional double value associated with an index.
|
||||
* The defaultValue is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index subscript
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public double optDouble(int index, double defaultValue) {
|
||||
try {
|
||||
return this.getDouble(index);
|
||||
}
|
||||
catch(Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional int value associated with an index.
|
||||
* Zero is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public int optInt(int index) {
|
||||
return this.optInt(index, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional int value associated with an index.
|
||||
* The defaultValue is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public int optInt(int index, int defaultValue) {
|
||||
try {
|
||||
return this.getInt(index);
|
||||
}
|
||||
catch(Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional JSONArray associated with an index.
|
||||
* @param index subscript
|
||||
* @return A JSONArray value, or null if the index has no value,
|
||||
* or if the value is not a JSONArray.
|
||||
*/
|
||||
public JSONArray optJSONArray(int index) {
|
||||
Object o = this.opt(index);
|
||||
return o instanceof JSONArray ? (JSONArray) o : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional JSONObject associated with an index.
|
||||
* Null is returned if the key is not found, or null if the index has
|
||||
* no value, or if the value is not a JSONObject.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A JSONObject value.
|
||||
*/
|
||||
public JSONObject optJSONObject(int index) {
|
||||
Object o = this.opt(index);
|
||||
return o instanceof JSONObject ? (JSONObject) o : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional long value associated with an index.
|
||||
* Zero is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public long optLong(int index) {
|
||||
return this.optLong(index, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional long value associated with an index.
|
||||
* The defaultValue is returned if there is no value for the index,
|
||||
* or if the value is not a number and cannot be converted to a number.
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public long optLong(int index, long defaultValue) {
|
||||
try {
|
||||
return this.getLong(index);
|
||||
}
|
||||
catch(Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional string value associated with an index. It returns an
|
||||
* empty string if there is no value at that index. If the value
|
||||
* is not a string and is not null, then it is coverted to a string.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A String value.
|
||||
*/
|
||||
public String optString(int index) {
|
||||
return this.optString(index, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional string associated with an index.
|
||||
* The defaultValue is returned if the key is not found.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return A String value.
|
||||
*/
|
||||
public String optString(int index, String defaultValue) {
|
||||
Object object = this.opt(index);
|
||||
return JSONObject.NULL.equals(object) ? defaultValue : object.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a boolean value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A boolean value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(boolean value) {
|
||||
this.put(value ? Boolean.TRUE : Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a
|
||||
* JSONArray which is produced from a Collection.
|
||||
* @param value A Collection value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Collection value) {
|
||||
this.put(new JSONArray(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a double value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A double value.
|
||||
* @throws JSONException if the value is not finite.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(double value) throws JSONException {
|
||||
Double d = new Double(value);
|
||||
JSONObject.testValidity(d);
|
||||
this.put(d);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an int value. This increases the array's length by one.
|
||||
*
|
||||
* @param value An int value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(int value) {
|
||||
this.put(new Integer(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an long value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A long value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(long value) {
|
||||
this.put(new Long(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a
|
||||
* JSONObject which is produced from a Map.
|
||||
* @param value A Map value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Map value) {
|
||||
this.put(new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an object value. This increases the array's length by one.
|
||||
* @param value An object value. The value should be a
|
||||
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
|
||||
* JSONObject.NULL object.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Object value) {
|
||||
this.myArrayList.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a boolean value in the JSONArray. If the index is greater
|
||||
* than the length of the JSONArray, then null elements will be added as
|
||||
* necessary to pad it out.
|
||||
* @param index The subscript.
|
||||
* @param value A boolean value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, boolean value) throws JSONException {
|
||||
this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a
|
||||
* JSONArray which is produced from a Collection.
|
||||
* @param index The subscript.
|
||||
* @param value A Collection value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the value is
|
||||
* not finite.
|
||||
*/
|
||||
public JSONArray put(int index, Collection value) throws JSONException {
|
||||
this.put(index, new JSONArray(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a double value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad
|
||||
* it out.
|
||||
* @param index The subscript.
|
||||
* @param value A double value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the value is
|
||||
* not finite.
|
||||
*/
|
||||
public JSONArray put(int index, double value) throws JSONException {
|
||||
this.put(index, new Double(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace an int value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad
|
||||
* it out.
|
||||
* @param index The subscript.
|
||||
* @param value An int value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, int value) throws JSONException {
|
||||
this.put(index, new Integer(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a long value. If the index is greater than the length of
|
||||
* the JSONArray, then null elements will be added as necessary to pad
|
||||
* it out.
|
||||
* @param index The subscript.
|
||||
* @param value A long value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, long value) throws JSONException {
|
||||
this.put(index, new Long(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a
|
||||
* JSONObject that is produced from a Map.
|
||||
* @param index The subscript.
|
||||
* @param value The Map value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the the value is
|
||||
* an invalid number.
|
||||
*/
|
||||
public JSONArray put(int index, Map value) throws JSONException {
|
||||
this.put(index, new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace an object value in the JSONArray. If the index is greater
|
||||
* than the length of the JSONArray, then null elements will be added as
|
||||
* necessary to pad it out.
|
||||
* @param index The subscript.
|
||||
* @param value The value to put into the array. The value should be a
|
||||
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
|
||||
* JSONObject.NULL object.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the the value is
|
||||
* an invalid number.
|
||||
*/
|
||||
public JSONArray put(int index, Object value) throws JSONException {
|
||||
JSONObject.testValidity(value);
|
||||
if(index < 0) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
if(index < this.length()) {
|
||||
this.myArrayList.set(index, value);
|
||||
}
|
||||
else {
|
||||
while(index != this.length()) {
|
||||
this.put(JSONObject.NULL);
|
||||
}
|
||||
this.put(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an index and close the hole.
|
||||
* @param index The index of the element to be removed.
|
||||
* @return The value that was associated with the index,
|
||||
* or null if there was no value.
|
||||
*/
|
||||
public Object remove(int index) {
|
||||
Object o = this.opt(index);
|
||||
this.myArrayList.remove(index);
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONObject by combining a JSONArray of names with the values
|
||||
* of this JSONArray.
|
||||
* @param names A JSONArray containing a list of key strings. These will be
|
||||
* paired with the values.
|
||||
* @return A JSONObject, or null if there are no names or if this JSONArray
|
||||
* has no values.
|
||||
* @throws JSONException If any of the names are null.
|
||||
*/
|
||||
public JSONObject toJSONObject(JSONArray names) throws JSONException {
|
||||
if(names == null || names.length() == 0 || this.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jo = new JSONObject();
|
||||
for(int i = 0; i < names.length(); i += 1) {
|
||||
jo.put(names.getString(i), this.opt(i));
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSON text of this JSONArray. For compactness, no
|
||||
* unnecessary whitespace is added. If it is not possible to produce a
|
||||
* syntactically correct JSON text then null will be returned instead. This
|
||||
* could occur if the array contains an invalid number.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @return a printable, displayable, transmittable
|
||||
* representation of the array.
|
||||
*/
|
||||
public String toString() {
|
||||
try {
|
||||
return '[' + this.join(",") + ']';
|
||||
}
|
||||
catch(Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a prettyprinted JSON text of this JSONArray.
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
* @param indentFactor The number of spaces to add to each level of
|
||||
* indentation.
|
||||
* @return a printable, displayable, transmittable
|
||||
* representation of the object, beginning
|
||||
* with <code>[</code> <small>(left bracket)</small> and ending
|
||||
* with <code>]</code> <small>(right bracket)</small>.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public String toString(int indentFactor) throws JSONException {
|
||||
StringWriter sw = new StringWriter();
|
||||
synchronized(sw.getBuffer()) {
|
||||
return this.write(sw, indentFactor, 0).toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of the JSONArray as JSON text to a writer. For
|
||||
* compactness, no whitespace is added.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @return The writer.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public Writer write(Writer writer) throws JSONException {
|
||||
return this.write(writer, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of the JSONArray as JSON text to a writer. For
|
||||
* compactness, no whitespace is added.
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @param indentFactor
|
||||
* The number of spaces to add to each level of indentation.
|
||||
* @param indent
|
||||
* The indention of the top level.
|
||||
* @return The writer.
|
||||
* @throws JSONException
|
||||
*/
|
||||
Writer write(Writer writer, int indentFactor, int indent) throws JSONException {
|
||||
try {
|
||||
boolean commanate = false;
|
||||
int length = this.length();
|
||||
writer.write('[');
|
||||
|
||||
if(length == 1) {
|
||||
JSONObject.writeValue(writer, this.myArrayList.get(0), indentFactor, indent);
|
||||
}
|
||||
else if(length != 0) {
|
||||
final int newindent = indent + indentFactor;
|
||||
|
||||
for(int i = 0; i < length; i += 1) {
|
||||
if(commanate) {
|
||||
writer.write(',');
|
||||
}
|
||||
if(indentFactor > 0) {
|
||||
writer.write('\n');
|
||||
}
|
||||
JSONObject.indent(writer, newindent);
|
||||
JSONObject.writeValue(writer, this.myArrayList.get(i), indentFactor, newindent);
|
||||
commanate = true;
|
||||
}
|
||||
if(indentFactor > 0) {
|
||||
writer.write('\n');
|
||||
}
|
||||
JSONObject.indent(writer, indent);
|
||||
}
|
||||
writer.write(']');
|
||||
return writer;
|
||||
}
|
||||
catch(IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Common/src/com/common/util/json/JSONException.java
Normal file
27
Common/src/com/common/util/json/JSONException.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.common.util.json;
|
||||
|
||||
/**
|
||||
* The JSONException is thrown by the JSON.org classes when things are amiss.
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class JSONException extends Exception {
|
||||
private static final long serialVersionUID = 0;
|
||||
private Throwable cause;
|
||||
/**
|
||||
* Constructs a JSONException with an explanatory message.
|
||||
* @param message Detail about the reason for the exception.
|
||||
*/
|
||||
public JSONException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONException(Throwable cause) {
|
||||
super(cause.getMessage());
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public Throwable getCause() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
1454
Common/src/com/common/util/json/JSONObject.java
Normal file
1454
Common/src/com/common/util/json/JSONObject.java
Normal file
File diff suppressed because it is too large
Load Diff
19
Common/src/com/common/util/json/JSONString.java
Normal file
19
Common/src/com/common/util/json/JSONString.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.common.util.json;
|
||||
|
||||
/**
|
||||
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
|
||||
* method so that a class can change the behavior of
|
||||
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
|
||||
* and <code>JSONWriter.value(</code>Object<code>)</code>. The
|
||||
* <code>toJSONString</code> method will be used instead of the default behavior
|
||||
* of using the Object's <code>toString()</code> method and quoting the result.
|
||||
*/
|
||||
public interface JSONString {
|
||||
/**
|
||||
* The <code>toJSONString</code> method allows a class to produce its own JSON
|
||||
* serialization.
|
||||
*
|
||||
* @return A strictly syntactically correct JSON text.
|
||||
*/
|
||||
public String toJSONString();
|
||||
}
|
||||
78
Common/src/com/common/util/json/JSONStringer.java
Normal file
78
Common/src/com/common/util/json/JSONStringer.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package com.common.util.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2006 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* JSONStringer 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
|
||||
* JSONStringer can produce one JSON text.
|
||||
* <p>
|
||||
* A JSONStringer instance provides a <code>value</code> method for appending
|
||||
* values to the
|
||||
* text, and a <code>key</code>
|
||||
* method for adding keys before values in objects. There are <code>array</code>
|
||||
* and <code>endArray</code> methods that make and bound array values, and
|
||||
* <code>object</code> and <code>endObject</code> methods which make and bound
|
||||
* object values. All of these methods return the JSONWriter instance,
|
||||
* permitting cascade style. For example, <pre>
|
||||
* myString = new JSONStringer()
|
||||
* .object()
|
||||
* .key("JSON")
|
||||
* .value("Hello, World!")
|
||||
* .endObject()
|
||||
* .toString();</pre> which produces the string <pre>
|
||||
* {"JSON":"Hello, World!"}</pre>
|
||||
* <p>
|
||||
* The first method called must be <code>array</code> or <code>object</code>.
|
||||
* There are no methods for adding commas or colons. JSONStringer adds them for
|
||||
* you. Objects and arrays can be nested up to 20 levels deep.
|
||||
* <p>
|
||||
* This can sometimes be easier than using a JSONObject to build a string.
|
||||
* @author JSON.org
|
||||
* @version 2008-09-18
|
||||
*/
|
||||
public class JSONStringer extends JSONWriter {
|
||||
/**
|
||||
* Make a fresh JSONStringer. It can be used to build one JSON text.
|
||||
*/
|
||||
public JSONStringer() {
|
||||
super(new StringWriter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JSON text. This method is used to obtain the product of the
|
||||
* JSONStringer instance. It will return <code>null</code> if there was a
|
||||
* problem in the construction of the JSON text (such as the calls to
|
||||
* <code>array</code> were not properly balanced with calls to
|
||||
* <code>endArray</code>).
|
||||
* @return The JSON text.
|
||||
*/
|
||||
public String toString() {
|
||||
return this.mode == 'd' ? this.writer.toString() : null;
|
||||
}
|
||||
}
|
||||
446
Common/src/com/common/util/json/JSONTokener.java
Normal file
446
Common/src/com/common/util/json/JSONTokener.java
Normal file
@@ -0,0 +1,446 @@
|
||||
package com.common.util.json;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A JSONTokener takes a source string and extracts characters and tokens from
|
||||
* it. It is used by the JSONObject and JSONArray constructors to parse
|
||||
* JSON source strings.
|
||||
* @author JSON.org
|
||||
* @version 2012-02-16
|
||||
*/
|
||||
public class JSONTokener {
|
||||
|
||||
private long character;
|
||||
private boolean eof;
|
||||
private long index;
|
||||
private long line;
|
||||
private char previous;
|
||||
private Reader reader;
|
||||
private boolean usePrevious;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a Reader.
|
||||
*
|
||||
* @param reader A reader.
|
||||
*/
|
||||
public JSONTokener(Reader reader) {
|
||||
this.reader = reader.markSupported()
|
||||
? reader
|
||||
: new BufferedReader(reader);
|
||||
this.eof = false;
|
||||
this.usePrevious = false;
|
||||
this.previous = 0;
|
||||
this.index = 0;
|
||||
this.character = 1;
|
||||
this.line = 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from an InputStream.
|
||||
*/
|
||||
public JSONTokener(InputStream inputStream) throws JSONException {
|
||||
this(new InputStreamReader(inputStream));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a string.
|
||||
*
|
||||
* @param s A source string.
|
||||
*/
|
||||
public JSONTokener(String s) {
|
||||
this(new StringReader(s));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Back up one character. This provides a sort of lookahead capability,
|
||||
* so that you can test for a digit or letter before attempting to parse
|
||||
* the next number or identifier.
|
||||
*/
|
||||
public void back() throws JSONException {
|
||||
if (this.usePrevious || this.index <= 0) {
|
||||
throw new JSONException("Stepping back two steps is not supported");
|
||||
}
|
||||
this.index -= 1;
|
||||
this.character -= 1;
|
||||
this.usePrevious = true;
|
||||
this.eof = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the hex value of a character (base16).
|
||||
* @param c A character between '0' and '9' or between 'A' and 'F' or
|
||||
* between 'a' and 'f'.
|
||||
* @return An int between 0 and 15, or -1 if c was not a hex digit.
|
||||
*/
|
||||
public static int dehexchar(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return c - ('A' - 10);
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return c - ('a' - 10);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean end() {
|
||||
return this.eof && !this.usePrevious;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the source string still contains characters that next()
|
||||
* can consume.
|
||||
* @return true if not yet at the end of the source.
|
||||
*/
|
||||
public boolean more() throws JSONException {
|
||||
this.next();
|
||||
if (this.end()) {
|
||||
return false;
|
||||
}
|
||||
this.back();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next character in the source string.
|
||||
*
|
||||
* @return The next character, or 0 if past the end of the source string.
|
||||
*/
|
||||
public char next() throws JSONException {
|
||||
int c;
|
||||
if (this.usePrevious) {
|
||||
this.usePrevious = false;
|
||||
c = this.previous;
|
||||
} else {
|
||||
try {
|
||||
c = this.reader.read();
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
|
||||
if (c <= 0) { // End of stream
|
||||
this.eof = true;
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
this.index += 1;
|
||||
if (this.previous == '\r') {
|
||||
this.line += 1;
|
||||
this.character = c == '\n' ? 0 : 1;
|
||||
} else if (c == '\n') {
|
||||
this.line += 1;
|
||||
this.character = 0;
|
||||
} else {
|
||||
this.character += 1;
|
||||
}
|
||||
this.previous = (char) c;
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Consume the next character, and check that it matches a specified
|
||||
* character.
|
||||
* @param c The character to match.
|
||||
* @return The character.
|
||||
* @throws JSONException if the character does not match.
|
||||
*/
|
||||
public char next(char c) throws JSONException {
|
||||
char n = this.next();
|
||||
if (n != c) {
|
||||
throw this.syntaxError("Expected '" + c + "' and instead saw '" +
|
||||
n + "'");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next n characters.
|
||||
*
|
||||
* @param n The number of characters to take.
|
||||
* @return A string of n characters.
|
||||
* @throws JSONException
|
||||
* Substring bounds error if there are not
|
||||
* n characters remaining in the source string.
|
||||
*/
|
||||
public String next(int n) throws JSONException {
|
||||
if (n == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
char[] chars = new char[n];
|
||||
int pos = 0;
|
||||
|
||||
while (pos < n) {
|
||||
chars[pos] = this.next();
|
||||
if (this.end()) {
|
||||
throw this.syntaxError("Substring bounds error");
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next char in the string, skipping whitespace.
|
||||
* @throws JSONException
|
||||
* @return A character, or 0 if there are no more characters.
|
||||
*/
|
||||
public char nextClean() throws JSONException {
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == 0 || c > ' ') {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the characters up to the next close quote character.
|
||||
* Backslash processing is done. The formal JSON format does not
|
||||
* allow strings in single quotes, but an implementation is allowed to
|
||||
* accept them.
|
||||
* @param quote The quoting character, either
|
||||
* <code>"</code> <small>(double quote)</small> or
|
||||
* <code>'</code> <small>(single quote)</small>.
|
||||
* @return A String.
|
||||
* @throws JSONException Unterminated string.
|
||||
*/
|
||||
public String nextString(char quote) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '\n':
|
||||
case '\r':
|
||||
throw this.syntaxError("Unterminated string");
|
||||
case '\\':
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 'u':
|
||||
sb.append((char)Integer.parseInt(this.next(4), 16));
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.append(c);
|
||||
break;
|
||||
default:
|
||||
throw this.syntaxError("Illegal escape.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c == quote) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the text up but not including the specified character or the
|
||||
* end of line, whichever comes first.
|
||||
* @param delimiter A delimiter character.
|
||||
* @return A string.
|
||||
*/
|
||||
public String nextTo(char delimiter) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the text up but not including one of the specified delimiter
|
||||
* characters or the end of line, whichever comes first.
|
||||
* @param delimiters A set of delimiter characters.
|
||||
* @return A string, trimmed.
|
||||
*/
|
||||
public String nextTo(String delimiters) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 ||
|
||||
c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next value. The value can be a Boolean, Double, Integer,
|
||||
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
|
||||
* @throws JSONException If syntax error.
|
||||
*
|
||||
* @return An object.
|
||||
*/
|
||||
public Object nextValue() throws JSONException {
|
||||
char c = this.nextClean();
|
||||
String string;
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\'':
|
||||
return this.nextString(c);
|
||||
case '{':
|
||||
this.back();
|
||||
return new JSONObject(this);
|
||||
case '[':
|
||||
this.back();
|
||||
return new JSONArray(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle unquoted text. This could be the values true, false, or
|
||||
* null, or it can be a number. An implementation (such as this one)
|
||||
* is allowed to also accept non-standard forms.
|
||||
*
|
||||
* Accumulate characters until we reach the end of the text or a
|
||||
* formatting character.
|
||||
*/
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||
sb.append(c);
|
||||
c = this.next();
|
||||
}
|
||||
this.back();
|
||||
|
||||
string = sb.toString().trim();
|
||||
if ("".equals(string)) {
|
||||
throw this.syntaxError("Missing value");
|
||||
}
|
||||
return JSONObject.stringToValue(string);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Skip characters until the next character is the requested character.
|
||||
* If the requested character is not found, no characters are skipped.
|
||||
* @param to A character to skip to.
|
||||
* @return The requested character, or zero if the requested character
|
||||
* is not found.
|
||||
*/
|
||||
public char skipTo(char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
long startIndex = this.index;
|
||||
long startCharacter = this.character;
|
||||
long startLine = this.line;
|
||||
this.reader.mark(1000000);
|
||||
do {
|
||||
c = this.next();
|
||||
if (c == 0) {
|
||||
this.reader.reset();
|
||||
this.index = startIndex;
|
||||
this.character = startCharacter;
|
||||
this.line = startLine;
|
||||
return c;
|
||||
}
|
||||
} while (c != to);
|
||||
} catch (IOException exc) {
|
||||
throw new JSONException(exc);
|
||||
}
|
||||
|
||||
this.back();
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a JSONException to signal a syntax error.
|
||||
*
|
||||
* @param message The error message.
|
||||
* @return A JSONException object, suitable for throwing
|
||||
*/
|
||||
public JSONException syntaxError(String message) {
|
||||
return new JSONException(message + this.toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a printable string of this JSONTokener.
|
||||
*
|
||||
* @return " at {index} [character {character} line {line}]"
|
||||
*/
|
||||
public String toString() {
|
||||
return " at " + this.index + " [character " + this.character + " line " +
|
||||
this.line + "]";
|
||||
}
|
||||
}
|
||||
271
Common/src/com/common/util/json/JSONWriter.java
Normal file
271
Common/src/com/common/util/json/JSONWriter.java
Normal file
@@ -0,0 +1,271 @@
|
||||
package com.common.util.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/*
|
||||
Copyright (c) 2006 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* A JSONWriter instance provides a <code>value</code> method for appending
|
||||
* values to the
|
||||
* text, and a <code>key</code>
|
||||
* method for adding keys before values in objects. There are <code>array</code>
|
||||
* and <code>endArray</code> methods that make and bound array values, and
|
||||
* <code>object</code> and <code>endObject</code> methods which make and bound
|
||||
* object values. All of these methods return the JSONWriter instance,
|
||||
* permitting a cascade style. For example, <pre>
|
||||
* new JSONWriter(myWriter)
|
||||
* .object()
|
||||
* .key("JSON")
|
||||
* .value("Hello, World!")
|
||||
* .endObject();</pre> which writes <pre>
|
||||
* {"JSON":"Hello, World!"}</pre>
|
||||
* <p>
|
||||
* The first method called must be <code>array</code> or <code>object</code>.
|
||||
* There are no methods for adding commas or colons. JSONWriter adds them for
|
||||
* you. Objects and arrays can be nested up to 20 levels deep.
|
||||
* <p>
|
||||
* This can sometimes be easier than using a JSONObject to build a string.
|
||||
* @author JSON.org
|
||||
* @version 2011-11-24
|
||||
*/
|
||||
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 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 stack top index. A value of 0 indicates that the stack is empty. */
|
||||
private int top;
|
||||
/** The writer that will receive the output. */
|
||||
protected Writer writer;
|
||||
/**
|
||||
* Make a fresh JSONWriter. It can be used to build one JSON text.
|
||||
*/
|
||||
public JSONWriter(Writer 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.write(',');
|
||||
}
|
||||
this.writer.write(string);
|
||||
}
|
||||
catch(IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
if(this.mode == 'o') {
|
||||
this.mode = 'k';
|
||||
}
|
||||
this.comma = true;
|
||||
return this;
|
||||
}
|
||||
throw new JSONException("Value out of sequence.");
|
||||
}
|
||||
/**
|
||||
* Begin appending a new array. All values until the balancing <code>endArray</code> will be appended to this array. The <code>endArray</code> 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 something.
|
||||
* @param mode Mode
|
||||
* @param c Closing character
|
||||
* @return this
|
||||
* @throws JSONException If unbalanced.
|
||||
*/
|
||||
private JSONWriter end(char mode, char c) throws JSONException {
|
||||
if(this.mode != mode) {
|
||||
throw new JSONException(mode == 'a' ? "Misplaced endArray." : "Misplaced endObject.");
|
||||
}
|
||||
this.pop(mode);
|
||||
try {
|
||||
this.writer.write(c);
|
||||
}
|
||||
catch(IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
this.comma = true;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* End an array. This method most be called to balance calls to <code>array</code>.
|
||||
* @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 <code>object</code>.
|
||||
* @return this
|
||||
* @throws JSONException If incorrectly nested.
|
||||
*/
|
||||
public JSONWriter endObject() throws JSONException {
|
||||
return this.end('k', '}');
|
||||
}
|
||||
/**
|
||||
* 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 {
|
||||
this.stack[this.top - 1].putOnce(string, Boolean.TRUE);
|
||||
if(this.comma) {
|
||||
this.writer.write(',');
|
||||
}
|
||||
this.writer.write(JSONObject.quote(string));
|
||||
this.writer.write(':');
|
||||
this.comma = false;
|
||||
this.mode = 'o';
|
||||
return this;
|
||||
}
|
||||
catch(IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
throw new JSONException("Misplaced key.");
|
||||
}
|
||||
/**
|
||||
* Begin appending a new object. All keys and values until the balancing <code>endObject</code> will be appended to this object. The <code>endObject</code> 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';
|
||||
}
|
||||
/**
|
||||
* Push an array or object scope.
|
||||
* @param c 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 <code>true</code> or the value <code>false</code>.
|
||||
* @param b A boolean.
|
||||
* @return this
|
||||
* @throws JSONException
|
||||
*/
|
||||
public JSONWriter value(boolean b) throws JSONException {
|
||||
return this.append(b ? "true" : "false");
|
||||
}
|
||||
/**
|
||||
* 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(new Double(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(JSONObject.valueToString(object));
|
||||
}
|
||||
}
|
||||
642
Common/src/com/common/util/json/JsonBuilder.java
Normal file
642
Common/src/com/common/util/json/JsonBuilder.java
Normal file
@@ -0,0 +1,642 @@
|
||||
package com.common.util.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.debug.Debug;
|
||||
import com.common.exception.InvalidArgumentException;
|
||||
import com.common.util.ICollection;
|
||||
import com.common.util.IHashMap;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.common.util.LiteHashSet;
|
||||
import com.common.util.LiteList;
|
||||
import com.common.util.Map.Iterator;
|
||||
import com.common.metadata.*;
|
||||
|
||||
/**
|
||||
* Reads and writes JSON formatted data.
|
||||
* Uses hashmaps and lists for the java side data structure.
|
||||
*/
|
||||
public class JsonBuilder {
|
||||
/**
|
||||
* The context class used to build JSON output for a graph of objects.
|
||||
*/
|
||||
private static class JsonContext implements IJsonContext {
|
||||
private String indent = null;
|
||||
private int indentCount = 0;
|
||||
private StringWriter writer = null;
|
||||
private LiteHashSet included = null;
|
||||
private MetadataContainer metadataContainer = null;
|
||||
|
||||
public JsonContext(String indent, MetadataContainer metadataContainer, StringWriter writer) {
|
||||
this.indent = indent == null ? "" : indent;
|
||||
this.metadataContainer = metadataContainer;
|
||||
this.writer = writer;
|
||||
}//JsonContext()//
|
||||
public MetadataContainer getMetadataContainer() {
|
||||
return metadataContainer;
|
||||
}//getMetadataContainer()//
|
||||
public void writeNewLine() {
|
||||
if(indent != null) {
|
||||
writer.write('\n');
|
||||
|
||||
for(int indentCounter = 0; indentCounter < indentCount; indentCounter++) {
|
||||
writer.write(indent);
|
||||
}//for//
|
||||
}//if//
|
||||
else {
|
||||
writer.write(' ');
|
||||
}//else//
|
||||
}//writeNewLine()//
|
||||
public LiteHashSet getIncluded() {
|
||||
return included;
|
||||
}//getIncluded()//
|
||||
public StringWriter getWriter() {
|
||||
return writer;
|
||||
}//getWriter()//
|
||||
public boolean isCompact() {
|
||||
return indent == null;
|
||||
}//isCompact()//
|
||||
public void toJson(Object value) throws IOException {
|
||||
indentCount++;
|
||||
|
||||
//Write the value.//
|
||||
if(value == null || value.equals(null)) {
|
||||
writer.write("null");
|
||||
}//if//
|
||||
else if(value instanceof IJsonAware) {
|
||||
((IJsonAware) value).toJson(this);
|
||||
}//else if//
|
||||
else if(value instanceof ICollection) {
|
||||
toJson((ICollection) value);
|
||||
}//else if//
|
||||
else if(value instanceof IHashMap) {
|
||||
toJson((IHashMap) value);
|
||||
}//else if//
|
||||
else if(value instanceof java.util.Map) {
|
||||
toJson((java.util.Map) value);
|
||||
}//else if//
|
||||
else if(value instanceof java.util.Collection) {
|
||||
toJson((java.util.Collection) value);
|
||||
}//else if//
|
||||
else if(value.getClass().isArray()) {
|
||||
toJson((Array) value);
|
||||
}//else if//
|
||||
else if(value instanceof Number) {
|
||||
if(value instanceof Double && (((Double) value).isInfinite() || ((Double) value).isNaN()) || value instanceof Float && (((Float) value).isInfinite() || ((Float) value).isNaN())) {
|
||||
//TODO: Can we convert the number perhaps? Show a max value or something?
|
||||
throw new RuntimeException("JSON does not allow non-finite numbers.");
|
||||
}//if//
|
||||
|
||||
String s = value.toString().toLowerCase();
|
||||
|
||||
//Remove trailing zeros and the decimal if possible.//
|
||||
if(s.indexOf('.') > 0 && s.indexOf('e') < 0) {
|
||||
int sIndex = s.length() - 1;
|
||||
|
||||
while(s.charAt(sIndex) == '0') {
|
||||
sIndex--;
|
||||
}//while//
|
||||
|
||||
if(s.charAt(sIndex) == '.') {
|
||||
s = s.substring(0, sIndex);
|
||||
}//if//
|
||||
else {
|
||||
s = s.substring(0, sIndex + 1);
|
||||
}//else//
|
||||
}//if//
|
||||
|
||||
writer.write(s);
|
||||
}//else if//
|
||||
else if(value instanceof Boolean) {
|
||||
writer.write(value.toString());
|
||||
}//else if//
|
||||
else if(value instanceof String) {
|
||||
jsonQuote((String) value, writer);
|
||||
}//else if//
|
||||
else {
|
||||
jsonQuote(value.toString(), writer);
|
||||
}//else if//
|
||||
|
||||
indentCount--;
|
||||
}//toJson()//
|
||||
/**
|
||||
* Writes an array of values to the JSON stream. This method is intended for internal framework use and is subject to change.
|
||||
* @param value The collection.
|
||||
*/
|
||||
private void toJson(IHashMap value) throws IOException {
|
||||
writer.write("{");
|
||||
|
||||
if(value != null) {
|
||||
boolean isFirst = true;
|
||||
|
||||
for(IIterator iterator = value.keyIterator(); iterator.hasNext(); ) {
|
||||
Object key = iterator.next();
|
||||
Object next = value.get(key);
|
||||
|
||||
if(!isFirst) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
isFirst = false;
|
||||
}//else//
|
||||
|
||||
if(key != null && !(key instanceof String)) {
|
||||
key = key.toString();
|
||||
}//if//
|
||||
|
||||
//Just roll over null's by converting them to strings. Better than an error IMO.
|
||||
if(key == null) {
|
||||
key = "null";
|
||||
}//if//
|
||||
|
||||
toJson(key);
|
||||
writer.write(':');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
|
||||
toJson(next);
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
writer.write("}");
|
||||
}//toJson()//
|
||||
/**
|
||||
* Writes an array of values to the JSON stream. This method is intended for internal framework use and is subject to change.
|
||||
* @param value The collection.
|
||||
*/
|
||||
private void toJson(ICollection value) throws IOException {
|
||||
writer.write("[");
|
||||
|
||||
if(value != null) {
|
||||
boolean isFirst = true;
|
||||
|
||||
for(IIterator iterator = value.iterator(); iterator.hasNext(); ) {
|
||||
Object next = iterator.next();
|
||||
|
||||
if(!isFirst) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
isFirst = false;
|
||||
}//else//
|
||||
|
||||
toJson(next);
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
writer.write("]");
|
||||
}//toJson()//
|
||||
/**
|
||||
* Writes an array of values to the JSON stream. This method is intended for internal framework use and is subject to change.
|
||||
* @param value The collection.
|
||||
*/
|
||||
private void toJson(java.util.Map value) throws IOException {
|
||||
writer.write("{");
|
||||
|
||||
if(value != null) {
|
||||
boolean isFirst = true;
|
||||
|
||||
for(java.util.Iterator iterator = value.keySet().iterator(); iterator.hasNext(); ) {
|
||||
Object key = iterator.next();
|
||||
Object next = value.get(key);
|
||||
|
||||
if(!isFirst) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
isFirst = false;
|
||||
}//else//
|
||||
|
||||
if(key != null && !(key instanceof String)) {
|
||||
key = key.toString();
|
||||
}//if//
|
||||
|
||||
//Just roll over null's by converting them to strings. Better than an error IMO.
|
||||
if(key == null) {
|
||||
key = "null";
|
||||
}//if//
|
||||
|
||||
toJson(key);
|
||||
writer.write(':');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
|
||||
toJson(next);
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
writer.write("}");
|
||||
}//toJson()//
|
||||
/**
|
||||
* Writes an array of values to the JSON stream. This method is intended for internal framework use and is subject to change.
|
||||
* @param value The collection.
|
||||
*/
|
||||
private void toJson(java.util.Collection value) throws IOException {
|
||||
writer.write("[");
|
||||
|
||||
if(value != null) {
|
||||
boolean isFirst = true;
|
||||
|
||||
for(java.util.Iterator iterator = value.iterator(); iterator.hasNext(); ) {
|
||||
Object next = iterator.next();
|
||||
|
||||
if(!isFirst) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
isFirst = false;
|
||||
}//else//
|
||||
|
||||
toJson(next);
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
writer.write("]");
|
||||
}//toJson()//
|
||||
/**
|
||||
* Writes an array of values to the JSON stream. This method is intended for internal framework use and is subject to change.
|
||||
* @param value The array.
|
||||
*/
|
||||
private void toJson(Array value) throws IOException {
|
||||
writer.write("[");
|
||||
|
||||
if(value != null) {
|
||||
int length = Array.getLength(value);
|
||||
Class valueType = value.getClass().getComponentType();
|
||||
|
||||
if(valueType.isPrimitive()) {
|
||||
switch(valueType.getName().charAt(0)) {
|
||||
case 'Z':
|
||||
for(int index = 0; index < length; index++) {
|
||||
if(index > 0) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
writer.write(Array.getBoolean(value, index) ? "true" : "false");
|
||||
}//for//
|
||||
break;
|
||||
case 'C':
|
||||
for(int index = 0; index < length; index++) {
|
||||
if(index > 0) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
//TODO: This could be optimized.
|
||||
jsonQuote("" + Array.getChar(value, index), writer);
|
||||
}//for//
|
||||
break;
|
||||
case 'B':
|
||||
for(int index = 0; index < length; index++) {
|
||||
if(index > 0) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
writer.write(Byte.toString(Array.getByte(value, index)));
|
||||
}//for//
|
||||
break;
|
||||
case 'S':
|
||||
for(int index = 0; index < length; index++) {
|
||||
if(index > 0) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
writer.write(Short.toString(Array.getShort(value, index)));
|
||||
}//for//
|
||||
break;
|
||||
case 'I':
|
||||
for(int index = 0; index < length; index++) {
|
||||
if(index > 0) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
writer.write(Integer.toString(Array.getInt(value, index)));
|
||||
}//for//
|
||||
break;
|
||||
case 'J':
|
||||
for(int index = 0; index < length; index++) {
|
||||
if(index > 0) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
writer.write(Long.toString(Array.getLong(value, index)));
|
||||
}//for//
|
||||
break;
|
||||
case 'F':
|
||||
for(int index = 0; index < length; index++) {
|
||||
if(index > 0) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
writer.write(Float.toString(Array.getFloat(value, index)));
|
||||
}//for//
|
||||
break;
|
||||
case 'D':
|
||||
for(int index = 0; index < length; index++) {
|
||||
if(index > 0) {
|
||||
writer.write(',');
|
||||
|
||||
if(indent != null) {
|
||||
writer.write(' ');
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
writer.write(Double.toString(Array.getDouble(value, index)));
|
||||
}//for//
|
||||
break;
|
||||
}//switch//
|
||||
}//if//
|
||||
else {
|
||||
for(int index = 0; index < length; index++) {
|
||||
Object next = Array.get(value, index);
|
||||
|
||||
toJson(next);
|
||||
}//for//
|
||||
}//else//
|
||||
}//if//
|
||||
|
||||
writer.write("]");
|
||||
}//toJson()//
|
||||
public void jsonQuote(String string, Writer writer) throws IOException {
|
||||
if(string == null || string.length() == 0) {
|
||||
writer.write("\"\"");
|
||||
}//if//
|
||||
else {
|
||||
char previousChar;
|
||||
char currentChar = 0;
|
||||
String hex;
|
||||
int len = string.length();
|
||||
|
||||
writer.write('"');
|
||||
|
||||
for(int index = 0; index < len; index += 1) {
|
||||
previousChar = currentChar;
|
||||
currentChar = string.charAt(index);
|
||||
|
||||
switch(currentChar) {
|
||||
case '\\':
|
||||
case '"': {
|
||||
writer.write('\\');
|
||||
writer.write(currentChar);
|
||||
break;
|
||||
}//case//
|
||||
case '/': {
|
||||
if(previousChar == '<') {
|
||||
writer.write('\\');
|
||||
}//if//
|
||||
|
||||
writer.write(currentChar);
|
||||
break;
|
||||
}//case//
|
||||
case '\b':
|
||||
writer.write("\\b");
|
||||
break;
|
||||
case '\t':
|
||||
writer.write("\\t");
|
||||
break;
|
||||
case '\n':
|
||||
writer.write("\\n");
|
||||
break;
|
||||
case '\f':
|
||||
writer.write("\\f");
|
||||
break;
|
||||
case '\r':
|
||||
writer.write("\\r");
|
||||
break;
|
||||
default: {
|
||||
if((currentChar < ' ') ||
|
||||
(currentChar >= '\u0080' && currentChar < '\u00a0') ||
|
||||
(currentChar >= '\u2000' && currentChar < '\u2100')) {
|
||||
|
||||
hex = "000" + Integer.toHexString(currentChar);
|
||||
writer.write("\\u" + hex.substring(hex.length() - 4));
|
||||
}//if//
|
||||
else {
|
||||
writer.write(currentChar);
|
||||
}//else//
|
||||
}//default//
|
||||
}//switch//
|
||||
}//for//
|
||||
|
||||
writer.write('"');
|
||||
}//else//
|
||||
}//jsonQuote()//
|
||||
}//JsonContext//
|
||||
private JsonBuilder() {
|
||||
}//JsonBuilder()//
|
||||
private static LiteHashMap readObject(JSONObject object) throws JSONException {
|
||||
String[] keys = JSONObject.getNames(object);
|
||||
LiteHashMap result = new LiteHashMap(keys.length > 10 ? keys.length : 10);
|
||||
|
||||
for(int index = 0; index < keys.length; index++) {
|
||||
Object value = object.get(keys[index]);
|
||||
|
||||
if(value instanceof JSONObject) {
|
||||
value = readObject((JSONObject) value);
|
||||
}//if//
|
||||
else if(value instanceof JSONArray) {
|
||||
value = readArray((JSONArray) value);
|
||||
}//else if//
|
||||
else if(value == JSONObject.NULL) {
|
||||
value = null;
|
||||
}//else if//
|
||||
|
||||
result.put(keys[index], value);
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
}//readObject()//
|
||||
private static LiteList readArray(JSONArray array) throws JSONException {
|
||||
LiteList result = new LiteList(array.length() > 10 ? array.length() : 10);
|
||||
|
||||
for(int index = 0; index < array.length(); index++) {
|
||||
Object value = array.get(index);
|
||||
|
||||
if(value instanceof JSONObject) {
|
||||
value = readObject((JSONObject) value);
|
||||
}//if//
|
||||
else if(value instanceof JSONArray) {
|
||||
value = readArray((JSONArray) value);
|
||||
}//else if//
|
||||
else if(value == JSONObject.NULL) {
|
||||
value = null;
|
||||
}//else if//
|
||||
|
||||
result.add(value);
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
}//readArray()//
|
||||
/**
|
||||
* Converts the JSON string into a collection of LiteHashMap and LiteList instances.
|
||||
* Note that we currently cannot inflate the original objects containing the data (such as Entity and specialized Collections).
|
||||
* @param json The JSON string.
|
||||
* @return The map for the JSON object.
|
||||
*/
|
||||
public static LiteHashMap readJson(String json) {
|
||||
try {
|
||||
JSONTokener tokener = new JSONTokener(json);
|
||||
//TODO: It may be an array starting the JSON, not an object.
|
||||
JSONObject object = new JSONObject(tokener);
|
||||
|
||||
return readObject(object);
|
||||
}//try//
|
||||
catch(JSONException e) {
|
||||
Debug.log(e);
|
||||
throw new InvalidArgumentException("Invalid JSON string.");
|
||||
}//catch//
|
||||
}//readJson()//
|
||||
private static JSONObject writeObject(LiteHashMap object) throws JSONException {
|
||||
JSONObject result = new JSONObject();
|
||||
|
||||
for(IIterator iterator = object.keyIterator(); iterator.hasNext(); ) {
|
||||
String key = (String) iterator.next();
|
||||
Object value = object.get(key);
|
||||
|
||||
if(value instanceof LiteHashMap) {
|
||||
value = writeObject((LiteHashMap) value);
|
||||
}//if//
|
||||
else if(value instanceof LiteList) {
|
||||
value = writeArray((LiteList) value);
|
||||
}//else if//
|
||||
else if(value == null) {
|
||||
value = JSONObject.NULL;
|
||||
}//else if//
|
||||
|
||||
result.put(key, value);
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
}//writeObject()//
|
||||
private static JSONArray writeArray(LiteList array) throws JSONException {
|
||||
JSONArray result = new JSONArray();
|
||||
|
||||
for(int index = 0; index < array.getSize(); index++) {
|
||||
Object value = array.get(index);
|
||||
|
||||
if(value instanceof LiteHashMap) {
|
||||
value = writeObject((LiteHashMap) value);
|
||||
}//if//
|
||||
else if(value instanceof LiteList) {
|
||||
value = writeArray((LiteList) value);
|
||||
}//else if//
|
||||
else if(value == null) {
|
||||
value = JSONObject.NULL;
|
||||
}//else if//
|
||||
|
||||
result.put(value);
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
}//writeArray()//
|
||||
/**
|
||||
* Converts the value into a compact JSON string.
|
||||
* Includes all attributes that are not cyclical.
|
||||
* @param value The value to be converted (it will delve into collections and examine Entity objects to build the JSON string).
|
||||
* @return The JSON formatted string for the tree of objects.
|
||||
*/
|
||||
public static String writeJson(Object value) {
|
||||
return writeJson(value, null, null);
|
||||
}//writeJson()//
|
||||
/**
|
||||
* Converts the value into a JSON string.
|
||||
* Includes all attributes that are not cyclical.
|
||||
* @param indent The indent string to be used, or null if spacing should be minimized.
|
||||
* @return The JSON formatted string for the tree of objects.
|
||||
*/
|
||||
public static String writeJson(Object value, String indent) {
|
||||
return writeJson(value, null, indent);
|
||||
}//toJson()//
|
||||
/**
|
||||
* Converts the value into a compact JSON string.
|
||||
* Includes all attributes that are not cyclical.
|
||||
* @param metadataContainer The container containing metadata on what to include in the JSON.
|
||||
* @return The JSON formatted string for the tree of objects.
|
||||
*/
|
||||
public static String writeJson(Object value, MetadataContainer metadataContainer) {
|
||||
return writeJson(value, metadataContainer, null);
|
||||
}//toJson()//
|
||||
/**
|
||||
* Converts the value into a JSON string.
|
||||
* Includes all attributes that are not cyclical.
|
||||
* @param metadataContainer The container containing metadata on what to include in the JSON.
|
||||
* @param indent The indent string to be used, or null if spacing should be minimized.
|
||||
* @return The JSON formatted string for the tree of objects.
|
||||
*/
|
||||
public static String writeJson(Object value, MetadataContainer metadataContainer, String indent) {
|
||||
String result = null;
|
||||
|
||||
if(value != null) {
|
||||
try {
|
||||
JsonContext context = new JsonContext(indent, metadataContainer, new StringWriter(10000));
|
||||
context.toJson(value);
|
||||
result = context.getWriter().toString();
|
||||
}//try//
|
||||
catch(IOException e) {
|
||||
//Shouldn't ever occur.//
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//writeJson()//
|
||||
}//JsonBuilder//
|
||||
Reference in New Issue
Block a user