blob: ed74924d23cae5c7206cc101de7f5cdf5c5b7df5 [file] [log] [blame]
/*
* Copyright (c) 2017 CEA.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* CEA - initial API and implementation
*/
package org.eclipse.sensinact.gateway.util;
import org.eclipse.sensinact.gateway.util.json.JSONValidator;
import org.json.JSONArray;
import org.json.JSONObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* JSON Helpers
*
* @author <a href="mailto:christophe.munilla@cea.fr">Christophe Munilla</a>
*/
public class JSONUtils {
protected static final Logger LOGGER = Logger.getLogger(JSONUtils.class.getCanonicalName());
public static final int DEFAULT_MAX_DEEP = 3;
/**
* Returns the JSON formated string representation
* of the {@link Object} passed as parameter, according
* to the DEFAULT_MAX_DEEP constant value possible
* iterations through the object hierarchy
*
* @param object the {@link Object} to describe as a JSON
* formated string
* @return the JSON formated string representation
*/
public static String toJSONFormat(Object object) {
return JSONUtils.toJSONFormat(object, new JSONUtils.JSONWriterContext(DEFAULT_MAX_DEEP, object));
}
/**
* Returns the JSON formated string representation
* of the {@link Object} passed as parameter according
* to the number passed as parameter of possible
* iterations through the object hierarchy
*
* @param object the {@link Object} to describe as a JSON
* formated string
* @param deep number of possible iterations through the
* object hierarchy
* @return the JSON formated string representation
*/
public static String toJSONFormat(Object object, int deep) {
return JSONUtils.toJSONFormat(object, new JSONUtils.JSONWriterContext(deep, object));
}
/**
* Returns the JSON formated string representation
* of the {@link Object} passed as parameter
*
* @param object the {@link Object} to return a JSON
* formated string representation of
* @param context state of the process
* @return the JSON formated string representation
*/
public static String toJSONFormat(Object object, JSONUtils.JSONWriterContext context) {
if (object == null) {
return JSON_NULL;
}
if (JSONObject.class.isAssignableFrom(object.getClass())) {
return ((JSONObject) object).toString();
}
if (JSONArray.class.isAssignableFrom(object.getClass())) {
return ((JSONArray) object).toString();
}
if (object.getClass().isArray()) {
return JSONUtils.arrayToJSONFormat(object, context);
}
if (List.class.isAssignableFrom(object.getClass())) {
return toJSONFormat(((List) object).toArray(new Object[0]), context);
}
if (Map.class.isAssignableFrom(object.getClass())) {
Iterator<Map.Entry> iterator = ((Map) object).entrySet().iterator();
int index = 0;
StringBuilder builder = new StringBuilder();
builder.append(JSONUtils.OPEN_BRACE);
while (iterator.hasNext()) {
Map.Entry entry = iterator.next();
String key = toJSONFormat(entry.getKey());
if (key == null) {
continue;
}
builder.append(index > 0 ? JSONUtils.COMMA : JSONUtils.EMPTY);
if (!key.startsWith("\"")) {
builder.append(JSONUtils.QUOTE);
builder.append(key);
builder.append(JSONUtils.QUOTE);
} else {
builder.append(key);
}
builder.append(JSONUtils.COLON);
builder.append(toJSONFormat(entry.getValue()));
index++;
}
builder.append(JSONUtils.CLOSE_BRACE);
return builder.toString();
}
if (String.class == object.getClass() && new JSONValidator((String) object).valid()) {
return (String) object;
}
if (CastUtils.isPrimitive(object.getClass())) {
return JSONUtils.primitiveToJSONFormat(object);
} else if (object.getClass().isEnum()) {
return JSONUtils.primitiveToJSONFormat(((Enum) object).name());
} else {
try {
Method method = object.getClass().getDeclaredMethod("getJSON");
if (method.getReturnType() == String.class) {
return (String) method.invoke(object);
}
} catch (Exception e) {
LOGGER.log(Level.CONFIG, "Object to cast is not a JSONable instance");
}
}
if (context.tooDeep() || context.circularReference()) {
return new StringBuilder().append(QUOTE).append(object.toString()).append(QUOTE).toString();
}
StringBuilder builder = new StringBuilder();
builder.append(OPEN_BRACE);
for (Field field : object.getClass().getDeclaredFields()) {
if (field.isAccessible()) {
String fieldJSON = null;
try {
context.decrementAndGet();
fieldJSON = toJSONFormat(field.get(object), context);
} catch (Exception e) {
continue;
}
if (fieldJSON != null && fieldJSON.length() > 0) {
builder.append(QUOTE);
builder.append(field.getName());
builder.append(QUOTE);
builder.append(COLON);
builder.append(fieldJSON);
}
}
}
builder.append(CLOSE_BRACE);
return builder.toString();
}
/**
* Returns the JSON formated string representation
* of the {@link Array} passed as parameter
*
* @param array the {@link array} to represent as a JSON
* formated string
* @param context the state of the process
* @return the JSON formated string representation
*/
private static String arrayToJSONFormat(Object array, JSONWriterContext context) {
StringBuilder builder = new StringBuilder();
builder.append(OPEN_BRACKET);
int length = Array.getLength(array);
for (int i = 0; i < length; i++) {
context.decrementAndGet();
String json = JSONUtils.toJSONFormat(Array.get(array, i), context);
if (json != null && json.length() > 0) {
builder.append(builder.length() > 1 ? COMMA : EMPTY);
builder.append(json);
}
}
builder.append(CLOSE_BRACKET);
return builder.toString();
}
/**
* Returns the JSON formated string representation of
* the Primitive object passed as parameter
*
* @param primitive the Primitive object to return the JSON formated
* string representation of
* @return the JSON formated string representation of
* the Primitive object passed as parameter
*/
private static String primitiveToJSONFormat(Object primitive) {
StringBuilder builder = new StringBuilder();
Class<?> argClass = primitive.getClass();
if (CastUtils.isPrimitive(argClass)) {
if (String.class.equals(argClass) || Character.class.equals(argClass) || char.class.equals(argClass)) {
builder.append(QUOTE);
builder.append(primitive);
builder.append(QUOTE);
} else {
builder.append(String.valueOf(primitive));
}
} else {
return JSON_NULL;
}
return builder.toString();
}
/**
* JSON writing process context
* TODO : handle circular reference
*/
private static final class JSONWriterContext {
public final Object jsonable;
private int deep = 0;
JSONWriterContext(int deep, Object jsonable) {
this.jsonable = jsonable;
this.deep = deep;
}
public boolean circularReference() {
//TODO : implement
return false;
}
int decrementAndGet() {
return --deep;
}
boolean tooDeep() {
return deep < 0;
}
}
public static final String EMPTY = "";
//JSON-RPC GRAMMAR
public static final String JSON_RPC_NAME = "jsonrpc";
public static final String JSON_RPC_VERSION = "2.0";
public static final String JSON_RPC_ID = "id";
public static final String JSON_RPC_FUNCTION = "function";
public static final String JSON_RPC_PROCEDURE = "procedure";
public static final String JSON_RPC_METHOD = "method";
public static final String JSON_RPC_PARAMS = "params";
public static final String JSON_RPC_RESULT = "result";
public static final String JSON_RPC_ERROR = "error";
public static final String JSON_RPC_MESSAGE = "message";
public static final String JSON_RPC_CODE = "code";
//JSON KEY-WORDS
public static final String JSON_TRUE = "true";
public static final String JSON_FALSE = "false";
public static final String JSON_NULL = "null";
//JSON DELIMITERS
public static final char OPEN_BRACE = '{';
public static final char CLOSE_BRACE = '}';
public static final char OPEN_BRACKET = '[';
public static final char CLOSE_BRACKET = ']';
public static final char COMMA = ',';
public static final char QUOTE = '\"';
public static final char APOSTROPHE = '\'';
public static final char COLON = ':';
public static final char SEMICOLON = ';';
}