/* 
 * Copyright (C) 2001-2013 Michael Fuchs
 *
 * This file is part of herold.
 * 
 * herold is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * herold 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with herold.  If not, see <http://www.gnu.org/licenses/>.  
 */
package org.dbdoclet.html.tokenizer.parser;

import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dbdoclet.progress.ProgressEvent;
import org.dbdoclet.progress.ProgressListener;
import org.dbdoclet.progress.ProgressManager;


public class HtmlTokenizer/*@bgen(jjtree)*/implements HtmlTokenizerTreeConstants, HtmlTokenizerConstants {/*@bgen(jjtree)*/
  protected JJTHtmlTokenizerState jjtree = new JJTHtmlTokenizerState();private static Log logger = LogFactory.getLog(HtmlTokenizer.class);

  private ArrayList<org.dbdoclet.html.tokenizer.Token> tokenList;
  private ArrayList<ProgressListener> listeners;

  public HtmlTokenizer(String buffer, String encoding) throws UnsupportedEncodingException
  {
    this (new ByteArrayInputStream(buffer.getBytes(encoding)), encoding);
  }

  public synchronized ArrayList < org.dbdoclet.html.tokenizer.Token > parse() throws ParseException
  {
    tokenList = new ArrayList < org.dbdoclet.html.tokenizer.Token > ();
    start();
    return tokenList;
  }

  final public void start() throws ParseException {
 /*@bgen(jjtree) start */
  SimpleNode jjtn000 = new SimpleNode(JJTSTART);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      label_1:
      while (true) {
        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
        case TAG_START:
        case QUOTE:
        case TEXT:
        case COMMENT:
          ;
          break;
        default:
          jj_la1[0] = jj_gen;
          break label_1;
        }
        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
        case TAG_START:
          tag();
          break;
        case QUOTE:
        case TEXT:
          text();
          break;
        case COMMENT:
          comment();
          break;
        default:
          jj_la1[1] = jj_gen;
          jj_consume_token(-1);
          throw new ParseException();
        }
      }
    } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      {if (true) throw (RuntimeException)jjte000;}
    }
    if (jjte000 instanceof ParseException) {
      {if (true) throw (ParseException)jjte000;}
    }
    {if (true) throw (Error)jjte000;}
    } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
    }
    }
  }

  final public void tag() throws ParseException {
 /*@bgen(jjtree) tag */
  SimpleNode jjtn000 = new SimpleNode(JJTTAG);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);Token tag = null;
  Token name = null;
  Token value = null;
  Token slash1 = null;
  Token slash2 = null;
  HashMap < String, String > attributeMap = new HashMap < String, String > ();
    try {
    org.dbdoclet.html.tokenizer.Token token = new org.dbdoclet.html.tokenizer.Token();
    tokenList.add(token);
    fireProgressEvent(token.toString());
      try {
        jj_consume_token(TAG_START);
        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
        case SLASH:
          slash1 = jj_consume_token(SLASH);
          break;
        default:
          jj_la1[2] = jj_gen;
          ;
        }
        tag = ncname();
      token.setTagName(tag.image);
      token.setLine(tag.beginLine);
      token.setColumn(tag.beginColumn);
          trace("Tag: " + tag.image);
        label_2:
        while (true) {
          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
          case NCNAME:
            ;
            break;
          default:
            jj_la1[3] = jj_gen;
            break label_2;
          }
          name = attrName();
          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
          case EQUALS:
            equals();
            value = attrValue();
            break;
          default:
            jj_la1[4] = jj_gen;
            ;
          }
      if (value != null)
      {

        String buffer = value.image;
        if (buffer != null)
        {
                if (buffer.startsWith("\u005c"") || buffer.startsWith("'"))
                {
                        buffer = buffer.substring(1, buffer.length());
                }

                if (buffer.endsWith("\u005c"") || buffer.endsWith("'"))
                {
                        buffer = buffer.substring(0, buffer.length() - 1);
                }

                attributeMap.put(name.image, buffer);
        }
      }
        }
        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
        case SLASH:
          slash2 = jj_consume_token(SLASH);
          break;
        default:
          jj_la1[5] = jj_gen;
          ;
        }
        jj_consume_token(TAG_END);
      } catch (Throwable oops) {
    logger.warn(String.format("Invalid attributes in Element %s line %d column %d",
                         token.getTagName(), token.getLine(), token.getColumn()));
    logger.warn(oops.getMessage());
    Token t = skipTo(DEFAULT, DEFAULT, token.toString(), TAG_END, TAG_START);
    token.setValue("&lt;" + token.getTagName() + toAttributesAsText(attributeMap) + t.image);
    {if (true) return;}
      }
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    StringBuilder buffer = new StringBuilder();
    buffer.append('<');
    if (slash1 != null)
    {
      buffer.append('/');
    }
    if (tag != null)
    {
      buffer.append(tag.image);
    }
    buffer.append(toAttributesAsText(attributeMap));
    if (slash2 != null)
    {
      buffer.append('/');
    }
    buffer.append('>');
    trace(buffer.toString());
    token.setValue(buffer.toString());
    token.setAttributes(attributeMap);
    } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      {if (true) throw (RuntimeException)jjte000;}
    }
    if (jjte000 instanceof ParseException) {
      {if (true) throw (ParseException)jjte000;}
    }
    {if (true) throw (Error)jjte000;}
    } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
    }
    }
  }

  final public void comment() throws ParseException {
 /*@bgen(jjtree) comment */
  SimpleNode jjtn000 = new SimpleNode(JJTCOMMENT);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);Token comment;
    try {
      comment = jj_consume_token(COMMENT);
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    org.dbdoclet.html.tokenizer.Token token = new org.dbdoclet.html.tokenizer.Token(comment.image);
    tokenList.add(token);
    fireProgressEvent(token.toString());
    } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
    }
    }
  }

  final public void text() throws ParseException {
 /*@bgen(jjtree) text */
  SimpleNode jjtn000 = new SimpleNode(JJTTEXT);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);Token text;
    try {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case TEXT:
        text = jj_consume_token(TEXT);
        break;
      case QUOTE:
        text = jj_consume_token(QUOTE);
        break;
      default:
        jj_la1[6] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    org.dbdoclet.html.tokenizer.Token token = new org.dbdoclet.html.tokenizer.Token(text.image);
        trace("text() " + token);
    tokenList.add(token);
    fireProgressEvent(token.toString());
    } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
    }
    }
  }

  @SuppressWarnings("unused")
