/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.celements.sajson;
import java.io.StringWriter;
import java.util.Stack;
/**
* Simple Api for JSON The JSON Builder helps do generate a valid JSON expression based on
* event handling.
*
* @author fabian
*/
public class Builder {
private Stack<ECommand> workerStack;
private StringWriter jsonOutput;
private boolean onFirstElement;
public Builder() {
workerStack = new Stack<ECommand>();
jsonOutput = new StringWriter();
onFirstElement = true;
}
public String getJSON() {
if (!workerStack.isEmpty()) {
throw new IllegalStateException(workerStack.peek() + " is still open.");
}
return jsonOutput.toString();
}
public void openArray() {
checkNoDictionary();
workerStack.push(ECommand.ARRAY_COMMAND);
addOpeningPart("[");
onFirstElement = true;
}
private void addOpeningPart(String openingPart) {
if (!onFirstElement) {
jsonOutput.append(", ");
}
jsonOutput.append(openingPart);
}
private void checkNoDictionary() {
if (!workerStack.isEmpty() && (workerStack.peek() == ECommand.DICTIONARY_COMMAND)) {
throw new IllegalStateException("Cannot added to a dictionary.");
}
}
public void closeArray() {
checkForOpenCommand(ECommand.ARRAY_COMMAND);
jsonOutput.append("]");
workerStack.pop();
implicitCloseProperty();
}
public void openDictionary() {
checkNoDictionary();
workerStack.push(ECommand.DICTIONARY_COMMAND);
addOpeningPart("{");
onFirstElement = true;
}
public void closeDictionary() {
checkForOpenCommand(ECommand.DICTIONARY_COMMAND);
jsonOutput.append("}");
workerStack.pop();
implicitCloseProperty();
}
public void openProperty(String key) {
if (workerStack.peek() != ECommand.DICTIONARY_COMMAND) {
throw new IllegalStateException("Properties may only be added " + "to a dictionary but found "
+ workerStack.peek());
}
workerStack.push(ECommand.PROPERTY_COMMAND);
addOpeningPart(toJSONString(key) + " : ");
onFirstElement = true;
}
public void closeProperty() {
checkForOpenCommand(ECommand.PROPERTY_COMMAND);
if (jsonOutput.toString().endsWith(" : ")) {
jsonOutput.append(toJSONString(null));
}
workerStack.pop();
onFirstElement = false;
}
public void addStringProperty(String key, String value) {
openProperty(key);
addString(value);
}
public void addString(String value) {
checkNoDictionary();
addOpeningPart(toJSONString(value));
implicitCloseProperty();
}
private void implicitCloseProperty() {
if (!workerStack.isEmpty() && (workerStack.peek() == ECommand.PROPERTY_COMMAND)) {
workerStack.pop();
}
onFirstElement = false;
}
public void addBoolean(Boolean value) {
checkNoDictionary();
if (value != null) {
addOpeningPart(Boolean.toString(value));
} else {
addNull();
}
implicitCloseProperty();
}
public void addNumber(Number value) {
checkNoDictionary();
if (value != null) {
addOpeningPart(value.toString());
} else {
addNull();
}
implicitCloseProperty();
}
/**
* @deprecated instead use {@link #addNumber(Number)}
*/
@Deprecated
public void addFloat(Float value) {
addNumber(value);
}
/**
* @deprecated instead use {@link #addNumber(Number)}
*/
@Deprecated
public void addInteger(Integer value) {
addNumber(value);
}
public void addNull() {
checkNoDictionary();
addOpeningPart(toJSONString(null));
implicitCloseProperty();
}
/**
* for internal use only (ONLY TESTS!!!)
*/
Stack<ECommand> internal_getWorkerStack() {
return workerStack;
}
/**
* for internal use only (ONLY TESTS!!!)
*/
String internal_getUnfinishedJSON() {
return jsonOutput.toString();
}
private void checkForOpenCommand(ECommand expectedCmd) {
if (workerStack.isEmpty()) {
throw new IllegalStateException("Cannot be applied a close " + expectedCmd
+ " on an empty stack.");
}
if (workerStack.peek() != expectedCmd) {
throw new IllegalStateException("Close " + expectedCmd + " cannot follow "
+ workerStack.peek());
}
}
String toJSONString(String outStr) {
if (outStr == null) {
return "null";
} else {
return "\"" + outStr.replaceAll("\"", "\\\\\"").replaceAll("\n", "\\\\n").replaceAll("\r",
"\\\\r").replaceAll("\t", "\\\\t") + "\"";
}
}
/**
* for internal use only (ONLY TESTS!!!)
*/
void setOnFirstElement(boolean onFirstElement) {
this.onFirstElement = onFirstElement;
}
boolean isOnFirstElement() {
return onFirstElement;
}
}