package nxm.ucl.lib;

/** This will parse a Java file.

    @author  J. Czechowski IV
    @version $Id: JavaParser.java,v 1.3 2015/10/19 14:01:44 ntn Exp $
    @since   NeXtMidas 1.9.2
 */
public class JavaParser extends Lexer {

  /** Creates a new instance of JavaParser.
   *  @param inputLines lines in the file to parse
   */
  public JavaParser (String[] inputLines) {
    super(null, inputLines);

    this.rules = JAVA_RULES;
  }

  /** A unicode escape sequence. (Sect 3.3) */
  private static final String UNICODE_ESCAPE
    = "(\\\\u+[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])";

  /** A Character. (Sect ) */
  private static final String ESCAPE_SEQ
    = "((\\\\b)|(\\\\t)|(\\\\n)|(\\\\f)|(\\\\r)|(\\\\\')|(\\\\\")|(\\\\\\\\)"
    + "|(\\[0-3]?[0-7]?[0-7])" /* Octal */
    + "|" + UNICODE_ESCAPE + ")";

  /** A character in a character literal. (Sect 3.10.4)*/
  private static final String CHAR = "([^'\\\\]|"+ESCAPE_SEQ + ")";

  /** A character in a string literal. (Sect 3.10.5)*/
  private static final String STR_CHAR = "([^\\\"\\\\]|"+ESCAPE_SEQ+")";

  /** A Java keyword. These are the ones used by Java 1.3 and earlier.
      (Sect 3.9)
   */
  private static final String JAVA13_KEYWORD
    = "((abstract)|(default)|(if)|(private)|(this)"
    + "|(boolean)|(do)|(implements)|(protected)|(throw)"
    + "|(break)|(double)|(import)|(public)|(throws)"
    + "|(byte)|(else)|(instanceof)|(return)|(transient)"
    + "|(case)|(extends)|(int)|(short)|(try)"
    + "|(catch)|(final)|(interface)|(static)|(void)"
    + "|(char)|(finally)|(long)|(strictfp)|(volatile)"
    + "|(class)|(float)|(native)|(super)|(while)"
    + "|(const)|(for)|(new)|(switch)"
    + "|(continue)|(goto)|(package)|(synchronized))";

  /** A decimal value. (Sect 3.10.1) */
  private static final String DEC_VAL = "((0)|([1-9][0-9]*))";

  /** An octal value. (Sect 3.10.1) */
  private static final String OCT_VAL = "(0[0-7]+)";

  /** An hex value. (Sect 3.10.1) */
  private static final String HEX_VAL = "(0[xX][0-9a-fA-F]+)";

  /** A floating-point exponent. (Sect 3.10.3) */
  private static final String EXPONENT = "([eE][+-]?[0-9]+)";

  /** A floating-point value. (Sect 3.10.3) */
  private static final String FLOAT_VAL
    = "(([0-9]+[.][0-9]?" + EXPONENT + "?[fFdD]?)"
    + "|([.][0-9]+" + EXPONENT + "?[fFdD]?)"
    + "|([0-9]+" + EXPONENT + "[fFdD]?)"
    + "|([0-9]+" + EXPONENT + "?[fFdD]))";

  /** An operator. (Sect 3.12) */
  private static final String OPERATOR
    = "(([=><!~?:])"
    + "|([=<>!]=)|(&&)|([|][|])|([+][+])|(--)"
    + "|((([-+*/&|^%])|(<<)|(>>)|(>>>))=?))";


  /** A Java keyword. These are the ones used by Java 1.4. */
  private static final String JAVA14_KEYWORD
    = "((assert)" + JAVA13_KEYWORD + ")";

  /** A Java keyword. These are the ones used by Java 5. */
  private static final String JAVA15_KEYWORD
    = "((enum)" + JAVA14_KEYWORD + ")";


  /** Indicates if in the middle of a multi-line block comment. */
  protected boolean inBlockComment;

  /** Rule defining a block comment in Java. */
  protected final Rule BLOCK_COMMENT = new Rule() {
    public boolean matches(String string, boolean lineStart, boolean lineEnd) {
      if(!inBlockComment) {
        if(string.startsWith("/*")) {
          string = string.substring(2);

        } else {
          return false;
        }
      }

      for(int i = 1; i < string.length() - 2; i++) {
        if((string.charAt(i) == '/') && (string.charAt(i-1) == '*')) {
          return false;
        }
      }

      return lineEnd || string.endsWith("*/");
    }

    public Token makeToken(String string, boolean lineStart, boolean lineEnd) {
      inBlockComment = lineEnd && !string.endsWith("*/");
      return new Token("BLOCK_CMT", string);
    }
  };

  // REGEX = "/[*]([^*]|\n)(([*]((/)|([*]*)))|([*](([^*/]|\n)))*|([^*]|\n)*)"

  private final Rule[] JAVA_RULES = new Rule[] {
    new SimpleRule("CHARACTER", "\'" + CHAR + "\'"),
    new SimpleRule("STRING",    "\"" + STR_CHAR +"*\""),
    BLOCK_COMMENT,
    new SimpleRule("LINE_CMT",  "//.*"),
    new SimpleRule("KEYWORD",   JAVA15_KEYWORD),
    new SimpleRule("INTEGER",   "("+DEC_VAL+"|"+OCT_VAL+"|"+HEX_VAL+")[lL]?"),
    new SimpleRule("FLOAT",     FLOAT_VAL),
    new SimpleRule("BOOLEAN",   "((true)|(false))"),
    new SimpleRule("NULL",      "(null)"),
    new SimpleRule("SEPARATOR", "[(){}\\[\\];,.]"),
    new SimpleRule("OPERATOR",  OPERATOR),
    new SimpleRule("IDENTIFIER","[a-zA-Z_$][a-zA-Z_$0-9]*"),
    new SimpleRule("WHITESPACE","[ \t]+")
  };
}
