/*
* file: JsonStreamWriter.java
* author: Jon Iles
* copyright: (c) Packwood Software 2015
* date: 18/02/2015
*/
/*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
package net.sf.mpxj.json;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Deque;
import java.util.LinkedList;
/**
* Writes JSON data to an output stream.
*/
public class JsonStreamWriter
{
/**
* Constructor.
*
* @param stream target output stream
*/
public JsonStreamWriter(OutputStream stream)
{
m_writer = new OutputStreamWriter(stream);
m_firstNameValuePair.push(Boolean.TRUE);
}
/**
* Retrieve the pretty-print flag.
*
* @return true if pretty printing is enabled
*/
public boolean getPretty()
{
return m_pretty;
}
/**
* Set the pretty-print flag.
*
* @param pretty true if pretty printing is enabled
*/
public void setPretty(boolean pretty)
{
m_pretty = pretty;
}
/**
* Flush the output stream.
*/
public void flush() throws IOException
{
m_writer.flush();
}
/**
* Begin writing a named object attribute.
*
* @param name attribute name
*/
public void writeStartObject(String name) throws IOException
{
writeComma();
writeNewLineIndent();
if (name != null)
{
writeName(name);
writeNewLineIndent();
}
m_writer.write("{");
increaseIndent();
}
/**
* Begin writing a named list attribute.
*
* @param name attribute name
*/
public void writeStartList(String name) throws IOException
{
writeComma();
writeNewLineIndent();
writeName(name);
writeNewLineIndent();
m_writer.write("[");
increaseIndent();
}
/**
* End writing an object.
*/
public void writeEndObject() throws IOException
{
decreaseIndent();
m_writer.write("}");
}
/**
* End writing a list.
*/
public void writeEndList() throws IOException
{
decreaseIndent();
m_writer.write("]");
}
/**
* Write a string attribute.
*
* @param name attribute name
* @param value attribute value
*/
public void writeNameValuePair(String name, String value) throws IOException
{
internalWriteNameValuePair(name, escapeString(value));
}
/**
* Write an int attribute.
*
* @param name attribute name
* @param value attribute value
*/
public void writeNameValuePair(String name, int value) throws IOException
{
internalWriteNameValuePair(name, Integer.toString(value));
}
/**
* Write a long attribute.
*
* @param name attribute name
* @param value attribute value
*/
public void writeNameValuePair(String name, long value) throws IOException
{
internalWriteNameValuePair(name, Long.toString(value));
}
/**
* Write a double attribute.
*
* @param name attribute name
* @param value attribute value
*/
public void writeNameValuePair(String name, double value) throws IOException
{
internalWriteNameValuePair(name, Double.toString(value));
}
/**
* Write a boolean attribute.
*
* @param name attribute name
* @param value attribute value
*/
public void writeNameValuePair(String name, boolean value) throws IOException
{
internalWriteNameValuePair(name, value ? "true" : "false");
}
/**
* Write a Date attribute.
*
* @param name attribute name
* @param value attribute value
*/
public void writeNameValuePair(String name, Date value) throws IOException
{
internalWriteNameValuePair(name, m_format.format(value));
}
/**
* Core write attribute implementation.
*
* @param name attribute name
* @param value attribute value
*/
private void internalWriteNameValuePair(String name, String value) throws IOException
{
writeComma();
writeNewLineIndent();
writeName(name);
if (m_pretty)
{
m_writer.write(' ');
}
m_writer.write(value);
}
/**
* Escape text to ensure valid JSON.
*
* @param value value
* @return escaped value
*/
private String escapeString(String value)
{
m_buffer.setLength(0);
m_buffer.append('"');
for (int index = 0; index < value.length(); index++)
{
char c = value.charAt(index);
switch (c)
{
case '"':
{
m_buffer.append("\\\"");
break;
}
case '\\':
{
m_buffer.append("\\\\");
break;
}
case '/':
{
m_buffer.append("\\/");
break;
}
case '\b':
{
m_buffer.append("\\b");
break;
}
case '\f':
{
m_buffer.append("\\f");
break;
}
case '\n':
{
m_buffer.append("\\n");
break;
}
case '\r':
{
m_buffer.append("\\r");
break;
}
case '\t':
{
m_buffer.append("\\t");
break;
}
default:
{
m_buffer.append(c);
break;
}
}
}
m_buffer.append('"');
return m_buffer.toString();
}
/**
* Write a comma to the output stream if required.
*/
private void writeComma() throws IOException
{
if (m_firstNameValuePair.peek().booleanValue())
{
m_firstNameValuePair.pop();
m_firstNameValuePair.push(Boolean.FALSE);
}
else
{
m_writer.write(',');
}
}
/**
* Write a new line and indent.
*/
private void writeNewLineIndent() throws IOException
{
if (m_pretty)
{
if (!m_indent.isEmpty())
{
m_writer.write('\n');
m_writer.write(m_indent);
}
}
}
/**
* Write an attribute name.
*
* @param name attribute name
*/
private void writeName(String name) throws IOException
{
m_writer.write('"');
m_writer.write(name);
m_writer.write('"');
m_writer.write(":");
}
/**
* Increase the indent level.
*/
private void increaseIndent()
{
m_firstNameValuePair.push(Boolean.TRUE);
if (m_pretty)
{
m_indent += INDENT;
}
}
/**
* Decrease the indent level.
*/
private void decreaseIndent() throws IOException
{
if (m_pretty)
{
m_writer.write('\n');
m_indent = m_indent.substring(0, m_indent.length() - INDENT.length());
m_writer.write(m_indent);
}
m_firstNameValuePair.pop();
}
private final StringBuilder m_buffer = new StringBuilder();
private final OutputStreamWriter m_writer;
private final Deque<Boolean> m_firstNameValuePair = new LinkedList<Boolean>();
private boolean m_pretty;
private String m_indent = "";
private DateFormat m_format = new SimpleDateFormat("\"yyyy-MM-dd'T'HH:mm:ss.S\"");
private static final String INDENT = " ";
}