final public Token ncname() throws ParseException {
 /*@bgen(jjtree) ncname */
  SimpleNode jjtn000 = new SimpleNode(JJTNCNAME);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);Token t;
    try {
      t = jj_consume_token(NCNAME);
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    {if (true) return t;}
    } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
    }
    }
    throw new Error("Missing return statement in function");
  }

  final public void equals() throws ParseException {
 /*@bgen(jjtree) equals */
  SimpleNode jjtn000 = new SimpleNode(JJTEQUALS);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      jj_consume_token(EQUALS);
    } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
    }
    }
  }

  @SuppressWarnings("unused")
final public Token attrName() throws ParseException {
 /*@bgen(jjtree) attrName */
  SimpleNode jjtn000 = new SimpleNode(JJTATTRNAME);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);Token token = null;
    try {
      try {
        token = jj_consume_token(NCNAME);
    // System.out.println(token.image);
    {if (true) return token;}
      } catch (Throwable oops) {
    logger.warn(String.format("Invalid attributes in Element %s line %d column %d",
                         token.image, token.beginLine, token.beginColumn));
        logger.warn(oops.getMessage());

        {if (true) return skipTo(DEFAULT, TAG_CONTEXT, "", TAG_END, QUOTE);}
      }
    } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
    }
    }
    throw new Error("Missing return statement in function");
  }

  @SuppressWarnings("unused")
