/*
* 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.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class LexicalParser<T extends IGenericLiteral> implements ILexicalParser<T> {
private static Log mLogger = LogFactory.getFactory().getInstance(LexicalParser.class);
private Stack<T> workerStack = new Stack<T>();
private T startLiteral;
private IEventHandler<T> eventHandler;
public LexicalParser(T startLiteral, IEventHandler<T> eventHandler) {
this.startLiteral = startLiteral;
this.eventHandler = eventHandler;
}
final public void initEvent() {
workerStack.clear();
workerStack.push(startLiteral);
mLogger.debug("init " + startLiteral + " ; " + startLiteral.getCommand());
}
final public void openArrayEvent() {
checkStackState(ECommand.ARRAY_COMMAND);
openLiteral();
}
final public void closeArrayEvent() {
if (workerStack.peek().getCommand() != ECommand.ARRAY_COMMAND) {
T lastLiteral = workerStack.pop();
mLogger.debug("close: " + lastLiteral + " ; " + lastLiteral.getCommand());
}
checkStackState(ECommand.ARRAY_COMMAND);
closeLiteral();
}
final public void openDictionaryEvent() {
checkStackState(ECommand.DICTIONARY_COMMAND);
openLiteral();
}
public void closeDictionaryEvent() {
if (workerStack.peek().getCommand() == ECommand.PROPERTY_COMMAND) {
workerStack.pop(); // remove optional property command
}
checkStackState(ECommand.DICTIONARY_COMMAND);
closeLiteral();
}
public void closePropertyEvent() {
checkStackState(ECommand.PROPERTY_COMMAND);
closeLiteral(); // close PROPERTY_COMMAND
checkStackState(ECommand.DICTIONARY_COMMAND);
advanceToNextDictionaryProperty();
}
public void openPropertyEvent(String key) {
checkStackState(ECommand.PROPERTY_COMMAND);
mLogger.debug("key: " + key + " ");
fixPropertyLiteralOnStack(key);
checkStackState(ECommand.PROPERTY_COMMAND);
openLiteral();
eventHandler.readPropertyKey(key);
}
/**
* important to allow unordered property lists in dictionaries / objects (see
* www.json.org : An object is an unordered set of name/value pairs. )
*
* @param key
*/
@SuppressWarnings("unchecked")
private void fixPropertyLiteralOnStack(String key) {
T placeholder = workerStack.pop(); // remove placeholder property-literal
checkStackState(ECommand.DICTIONARY_COMMAND);
T nextLiteral = (T) workerStack.peek().getPropertyLiteralForKey(key, placeholder);
if (nextLiteral != null) {
workerStack.push(nextLiteral);
mLogger.debug("fix property literal on stack: " + workerStack.peek() + " ; "
+ workerStack.peek().getCommand());
} else {
throw new IllegalStateException("illegal key value [" + key + "] in dictionary ["
+ workerStack.peek() + "]. Stack: " + workerStack.toString());
}
}
public void stringEvent(String value) {
checkStackState(ECommand.VALUE_COMMAND);
eventHandler.openEvent(workerStack.peek());
eventHandler.stringEvent(value);
mLogger.debug("string-value: " + value + " ");
closeLiteral(); // close VALUE_COMMAND
}
public void booleanEvent(boolean value) {
checkStackState(ECommand.VALUE_COMMAND);
eventHandler.openEvent(workerStack.peek());
eventHandler.booleanEvent(value);
mLogger.debug("boolean-value: " + value + " ");
closeLiteral(); // close VALUE_COMMAND
}
final public void finishEvent() {
if (!workerStack.isEmpty()) {
throw new IllegalStateException("SyntaxError: finishEvent on nonempty" + " stack:"
+ workerStack.peek());
}
}
final private void checkStackState(ECommand expectedCommand) {
if (!workerStack.isEmpty() && (workerStack.peek().getCommand() != expectedCommand)
&& (workerStack.peek().getCommand() == ECommand.ARRAY_COMMAND)) {
openLiteral();
mLogger.debug("reopen " + workerStack.peek() + " ; " + workerStack.peek().getCommand());
}
if (workerStack.isEmpty() || (workerStack.peek().getCommand() != expectedCommand)) {
throw new IllegalStateException("Expecting: " + workerStack.peek().getCommand() + " for "
+ workerStack.peek() + " but received " + expectedCommand + ". " + "Stack: "
+ workerStack.toString());
}
}
@SuppressWarnings("unchecked")
private void advanceToNextDictionaryProperty() {
T nextLiteral = (T) workerStack.peek().getNextLiteral();
if (nextLiteral != null) {
workerStack.push(nextLiteral);
mLogger.debug("advance open: " + workerStack.peek() + " ; "
+ workerStack.peek().getCommand());
}
}
@SuppressWarnings("unchecked")
private void openLiteral() {
T currentLiteral = workerStack.peek();
workerStack.push((T) currentLiteral.getFirstLiteral());
eventHandler.openEvent(currentLiteral);
mLogger.debug("open " + currentLiteral + " ; " + currentLiteral.getCommand());
}
private void closeLiteral() {
T lastLiteral = workerStack.pop();
mLogger.debug("close: " + lastLiteral + " ; " + lastLiteral.getCommand());
eventHandler.closeEvent(lastLiteral);
}
}