/*
* Copyright 2013 eXo Platform SAS
*
* 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 juzu.impl.common;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* A simple JSON object useful for marshalling and unmarshalling data.
*
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
*/
public final class JSON implements Serializable {
/** . */
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final Set<Class<?>> simpleTypes = new HashSet<Class<?>>(Arrays.asList(
Integer.class,
Long.class,
Boolean.class
));
public static JSON json() {
return new JSON();
}
/** . */
private final TreeMap<String, Object> entries = new TreeMap<String, Object>();
public Set<String> names() {
return entries.keySet();
}
public Object get(String name) {
return entries.get(name);
}
public String getString(String name) {
return (String)entries.get(name);
}
public List<?> getList(String name) {
return (List<?>)entries.get(name);
}
public Integer getInteger(String name) {
return (Integer)entries.get(name);
}
public <E> List<? extends E> getList(String name, Class<E> elementType) {
List<?> entry = (List<?>)entries.get(name);
int len = entry != null ? entry.size() : 0;
for (int i = 0;i < len;i++) {
Object obj = entry.get(i);
if (!elementType.isInstance(obj)) {
throw new ClassCastException("Cannot cast " + obj + "with class " + obj.getClass().getName() + " to class " + elementType.getName());
}
}
return (List<E>)entry;
}
public <E> E[] getArray(String name, Class<E> elementType) {
List<?> entry = (List<?>)entries.get(name);
if (entry == null) {
return (E[])Array.newInstance(elementType, 0);
}
else {
int len = entry.size();
Object array = Array.newInstance(elementType, len);
for (int i = 0;i < len;i++) {
Object obj = entry.get(i);
if (!elementType.isInstance(obj)) {
throw new ClassCastException("Cannot cast " + obj + "with class " + obj.getClass().getName() + " to class " + elementType.getName());
}
Array.set(array, i, obj);
}
return (E[])array;
}
}
public Boolean getBoolean(String name) {
return (Boolean)entries.get(name);
}
public JSON getJSON(String name) {
return (JSON)entries.get(name);
}
public boolean contains(String name) {
return entries.containsKey(name);
}
public JSON clear() {
entries.clear();
return this;
}
public int getSize() {
return entries.size();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof JSON) {
JSON that = (JSON)obj;
return entries.equals(that.entries);
}
return false;
}
public <E> JSON merge(JSON json) {
return merge(json.entries);
}
public <E> JSON merge(Map<String, E> map) {
for (Map.Entry<String, E> entry : map.entrySet()) {
set(entry.getKey(), entry.getValue());
}
return this;
}
public <E> JSON map(String name, Map<String, E> map) {
return set(name, map);
}
public JSON list(String name) {
return set(name, EMPTY_STRING_ARRAY);
}
public <E> JSON list(String name, E... elements) {
return set(name, elements);
}
public <E> JSON map(String name, Iterable<E> elements) {
return set(name, elements);
}
public JSON set(String name, Object o) {
entries.put(name, unwrap(o));
return this;
}
public TreeMap<String, Object> build() {
TreeMap<String, Object> ret = new TreeMap<String, Object>(entries);
for (java.util.Map.Entry<String, Object> entry : ret.entrySet()) {
Object value = entry.getValue();
if (value instanceof JSON) {
JSON json = (JSON)value;
entry.setValue(json.build());
}
}
return ret;
}
private static Object unwrap(Object o) {
if (o == null || o instanceof JSON) {
// Ok
}
else {
Class<?> type = o.getClass();
//
if (simpleTypes.contains(type)) {
// Ok
}
else {
if (o instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>)o;
JSON json = new JSON();
for (Map.Entry<?, ?> entry : map.entrySet()) {
String name = entry.getKey().toString();
Object value = unwrap(entry.getValue());
json.entries.put(name, value);
}
o = json;
}
else if (o instanceof Iterable<?>) {
ArrayList<Object> r = new ArrayList<Object>();
for (Object element : (Iterable<?>)o) {
Object unwrapped = unwrap(element);
r.add(unwrapped);
}
o = r;
}
else if (o.getClass().isArray()) {
int length = Array.getLength(o);
ArrayList<Object> r = new ArrayList<Object>(length);
for (int i = 0;i < length;i++) {
Object component = Array.get(o, i);
Object unwrapped = unwrap(component);
r.add(unwrapped);
}
o = r;
}
else {
Object json = null;
try {
Method toJSON = type.getMethod("toJSON");
if (toJSON.getReturnType() != Void.TYPE) {
try {
json = toJSON.invoke(o);
}
catch (IllegalAccessException e) {
throw new AssertionError(e);
}
catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
}
else if (t instanceof Error) {
throw (Error)t;
}
else {
throw new UndeclaredThrowableException(t);
}
}
}
}
catch (NoSuchMethodException ignore) {
}
//
if (json != null) {
o = unwrap(json);
}
else {
o = o.toString();
}
}
}
}
//
return o;
}
public <A extends Appendable> A toString(A appendable) throws IOException {
return toString(this, appendable);
}
public <A extends Appendable> A toString(A appendable, int indent) throws IOException {
return toString(this, appendable, indent);
}
public String toString() {
try {
return toString(new StringBuilder()).toString();
}
catch (IOException e) {
throw new UndeclaredThrowableException(e);
}
}
public static <A extends Appendable> A toString(Object o, A appendable) throws IOException {
return toString(o, appendable, 0);
}
public static <A extends Appendable> A toString(Object o, A appendable, int indent) throws IOException {
return toString(o, appendable, 0, indent);
}
private static <A extends Appendable> A toString(Object o, A appendable, final int margin, final int indent) throws IOException {
if (o == null) {
appendable.append("null");
}
else if (o instanceof JSON) {
JSON m = (JSON)o;
appendable.append('{');
for (Iterator<? extends Map.Entry<?, ?>> iterator = m.entries.entrySet().iterator();iterator.hasNext();) {
Map.Entry<?, ?> entry = iterator.next();
if (indent > 0) {
appendable.append('\n');
pad(appendable, margin + indent);
}
appendable.append('"');
appendable.append(entry.getKey().toString());
appendable.append("\":");
toString(entry.getValue(), appendable, margin + indent, indent);
if (iterator.hasNext()) {
appendable.append(',');
}
else if (indent > 0) {
appendable.append('\n');
pad(appendable, margin);
}
}
appendable.append('}');
}
else if (o instanceof List) {
appendable.append('[');
List<?> list = (List<?>)o;
for (Iterator<?> i = list.iterator();i.hasNext();) {
Object e = i.next();
toString(e, appendable, margin + indent, indent);
if (i.hasNext()) {
appendable.append(',');
}
}
appendable.append("]");
}
else if (o instanceof Boolean || o instanceof Number) {
appendable.append(o.toString());
}
else {
appendable.append('"');
CharSequence s = o instanceof CharSequence ? (CharSequence)o : o.toString();
for (int i = 0, len = s.length();i < len;i++) {
char c = s.charAt(i);
switch (c) {
case '"':
appendable.append("\\\"");
break;
case '\n':
appendable.append("\\n");
break;
case '\r':
appendable.append("\\r");
break;
case '\b':
appendable.append("\\b");
break;
case '\f':
appendable.append("\\f");
break;
case '\t':
appendable.append("\\t");
break;
default:
appendable.append(c);
}
}
appendable.append('"');
}
//
return appendable;
}
private static void pad(Appendable appendable, int size) throws IOException {
while (size-- > 0) {
appendable.append(' ');
}
}
public static Object parse(String json) {
JSONParser parser = new JSONParser(new StringReader(json));
try {
return parser.parse();
}
catch (ParseException e) {
throw new AssertionError(e);
}
// try {
// Bindings bindings = new SimpleBindings();
// String eval = "var tmp = (" + json + ");var o = new java.util.concurrent.atomic.AtomicReference(tmp.toJava());";
// engine.eval(eval, bindings);
// AtomicReference ret = (AtomicReference)bindings.get("o");
// return ret.get();
// }
// catch (ScriptException e) {
// throw new AssertionError(e);
// }
}
}