package org.nutz.json.impl;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;
import org.nutz.json.Json;
import org.nutz.json.JsonFormat;
import org.nutz.json.JsonRender;
import org.nutz.json.entity.JsonEntity;
import org.nutz.json.entity.JsonEntityField;
import org.nutz.lang.FailToGetValueException;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.Strings;
/**
* @author zozoh(zozohtnt@gmail.com)
* @author wendal(wendal1985@gmail.com)
*
*/
@SuppressWarnings({"rawtypes"})
public class JsonRenderImpl implements JsonRender {
private static String NL = "\n";
private JsonFormat format;
private Writer writer;
private Set<Object> memo = new HashSet<Object>();
public JsonFormat getFormat() {
return format;
}
public void setFormat(JsonFormat format) {
this.format = format;
}
public Writer getWriter() {
return writer;
}
public void setWriter(Writer writer) {
this.writer = writer;
}
public void render(Object obj) throws IOException {
if (null == obj) {
writer.write("null");
} else if (obj instanceof JsonRender) {
((JsonRender) obj).render(null);
} else if (obj instanceof Class) {
string2Json(((Class<?>) obj).getName());
} else if (obj instanceof Mirror) {
string2Json(((Mirror<?>) obj).getType().getName());
} else {
Mirror mr = Mirror.me(obj.getClass());
// 枚举
if (mr.isEnum()) {
string2Json(((Enum) obj).name());
}
// 数字,布尔等
else if (mr.isNumber() || mr.isBoolean()) {
writer.append(obj.toString());
}
// 字符串
else if (mr.isStringLike() || mr.isChar()) {
string2Json(obj.toString());
}
// 日期时间
else if (mr.isDateTimeLike()) {
string2Json(format.getCastors().castToString(obj));
}
// 其他
else {
// Map
if (obj instanceof Map) {
map2Json((Map) obj);
}
// 集合
else if (obj instanceof Collection) {
coll2Json((Collection) obj);
}
// 数组
else if (obj.getClass().isArray()) {
array2Json(obj);
}
// 普通 Java 对象
else {
memo.add(obj);
pojo2Json(obj);
memo.remove(obj);
}
}
}
}
public JsonRenderImpl() {}
public JsonRenderImpl(Writer writer, JsonFormat format) {
this.format = format;
this.writer = writer;
}
private static boolean isCompact(JsonRenderImpl render) {
return render.format.isCompact();
}
private static final Pattern p = Pattern.compile("^[a-z_A-Z$]+[a-zA-Z_0-9$]*$");
private void appendName(String name) throws IOException {
if (format.isQuoteName() || !p.matcher(name).find())
string2Json(name);
else
writer.append(name);
}
private void appendPairBegin() throws IOException {
if (!isCompact(this))
writer.append(NL).append(Strings.dup(format.getIndentBy(),
format.getIndent()));
}
private void appendPairSep() throws IOException {
writer.append(!isCompact(this) ? " :" : ":");
}
protected void appendPair(boolean needPairEnd, String name, Object value)
throws IOException {
appendPairBegin();
appendName(name);
appendPairSep();
render(value);
if (needPairEnd) {
appendPairEnd();
}
}
private boolean isIgnore(String name, Object value) {
if (null == value && format.isIgnoreNull())
return true;
return format.ignore(name);
}
private void appendPairEnd() throws IOException {
writer.append(',');
}
private void appendBraceBegin() throws IOException {
writer.append('{');
}
private void appendBraceEnd() throws IOException {
if (!isCompact(this))
writer.append(NL).append(Strings.dup(format.getIndentBy(),
format.getIndent()));
writer.append('}');
}
static class Pair {
public Pair(String name, Object value) {
this.name = name;
this.value = value;
}
String name;
Object value;
}
@SuppressWarnings({"unchecked"})
private void map2Json(Map map) throws IOException {
if (null == map)
return;
appendBraceBegin();
increaseFormatIndent();
ArrayList<Pair> list = new ArrayList<Pair>(map.size());
Set<Entry<?, ?>> entrySet = map.entrySet();
for (Entry entry : entrySet) {
String name = null == entry.getKey() ? "null" : entry.getKey()
.toString();
Object value = entry.getValue();
if (!this.isIgnore(name, value))
list.add(new Pair(name, value));
}
writeItem(list);
}
@SuppressWarnings("unchecked")
private void pojo2Json(Object obj) throws IOException {
if (null == obj)
return;
/*
* Default
*/
Class<?> type = obj.getClass();
JsonEntity jen = Json.getEntity(Mirror.me(type));
Method toJsonMethod = jen.getToJsonMethod();
if (toJsonMethod != null) {
try {
if (toJsonMethod.getParameterTypes().length == 0) {
writer.append(String.valueOf(toJsonMethod.invoke(obj)));
} else {
writer.append(String.valueOf(toJsonMethod.invoke(obj,
format)));
}
return;
}
catch (Exception e) {
throw Lang.wrapThrow(e);
}
}
List<JsonEntityField> fields = jen.getFields();
appendBraceBegin();
increaseFormatIndent();
ArrayList<Pair> list = new ArrayList<Pair>(fields.size());
for (JsonEntityField jef : fields) {
if (jef.isIgnore())
continue;
String name = jef.getName();
try {
Object value = jef.getValue(obj);
// 判断是否应该被忽略
if (!this.isIgnore(name, value)) {
// 以前曾经输出过 ...
if (null != value) {
// zozoh: 循环引用的默认行为,应该为 null,以便和其他语言交换数据
Mirror mirror = Mirror.me(value);
if (mirror.isPojo()) {
if (memo.contains(value))
value = null;
}
}
// 如果是强制输出为字符串的
if (null != value && jef.isForceString()) {
// 数组
if (value.getClass().isArray()) {
String[] ss = new String[Array.getLength(value)];
for (int i = 0; i < ss.length; i++) {
ss[i] = Array.get(value, i).toString();
}
value = ss;
}
// 集合
else if (value instanceof Collection) {
Collection col = (Collection) Mirror.me(value)
.born();
for (Object ele : (Collection) value) {
col.add(ele.toString());
}
value = col;
}
// 其他统统变字符串
else {
value = value.toString();
}
}
// 加入输出列表 ...
list.add(new Pair(name, value));
}
}
catch (FailToGetValueException e) {}
}
writeItem(list);
}
private void writeItem(List<Pair> list) throws IOException {
Iterator<Pair> it = list.iterator();
while (it.hasNext()) {
Pair p = it.next();
appendPair(it.hasNext(), p.name, p.value);
}
decreaseFormatIndent();
appendBraceEnd();
}
private void decreaseFormatIndent() {
if (!isCompact(this))
format.decreaseIndent();
}
private void increaseFormatIndent() {
if (!isCompact(this))
format.increaseIndent();
}
private void string2Json(String s) throws IOException {
if (null == s)
writer.append("null");
else {
char[] cs = s.toCharArray();
writer.append(format.getSeparator());
for (char c : cs) {
switch (c) {
case '"':
writer.append("\\\"");
break;
case '\n':
writer.append("\\n");
break;
case '\t':
case 0x0B: // \v
writer.append("\\t");
break;
case '\r':
writer.append("\\r");
break;
case '\f':
writer.append("\\f");
break;
case '\b':
writer.append("\\b");
break;
case '\\':
writer.append("\\\\");
break;
default:
if (c >= 256 && format.isAutoUnicode())
writer.append("\\u").append(Strings.fillHex(c, 4)
.toUpperCase());
else
writer.append(c);
}
}
writer.append(format.getSeparator());
}
}
private void array2Json(Object obj) throws IOException {
writer.append('[');
int len = Array.getLength(obj) - 1;
if (len > -1) {
int i;
for (i = 0; i < len; i++) {
render(Array.get(obj, i));
appendPairEnd();
writer.append(' ');
}
render(Array.get(obj, i));
}
writer.append(']');
}
private void coll2Json(Collection iterable) throws IOException {
writer.append('[');
for (Iterator<?> it = iterable.iterator(); it.hasNext();) {
render(it.next());
if (it.hasNext()) {
appendPairEnd();
writer.append(' ');
} else
break;
}
writer.append(']');
}
}