final public Token attrValue() throws ParseException {
 /*@bgen(jjtree) attrValue */
  SimpleNode jjtn000 = new SimpleNode(JJTATTRVALUE);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);Token token = null;
    try {
      try {
        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
        case VALUE:
          token = jj_consume_token(VALUE);
          break;
        case NCNAME:
          token = jj_consume_token(NCNAME);
          break;
        default:
          jj_la1[7] = jj_gen;
          jj_consume_token(-1);
          throw new ParseException();
        }
        {if (true) return token;}
      } catch (Throwable oops) {
    logger.warn(String.format("Invalid attributes in Element %s line %d column %d",
                         token.image, token.beginLine, token.beginColumn));
    logger.warn(oops.getMessage());
        {if (true) return skipTo(DEFAULT, TAG_CONTEXT, "", TAG_END, QUOTE);}
      }
    } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
    }
    }
    throw new Error("Missing return statement in function");
  }

  private Token skipTo(int preContext, int postContext, String prev, int... kind) throws ParseException {
 /*@bgen(jjtree) skipTo */
SimpleNode jjtn000 = new SimpleNode(JJTSKIPTO);
boolean jjtc000 = true;
jjtree.openNodeScope(jjtn000);
try {StringBuilder buffer = new StringBuilder();

  boolean skip = true;

  token_source.SwitchTo(preContext);

  while (skip)
  {
    Token t = getNextToken();
        buffer.append(t.image);

        if (t.kind == EOF)
        {
          break;
        }

        for (int stop : kind)
        {
          if (t.kind == stop)
          {
                skip = false;
          }
        }
  }

  token_source.SwitchTo(postContext);
  return new Token(VALUE, buffer.toString());/*@bgen(jjtree)*/
} finally {
  if (jjtc000) {
    jjtree.closeNodeScope(jjtn000, true);
  }
}
  }

  private String toAttributesAsText(HashMap<String, String > attributeMap) throws ParseException {
 /*@bgen(jjtree) toAttributesAsText */
     SimpleNode jjtn000 = new SimpleNode(JJTTOATTRIBUTESASTEXT);
     boolean jjtc000 = true;
     jjtree.openNodeScope(jjtn000);
     try {StringBuilder buffer = new StringBuilder();

    for (String attr : attributeMap.keySet())
    {
      buffer.append(' ');
      buffer.append(attr);
      buffer.append("=\u005c"");
      buffer.append(attributeMap.get(attr));
      buffer.append("\u005c"");
    }

    return buffer.toString();/*@bgen(jjtree)*/
     } finally {
       if (jjtc000) {
         jjtree.closeNodeScope(jjtn000, true);
       }
     }
  }

  public void setProgressListeners(ArrayList<ProgressListener> listeners) throws ParseException {
 /*@bgen(jjtree) setProgressListeners */
     SimpleNode jjtn000 = new SimpleNode(JJTSETPROGRESSLISTENERS);
     boolean jjtc000 = true;
     jjtree.openNodeScope(jjtn000);
     try {this.listeners = listeners;/*@bgen(jjtree)*/
     } finally {
       if (jjtc000) {
         jjtree.closeNodeScope(jjtn000, true);
       }
     }
  }

  public void fireProgressEvent(String action) throws ParseException {
 /*@bgen(jjtree) fireProgressEvent */
     SimpleNode jjtn000 = new SimpleNode(JJTFIREPROGRESSEVENT);
     boolean jjtc000 = true;
     jjtree.openNodeScope(jjtn000);
     try {if (listeners != null)
        {
                ProgressEvent event = new ProgressEvent("Tokenizer: " + action);
                event.setStage(ProgressEvent.STAGE_PREPARE);

                ProgressManager pm = new ProgressManager(listeners);
                pm.fireProgressEvent(event);
        }/*@bgen(jjtree)*/
     } finally {
       if (jjtc000) {
         jjtree.closeNodeScope(jjtn000, true);
       }
     }
  }

  private void trace(String text) throws ParseException {
 /*@bgen(jjtree) trace */
SimpleNode jjtn000 = new SimpleNode(JJTTRACE);
boolean jjtc000 = true;
jjtree.openNodeScope(jjtn000);
try {/*@bgen(jjtree)*/
} finally {
  if (jjtc000) {
    jjtree.closeNodeScope(jjtn000, true);
  }
}
  }

  /** Generated Token Manager. */
  public HtmlTokenizerTokenManager token_source;
  SimpleCharStream jj_input_stream;
  /** Current token. */
  public Token token;
  /** Next token. */
  public Token jj_nt;
  private int jj_ntk;
  private int jj_gen;
  final private int[] jj_la1 = new int[8];
  static private int[] jj_la1_0;
  static {
      jj_la1_init_0();
   }
   private static void jj_la1_init_0() {
      jj_la1_0 = new int[] {0x400700,0x400700,0x40000,0x80000,0x20000,0x40000,0x600,0x180000,};
   }

  /** Constructor with InputStream. */
  public HtmlTokenizer(java.io.InputStream stream) {
     this(stream, null);
  }
  /** Constructor with InputStream and supplied encoding */
  public HtmlTokenizer(java.io.InputStream stream, String encoding) {
    try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
    token_source = new HtmlTokenizerTokenManager(jj_input_stream);
    token = new Token();
    jj_ntk = -1;
    jj_gen = 0;
    for (int i = 0; i < 8; i++) jj_la1[i] = -1;
  }

  /** Reinitialise. */
  public void ReInit(java.io.InputStream stream) {
     ReInit(stream, null);
  }
  /** Reinitialise. */
  public void ReInit(java.io.InputStream stream, String encoding) {
    try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
    token_source.ReInit(jj_input_stream);
    token = new Token();
    jj_ntk = -1;
    jjtree.reset();
    jj_gen = 0;
    for (int i = 0; i < 8; i++) jj_la1[i] = -1;
  }

  /** Constructor. */
  public HtmlTokenizer(java.io.Reader stream) {
    jj_input_stream = new SimpleCharStream(stream, 1, 1);
    token_source = new HtmlTokenizerTokenManager(jj_input_stream);
    token = new Token();
    jj_ntk = -1;
    jj_gen = 0;
    for (int i = 0; i < 8; i++) jj_la1[i] = -1;
  }

  /** Reinitialise. */
  public void ReInit(java.io.Reader stream) {
    jj_input_stream.ReInit(stream, 1, 1);
    token_source.ReInit(jj_input_stream);
    token = new Token();
    jj_ntk = -1;
    jjtree.reset();
    jj_gen = 0;
    for (int i = 0; i < 8; i++) jj_la1[i] = -1;
  }

  /** Constructor with generated Token Manager. */
  public HtmlTokenizer(HtmlTokenizerTokenManager tm) {
    token_source = tm;
    token = new Token();
    jj_ntk = -1;
    jj_gen = 0;
    for (int i = 0; i < 8; i++) jj_la1[i] = -1;
  }

  /** Reinitialise. */
  public void ReInit(HtmlTokenizerTokenManager tm) {
    token_source = tm;
    token = new Token();
    jj_ntk = -1;
    jjtree.reset();
    jj_gen = 0;
    for (int i = 0; i < 8; i++) jj_la1[i] = -1;
  }

  private Token jj_consume_token(int kind) throws ParseException {
    Token oldToken;
    if ((oldToken = token).next != null) token = token.next;
    else token = token.next = token_source.getNextToken();
    jj_ntk = -1;
    if (token.kind == kind) {
      jj_gen++;
      return token;
    }
    token = oldToken;
    jj_kind = kind;
    throw generateParseException();
  }


/** Get the next Token. */
  final public Token getNextToken() {
    if (token.next != null) token = token.next;
    else token = token.next = token_source.getNextToken();
    jj_ntk = -1;
    jj_gen++;
    return token;
  }

/** Get the specific Token. */
  final public Token getToken(int index) {
    Token t = token;
    for (int i = 0; i < index; i++) {
      if (t.next != null) t = t.next;
      else t = t.next = token_source.getNextToken();
    }
    return t;
  }

  private int jj_ntk() {
    if ((jj_nt=token.next) == null)
      return (jj_ntk = (token.next=token_source.getNextToken()).kind);
    else
      return (jj_ntk = jj_nt.kind);
  }

  private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
  private int[] jj_expentry;
  private int jj_kind = -1;

  /** Generate ParseException. */
  public ParseException generateParseException() {
    jj_expentries.clear();
    boolean[] la1tokens = new boolean[30];
    if (jj_kind >= 0) {
      la1tokens[jj_kind] = true;
      jj_kind = -1;
    }
    for (int i = 0; i < 8; i++) {
      if (jj_la1[i] == jj_gen) {
        for (int j = 0; j < 32; j++) {
          if ((jj_la1_0[i] & (1<<j)) != 0) {
            la1tokens[j] = true;
          }
        }
      }
    }
    for (int i = 0; i < 30; i++) {
      if (la1tokens[i]) {
        jj_expentry = new int[1];
        jj_expentry[0] = i;
        jj_expentries.add(jj_expentry);
      }
    }
    int[][] exptokseq = new int[jj_expentries.size()][];
    for (int i = 0; i < jj_expentries.size(); i++) {
      exptokseq[i] = jj_expentries.get(i);
    }
    return new ParseException(token, exptokseq, tokenImage);
  }

  /** Enable tracing. */
  final public void enable_tracing() {
  }

  /** Disable tracing. */
  final public void disable_tracing() {
  }

}
