blob: 9d90bf093795be1110dbcc5b0007a62720a5e4b8 [file] [log] [blame]
/*
* Copyright 1999-2011 Alibaba Group.
*
* 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 com.alibaba.dubbo.common.json;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import com.alibaba.dubbo.common.utils.Stack;
/**
* JSON Writer.
*
* w.objectBegin().objectItem("name").valueString("qianlei").objectEnd() = {name:"qianlei"}.
*
* @author qian.lei
*/
public class JSONWriter
{
private static final byte UNKNOWN = 0, ARRAY = 1, OBJECT = 2, OBJECT_VALUE = 3;
private static class State
{
private byte type;
private int itemCount = 0;
State(byte t){ type = t; }
}
private Writer mWriter;
private State mState = new State(UNKNOWN);
private Stack<State> mStack = new Stack<State>();
public JSONWriter(Writer writer)
{
mWriter = writer;
}
public JSONWriter(OutputStream is, String charset) throws UnsupportedEncodingException
{
mWriter = new OutputStreamWriter(is, charset);
}
/**
* object begin.
*
* @return this.
* @throws IOException.
*/
public JSONWriter objectBegin() throws IOException
{
beforeValue();
mWriter.write(JSON.LBRACE);
mStack.push(mState);
mState = new State(OBJECT);
return this;
}
/**
* object end.
*
* @return this.
* @throws IOException.
*/
public JSONWriter objectEnd() throws IOException
{
mWriter.write(JSON.RBRACE);
mState = mStack.pop();
return this;
}
/**
* object item.
*
* @param name name.
* @return this.
* @throws IOException.
*/
public JSONWriter objectItem(String name) throws IOException
{
beforeObjectItem();
mWriter.write(JSON.QUOTE);
mWriter.write(escape(name));
mWriter.write(JSON.QUOTE);
mWriter.write(JSON.COLON);
return this;
}
/**
* array begin.
*
* @return this.
* @throws IOException.
*/
public JSONWriter arrayBegin() throws IOException
{
beforeValue();
mWriter.write(JSON.LSQUARE);
mStack.push(mState);
mState = new State(ARRAY);
return this;
}
/**
* array end, return array value.
*
* @return this.
* @throws IOException.
*/
public JSONWriter arrayEnd() throws IOException
{
mWriter.write(JSON.RSQUARE);
mState = mStack.pop();
return this;
}
/**
* value.
*
* @return this.
* @throws IOException.
*/
public JSONWriter valueNull() throws IOException
{
beforeValue();
mWriter.write(JSON.NULL);
return this;
}
/**
* value.
*
* @param value value.
* @return this.
* @throws IOException
*/
public JSONWriter valueString(String value) throws IOException
{
beforeValue();
mWriter.write(JSON.QUOTE);
mWriter.write(escape(value));
mWriter.write(JSON.QUOTE);
return this;
}
/**
* value.
*
* @param value value.
* @return this.
* @throws IOException
*/
public JSONWriter valueBoolean(boolean value) throws IOException
{
beforeValue();
mWriter.write(value ? "true" : "false");
return this;
}
/**
* value.
*
* @param value value.
* @return this.
* @throws IOException
*/
public JSONWriter valueInt(int value) throws IOException
{
beforeValue();
mWriter.write(String.valueOf(value));
return this;
}
/**
* value.
*
* @param value value.
* @return this.
* @throws IOException
*/
public JSONWriter valueLong(long value) throws IOException
{
beforeValue();
mWriter.write(String.valueOf(value));
return this;
}
/**
* value.
*
* @param value value.
* @return this.
* @throws IOException
*/
public JSONWriter valueFloat(float value) throws IOException
{
beforeValue();
mWriter.write(String.valueOf(value));
return this;
}
/**
* value.
*
* @param value value.
* @return this.
* @throws IOException
*/
public JSONWriter valueDouble(double value) throws IOException
{
beforeValue();
mWriter.write(String.valueOf(value));
return this;
}
private void beforeValue() throws IOException
{
switch( mState.type )
{
case ARRAY:
if( mState.itemCount++ > 0 )
mWriter.write(JSON.COMMA);
return;
case OBJECT:
throw new IOException("Must call objectItem first.");
case OBJECT_VALUE:
mState.type = OBJECT;
return;
}
}
private void beforeObjectItem() throws IOException
{
switch( mState.type )
{
case OBJECT_VALUE:
mWriter.write(JSON.NULL);
case OBJECT:
mState.type = OBJECT_VALUE;
if( mState.itemCount++ > 0 )
mWriter.write(JSON.COMMA);
return;
default:
throw new IOException("Must call objectBegin first.");
}
}
private static final String[] CONTROL_CHAR_MAP = new String[]{
"\\u0000","\\u0001","\\u0002","\\u0003","\\u0004","\\u0005","\\u0006","\\u0007",
"\\b","\\t","\\n","\\u000b","\\f","\\r","\\u000e","\\u000f",
"\\u0010","\\u0011","\\u0012","\\u0013","\\u0014","\\u0015","\\u0016","\\u0017",
"\\u0018","\\u0019","\\u001a","\\u001b","\\u001c","\\u001d","\\u001e","\\u001f"
};
private static String escape(String str)
{
if( str == null )
return str;
int len = str.length();
if( len == 0 )
return str;
char c;
StringBuilder sb = null;
for(int i=0;i<len;i++)
{
c = str.charAt(i);
if( c < ' ' ) // control char.
{
if( sb == null )
{
sb = new StringBuilder(len<<1);
sb.append(str,0,i);
}
sb.append(CONTROL_CHAR_MAP[c]);
}
else
{
switch( c )
{
case '\\': case '/': case '"':
if( sb == null )
{
sb = new StringBuilder(len<<1);
sb.append(str,0,i);
}
sb.append('\\').append(c);
break;
default:
if( sb != null )
sb.append(c);
}
}
}
return sb == null ? str : sb.toString();
}
}