package net.minidev.json;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import net.minidev.json.writer.JsonReaderI;
public class JSONNavi<T> {
private JsonReaderI<? super T> mapper;
private T root;
private Stack<Object> stack = new Stack<Object>();
private Stack<Object> path = new Stack<Object>();
private Object current;
private boolean failure = false;
private String failureMessage;
private boolean readonly = false;
private Object missingKey = null;
public static JSONNavi<JSONAwareEx> newInstance() {
return new JSONNavi<JSONAwareEx>(JSONValue.defaultReader.DEFAULT_ORDERED);
}
public static JSONNavi<JSONObject> newInstanceObject() {
JSONNavi<JSONObject> o = new JSONNavi<JSONObject>(JSONValue.defaultReader.getMapper(JSONObject.class));
o.object();
return o;
}
public static JSONNavi<JSONArray> newInstanceArray() {
JSONNavi<JSONArray> o = new JSONNavi<JSONArray>(JSONValue.defaultReader.getMapper(JSONArray.class));
o.array();
return o;
}
public JSONNavi(JsonReaderI<? super T> mapper) {
this.mapper = mapper;
}
@SuppressWarnings("unchecked")
public JSONNavi(String json) {
this.root = (T) JSONValue.parse(json);
this.current = this.root;
readonly = true;
}
public JSONNavi(String json, JsonReaderI<T> mapper) {
this.root = JSONValue.parse(json, mapper);
this.mapper = mapper;
this.current = this.root;
readonly = true;
}
public JSONNavi(String json, Class<T> mapTo) {
this.root = JSONValue.parse(json, mapTo);
this.mapper = JSONValue.defaultReader.getMapper(mapTo);
this.current = this.root;
readonly = true;
}
public JSONNavi<T> root() {
this.current = this.root;
this.stack.clear();
this.path.clear();
this.failure = false;
this.missingKey = null;
this.failureMessage = null;
return this;
}
public boolean hasFailure() {
return failure;
}
public Object getCurrentObject() {
return current;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Collection<String> getKeys() {
if (current instanceof Map)
return ((Map) current).keySet();
return null;
}
public int getSize() {
if (current == null)
return 0;
if (isArray())
return ((List<?>) current).size();
if (isObject())
return ((Map<?, ?>) current).size();
return 1;
}
public String getString(String key) {
String v = null;
if (!hasKey(key))
return v;
at(key);
v = asString();
up();
return v;
}
public int getInt(String key) {
int v = 0;
if (!hasKey(key))
return v;
at(key);
v = asInt();
up();
return v;
}
public Integer getInteger(String key) {
Integer v = null;
if (!hasKey(key))
return v;
at(key);
v = asIntegerObj();
up();
return v;
}
public double getDouble(String key) {
double v = 0;
if (!hasKey(key))
return v;
at(key);
v = asDouble();
up();
return v;
}
public boolean hasKey(String key) {
if (!isObject())
return false;
return o(current).containsKey(key);
}
public JSONNavi<?> at(String key) {
if (failure)
return this;
if (!isObject())
object();
if (!(current instanceof Map))
return failure("current node is not an Object", key);
if (!o(current).containsKey(key)) {
if (readonly)
return failure("current Object have no key named " + key, key);
stack.add(current);
path.add(key);
current = null;
missingKey = key;
return this;
}
Object next = o(current).get(key);
stack.add(current);
path.add(key);
current = next;
return this;
}
public Object get(String key) {
if (failure)
return this;
if (!isObject())
object();
if (!(current instanceof Map))
return failure("current node is not an Object", key);
return o(current).get(key);
}
public Object get(int index) {
if (failure)
return this;
if (!isArray())
array();
if (!(current instanceof List))
return failure("current node is not an List", index);
return a(current).get(index);
}
public JSONNavi<T> set(String key, String value) {
object();
if (failure)
return this;
o(current).put(key, value);
return this;
}
public JSONNavi<T> set(String key, Number value) {
object();
if (failure)
return this;
o(current).put(key, value);
return this;
}
public JSONNavi<T> set(String key, long value) {
return set(key, Long.valueOf(value));
}
public JSONNavi<T> set(String key, int value) {
return set(key, Integer.valueOf(value));
}
public JSONNavi<T> set(String key, double value) {
return set(key, Double.valueOf(value));
}
public JSONNavi<T> set(String key, float value) {
return set(key, Float.valueOf(value));
}
public JSONNavi<T> add(Object... values) {
array();
if (failure)
return this;
List<Object> list = a(current);
for (Object o : values)
list.add(o);
return this;
}
public String asString() {
if (current == null)
return null;
if (current instanceof String)
return (String) current;
return current.toString();
}
public double asDouble() {
if (current instanceof Number)
return ((Number) current).doubleValue();
return Double.NaN;
}
public Double asDoubleObj() {
if (current == null)
return null;
if (current instanceof Number) {
if (current instanceof Double)
return (Double) current;
return Double.valueOf(((Number) current).doubleValue());
}
return Double.NaN;
}
public double asFloat() {
if (current instanceof Number)
return ((Number) current).floatValue();
return Float.NaN;
}
public Float asFloatObj() {
if (current == null)
return null;
if (current instanceof Number) {
if (current instanceof Float)
return (Float) current;
return Float.valueOf(((Number) current).floatValue());
}
return Float.NaN;
}
public int asInt() {
if (current instanceof Number)
return ((Number) current).intValue();
return 0;
}
public Integer asIntegerObj() {
if (current == null)
return null;
if (current instanceof Number) {
if (current instanceof Integer)
return (Integer) current;
if (current instanceof Long) {
Long l = (Long) current;
if (l.longValue() == l.intValue()) {
return Integer.valueOf(l.intValue());
}
}
return null;
}
return null;
}
public long asLong() {
if (current instanceof Number)
return ((Number) current).longValue();
return 0L;
}
public Long asLongObj() {
if (current == null)
return null;
if (current instanceof Number) {
if (current instanceof Long)
return (Long) current;
if (current instanceof Integer)
return Long.valueOf(((Number) current).longValue());
return null;
}
return null;
}
public boolean asBoolean() {
if (current instanceof Boolean)
return ((Boolean) current).booleanValue();
return false;
}
public Boolean asBooleanObj() {
if (current == null)
return null;
if (current instanceof Boolean)
return (Boolean) current;
return null;
}
@SuppressWarnings("unchecked")
public JSONNavi<T> object() {
if (failure)
return this;
if (current == null && readonly)
failure("Can not create Object child in readonly", null);
if (current != null) {
if (isObject())
return this;
if (isArray())
failure("can not use Object feature on Array.", null);
failure("Can not use current possition as Object", null);
} else {
current = mapper.createObject();
}
if (root == null)
root = (T) current;
else
store();
return this;
}
@SuppressWarnings("unchecked")
public JSONNavi<T> array() {
if (failure)
return this;
if (current == null && readonly)
failure("Can not create Array child in readonly", null);
if (current != null) {
if (isArray())
return this;
if (isObject())
failure("can not use Object feature on Array.", null);
failure("Can not use current possition as Object", null);
} else {
current = mapper.createArray();
}
if (root == null)
root = (T) current;
else
store();
return this;
}
public JSONNavi<T> set(Number num) {
if (failure)
return this;
current = num;
store();
return this;
}
public JSONNavi<T> set(Boolean bool) {
if (failure)
return this;
current = bool;
store();
return this;
}
public JSONNavi<T> set(String text) {
if (failure)
return this;
current = text;
store();
return this;
}
public T getRoot() {
return root;
}
private void store() {
Object parent = stack.peek();
if (isObject(parent))
o(parent).put((String) missingKey, current);
else if (isArray(parent)) {
int index = ((Number) missingKey).intValue();
List<Object> lst = a(parent);
while (lst.size() <= index)
lst.add(null);
lst.set(index, current);
}
}
public boolean isArray() {
return isArray(current);
}
public boolean isObject() {
return isObject(current);
}
private boolean isArray(Object obj) {
if (obj == null)
return false;
return (obj instanceof List);
}
private boolean isObject(Object obj) {
if (obj == null)
return false;
return (obj instanceof Map);
}
@SuppressWarnings("unchecked")
private List<Object> a(Object obj) {
return (List<Object>) obj;
}
@SuppressWarnings("unchecked")
private Map<String, Object> o(Object obj) {
return (Map<String, Object>) obj;
}
public JSONNavi<?> at(int index) {
if (failure)
return this;
if (!(current instanceof List))
return failure("current node is not an Array", index);
@SuppressWarnings("unchecked")
List<Object> lst = ((List<Object>) current);
if (index < 0) {
index = lst.size() + index;
if (index < 0)
index = 0;
}
if (index >= lst.size())
if (readonly)
return failure("Out of bound exception for index", index);
else {
stack.add(current);
path.add(index);
current = null;
missingKey = index;
return this;
}
Object next = lst.get(index);
stack.add(current);
path.add(index);
current = next;
return this;
}
public JSONNavi<?> atNext() {
if (failure)
return this;
if (!(current instanceof List))
return failure("current node is not an Array", null);
@SuppressWarnings("unchecked")
List<Object> lst = ((List<Object>) current);
return at(lst.size());
}
public JSONNavi<?> up(int level) {
while (level-- > 0) {
if (stack.size() > 0) {
current = stack.pop();
path.pop();
} else
break;
}
return this;
}
public JSONNavi<?> up() {
if (stack.size() > 0) {
current = stack.pop();
path.pop();
}
return this;
}
private final static JSONStyle ERROR_COMPRESS = new JSONStyle(JSONStyle.FLAG_PROTECT_4WEB);
public String toString() {
if (failure)
return JSONValue.toJSONString(failureMessage, ERROR_COMPRESS);
return JSONValue.toJSONString(root);
}
public String toString(JSONStyle compression) {
if (failure)
return JSONValue.toJSONString(failureMessage, compression);
return JSONValue.toJSONString(root, compression);
}
private JSONNavi<?> failure(String err, Object jPathPostfix) {
failure = true;
StringBuilder sb = new StringBuilder();
sb.append("Error: ");
sb.append(err);
sb.append(" at ");
sb.append(getJPath());
if (jPathPostfix != null)
if (jPathPostfix instanceof Integer)
sb.append('[').append(jPathPostfix).append(']');
else
sb.append('/').append(jPathPostfix);
this.failureMessage = sb.toString();
return this;
}
public String getJPath() {
StringBuilder sb = new StringBuilder();
for (Object o : path) {
if (o instanceof String)
sb.append('/').append(o.toString());
else
sb.append('[').append(o.toString()).append(']');
}
return sb.toString();
}
}