/**
* Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.linkedin.pinot.pql.parsers.utils;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.FieldInfo;
import com.alibaba.fastjson.util.TypeUtils;
public class JSONUtil {
public static class FastJSONObject extends JSONObject {
private com.alibaba.fastjson.JSONObject _inner;
public FastJSONObject() {
_inner = new com.alibaba.fastjson.JSONObject();
}
public FastJSONObject(String str) throws JSONException {
try {
_inner = (com.alibaba.fastjson.JSONObject) JSON.parse(str);
} catch (Exception e) {
throw new JSONException(e);
}
}
public FastJSONObject(Map value) {
if (value instanceof com.alibaba.fastjson.JSONObject) {
_inner = (com.alibaba.fastjson.JSONObject) value;
} else {
_inner = (com.alibaba.fastjson.JSONObject) toJSON(value);
}
}
public FastJSONObject(com.alibaba.fastjson.JSONObject inner) {
_inner = inner;
}
public com.alibaba.fastjson.JSONObject getInnerJSONObject() {
return _inner;
}
/**
* Accumulate values under a key. It is similar to the put method except
* that if there is already an object stored under the key then a
* JSONArray is stored under the key to hold all of the accumulated values.
* If there is already a JSONArray, then the new value is appended to it.
* In contrast, the put method replaces the previous value.
*
* If only one value is accumulated that is not a JSONArray, then the
* result will be the same as using put. But if multiple values are
* accumulated, then the result will be like append.
* @param key A key string.
* @param value An object to be accumulated under the key.
* @return this.
* @throws JSONException If the value is an invalid number
* or if the key is null.
*/
@Override
public JSONObject accumulate(String key, Object value) throws JSONException {
Object object = _inner.get(key);
if (object == null) {
com.alibaba.fastjson.JSONArray array = new com.alibaba.fastjson.JSONArray();
array.add(value);
_inner.put(key, array);
} else if (object instanceof com.alibaba.fastjson.JSONArray) {
((com.alibaba.fastjson.JSONArray) object).add(value);
} else {
com.alibaba.fastjson.JSONArray array = new com.alibaba.fastjson.JSONArray();
array.add(object);
array.add(value);
_inner.put(key, array);
}
return this;
}
/**
* Append values to the array under a key. If the key does not exist in the
* JSONObject, then the key is put in the JSONObject with its value being a
* JSONArray containing the value parameter. If the key was already
* associated with a JSONArray, then the value parameter is appended to it.
* @param key A key string.
* @param value An object to be accumulated under the key.
* @return this.
* @throws JSONException If the key is null or if the current value
* associated with the key is not a JSONArray.
*/
@Override
public JSONObject append(String key, Object value) throws JSONException {
Object object = _inner.get(key);
if (object == null) {
com.alibaba.fastjson.JSONArray array = new com.alibaba.fastjson.JSONArray();
array.add(value);
_inner.put(key, array);
} else if (object instanceof com.alibaba.fastjson.JSONArray) {
((com.alibaba.fastjson.JSONArray) object).add(value);
} else {
throw new JSONException("JSONObject[" + key + "] is not a JSONArray.");
}
return this;
}
/**
* Get the value object associated with a key.
*
* @param key A key string.
* @return The object associated with the key.
* @throws JSONException if the key is not found.
*/
@Override
public Object get(String key) throws JSONException {
Object object = this.opt(key);
if (object == null) {
throw new JSONException("JSONObject[" + key + "] not found.");
}
return object;
}
/**
* Get the JSONArray value associated with a key.
*
* @param key A key string.
* @return A JSONArray which is the value.
* @throws JSONException if the key is not found or
* if the value is not a JSONArray.
*/
@Override
public JSONArray getJSONArray(String key) throws JSONException {
Object object = this.get(key);
if (object instanceof com.alibaba.fastjson.JSONArray) {
return new FastJSONArray((com.alibaba.fastjson.JSONArray) object);
} else if (object instanceof Collection) {
return new FastJSONArray((com.alibaba.fastjson.JSONArray) toJSON(object));
} else if (object instanceof JSONArray) {
return (JSONArray) object;
}
throw new JSONException("JSONObject[" + quote(key) + "] is not a JSONArray.");
}
/**
* Get the JSONObject value associated with a key.
*
* @param key A key string.
* @return A JSONObject which is the value.
* @throws JSONException if the key is not found or
* if the value is not a JSONObject.
*/
@Override
public JSONObject getJSONObject(String key) throws JSONException {
Object object = this.get(key);
if (object instanceof com.alibaba.fastjson.JSONObject) {
return new FastJSONObject((com.alibaba.fastjson.JSONObject) object);
} else if (object instanceof Map) {
return new FastJSONObject((com.alibaba.fastjson.JSONObject) toJSON(object));
} else if (object instanceof JSONObject) {
return (JSONObject) object;
}
throw new JSONException("JSONObject[" + quote(key) + "] is not a JSONObject.");
}
/**
* Determine if the JSONObject contains a specific key.
* @param key A key string.
* @return true if the key exists in the JSONObject.
*/
@Override
public boolean has(String key) {
return _inner.containsKey(key);
}
/**
* Get an enumeration of the keys of the JSONObject.
*
* @return An iterator of the keys.
*/
@Override
public Iterator keys() {
return _inner.keySet().iterator();
}
@Override
public Iterator sortedKeys() {
return new TreeSet(_inner.keySet()).iterator();
}
/**
* Get the number of keys stored in the JSONObject.
*
* @return The number of keys in the JSONObject.
*/
@Override
public int length() {
return _inner.size();
}
/**
* Produce a JSONArray containing the names of the elements of this
* JSONObject.
* @return A JSONArray containing the key strings, or null if the JSONObject
* is empty.
*/
@Override
public JSONArray names() {
JSONArray ja = new FastJSONArray();
Iterator keys = this.keys();
while (keys.hasNext()) {
ja.put(keys.next());
}
return ja.length() == 0 ? null : ja;
}
/**
* Get an optional value associated with a key.
* @param key A key string.
* @return An object which is the value, or null if there is no value.
*/
@Override
public Object opt(String key) {
if (key == null) {
return null;
}
Object object = _inner.get(key);
if (object == null) {
return null;
} else if (object instanceof com.alibaba.fastjson.JSONObject) {
return new FastJSONObject((com.alibaba.fastjson.JSONObject) object);
} else if (object instanceof com.alibaba.fastjson.JSONArray) {
return new FastJSONArray((com.alibaba.fastjson.JSONArray) object);
} else if (object instanceof Map) {
return new FastJSONObject((com.alibaba.fastjson.JSONObject) toJSON(object));
} else if (object instanceof Collection) {
return new FastJSONArray((com.alibaba.fastjson.JSONArray) toJSON(object));
}
return object;
}
/**
* Get an optional JSONArray associated with a key.
* It returns null if there is no such key, or if its value is not a
* JSONArray.
*
* @param key A key string.
* @return A JSONArray which is the value.
*/
@Override
public JSONArray optJSONArray(String key) {
try {
return this.getJSONArray(key);
} catch (Exception e) {
return null;
}
}
/**
* Get an optional JSONObject associated with a key.
* It returns null if there is no such key, or if its value is not a
* JSONObject.
*
* @param key A key string.
* @return A JSONObject which is the value.
*/
@Override
public JSONObject optJSONObject(String key) {
try {
return this.getJSONObject(key);
} catch (Exception e) {
return null;
}
}
/**
* Get an optional string associated with a key.
* It returns the defaultValue if there is no such key.
*
* @param key A key string.
* @param defaultValue The default.
* @return A string which is the value.
*/
@Override
public String optString(String key, String defaultValue) {
Object object = this.opt(key);
if (object == null || NULL.equals(object)) {
return defaultValue;
}
return object.toString();
}
/**
* Put a key/value pair in the JSONObject, where the value will be a
* JSONArray which is produced from a Collection.
* @param key A key string.
* @param value A Collection value.
* @return this.
* @throws JSONException
*/
@Override
public JSONObject put(String key, Collection value) throws JSONException {
this.put(key, (Object) value);
return this;
}
/**
* Put a key/value pair in the JSONObject, where the value will be a
* JSONObject which is produced from a Map.
* @param key A key string.
* @param value A Map value.
* @return this.
* @throws JSONException
*/
@Override
public JSONObject put(String key, Map value) throws JSONException {
this.put(key, (Object) value);
return this;
}
/**
* Put a key/value pair in the JSONObject. If the value is null,
* then the key will be removed from the JSONObject if it is present.
* @param key A key string.
* @param value An object which is the value. It should be of one of these
* types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
* or the JSONObject.NULL object.
* @return this.
* @throws JSONException If the value is non-finite number
* or if the key is null.
*/
@Override
public JSONObject put(String key, Object value) throws JSONException {
if (key == null) {
throw new JSONException("Null key.");
}
if (value != null) {
if (value instanceof FastJSONObject) {
_inner.put(key, ((FastJSONObject) value).getInnerJSONObject());
} else if (value instanceof FastJSONArray) {
_inner.put(key, ((FastJSONArray) value).getInnerJSONArray());
} else {
if (value instanceof JSONObject || value instanceof JSONArray) {
throw new JSONException("the value is not fast version of JSONObjects: " + value);
}
_inner.put(key, toJSON(value));
}
} else {
this.remove(key);
}
return this;
}
/**
* Remove a name and its value, if present.
* @param key The name to be removed.
* @return The value that was associated with the name,
* or null if there was no value.
*/
@Override
public Object remove(String key) {
return _inner.remove(key);
}
/**
* Make a JSON text of this JSONObject. For compactness, no whitespace
* is added. If this would not result in a syntactically correct JSON text,
* then null will be returned instead.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @return a printable, displayable, portable, transmittable
* representation of the object, beginning
* with <code>{</code> <small>(left brace)</small> and ending
* with <code>}</code> <small>(right brace)</small>.
*/
@Override
public String toString() {
try {
return _inner.toString();
} catch (Exception e) {
return null;
}
}
/**
* Not a prettyprinted JSON text, just the same as toString().
*/
@Override
public String toString(int indentFactor) throws JSONException {
return _inner.toString();
}
}
public static class FastJSONArray extends JSONArray {
private com.alibaba.fastjson.JSONArray _inner;
/**
* Construct an empty JSONArray.
*/
public FastJSONArray() {
_inner = new com.alibaba.fastjson.JSONArray();
}
public FastJSONArray(String str) throws JSONException {
try {
_inner = (com.alibaba.fastjson.JSONArray) JSON.parse(str);
} catch (Exception e) {
throw new JSONException(e);
}
}
public FastJSONArray(Collection value) {
if (value instanceof com.alibaba.fastjson.JSONArray) {
_inner = (com.alibaba.fastjson.JSONArray) value;
} else {
_inner = (com.alibaba.fastjson.JSONArray) toJSON(value);
}
}
public FastJSONArray(com.alibaba.fastjson.JSONArray inner) {
_inner = inner;
}
public com.alibaba.fastjson.JSONArray getInnerJSONArray() {
return _inner;
}
/**
* 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.
*/
@Override
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 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
*/
@Override
public JSONArray getJSONArray(int index) throws JSONException {
Object object = this.get(index);
if (object instanceof com.alibaba.fastjson.JSONArray) {
return new FastJSONArray((com.alibaba.fastjson.JSONArray) object);
} else if (object instanceof Collection) {
return new FastJSONArray((com.alibaba.fastjson.JSONArray) toJSON(object));
} else 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
*/
@Override
public JSONObject getJSONObject(int index) throws JSONException {
Object object = this.get(index);
if (object instanceof com.alibaba.fastjson.JSONObject) {
return new FastJSONObject((com.alibaba.fastjson.JSONObject) object);
} else if (object instanceof Map) {
return new FastJSONObject((com.alibaba.fastjson.JSONObject) toJSON(object));
} else if (object instanceof JSONObject) {
return (JSONObject) object;
}
throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
}
/**
* Get the number of elements in the JSONArray, included nulls.
*
* @return The length (or size).
*/
@Override
public int length() {
return _inner.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.
*/
@Override
public Object opt(int index) {
if (index < 0 || index >= this.length()) {
return null;
}
Object object = _inner.get(index);
if (object == null) {
return null;
} else if (object instanceof com.alibaba.fastjson.JSONObject) {
return new FastJSONObject((com.alibaba.fastjson.JSONObject) object);
} else if (object instanceof com.alibaba.fastjson.JSONArray) {
return new FastJSONArray((com.alibaba.fastjson.JSONArray) object);
} else if (object instanceof Map) {
return new FastJSONObject((com.alibaba.fastjson.JSONObject) toJSON(object));
} else if (object instanceof Collection) {
return new FastJSONArray((com.alibaba.fastjson.JSONArray) toJSON(object));
}
return object;
}
/**
* 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.
*/
@Override
public JSONArray optJSONArray(int index) {
try {
return this.getJSONArray(index);
} catch (Exception e) {
return 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.
*/
@Override
public JSONObject optJSONObject(int index) {
try {
return this.getJSONObject(index);
} catch (Exception e) {
return null;
}
}
/**
* 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.
*/
@Override
public String optString(int index, String defaultValue) {
Object object = this.opt(index);
if (object == null || JSONObject.NULL.equals(object)) {
return defaultValue;
}
return object.toString();
}
/**
* 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.
*/
@Override
public JSONArray put(Collection value) {
this.put((Object) 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.
*/
@Override
public JSONArray put(Map value) {
this.put((Object) 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.
*/
@Override
public JSONArray put(Object value) {
if (value instanceof FastJSONObject) {
_inner.add(((FastJSONObject) value).getInnerJSONObject());
} else if (value instanceof FastJSONArray) {
_inner.add(((FastJSONArray) value).getInnerJSONArray());
} else {
if (value instanceof JSONObject || value instanceof JSONArray) {
throw new IllegalArgumentException("the value is not fast version of JSONObjects: " + value);
}
_inner.add(toJSON(value));
}
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.
*/
@Override
public JSONArray put(int index, Collection value) throws JSONException {
this.put(index, (Object) 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.
*/
@Override
public JSONArray put(int index, Map value) throws JSONException {
this.put(index, (Object) 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.
*/
@Override
public JSONArray put(int index, Object value) throws JSONException {
if (index < 0) {
throw new JSONException("JSONArray[" + index + "] not found.");
}
if (index < this.length()) {
if (value instanceof FastJSONObject) {
_inner.set(index, ((FastJSONObject) value).getInnerJSONObject());
} else if (value instanceof FastJSONArray) {
_inner.set(index, ((FastJSONArray) value).getInnerJSONArray());
} else {
if (value instanceof JSONObject || value instanceof JSONArray) {
throw new IllegalArgumentException("the value is not fast version of JSONObjects: " + value);
}
_inner.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);
_inner.remove(index);
return o;
}
/**
* 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.
*/
@Override
public String toString() {
try {
return _inner.toString();
} catch (Exception e) {
return null;
}
}
/**
* Not a prettyprinted JSON text, just the same as toString().
*/
@Override
public String toString(int indentFactor) throws JSONException {
return this.toString();
}
}
public static String normalize(String str) {
return str.replaceAll("[\\s\\\\]*\n[\\s\\\\]*", " ");
}
@SuppressWarnings("unchecked")
public static Object toJSON(Object javaObject) {
if (javaObject == null) {
return null;
}
if (javaObject instanceof JSON) {
return javaObject;
}
if (javaObject instanceof FastJSONObject) {
return ((FastJSONObject) javaObject).getInnerJSONObject();
}
if (javaObject instanceof FastJSONArray) {
return ((FastJSONArray) javaObject).getInnerJSONArray();
}
if (javaObject instanceof Map) {
Map<Object, Object> map = (Map<Object, Object>) javaObject;
com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject(map.size());
for (Map.Entry<Object, Object> entry : map.entrySet()) {
Object key = entry.getKey();
String jsonKey = TypeUtils.castToString(key);
Object jsonValue = toJSON(entry.getValue());
json.put(jsonKey, jsonValue);
}
return json;
}
if (javaObject instanceof Collection) {
Collection<Object> collection = (Collection<Object>) javaObject;
com.alibaba.fastjson.JSONArray array = new com.alibaba.fastjson.JSONArray(collection.size());
for (Object item : collection) {
Object jsonValue = toJSON(item);
array.add(jsonValue);
}
return array;
}
Class<?> clazz = javaObject.getClass();
if (clazz.isEnum()) {
return ((Enum<?>) javaObject).name();
}
if (clazz.isArray()) {
int len = Array.getLength(javaObject);
com.alibaba.fastjson.JSONArray array = new com.alibaba.fastjson.JSONArray(len);
for (int i = 0; i < len; ++i) {
Object item = Array.get(javaObject, i);
Object jsonValue = toJSON(item);
array.add(jsonValue);
}
return array;
}
if (ParserConfig.getGlobalInstance().isPrimitive(clazz)) {
return javaObject;
}
try {
List<FieldInfo> getters = TypeUtils.computeGetters(clazz, null);
com.alibaba.fastjson.JSONObject json = new com.alibaba.fastjson.JSONObject(getters.size());
for (FieldInfo field : getters) {
Object value = field.get(javaObject);
Object jsonValue = toJSON(value);
json.put(field.getName(), jsonValue);
}
return json;
} catch (Exception e) {
throw new com.alibaba.fastjson.JSONException("toJSON error", e);
}
}
public static boolean optBooleanValue(com.alibaba.fastjson.JSONObject json, String key)
throws com.alibaba.fastjson.JSONException {
return optBooleanValue(json, key, false);
}
public static boolean optBooleanValue(com.alibaba.fastjson.JSONObject json, String key, boolean defaultValue)
throws com.alibaba.fastjson.JSONException {
Boolean val = json.getBoolean(key);
if (val == null)
return defaultValue;
return val.booleanValue();
}
public static boolean optBooleanValue(com.alibaba.fastjson.JSONArray json, int index)
throws com.alibaba.fastjson.JSONException {
return optBooleanValue(json, index, false);
}
public static boolean optBooleanValue(com.alibaba.fastjson.JSONArray json, int index, boolean defaultValue)
throws com.alibaba.fastjson.JSONException {
Boolean val = json.getBoolean(index);
if (val == null)
return defaultValue;
return val.booleanValue();
}
public static com.alibaba.fastjson.JSONObject optJSONObject(com.alibaba.fastjson.JSONObject json, String key)
throws com.alibaba.fastjson.JSONException {
Object obj = json.get(key);
if (obj == null)
return null;
if (!(obj instanceof com.alibaba.fastjson.JSONObject)) {
try {
return (com.alibaba.fastjson.JSONObject) toJSON(obj);
} catch (Exception e) {
return null;
}
}
return (com.alibaba.fastjson.JSONObject) obj;
}
public static com.alibaba.fastjson.JSONObject optJSONObject(com.alibaba.fastjson.JSONArray json, int index)
throws com.alibaba.fastjson.JSONException {
Object obj = json.get(index);
if (obj == null)
return null;
if (!(obj instanceof com.alibaba.fastjson.JSONObject)) {
try {
return (com.alibaba.fastjson.JSONObject) toJSON(obj);
} catch (Exception e) {
return null;
}
}
return (com.alibaba.fastjson.JSONObject) obj;
}
public static com.alibaba.fastjson.JSONArray optJSONArray(com.alibaba.fastjson.JSONObject json, String key)
throws com.alibaba.fastjson.JSONException {
Object obj = json.get(key);
if (obj == null)
return null;
if (!(obj instanceof com.alibaba.fastjson.JSONArray)) {
try {
return (com.alibaba.fastjson.JSONArray) toJSON(obj);
} catch (Exception e) {
return null;
}
}
return (com.alibaba.fastjson.JSONArray) obj;
}
public static com.alibaba.fastjson.JSONArray optJSONArray(com.alibaba.fastjson.JSONArray json, int index)
throws com.alibaba.fastjson.JSONException {
Object obj = json.get(index);
if (obj == null)
return null;
if (!(obj instanceof com.alibaba.fastjson.JSONArray)) {
try {
return (com.alibaba.fastjson.JSONArray) toJSON(obj);
} catch (Exception e) {
return null;
}
}
return (com.alibaba.fastjson.JSONArray) obj;
}
}