/*
 * 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.web.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) {
      //XXX probably dirty hack!
      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() {
    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 + " ");
    openLiteral();
    eventHandler.readPropertyKey(key);
  }

  public void stringEvent(String value) {
    checkStackState(ECommand.VALUE_COMMAND);
    eventHandler.openEvent(workerStack.peek());
    eventHandler.stringEvent(value);
    mLogger.debug("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);
      eventHandler.openEvent(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);
  }

}

@@ -1892,41 +1892,8 @@
 ) %7B%0A
-      //XXX probably dirty hack!%0A
     
@@ -4134,51 +4134,8 @@
 l);%0A
-      eventHandler.openEvent(nextLiteral);%0A
     

fix double call of openEvent caused by advanceToNextDictionaryProperty