package jp.co.corecorp.s3pdf;

import java.text.NumberFormat;
import java.util.Stack;

class JsonException extends Exception {
	private static final long serialVersionUID = 4650952794211831135L;
}
class JsonHierarchException extends JsonException {
	private static final long serialVersionUID = 7219221077859708012L;
}
class JsonEmptyObjectException extends JsonException {
	private static final long serialVersionUID = 7219223093656718113L;
}
class JsonEmptyArrayException extends JsonException {
	private static final long serialVersionUID = 7219223097656718115L;
}

class JsonBuilder {
	enum State {
		DATASPACE,
		OBJECT,
		ARRAY,
	}
	class NodeInfo {
		public State st;
		public int nData;
		public NodeInfo(State v) {
			st 		= v;
			nData	= 0;
		}
	}
	Stack<NodeInfo> st;
	NodeInfo pc = null;
	StringBuffer buff;
	JsonBuilder() {
		pc = new NodeInfo(State.DATASPACE);
		st = new Stack<NodeInfo>();
		buff = new StringBuffer(512);
	}
	private void closeDataspace() throws JsonHierarchException {
		if( pc.st != State.DATASPACE ) 
			throw new JsonHierarchException();
		if( !st.isEmpty() )
			pc = st.pop();
		else
			pc = null;
	}
	void beginObject() throws JsonHierarchException {
		if( pc == null || pc.st != State.DATASPACE )
			throw new JsonHierarchException();
		if( pc.nData != 0 ) 
			buff.append(",");
		pc.nData++;
		st.push(pc);
		buff.append("{");
		pc	= new NodeInfo(State.OBJECT);
	}
	void addObjectProp(String name) throws JsonHierarchException {
		if( pc == null || pc.st != State.OBJECT )
			throw new JsonHierarchException();
		if( pc.nData != 0 ) 
			buff.append(",");
		pc.nData++;
		st.push(pc);
		buff.append("\"");
		buff.append(name);
		buff.append("\":");
		pc	= new NodeInfo(State.DATASPACE);
	}
	void endObject() throws JsonHierarchException, JsonEmptyObjectException {
		if( pc == null || pc.st != State.OBJECT ) 
			throw new JsonHierarchException();
		if( pc.nData == 0 )
			throw new JsonEmptyObjectException();
		if( st.isEmpty() )
			throw new JsonHierarchException();
		buff.append("}");
		pc = st.pop();
		closeDataspace();
	}
	void beginArray() throws JsonHierarchException {
		if( pc == null || pc.st != State.DATASPACE )
			throw new JsonHierarchException();
		if( pc.nData != 0 )
			buff.append(",");
		pc.nData++;
		st.push(pc);
		buff.append("[");
		pc	= new NodeInfo(State.ARRAY);
	}
	void addArrayContent() throws JsonHierarchException {
		if( pc == null || pc.st != State.ARRAY )
			throw new JsonHierarchException();
		if( pc.nData != 0 )
			buff.append(",");
		pc.nData++;
		st.push(pc);
		pc	= new NodeInfo(State.DATASPACE);
	}
	void endArray() throws JsonHierarchException, JsonEmptyObjectException {
		if( pc == null || pc.st != State.ARRAY )
			throw new JsonHierarchException();
		if( pc.nData == 0 )
			throw new JsonEmptyObjectException();
		buff.append("]");
		if( st.isEmpty() )
			throw new JsonHierarchException();
		pc = st.pop();
		closeDataspace();
	}
	void valueNull() throws JsonHierarchException  {
		if( pc == null || pc.st != State.DATASPACE )
			throw new JsonHierarchException();
		buff.append("null");
		closeDataspace();
	}
	void value(boolean val) throws JsonHierarchException  {
		if( pc == null || pc.st != State.DATASPACE )
			throw new JsonHierarchException();
		String v = val ? "true" : "false";
		buff.append(v);
		closeDataspace();
	}
	void value(int val) throws JsonHierarchException  {
		if( pc == null || pc.st != State.DATASPACE )
			throw new JsonHierarchException();
		String v = NumberFormat.getNumberInstance().format(val);
		buff.append(v);
		closeDataspace();
	}
	void value(double val) throws JsonHierarchException  {
		if( pc == null || pc.st != State.DATASPACE )
			throw new JsonHierarchException();
		String v = NumberFormat.getNumberInstance().format(val);
		buff.append('"');
		buff.append(v);
		buff.append('"');
		closeDataspace();
	}
	void value(String val) throws JsonHierarchException {
		if( pc == null || pc.st != State.DATASPACE )
			throw new JsonHierarchException();
		int escLen = escapeSize(val);
		int rawLen = val.length();
		buff.append('"');
		if( escLen == rawLen ) {
			buff.append(val);
		} else {
			StringBuffer e = new StringBuffer(escLen);
			escapeText(e, val);
			buff.append(e);
		}
		buff.append('"');
		closeDataspace();
	}
	String endBuild() throws JsonHierarchException {
		if( pc != null )
			throw new JsonHierarchException();
		if( !st.isEmpty() )
			throw new JsonHierarchException();
		return buff.toString();
	}

	int	escapeSize(String val) {
		return escapeText(null, val);
	}
	int escapeText(StringBuffer dest, String val) {
		int len=0;
		char v[] = val.toCharArray();
		int vl=val.length();
		int i;
		for(i=0; i<vl; i++) {
			char c=v[i];
			if( c == '"' || c == '\b' || c == '\r' || c == '\t' || c == '\n' || c == '\f' || c == '\\' ) {
				len++;
				if( dest != null ) {
					dest.append('\\');
				}
				len++;
				if( dest != null ) {
					switch(c) {
					case '"':	dest.append('"');	break;
					case '\b':	dest.append('b');	break;
					case '\r':	dest.append('r');	break;
					case '\t':	dest.append('t');	break;
					case '\n':	dest.append('n');	break;
					case '\f':	dest.append('f');	break;
					default:	dest.append('\\');	break;
					}
				}
			} else {
				len++;
				if( dest != null ) {
					dest.append(c);
				}
			}
		}
		return len;		
	}
}
