Java tutorial
/* * This file is part of Dorado 7.x (http://dorado7.bsdn.org). * * Copyright (c) 2002-2012 BSTEK Corp. All rights reserved. * * This file is dual-licensed under the AGPLv3 (http://www.gnu.org/licenses/agpl-3.0.html) * and BSDN commercial (http://www.bsdn.org/licenses) licenses. * * If you are unsure which license is appropriate for your use, please contact the sales department * at http://www.bstek.com/contact. */ package com.bstek.dorado.view.output; import java.io.IOException; import java.io.Writer; import java.lang.reflect.Array; import org.apache.commons.lang.StringEscapeUtils; import com.bstek.dorado.data.IllegalJsonFormatException; /** * JSON * * @author Benny Bao (mailto:benny.bao@bstek.com) * @since Oct 6, 2008 */ public class JsonBuilder { private static final int MAXDEPTH = 100; private Writer writer; private boolean reuseable; private boolean comma; private boolean prettyFormat; private char state; private char stack[]; private char escapeStack[]; private String escapeKeyStack[]; private int top; private int escapeTop; private int leadingTab; public JsonBuilder(Writer w) { comma = false; state = 'i'; stack = new char[MAXDEPTH]; escapeStack = new char[MAXDEPTH]; escapeKeyStack = new String[MAXDEPTH]; top = 0; escapeTop = 0; writer = w; } public JsonBuilder(Writer w, boolean reuseable) { this(w); this.reuseable = reuseable; } /** * Writer */ public Writer getWriter() { return writer; } public boolean isPrettyFormat() { return prettyFormat; } public void setPrettyFormat(boolean prettyFormat) { this.prettyFormat = prettyFormat; } public int getLeadingTab() { return leadingTab; } public void setLeadingTab(int leadingTab) { this.leadingTab = leadingTab; } private void write(char c) throws IOException { writer.write(c); } private void write(String s) throws IOException { writer.write(s); } private JsonBuilder append(String s, boolean skipNull, boolean quote) { if (state == 'o' && s == null) { throw new IllegalJsonFormatException("Null pointer"); } if (state == 'i' || state == 'o' || state == 'k' || state == 'a' || state == 'v') { try { if (state == 'a') { if (comma) { write(','); } outputLeadingTab(); } if (s != null) { if (quote) { write('\"'); write(s); write('\"'); } else { write(s); } } else if (!skipNull) { write("null"); } } catch (IOException e) { throw new IllegalJsonFormatException(e); } comma = (top > 0); return this; } throw new IllegalJsonFormatException("Value out of sequence."); } private JsonBuilder end(char m, char c) { if (state != m) { String msg; switch (m) { case 'o': msg = "Misplaced endObject."; break; case 'a': msg = "Misplaced endArray."; break; default: msg = "Misplaced endValue."; break; } throw new IllegalJsonFormatException(msg); } pop(m); try { if (c != 0) { outputLeadingTab(); write(c); } } catch (IOException e) { throw new IllegalJsonFormatException(e); } comma = (top > 0); return this; } private void outputLeadingTab() throws IOException { if (prettyFormat) { write('\n'); for (int i = 0; i < leadingTab; i++) { write('\t'); } } } private void push(char c) { if (top >= MAXDEPTH) { throw new IllegalJsonFormatException("Nesting too deep."); } stack[top] = c; state = c; top++; } private void pop(char c) { if (top <= 0 || stack[top - 1] != c) { throw new IllegalJsonFormatException("Nesting error."); } top--; state = top == 0 ? (reuseable ? 'i' : 'd') : stack[top - 1]; } private void pushEscapeablePart(char c, String s) { escapeStack[escapeTop] = c; escapeKeyStack[escapeTop] = s; escapeTop++; } private boolean popEscapeablePart(char c) { boolean escaped = false; if (escapeTop > 0) { char m = escapeStack[escapeTop - 1]; if (m != c) { String msg; switch (c) { case 'o': msg = "Misplaced endObject."; break; case 'a': msg = "Misplaced endArray."; break; default: msg = "Misplaced endValue."; break; } throw new IllegalJsonFormatException(msg); } else { escapeTop--; if (escapeTop > 0) { escapeKeyStack[escapeTop] = null; } escaped = true; } } return escaped; } private void writeEscapeableParts() { int num = escapeTop; escapeTop = 0; for (int i = 0; i < num; i++) { char c = escapeStack[i]; switch (c) { case 'a': array(); break; case 'o': object(); break; case 'k': key(escapeKeyStack[i]); escapeKeyStack[i] = null; break; } } } public boolean inEscapeableParts() { return escapeTop > 0; } /** * */ public JsonBuilder array() { writeEscapeableParts(); if (state == 'i' || state == 'k' || state == 'a' || state == 'v') { append("[", true, false); push('a'); comma = false; if (prettyFormat) { leadingTab++; } return this; } throw new IllegalJsonFormatException("Misplaced array."); } public JsonBuilder escapeableArray() { pushEscapeablePart('a', null); return this; } /** * ? */ public JsonBuilder endArray() { if (!popEscapeablePart('a')) { if (prettyFormat) { leadingTab--; } end('a', ']'); } return this; } /** * */ public JsonBuilder object() { writeEscapeableParts(); if (state == 'i' || state == 'k' || state == 'a' || state == 'v') { append("{", true, false); push('o'); if (prettyFormat) { leadingTab++; } comma = false; return this; } throw new IllegalJsonFormatException("Misplaced object."); } public JsonBuilder escapeableObject() { pushEscapeablePart('o', null); return this; } /** * ? */ public JsonBuilder endObject() { if (!popEscapeablePart('o')) { if (prettyFormat) { leadingTab--; } end('o', '}'); } return this; } /** * JSON */ public JsonBuilder key(String s) { if (s == null) { throw new IllegalJsonFormatException("Null key."); } writeEscapeableParts(); if (state == 'o') { try { if (comma) { write(','); } outputLeadingTab(); // write(JsonUtils.quote(s)); write('"'); write(s); // for Performance 2012/05/15 write('"'); write(':'); comma = false; state = 'k'; return this; } catch (IOException e) { throw new IllegalJsonFormatException(e); } } throw new IllegalJsonFormatException("Misplaced key."); } public JsonBuilder escapeableKey(String s) { pushEscapeablePart('k', s); return this; } public JsonBuilder endKey() { popEscapeablePart('k'); return this; } /** * */ public JsonBuilder value(boolean b) { return outputValue(b ? "true" : "false", false); } /** * ? */ public JsonBuilder value(double d) { return outputValue(Double.toString(d), false); } /** * */ public JsonBuilder value(long l) { return outputValue(Long.toString(l), false); } /** * */ public JsonBuilder value(Object o) { if (o == null) { outputValue("null", false); } else if (o instanceof Number || o instanceof Boolean) { if (o instanceof Float && (Float.isNaN((Float) o))) { outputValue("undefined", false); } if (o instanceof Double && (Double.isNaN((Double) o))) { outputValue("undefined", false); } else { outputValue(o.toString(), false); } } else if (o.getClass().isArray()) { array(); int len = Array.getLength(o); for (int i = 0; i < len; i++) { value(Array.get(o, i)); } endArray(); } else { String s = o.toString(); if (s.length() == 0) { outputValue("\"\"", false); } else { outputValue(StringEscapeUtils.escapeJavaScript(s), true); } } return this; } private JsonBuilder outputValue(String s, boolean quote) { writeEscapeableParts(); if (state == 'k' || state == 'a' || state == 'i') { append(s, true, quote); if (state == 'k') { state = 'o'; } return this; } throw new IllegalJsonFormatException("Misplaced value."); } /** * ?? */ public JsonBuilder beginValue() { writeEscapeableParts(); if (state == 'k' || state == 'a') { append(null, true, false); push('v'); return this; } throw new IllegalJsonFormatException("Misplaced beginValue."); } /** * ??? */ public JsonBuilder endValue() { return end('v', '\0'); } /** * @return the state */ public char getState() { return state; } }