package com.alibaba.druid.sql.parser;

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.expr.SQLNumberExpr;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlLexer;
import com.alibaba.druid.util.FnvHash;
import com.alibaba.druid.util.StringUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TimeZone;

public class Lexer {
   protected static SymbolTable symbols_l2 = new SymbolTable(512);
   protected char[] moneySigns;
   protected int features;
   protected TimeZone timeZone;
   public final String text;
   protected int pos;
   protected int mark;
   protected int numberSale;
   protected boolean numberExp;
   protected char ch;
   protected char[] buf;
   protected int bufPos;
   protected Token token;
   protected Keywords keywords;
   protected String stringVal;
   protected long hash_lower;
   protected long hash;
   protected int commentCount;
   protected List<String> comments;
   protected boolean skipComment;
   private SavePoint savePoint;
   private boolean allowComment;
   private int varIndex;
   protected CommentHandler commentHandler;
   protected boolean endOfComment;
   protected boolean keepComments;
   protected int line;
   protected int lines;
   protected DbType dbType;
   protected boolean optimizedForParameterized;
   protected boolean keepSourceLocation;
   protected int startPos;
   protected int posLine;
   protected int posColumn;
   public boolean throwIfStateEnd;
   private static final long MULTMIN_RADIX_TEN = -922337203685477580L;
   private static final long N_MULTMAX_RADIX_TEN = -922337203685477580L;
   private static final int[] digits = new int[58];

   public Lexer(String input) {
      this(input, (CommentHandler)null);
   }

   public Lexer(String input, CommentHandler commentHandler) {
      this(input, true);
      this.commentHandler = commentHandler;
   }

   public Lexer(String input, CommentHandler commentHandler, DbType dbType) {
      this(input, true);
      this.commentHandler = commentHandler;
      this.dbType = dbType;
      if (DbType.sqlite == dbType) {
         this.keywords = Keywords.SQLITE_KEYWORDS;
      } else if (DbType.dm == dbType) {
         this.keywords = Keywords.DM_KEYWORDS;
      }

   }

   public boolean isKeepComments() {
      return this.keepComments;
   }

   public void setKeepComments(boolean keepComments) {
      this.keepComments = keepComments;
   }

   public CommentHandler getCommentHandler() {
      return this.commentHandler;
   }

   public void setCommentHandler(CommentHandler commentHandler) {
      this.commentHandler = commentHandler;
   }

   public final char charAt(int index) {
      return index >= this.text.length() ? '\u001a' : this.text.charAt(index);
   }

   public final String addSymbol() {
      return this.subString(this.mark, this.bufPos);
   }

   public final String subString(int offset, int count) {
      return this.text.substring(offset, offset + count);
   }

   public final char[] sub_chars(int offset, int count) {
      char[] chars = new char[count];
      this.text.getChars(offset, offset + count, chars, 0);
      return chars;
   }

   protected void initBuff(int size) {
      if (this.buf == null) {
         if (size < 32) {
            this.buf = new char[32];
         } else {
            this.buf = new char[size + 32];
         }
      } else if (this.buf.length < size) {
         this.buf = Arrays.copyOf(this.buf, size);
      }

   }

   public void arraycopy(int srcPos, char[] dest, int destPos, int length) {
      this.text.getChars(srcPos, srcPos + length, dest, destPos);
   }

   public boolean isAllowComment() {
      return this.allowComment;
   }

   public void setAllowComment(boolean allowComment) {
      this.allowComment = allowComment;
   }

   public int nextVarIndex() {
      return ++this.varIndex;
   }

   public Keywords getKeywords() {
      return this.keywords;
   }

   public SavePoint mark() {
      SavePoint savePoint = new SavePoint();
      savePoint.bp = this.pos;
      savePoint.sp = this.bufPos;
      savePoint.np = this.mark;
      savePoint.ch = this.ch;
      savePoint.token = this.token;
      savePoint.stringVal = this.stringVal;
      savePoint.hash = this.hash;
      savePoint.hash_lower = this.hash_lower;
      return this.savePoint = savePoint;
   }

   public void reset(SavePoint savePoint) {
      this.pos = savePoint.bp;
      this.bufPos = savePoint.sp;
      this.mark = savePoint.np;
      this.ch = savePoint.ch;
      this.token = savePoint.token;
      this.stringVal = savePoint.stringVal;
      this.hash = savePoint.hash;
      this.hash_lower = savePoint.hash_lower;
   }

   public void reset() {
      this.reset(this.savePoint);
   }

   public void reset(int pos) {
      this.pos = pos;
      this.ch = this.charAt(pos);
   }

   public Lexer(String input, boolean skipComment) {
      this.moneySigns = new char[]{'$', '¢', '£', '¤', '¥', '৲', '৳', '฿', '៛', '₠', '₡', '₢', '₣', '₤', '₥', '₦', '₧', '₨', '₩', '₪', '₫', '€', '₭', '₮', '₯', '₰', '₱', '﷼', '﹩', '︄', '￠', '￡', '￥', '￦'};
      this.features = 0;
      this.keywords = Keywords.DEFAULT_KEYWORDS;
      this.commentCount = 0;
      this.comments = null;
      this.skipComment = true;
      this.savePoint = null;
      this.allowComment = true;
      this.varIndex = -1;
      this.endOfComment = false;
      this.keepComments = false;
      this.line = 0;
      this.lines = 0;
      this.optimizedForParameterized = false;
      this.keepSourceLocation = false;
      this.throwIfStateEnd = true;
      this.skipComment = skipComment;
      this.text = input;
      this.pos = 0;

      for(this.ch = this.charAt(this.pos); this.ch == 8203 || this.ch == '\n'; this.ch = this.charAt(++this.pos)) {
         if (this.ch == '\n') {
            ++this.line;
         }
      }

   }

   public Lexer(char[] input, int inputLength, boolean skipComment) {
      this(new String(input, 0, inputLength), skipComment);
   }

   protected final void scanChar() {
      this.ch = this.charAt(++this.pos);
   }

   protected void unscan() {
      this.ch = this.charAt(--this.pos);
   }

   public boolean isEOF() {
      return this.pos >= this.text.length();
   }

   protected void lexError(String key, Object... args) {
      this.token = Token.ERROR;
   }

   public final Token token() {
      return this.token;
   }

   public void setToken(Token token) {
      this.token = token;
   }

   public final DbType getDbType() {
      return this.dbType;
   }

   public String info() {
      int line = 1;
      int column = 1;

      for(int i = 0; i < this.startPos; ++column) {
         char ch = this.text.charAt(i);
         if (ch == '\n') {
            column = 1;
            ++line;
         }

         ++i;
      }

      this.posLine = line;
      this.posColumn = column;
      StringBuilder buf = new StringBuilder();
      buf.append("pos ").append(this.pos).append(", line ").append(line).append(", column ").append(column);
      if (this.token != null) {
         if (this.token.name != null) {
            buf.append(", token ").append(this.token.name);
         } else {
            buf.append(", token ").append(this.token.name());
         }
      }

      if (this.token == Token.IDENTIFIER || this.token == Token.LITERAL_ALIAS || this.token == Token.LITERAL_CHARS) {
         buf.append(" ").append(this.stringVal());
      }

      if (this.isEnabled(SQLParserFeature.PrintSQLWhileParsingFailed)) {
         buf.append(", SQL : ");
         buf.append(this.text);
      }

      return buf.toString();
   }

   public final void nextTokenComma() {
      if (this.ch == ' ') {
         this.scanChar();
      }

      if (this.ch != ',' && this.ch != '，') {
         if (this.ch != ')' && this.ch != '）') {
            if (this.ch == '.') {
               this.scanChar();
               this.token = Token.DOT;
            } else {
               if (this.ch == 'a' || this.ch == 'A') {
                  char ch_next = this.charAt(this.pos + 1);
                  if (ch_next == 's' || ch_next == 'S') {
                     char ch_next_2 = this.charAt(this.pos + 2);
                     if (ch_next_2 == ' ') {
                        this.pos += 2;
                        this.ch = ' ';
                        this.token = Token.AS;
                        this.stringVal = "AS";
                        return;
                     }
                  }
               }

               this.nextToken();
            }
         } else {
            this.scanChar();
            this.token = Token.RPAREN;
         }
      } else {
         this.scanChar();
         this.token = Token.COMMA;
      }
   }

   public final void nextTokenCommaValue() {
      if (this.ch == ' ') {
         this.scanChar();
      }

      if (this.ch != ',' && this.ch != '，') {
         if (this.ch != ')' && this.ch != '）') {
            if (this.ch == '.') {
               this.scanChar();
               this.token = Token.DOT;
            } else {
               if (this.ch == 'a' || this.ch == 'A') {
                  char ch_next = this.charAt(this.pos + 1);
                  if (ch_next == 's' || ch_next == 'S') {
                     char ch_next_2 = this.charAt(this.pos + 2);
                     if (ch_next_2 == ' ') {
                        this.pos += 2;
                        this.ch = ' ';
                        this.token = Token.AS;
                        this.stringVal = "AS";
                        return;
                     }
                  }
               }

               this.nextTokenValue();
            }
         } else {
            this.scanChar();
            this.token = Token.RPAREN;
         }
      } else {
         this.scanChar();
         this.token = Token.COMMA;
      }
   }

   public final void nextTokenOperator() {
      if (this.ch == ' ') {
         this.scanChar();
      }

      if (this.ch == '.') {
         this.scanChar();
         this.token = Token.DOT;
      } else {
         this.scanOperator();
      }
   }

   public final void nextTokenEq() {
      if (this.ch == ' ') {
         this.scanChar();
      }

      if (this.ch == '=') {
         this.scanChar();
         if (this.ch == '=') {
            this.scanChar();
            this.token = Token.EQEQ;
         } else if (this.ch == '>') {
            this.scanChar();
            this.token = Token.EQGT;
         }

         this.token = Token.EQ;
      } else if (this.ch == '.') {
         this.scanChar();
         this.token = Token.DOT;
      } else {
         if (this.ch == 'a' || this.ch == 'A') {
            char ch_next = this.charAt(this.pos + 1);
            if (ch_next == 's' || ch_next == 'S') {
               char ch_next_2 = this.charAt(this.pos + 2);
               if (ch_next_2 == ' ') {
                  this.pos += 2;
                  this.ch = ' ';
                  this.token = Token.AS;
                  this.stringVal = "AS";
                  return;
               }
            }
         }

         this.nextToken();
      }
   }

   public final void nextTokenLParen() {
      if (this.ch == ' ') {
         this.scanChar();
      }

      if (this.ch != '(' && this.ch != '（') {
         this.nextToken();
      } else {
         this.scanChar();
         this.token = Token.LPAREN;
      }
   }

   public void nextTokenValue() {
      this.startPos = this.pos;

      while(this.ch == ' ') {
         this.scanChar();
      }

      if (this.ch == '\'') {
         this.bufPos = 0;
         if (this.dbType == DbType.mysql) {
            this.scanString2();
         } else {
            this.scanString();
         }

      } else if (this.ch == '"' && !this.isEnabled(SQLParserFeature.KeepNameQuotes)) {
         this.bufPos = 0;
         this.scanString2_d();
      } else if (this.ch == '0') {
         this.bufPos = 0;
         if (this.charAt(this.pos + 1) == 'x') {
            this.scanChar();
            this.scanChar();
            this.scanHexaDecimal();
         } else {
            this.scanNumber();
         }

      } else if (this.ch > '0' && this.ch <= '9') {
         this.bufPos = 0;
         this.scanNumber();
      } else {
         if (this.ch == '-') {
            char next = this.charAt(this.pos + 1);
            if (next >= '0' && next <= '9') {
               this.bufPos = 0;
               this.scanNumber();
               return;
            }
         }

         if (this.ch == '?') {
            this.scanChar();
            this.token = Token.QUES;
         } else {
            if (this.ch == 'n' || this.ch == 'N') {
               char c1 = 0;
               char c2;
               char c3;
               char c4;
               if (this.pos + 4 < this.text.length() && ((c1 = this.text.charAt(this.pos + 1)) == 'u' || c1 == 'U') && ((c2 = this.text.charAt(this.pos + 2)) == 'l' || c2 == 'L') && ((c3 = this.text.charAt(this.pos + 3)) == 'l' || c3 == 'L') && (CharTypes.isWhitespace(c4 = this.text.charAt(this.pos + 4)) || c4 == ',' || c4 == ')' || c4 == '）')) {
                  this.pos += 4;
                  this.ch = c4;
                  this.token = Token.NULL;
                  this.stringVal = "NULL";
                  return;
               }

               if (c1 == '\'') {
                  this.bufPos = 0;
                  ++this.pos;
                  this.ch = '\'';
                  this.scanString();
                  this.token = Token.LITERAL_NCHARS;
                  return;
               }
            }

            if (this.ch != ')' && this.ch != '）') {
               if (this.ch == '$' && isVaraintChar(this.charAt(this.pos + 1))) {
                  this.scanVariable();
               } else if (CharTypes.isFirstIdentifierChar(this.ch)) {
                  this.scanIdentifier();
               } else {
                  this.nextToken();
               }
            } else {
               this.scanChar();
               this.token = Token.RPAREN;
            }
         }
      }
   }

   protected static boolean isVaraintChar(char ch) {
      return ch == '{' || ch >= '0' && ch <= '9';
   }

   public final void nextTokenBy() {
      while(this.ch == ' ') {
         this.scanChar();
      }

      if (this.ch == 'b' || this.ch == 'B') {
         char ch_next = this.charAt(this.pos + 1);
         if (ch_next == 'y' || ch_next == 'Y') {
            char ch_next_2 = this.charAt(this.pos + 2);
            if (ch_next_2 == ' ') {
               this.pos += 2;
               this.ch = ' ';
               this.token = Token.BY;
               this.stringVal = "BY";
               return;
            }
         }
      }

      this.nextToken();
   }

   public final void nextTokenNotOrNull() {
      while(this.ch == ' ') {
         this.scanChar();
      }

      if ((this.ch == 'n' || this.ch == 'N') && this.pos + 3 < this.text.length()) {
         char c1 = this.text.charAt(this.pos + 1);
         char c2 = this.text.charAt(this.pos + 2);
         char c3 = this.text.charAt(this.pos + 3);
         if ((c1 == 'o' || c1 == 'O') && (c2 == 't' || c2 == 'T') && CharTypes.isWhitespace(c3)) {
            this.pos += 3;
            this.ch = c3;
            this.token = Token.NOT;
            this.stringVal = "NOT";
            return;
         }

         char c4;
         if (this.pos + 4 < this.text.length() && (c1 == 'u' || c1 == 'U') && (c2 == 'l' || c2 == 'L') && (c3 == 'l' || c3 == 'L') && CharTypes.isWhitespace(c4 = this.text.charAt(this.pos + 4))) {
            this.pos += 4;
            this.ch = c4;
            this.token = Token.NULL;
            this.stringVal = "NULL";
            return;
         }
      }

      this.nextToken();
   }

   public final void nextTokenIdent() {
      while(this.ch == ' ') {
         this.scanChar();
      }

      if (this.ch == '$' && isVaraintChar(this.charAt(this.pos + 1))) {
         this.scanVariable();
      } else if (CharTypes.isFirstIdentifierChar(this.ch) && this.ch != '（') {
         this.scanIdentifier();
      } else if (this.ch == ')') {
         this.scanChar();
         this.token = Token.RPAREN;
      } else {
         this.nextToken();
      }
   }

   public final SQLType scanSQLType() {
      while(true) {
         if (CharTypes.isWhitespace(this.ch)) {
            this.ch = this.charAt(++this.pos);
         } else if (this.ch == '/' && this.pos + 1 < this.text.length() && this.text.charAt(this.pos + 1) == '*') {
            int index = this.text.indexOf("*/", this.pos + 2);
            if (index == -1) {
               return SQLType.UNKNOWN;
            }

            this.pos = index + 2;
            this.ch = this.charAt(this.pos);
         } else {
            if (this.ch != '-' || this.pos + 1 >= this.text.length() || this.text.charAt(this.pos + 1) != '-') {
               while(this.ch == '(') {
                  for(this.ch = this.charAt(++this.pos); CharTypes.isWhitespace(this.ch); this.ch = this.charAt(++this.pos)) {
                  }
               }

               long hashCode = -3750763034362895579L;

               while(true) {
                  char c;
                  if (this.ch >= 'a' && this.ch <= 'z') {
                     c = this.ch;
                  } else {
                     if (this.ch < 'A' || this.ch > 'Z') {
                        if (this.ch != '_' && (this.ch < '0' || this.ch > '9')) {
                           if (hashCode == FnvHash.Constants.SELECT) {
                              return SQLType.SELECT;
                           }

                           if (hashCode == FnvHash.Constants.INSERT) {
                              return SQLType.INSERT;
                           }

                           if (hashCode == FnvHash.Constants.DELETE) {
                              return SQLType.DELETE;
                           }

                           if (hashCode == FnvHash.Constants.UPDATE) {
                              return SQLType.UPDATE;
                           }

                           if (hashCode == FnvHash.Constants.REPLACE) {
                              return SQLType.REPLACE;
                           }

                           if (hashCode == FnvHash.Constants.TRUNCATE) {
                              return SQLType.TRUNCATE;
                           }

                           if (hashCode == FnvHash.Constants.MERGE) {
                              return SQLType.MERGE;
                           }

                           if (hashCode == FnvHash.Constants.CREATE) {
                              return SQLType.CREATE;
                           }

                           if (hashCode == FnvHash.Constants.ALTER) {
                              return SQLType.ALTER;
                           }

                           if (hashCode == FnvHash.Constants.SHOW) {
                              return SQLType.SHOW;
                           }

                           if (hashCode == FnvHash.Constants.DESC) {
                              return SQLType.DESC;
                           }

                           if (hashCode == FnvHash.Constants.DESCRIBE) {
                              return SQLType.DESC;
                           }

                           if (hashCode == FnvHash.Constants.SET) {
                              return SQLType.SET;
                           }

                           if (hashCode == FnvHash.Constants.KILL) {
                              return SQLType.KILL;
                           }

                           if (hashCode == FnvHash.Constants.MSCK) {
                              return SQLType.MSCK;
                           }

                           if (hashCode == FnvHash.Constants.USE) {
                              return SQLType.USE;
                           }

                           if (hashCode == FnvHash.Constants.DROP) {
                              return SQLType.DROP;
                           }

                           if (hashCode == FnvHash.Constants.LIST) {
                              return SQLType.LIST;
                           }

                           if (hashCode == FnvHash.Constants.ROLLBACK) {
                              return SQLType.ROLLBACK;
                           }

                           if (hashCode == FnvHash.Constants.COMMIT) {
                              return SQLType.COMMIT;
                           }

                           if (hashCode == FnvHash.Constants.WHO) {
                              return SQLType.WHO;
                           }

                           if (hashCode == FnvHash.Constants.GRANT) {
                              return SQLType.GRANT;
                           }

                           if (hashCode == FnvHash.Constants.REVOKE) {
                              return SQLType.REVOKE;
                           }

                           if (hashCode == FnvHash.Constants.ANALYZE) {
                              return SQLType.ANALYZE;
                           }

                           if (hashCode == FnvHash.Constants.EXPLAIN) {
                              return SQLType.EXPLAIN;
                           }

                           if (hashCode == FnvHash.Constants.READ) {
                              return SQLType.READ;
                           }

                           if (hashCode == FnvHash.Constants.WITH) {
                              if (this.dbType != DbType.mysql && this.dbType != DbType.hive && this.dbType != DbType.odps && this.dbType != DbType.oracle) {
                                 return SQLType.UNKNOWN;
                              }

                              return SQLType.SELECT;
                           }

                           if (hashCode == FnvHash.Constants.DUMP) {
                              this.nextToken();
                              if (this.identifierEquals(FnvHash.Constants.DATA)) {
                                 return SQLType.DUMP_DATA;
                              }
                           } else if (hashCode == FnvHash.Constants.ADD) {
                              this.nextToken();
                              if (this.token == Token.USER || this.identifierEquals(FnvHash.Constants.USER)) {
                                 return SQLType.ADD_USER;
                              }

                              if (this.token == Token.TABLE) {
                                 return SQLType.ADD_TABLE;
                              }
                           } else if (hashCode == FnvHash.Constants.REMOVE) {
                              this.nextToken();
                              if (this.token == Token.USER || this.identifierEquals(FnvHash.Constants.USER)) {
                                 return SQLType.REMOVE_USER;
                              }
                           } else if (hashCode == FnvHash.Constants.TUNNEL) {
                              this.nextToken();
                              if (this.identifierEquals(FnvHash.Constants.DOWNLOAD)) {
                                 return SQLType.TUNNEL_DOWNLOAD;
                              }
                           } else {
                              if (hashCode == FnvHash.Constants.UPLOAD) {
                                 return SQLType.UPLOAD;
                              }

                              if (hashCode == FnvHash.Constants.SCAN) {
                                 return SQLType.SCAN;
                              }
                           }

                           return SQLType.UNKNOWN;
                        }

                        return SQLType.UNKNOWN;
                     }

                     c = (char)(this.ch + 32);
                  }

                  hashCode ^= (long)c;
                  hashCode *= 1099511628211L;
                  this.ch = this.charAt(++this.pos);
               }
            }

            int index = this.text.indexOf(10, this.pos + 2);
            if (index == -1) {
               return SQLType.UNKNOWN;
            }

            this.pos = index + 1;
            this.ch = this.charAt(this.pos);
         }
      }
   }

   public final SQLType scanSQLTypeV2() {
      SQLType sqlType = this.scanSQLType();
      if (sqlType == SQLType.CREATE) {
         this.nextToken();
         if (this.token == Token.USER || this.identifierEquals(FnvHash.Constants.USER)) {
            return SQLType.CREATE_USER;
         }

         for(int i = 0; i < 1000; ++i) {
            switch (this.token) {
               case EOF:
               case ERROR:
                  return sqlType;
               case TABLE:
                  sqlType = SQLType.CREATE_TABLE;
                  break;
               case VIEW:
                  sqlType = SQLType.CREATE_VIEW;
                  return sqlType;
               case FUNCTION:
                  sqlType = SQLType.CREATE_FUNCTION;
                  break;
               case SELECT:
                  if (sqlType == SQLType.CREATE_TABLE) {
                     sqlType = SQLType.CREATE_TABLE_AS_SELECT;
                     return sqlType;
                  }
                  break;
               default:
                  if (this.identifierEquals(FnvHash.Constants.ROLE)) {
                     sqlType = SQLType.CREATE_ROLE;
                     return sqlType;
                  }
            }

            this.nextToken();
         }
      } else if (sqlType == SQLType.DROP) {
         this.nextToken();
         if (this.token == Token.USER || this.identifierEquals(FnvHash.Constants.USER)) {
            return SQLType.DROP_USER;
         }

         if (this.token == Token.TABLE) {
            return SQLType.DROP_TABLE;
         }

         if (this.token == Token.VIEW) {
            return SQLType.DROP_VIEW;
         }

         if (this.token == Token.FUNCTION) {
            return SQLType.DROP_FUNCTION;
         }

         if (this.identifierEquals(FnvHash.Constants.RESOURCE)) {
            return SQLType.DROP_RESOURCE;
         }
      } else if (sqlType == SQLType.ALTER) {
         this.nextToken();
         if (this.token == Token.USER || this.identifierEquals(FnvHash.Constants.USER)) {
            return SQLType.ALTER_USER;
         }

         if (this.token == Token.TABLE) {
            return SQLType.ALTER_TABLE;
         }
      } else if (sqlType == SQLType.INSERT) {
         int i = 0;

         while(true) {
            if (i < 1000) {
               this.nextToken();
               if (this.token == Token.SELECT) {
                  return SQLType.INSERT_SELECT;
               }

               if (this.token == Token.VALUES) {
                  return SQLType.INSERT_VALUES;
               }

               if (this.token != Token.ERROR && this.token != Token.EOF) {
                  ++i;
                  continue;
               }
            }

            return sqlType;
         }
      }

      return sqlType;
   }

   public final void nextTokenAlias() {
      this.startPos = this.pos;

      for(this.bufPos = 0; CharTypes.isWhitespace(this.ch); this.startPos = this.pos) {
         if (this.ch == '\n') {
            ++this.line;
         }

         this.ch = this.charAt(++this.pos);
      }

      if (this.ch == '"') {
         this.scanAlias();
      } else if (this.ch == '\'') {
         this.scanAlias();
         int p;
         if (this.stringVal.length() > 1 && this.stringVal.indexOf(34) == -1 && ((p = this.stringVal.indexOf(39, 1)) == -1 || p == this.stringVal.length() - 1)) {
            char[] chars = this.stringVal.toCharArray();
            chars[0] = '"';
            chars[chars.length - 1] = '"';
            this.stringVal = new String(chars);
         }

         this.token = Token.LITERAL_ALIAS;
      } else {
         this.nextToken();
      }

   }

   public final void nextPath() {
      while(CharTypes.isWhitespace(this.ch)) {
         this.ch = this.charAt(++this.pos);
      }

      this.stringVal = null;

      for(this.mark = this.pos; !CharTypes.isWhitespace(this.ch); this.ch = this.charAt(++this.pos)) {
      }

      this.bufPos = this.pos - this.mark;
      this.ch = this.charAt(++this.pos);
      this.token = Token.LITERAL_PATH;
   }

   public void nextToken() {
      this.startPos = this.pos;
      this.bufPos = 0;
      if (this.comments != null && this.comments.size() > 0) {
         this.comments = null;
      }

      this.lines = 0;
      int startLine = this.line;

      while(true) {
         while(CharTypes.isWhitespace(this.ch)) {
            if (this.ch == '\n') {
               ++this.line;
               this.lines = this.line - startLine;
            }

            this.ch = this.charAt(++this.pos);
            this.startPos = this.pos;
         }

         if (this.ch == '$' && isVaraintChar(this.charAt(this.pos + 1))) {
            this.scanVariable();
            return;
         }

         if (this.isMoney(this.ch) && DbType.sqlserver == this.dbType) {
            this.scanMoney();
            return;
         }

         if (this.dbType == DbType.kingbase && (this.ch == 'B' || this.ch == 'b')) {
            this.scanChar();
            if (this.ch == '\'') {
               this.scanString();
               this.token = Token.LITERAL_BCHARS;
               return;
            }

            this.unscan();
         }

         if (CharTypes.isFirstIdentifierChar(this.ch)) {
            if (this.ch == '（') {
               this.scanChar();
               this.token = Token.LPAREN;
               return;
            }

            if (this.ch == '）') {
               this.scanChar();
               this.token = Token.RPAREN;
               return;
            }

            if ((this.ch == 'N' || this.ch == 'n') && this.charAt(this.pos + 1) == '\'') {
               ++this.pos;
               this.ch = '\'';
               this.scanString();
               this.token = Token.LITERAL_NCHARS;
               return;
            }

            this.scanIdentifier();
            return;
         }

         switch (this.ch) {
            case '"':
               this.scanAlias();
               return;
            case '#':
               this.scanSharp();
               if (this.token != Token.LINE_COMMENT && this.token != Token.MULTI_LINE_COMMENT || !this.skipComment) {
                  return;
               }

               this.bufPos = 0;
               break;
            case '\'':
               this.scanString();
               return;
            case '(':
            case '（':
               this.scanChar();
               this.token = Token.LPAREN;
               return;
            case ')':
            case '）':
               this.scanChar();
               this.token = Token.RPAREN;
               return;
            case '*':
               this.scanChar();
               if (this.ch == '=' && DbType.sqlserver == this.dbType) {
                  this.scanChar();
                  this.token = Token.MultiplyAssignment;
               } else {
                  this.token = Token.STAR;
               }

               return;
            case ',':
            case '，':
               this.scanChar();
               this.token = Token.COMMA;
               return;
            case '-':
               char next = this.charAt(this.pos + 1);
               if (next == '-') {
                  this.scanComment();
                  if ((this.token == Token.LINE_COMMENT || this.token == Token.MULTI_LINE_COMMENT) && this.skipComment) {
                     this.bufPos = 0;
                     break;
                  }
               } else if (this.isMoney(next) && DbType.sqlserver == this.dbType) {
                  this.scanMoney();
               } else if (next >= '0' && next <= '9') {
                  if (this.token == null) {
                     this.scanNumber();
                     return;
                  }

                  switch (this.token) {
                     case COMMA:
                     case LPAREN:
                     case WITH:
                     case BY:
                        this.scanNumber();
                        break;
                     default:
                        this.scanOperator();
                  }
               } else {
                  this.scanOperator();
               }

               return;
            case '.':
               this.scanChar();
               if (!this.isDigit(this.ch) || this.pos != 1 && this.token == Token.IDENTIFIER) {
                  if (this.ch == '.') {
                     this.scanChar();
                     if (this.ch == '.') {
                        this.scanChar();
                        this.token = Token.DOTDOTDOT;
                     } else {
                        this.token = Token.DOTDOT;
                     }
                  } else {
                     this.token = Token.DOT;
                  }

                  return;
               }

               this.unscan();
               this.scanNumber();
               return;
            case '/':
               char nextChar = this.charAt(this.pos + 1);
               if (nextChar == '/' || nextChar == '*' || nextChar == '!' && this.isEnabled(SQLParserFeature.TDDLHint)) {
                  this.scanComment();
                  if ((this.token == Token.LINE_COMMENT || this.token == Token.MULTI_LINE_COMMENT) && this.skipComment) {
                     this.bufPos = 0;
                     break;
                  }
               } else if (nextChar == ' ' && this.charAt(this.pos + 2) == '*') {
                  this.scanComment();
                  if ((this.token == Token.LINE_COMMENT || this.token == Token.MULTI_LINE_COMMENT) && this.skipComment) {
                     this.bufPos = 0;
                     break;
                  }
               } else {
                  this.scanChar();
                  if (this.ch == '=' && DbType.sqlserver == this.dbType) {
                     this.scanChar();
                     this.token = Token.DivideAssignment;
                  } else {
                     this.token = Token.SLASH;
                  }
               }

               return;
            case '0':
               if (this.charAt(this.pos + 1) == 'x') {
                  this.scanChar();
                  this.scanChar();
                  this.scanHexaDecimal();
               } else {
                  this.scanNumber();
               }

               return;
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
               this.scanNumber();
               return;
            case ':':
               this.scanChar();
               if (this.ch == '=') {
                  this.scanChar();
                  this.token = Token.COLONEQ;
               } else if (this.ch == ':') {
                  this.scanChar();
                  this.token = Token.COLONCOLON;
               } else {
                  if (this.isEnabled(SQLParserFeature.TDDLHint) || this.dbType == DbType.hive || this.dbType == DbType.odps) {
                     this.token = Token.COLON;
                     return;
                  }

                  this.unscan();
                  this.scanVariable();
               }

               return;
            case ';':
               this.scanChar();
               this.token = Token.SEMI;
               return;
            case '?':
               this.scanChar();
               if (this.ch == '?' && DbType.postgresql == this.dbType) {
                  this.scanChar();
                  if (this.ch == '|') {
                     this.scanChar();
                     this.token = Token.QUESBAR;
                  } else {
                     this.token = Token.QUESQUES;
                  }
               } else if (this.ch == '|' && DbType.postgresql == this.dbType) {
                  this.scanChar();
                  if (this.ch == '|') {
                     this.unscan();
                     this.token = Token.QUES;
                  } else {
                     this.token = Token.QUESBAR;
                  }
               } else if (this.ch == '&' && DbType.postgresql == this.dbType) {
                  this.scanChar();
                  this.token = Token.QUESAMP;
               } else {
                  this.token = Token.QUES;
               }

               return;
            case '@':
               this.scanVariable_at();
               return;
            case '[':
               this.scanLBracket();
               return;
            case ']':
               this.scanChar();
               this.token = Token.RBRACKET;
               return;
            case '`':
               throw new ParserException("TODO. " + this.info());
            case '{':
               this.scanChar();
               this.token = Token.LBRACE;
               return;
            case '}':
               this.scanChar();
               this.token = Token.RBRACE;
               return;
            default:
               if (Character.isLetter(this.ch)) {
                  this.scanIdentifier();
                  return;
               }

               if (this.isOperator(this.ch)) {
                  this.scanOperator();
                  return;
               }

               if (this.ch == '\\' && this.charAt(this.pos + 1) == 'N' && DbType.mysql == this.dbType) {
                  this.scanChar();
                  this.scanChar();
                  this.token = Token.NULL;
                  return;
               }

               if (this.isEOF()) {
                  this.token = Token.EOF;
               } else {
                  this.lexError("illegal.char", String.valueOf(this.ch));
                  this.scanChar();
               }

               return;
         }
      }
   }

   protected void scanMoney() {
      int offset = 0;
      int temp_mark = this.pos;
      if (this.ch == '-' || this.ch == '+') {
         this.ch = this.charAt(++this.pos);
         ++offset;
      }

      if (this.isMoney(this.ch)) {
         this.ch = this.charAt(++this.pos);
         ++offset;
      }

      this.scanNumber();
      this.mark = temp_mark;
      this.bufPos += offset;
      this.token = Token.MONEY;
      this.stringVal = this.subString(this.mark, this.bufPos);
   }

   protected boolean isMoney(char b) {
      for(char a : this.moneySigns) {
         if (b == a) {
            return true;
         }
      }

      return false;
   }

   protected void scanLBracket() {
      this.scanChar();
      this.token = Token.LBRACKET;
   }

   protected final void scanOperator() {
      switch (this.ch) {
         case '!':
            this.scanChar();

            while(CharTypes.isWhitespace(this.ch)) {
               this.scanChar();
            }

            if (this.ch == '=') {
               this.scanChar();
               this.token = Token.BANGEQ;
            } else if (this.ch == '>') {
               this.scanChar();
               this.token = Token.BANGGT;
            } else if (this.ch == '<') {
               this.scanChar();
               this.token = Token.BANGLT;
            } else if (this.ch == '!') {
               this.scanChar();
               this.token = Token.BANGBANG;
            } else if (this.ch == '~') {
               this.scanChar();
               if (this.ch == '*') {
                  this.scanChar();
                  this.token = Token.BANG_TILDE_STAR;
               } else {
                  this.token = Token.BANG_TILDE;
               }
            } else {
               this.token = Token.BANG;
            }
            break;
         case '%':
            this.scanChar();
            if (this.ch == '=' && DbType.sqlserver == this.dbType) {
               this.scanChar();
               this.token = Token.ModulusAssignment;
            } else {
               this.token = Token.PERCENT;
            }
            break;
         case '&':
            this.scanChar();
            if (this.ch == '&') {
               this.scanChar();
               this.token = Token.AMPAMP;
            } else if (this.ch == '=' && DbType.sqlserver == this.dbType) {
               this.scanChar();
               this.token = Token.BitwiseAndAssignment;
            } else {
               this.token = Token.AMP;
            }
            break;
         case '*':
            this.scanChar();
            if (this.ch == '=' && DbType.sqlserver == this.dbType) {
               this.scanChar();
               this.token = Token.MultiplyAssignment;
            } else {
               this.token = Token.STAR;
            }
            break;
         case '+':
            char next = this.charAt(this.pos + 1);
            if (this.isMoney(next) && DbType.sqlserver == this.dbType) {
               this.scanMoney();
            } else {
               this.scanChar();
               if (this.ch == '=' && DbType.sqlserver == this.dbType) {
                  this.scanChar();
                  this.token = Token.AddAssignment;
               } else {
                  this.token = Token.PLUS;
               }
            }
            break;
         case '-':
            this.scanChar();
            if (this.ch == '>') {
               this.scanChar();
               if (this.ch == '>') {
                  this.scanChar();
                  this.token = Token.SUBGTGT;
               } else {
                  this.token = Token.SUBGT;
               }
            } else if (this.ch == '=' && DbType.sqlserver == this.dbType) {
               this.scanChar();
               this.token = Token.SubtractAssignment;
            } else {
               this.token = Token.SUB;
            }
            break;
         case '/':
            this.scanChar();
            if (this.ch == '=' && DbType.sqlserver == this.dbType) {
               this.scanChar();
               this.token = Token.DivideAssignment;
            } else {
               this.token = Token.SLASH;
            }
            break;
         case '<':
            this.scanChar();
            if (this.ch == '=') {
               this.scanChar();
               if (this.ch == '>') {
                  this.token = Token.LTEQGT;
                  this.scanChar();
               } else {
                  this.token = Token.LTEQ;
               }
            } else if (this.ch == '>') {
               this.scanChar();
               this.token = Token.LTGT;
            } else if (this.ch == '<') {
               this.scanChar();
               this.token = Token.LTLT;
            } else if (this.ch == '@') {
               this.scanChar();
               this.token = Token.LT_MONKEYS_AT;
            } else if (this.ch == '-' && this.charAt(this.pos + 1) == '>') {
               this.scanChar();
               this.scanChar();
               this.token = Token.LT_SUB_GT;
            } else if (this.ch == ' ') {
               char c1 = this.charAt(this.pos + 1);
               if (c1 == '=') {
                  this.scanChar();
                  this.scanChar();
                  if (this.ch == '>') {
                     this.token = Token.LTEQGT;
                     this.scanChar();
                  } else {
                     this.token = Token.LTEQ;
                  }
               } else if (c1 == '>') {
                  this.scanChar();
                  this.scanChar();
                  this.token = Token.LTGT;
               } else if (c1 == '<') {
                  this.scanChar();
                  this.scanChar();
                  this.token = Token.LTLT;
               } else if (c1 == '@') {
                  this.scanChar();
                  this.scanChar();
                  this.token = Token.LT_MONKEYS_AT;
               } else if (c1 == '-' && this.charAt(this.pos + 2) == '>') {
                  this.scanChar();
                  this.scanChar();
                  this.scanChar();
                  this.token = Token.LT_SUB_GT;
               } else {
                  this.token = Token.LT;
               }
            } else {
               this.token = Token.LT;
            }
            break;
         case '=':
            this.scanChar();
            if (this.ch == ' ') {
               this.scanChar();
            }

            if (this.ch == '=') {
               this.scanChar();
               this.token = Token.EQEQ;
            } else if (this.ch == '>') {
               this.scanChar();
               this.token = Token.EQGT;
            } else {
               this.token = Token.EQ;
            }
            break;
         case '>':
            this.scanChar();
            if (this.ch == '=') {
               this.scanChar();
               this.token = Token.GTEQ;
            } else if (this.ch == '>') {
               this.scanChar();
               this.token = Token.GTGT;
            } else {
               this.token = Token.GT;
            }
            break;
         case '?':
            this.scanChar();
            this.token = Token.QUES;
            break;
         case '^':
            this.scanChar();
            if (this.ch == '=' && DbType.sqlserver == this.dbType) {
               this.scanChar();
               this.token = Token.CARETEQ;
            } else {
               this.token = Token.CARET;
            }
            break;
         case '|':
            this.scanChar();
            if (this.ch == '|') {
               this.scanChar();
               if (this.ch == '/') {
                  this.scanChar();
                  this.token = Token.BARBARSLASH;
               } else {
                  this.token = Token.BARBAR;
               }
            } else if (this.ch == '=' && DbType.sqlserver == this.dbType) {
               this.scanChar();
               this.token = Token.BitwiseOrAssignment;
            } else if (this.ch == '/') {
               this.scanChar();
               this.token = Token.BARSLASH;
            } else {
               this.token = Token.BAR;
            }
            break;
         case '~':
            this.scanChar();
            if (this.ch == '*') {
               this.scanChar();
               this.token = Token.TILDE_STAR;
            } else if (this.ch == '=') {
               this.scanChar();
               this.token = Token.TILDE_EQ;
            } else {
               this.token = Token.TILDE;
            }
            break;
         default:
            throw new ParserException("TODO. " + this.info());
      }

   }

   protected void scanString() {
      this.mark = this.pos;
      boolean hasSpecial = false;
      Token preToken = this.token;

      while(!this.isEOF()) {
         this.ch = this.charAt(++this.pos);
         if (this.ch == '\'') {
            this.scanChar();
            if (this.ch != '\'') {
               this.token = Token.LITERAL_CHARS;
               if (!hasSpecial) {
                  if (preToken == Token.AS) {
                     this.stringVal = this.subString(this.mark, this.bufPos + 2);
                  } else {
                     this.stringVal = this.subString(this.mark + 1, this.bufPos);
                  }
               } else {
                  this.stringVal = new String(this.buf, 0, this.bufPos);
               }

               return;
            }

            if (!hasSpecial) {
               this.initBuff(this.bufPos);
               this.arraycopy(this.mark + 1, this.buf, 0, this.bufPos);
               hasSpecial = true;
            }

            this.putChar('\'');
         } else if (!hasSpecial) {
            ++this.bufPos;
         } else if (this.bufPos == this.buf.length) {
            this.putChar(this.ch);
         } else {
            this.buf[this.bufPos++] = this.ch;
         }
      }

      this.lexError("unclosed.str.lit");
   }

   protected final void scanString2() {
      boolean hasSpecial = false;
      int startIndex = this.pos + 1;
      int endIndex = -1;

      for(int i = startIndex; i < this.text.length(); ++i) {
         char ch = this.text.charAt(i);
         if (ch == '\\') {
            hasSpecial = true;
         } else if (ch == '\'') {
            endIndex = i;
            break;
         }
      }

      if (endIndex == -1) {
         throw new ParserException("unclosed str. " + this.info());
      } else {
         String stringVal;
         if (this.token == Token.AS) {
            stringVal = this.text.substring(this.pos, endIndex + 1);
         } else if (startIndex == endIndex) {
            stringVal = "";
         } else {
            stringVal = this.text.substring(startIndex, endIndex);
         }

         if (!hasSpecial) {
            this.stringVal = stringVal;
            int pos = endIndex + 1;
            char ch = this.charAt(pos);
            if (ch != '\'') {
               this.pos = pos;
               this.ch = ch;
               this.token = Token.LITERAL_CHARS;
               return;
            }
         }

         this.mark = this.pos;
         hasSpecial = false;

         while(!this.isEOF()) {
            this.ch = this.charAt(++this.pos);
            if (this.ch == '\\') {
               this.scanChar();
               if (!hasSpecial) {
                  this.initBuff(this.bufPos);
                  this.arraycopy(this.mark + 1, this.buf, 0, this.bufPos);
                  hasSpecial = true;
               }

               switch (this.ch) {
                  case '"':
                     this.putChar('"');
                     break;
                  case '%':
                     if (this.dbType == DbType.mysql) {
                        this.putChar('\\');
                     }

                     this.putChar('%');
                     break;
                  case '\'':
                     this.putChar('\'');
                     break;
                  case '0':
                     this.putChar('\u0000');
                     break;
                  case 'Z':
                     this.putChar('\u001a');
                     break;
                  case '\\':
                     this.putChar('\\');
                     break;
                  case '_':
                     if (this.dbType == DbType.mysql) {
                        this.putChar('\\');
                     }

                     this.putChar('_');
                     break;
                  case 'b':
                     this.putChar('\b');
                     break;
                  case 'n':
                     this.putChar('\n');
                     break;
                  case 'r':
                     this.putChar('\r');
                     break;
                  case 't':
                     this.putChar('\t');
                     break;
                  case 'u':
                     if ((this.features & SQLParserFeature.SupportUnicodeCodePoint.mask) != 0) {
                        startIndex = this.charAt(++this.pos);
                        endIndex = this.charAt(++this.pos);
                        char c3 = this.charAt(++this.pos);
                        char c4 = this.charAt(++this.pos);
                        int intVal = Integer.parseInt(new String(new char[]{(char)startIndex, (char)endIndex, c3, c4}), 16);
                        this.putChar((char)intVal);
                     } else {
                        this.putChar(this.ch);
                     }
                     break;
                  default:
                     this.putChar(this.ch);
               }
            } else if (this.ch == '\'') {
               this.scanChar();
               if (this.ch != '\'') {
                  this.token = Token.LITERAL_CHARS;
                  if (!hasSpecial) {
                     this.stringVal = this.subString(this.mark + 1, this.bufPos);
                  } else {
                     this.stringVal = new String(this.buf, 0, this.bufPos);
                  }

                  return;
               }

               if (!hasSpecial) {
                  this.initBuff(this.bufPos);
                  this.arraycopy(this.mark + 1, this.buf, 0, this.bufPos);
                  hasSpecial = true;
               }

               this.putChar('\'');
            } else if (!hasSpecial) {
               ++this.bufPos;
            } else if (this.bufPos == this.buf.length) {
               this.putChar(this.ch);
            } else {
               this.buf[this.bufPos++] = this.ch;
            }
         }

         this.lexError("unclosed.str.lit");
      }
   }

   protected final void scanString2_d() {
      boolean hasSpecial = false;
      int startIndex = this.pos + 1;
      int endIndex = -1;

      for(int i = startIndex; i < this.text.length(); ++i) {
         char ch = this.text.charAt(i);
         if (ch == '\\') {
            int i1 = i + 1;
            if (i1 < this.text.length()) {
               hasSpecial = true;
               ++i;
               continue;
            }
         }

         if (ch == '"') {
            int i1 = i + 1;
            if (i1 >= this.text.length() || this.text.charAt(i1) != '"') {
               endIndex = i;
               break;
            }

            hasSpecial = true;
            ++i;
         }
      }

      if (endIndex == -1) {
         throw new ParserException("unclosed str. " + this.info());
      } else {
         String stringVal;
         if (this.token == Token.AS) {
            stringVal = this.subString(this.pos, endIndex + 1 - this.pos);
         } else if (this.charAt(endIndex + 1) == '.') {
            stringVal = this.subString(startIndex - 1, endIndex - startIndex + 2);
         } else {
            stringVal = this.subString(startIndex, endIndex - startIndex);
         }

         if (!hasSpecial) {
            this.stringVal = stringVal;
            int pos = endIndex + 1;
            char ch = this.charAt(pos);
            if (ch == '.') {
               this.pos = pos;
               this.ch = ch;
               this.token = Token.IDENTIFIER;
               return;
            }

            if (ch != '\'') {
               this.pos = pos;
               this.ch = ch;
               this.token = Token.LITERAL_CHARS;
               return;
            }
         }

         this.mark = this.pos;
         hasSpecial = false;

         while(!this.isEOF()) {
            this.ch = this.charAt(++this.pos);
            if (this.ch == '\\') {
               this.scanChar();
               if (!hasSpecial) {
                  this.initBuff(this.bufPos);
                  this.arraycopy(this.mark + 1, this.buf, 0, this.bufPos);
                  hasSpecial = true;
               }

               switch (this.ch) {
                  case '"':
                     this.putChar('"');
                     break;
                  case '%':
                     if (this.dbType == DbType.mysql) {
                        this.putChar('\\');
                     }

                     this.putChar('%');
                     break;
                  case '\'':
                     this.putChar('\'');
                     break;
                  case '0':
                     this.putChar('\u0000');
                     break;
                  case 'Z':
                     this.putChar('\u001a');
                     break;
                  case '\\':
                     this.putChar('\\');
                     break;
                  case '_':
                     if (this.dbType == DbType.mysql) {
                        this.putChar('\\');
                     }

                     this.putChar('_');
                     break;
                  case 'b':
                     this.putChar('\b');
                     break;
                  case 'n':
                     this.putChar('\n');
                     break;
                  case 'r':
                     this.putChar('\r');
                     break;
                  case 't':
                     this.putChar('\t');
                     break;
                  default:
                     this.putChar(this.ch);
               }
            } else if (this.ch == '"') {
               this.scanChar();
               if (this.ch != '"') {
                  if (this.buf != null && this.bufPos > 0) {
                     this.stringVal = new String(this.buf, 0, this.bufPos);
                  }

                  this.token = Token.LITERAL_CHARS;
                  if (!hasSpecial) {
                     this.stringVal = this.subString(this.mark + 1, this.bufPos);
                  } else {
                     this.stringVal = new String(this.buf, 0, this.bufPos);
                  }

                  return;
               }

               if (!hasSpecial) {
                  this.initBuff(this.bufPos);
                  this.arraycopy(this.mark + 1, this.buf, 0, this.bufPos);
                  hasSpecial = true;
               }

               this.putChar('"');
            } else if (!hasSpecial) {
               ++this.bufPos;
            } else if (this.bufPos == this.buf.length) {
               this.putChar(this.ch);
            } else {
               this.buf[this.bufPos++] = this.ch;
            }
         }

         this.lexError("unclosed.str.lit");
      }
   }

   protected final void scanAlias() {
      char quote = this.ch;
      boolean hasSpecial = false;
      int startIndex = this.pos + 1;
      int endIndex = -1;
      int i = startIndex;

      while(i < this.text.length()) {
         label78: {
            char ch = this.text.charAt(i);
            if (ch == '\\') {
               hasSpecial = true;
               ++i;
            } else if (ch == quote) {
               if (i + 1 >= this.text.length()) {
                  break label78;
               }

               char ch_next = this.charAt(i + 1);
               if (ch_next != quote) {
                  break label78;
               }

               hasSpecial = true;
               ++i;
            }

            ++i;
            continue;
         }

         endIndex = i;
         break;
      }

      if (endIndex == -1) {
         throw new ParserException("unclosed str. " + this.info());
      } else {
         String stringVal = this.subString(this.pos, endIndex + 1 - this.pos);
         if (!hasSpecial) {
            this.stringVal = stringVal;
            int pos = endIndex + 1;
            char ch = this.charAt(pos);
            if (ch != '\'') {
               this.pos = pos;
               this.ch = ch;
               this.token = Token.LITERAL_ALIAS;
               return;
            }
         }

         this.mark = this.pos;
         this.initBuff(this.bufPos);
         this.putChar(this.ch);

         while(!this.isEOF()) {
            this.ch = this.charAt(++this.pos);
            if (this.ch == '\\') {
               this.scanChar();
               switch (this.ch) {
                  case '"':
                     if (this.ch == quote) {
                        this.putChar('\\');
                     }

                     this.putChar('"');
                     break;
                  case '\'':
                     if (this.ch == quote) {
                        this.putChar('\\');
                     }

                     this.putChar('\'');
                     break;
                  case '0':
                     this.putChar('\u0000');
                     break;
                  case 'Z':
                     this.putChar((char) 0x1A);
                     break;
                  case '\\':
                     this.putChar('\\');
                     this.putChar('\\');
                     break;
                  case 'b':
                     this.putChar('\b');
                     break;
                  case 'n':
                     this.putChar('\n');
                     break;
                  case 'r':
                     this.putChar('\r');
                     break;
                  case 't':
                     this.putChar('\t');
                     break;
                  case 'u':
                     if (this.dbType == DbType.hive) {
                        char h1 = this.charAt(++this.pos);
                        startIndex = this.charAt(++this.pos);
                        endIndex = this.charAt(++this.pos);
                        char c4 = this.charAt(++this.pos);
                        int intVal = Integer.parseInt(new String(new char[]{h1, (char)startIndex, (char)endIndex, c4}), 16);
                        this.putChar((char)intVal);
                     } else {
                        this.putChar(this.ch);
                     }
                     break;
                  default:
                     this.putChar(this.ch);
               }
            } else if (this.ch == quote) {
               char h2 = this.charAt(this.pos + 1);
               if (h2 != quote) {
                  this.putChar(this.ch);
                  this.scanChar();
                  this.token = Token.LITERAL_ALIAS;
                  this.stringVal = new String(this.buf, 0, this.bufPos);
                  return;
               }

               this.putChar('\\');
               this.putChar(this.ch);
               this.scanChar();
            } else if (this.bufPos == this.buf.length) {
               this.putChar(this.ch);
            } else {
               this.buf[this.bufPos++] = this.ch;
            }
         }

         this.lexError("unclosed.str.lit");
      }
   }

   public void scanSharp() {
      this.scanVariable();
   }

   public void scanVariable() {
      if (this.ch == ':' || this.ch == '#' || this.ch == '$' || this.ch == '@' && this.dbType == DbType.odps) {
         this.mark = this.pos;
         this.bufPos = 1;
         char c1 = this.charAt(this.pos + 1);
         if (c1 == '>' && DbType.postgresql == this.dbType) {
            this.pos += 2;
            this.token = Token.MONKEYS_AT_GT;
            this.ch = this.charAt(++this.pos);
         } else if (c1 == '{') {
            ++this.pos;
            ++this.bufPos;

            while(true) {
               char ch = this.charAt(++this.pos);
               if (ch == '}') {
                  if (ch != '}') {
                     throw new ParserException("syntax error. " + this.info());
                  } else {
                     ++this.pos;
                     ++this.bufPos;
                     this.ch = this.charAt(this.pos);
                     if (this.dbType == DbType.odps) {
                        while(this.isDigit(this.ch)) {
                           ++this.pos;
                           ++this.bufPos;
                           this.ch = this.charAt(this.pos);
                        }
                     }

                     this.stringVal = this.addSymbol();
                     this.token = Token.VARIANT;
                     return;
                  }
               }

               ++this.bufPos;
            }
         } else {
            while(true) {
               char ch = this.charAt(++this.pos);
               if (!CharTypes.isIdentifierChar(ch)) {
                  this.ch = this.charAt(this.pos);
                  this.stringVal = this.addSymbol();
                  this.token = Token.VARIANT;
                  return;
               }

               ++this.bufPos;
            }
         }
      } else {
         throw new ParserException("illegal variable. " + this.info());
      }
   }

   protected void scanVariable_at() {
      if (this.ch != '@') {
         throw new ParserException("illegal variable. " + this.info());
      } else {
         this.mark = this.pos;
         this.bufPos = 1;
         char c1 = this.charAt(this.pos + 1);
         if (c1 == '@') {
            ++this.pos;
            ++this.bufPos;
         }

         while(true) {
            char ch = this.charAt(++this.pos);
            if (!CharTypes.isIdentifierChar(ch)) {
               this.ch = this.charAt(this.pos);
               this.stringVal = this.addSymbol();
               this.token = Token.VARIANT;
               return;
            }

            ++this.bufPos;
         }
      }
   }

   public void scanComment() {
      if (!this.allowComment) {
         throw new NotAllowCommentException();
      } else {
         if ((this.ch != '/' || this.charAt(this.pos + 1) != '/') && (this.ch != '-' || this.charAt(this.pos + 1) != '-')) {
            if (this.ch != '/' || this.charAt(this.pos + 1) != '*') {
               throw new IllegalStateException();
            }

            this.scanMultiLineComment();
         } else {
            this.scanSingleLineComment();
         }

      }
   }

   protected final void scanHiveComment() {
      if (this.ch != '/' && this.ch != '-') {
         throw new IllegalStateException();
      } else {
         Token lastToken = this.token;
         this.mark = this.pos;
         this.bufPos = 0;
         this.scanChar();
         if (this.ch == ' ') {
            this.mark = this.pos;
            this.bufPos = 0;
            this.scanChar();
         }

         if (this.ch == '*') {
            this.scanChar();
            ++this.bufPos;

            while(this.ch == ' ') {
               this.scanChar();
               ++this.bufPos;
            }

            boolean isHint = false;
            int startHintSp = this.bufPos + 1;
            if (this.ch == '+') {
               isHint = true;
               this.scanChar();
               ++this.bufPos;
            }

            char c2;
            while(this.ch != '*' || (c2 = this.charAt(this.pos + 1)) != '/' && (c2 != ' ' || this.charAt(this.pos + 2) != '/')) {
               this.scanChar();
               ++this.bufPos;
            }

            if (c2 == ' ') {
               ++this.bufPos;
               this.scanChar();
            }

            this.bufPos += 2;
            this.scanChar();
            this.scanChar();
            if (isHint) {
               this.stringVal = this.subString(this.mark + startHintSp, this.bufPos - startHintSp - 1);
               this.token = Token.HINT;
            } else {
               this.stringVal = this.subString(this.mark, this.bufPos + 1);
               this.token = Token.MULTI_LINE_COMMENT;
               ++this.commentCount;
               if (this.keepComments) {
                  this.addComment(this.stringVal);
               }
            }

            if (this.commentHandler == null || !this.commentHandler.handle(lastToken, this.stringVal)) {
               if (this.token != Token.HINT && !this.isAllowComment()) {
                  throw new NotAllowCommentException();
               }
            }
         } else if (!this.isAllowComment()) {
            throw new NotAllowCommentException();
         } else if (this.ch == '/' || this.ch == '-') {
            this.scanChar();
            ++this.bufPos;

            while(true) {
               if (this.ch == '\r') {
                  if (this.charAt(this.pos + 1) == '\n') {
                     ++this.line;
                     this.bufPos += 2;
                     this.scanChar();
                  } else {
                     ++this.bufPos;
                  }
                  break;
               }

               if (this.ch == 26) {
                  break;
               }

               if (this.ch == '\n') {
                  ++this.line;
                  this.scanChar();
                  ++this.bufPos;
                  break;
               }

               this.scanChar();
               ++this.bufPos;
            }

            this.stringVal = this.subString(this.mark, this.ch != 26 ? this.bufPos : this.bufPos + 1);
            this.token = Token.LINE_COMMENT;
            ++this.commentCount;
            if (this.keepComments) {
               this.addComment(this.stringVal);
            }

            this.endOfComment = this.isEOF();
            if (this.commentHandler == null || !this.commentHandler.handle(lastToken, this.stringVal)) {
               ;
            }
         }
      }
   }

   private void scanMultiLineComment() {
      Token lastToken = this.token;
      int depth = 1;
      this.scanChar();
      this.scanChar();
      this.mark = this.pos;
      this.bufPos = 0;

      while(true) {
         if (this.ch == '/' && this.charAt(this.pos + 1) == '*') {
            this.scanChar();
            this.scanChar();
            if (this.ch == '!' || this.ch == '+') {
               this.scanChar();
               ++depth;
            }
         }

         if (this.ch == '*' && this.charAt(this.pos + 1) == '/') {
            this.scanChar();
            this.scanChar();
            --depth;
            if (0 == depth) {
               this.stringVal = this.subString(this.mark, this.bufPos);
               this.token = Token.MULTI_LINE_COMMENT;
               ++this.commentCount;
               if (this.keepComments) {
                  this.addComment(this.stringVal);
               }

               if (this.commentHandler != null && this.commentHandler.handle(lastToken, this.stringVal)) {
                  return;
               }

               if (!this.isAllowComment() && !this.isSafeComment(this.stringVal)) {
                  throw new NotAllowCommentException();
               }

               return;
            }
         }

         if (this.ch == 26) {
            throw new ParserException("unterminated /* comment. " + this.info());
         }

         this.scanChar();
         ++this.bufPos;
      }
   }

   private void scanSingleLineComment() {
      Token lastToken = this.token;
      this.mark = this.pos;
      this.bufPos = 2;
      this.scanChar();
      this.scanChar();

      while(true) {
         if (this.ch == '\r') {
            if (this.charAt(this.pos + 1) == '\n') {
               ++this.line;
               this.scanChar();
            } else {
               ++this.bufPos;
            }
            break;
         }

         if (this.ch == '\n') {
            ++this.line;
            this.scanChar();
            break;
         }

         if (this.ch == 26) {
            break;
         }

         this.scanChar();
         ++this.bufPos;
      }

      this.stringVal = this.subString(this.mark, this.bufPos);
      this.token = Token.LINE_COMMENT;
      ++this.commentCount;
      if (this.keepComments) {
         this.addComment(this.stringVal);
      }

      if (this.commentHandler == null || !this.commentHandler.handle(lastToken, this.stringVal)) {
         if (!this.isAllowComment() && !this.isSafeComment(this.stringVal)) {
            throw new NotAllowCommentException();
         }
      }
   }

   public void scanIdentifier() {
      this.hash_lower = 0L;
      this.hash = 0L;
      char first = this.ch;
      if (this.ch == '`') {
         this.mark = this.pos;
         this.bufPos = 1;
         int startPos = this.pos + 1;
         int quoteIndex = this.text.indexOf(96, startPos);
         if (quoteIndex == -1) {
            throw new ParserException("illegal identifier. " + this.info());
         } else {
            this.hash_lower = -3750763034362895579L;
            this.hash = -3750763034362895579L;

            for(int i = startPos; i < quoteIndex; ++i) {
               char ch = this.text.charAt(i);
               this.hash_lower ^= (long)(ch >= 'A' && ch <= 'Z' ? ch + 32 : ch);
               this.hash_lower *= 1099511628211L;
               this.hash ^= (long)ch;
               this.hash *= 1099511628211L;
            }

            this.stringVal = MySqlLexer.quoteTable.addSymbol(this.text, this.pos, quoteIndex + 1 - this.pos, this.hash);
            this.pos = quoteIndex + 1;
            this.ch = this.charAt(this.pos);
            this.token = Token.IDENTIFIER;
         }
      } else {
         boolean firstFlag = CharTypes.isFirstIdentifierChar(first);
         if (!firstFlag) {
            throw new ParserException("illegal identifier. " + this.info());
         } else {
            this.hash_lower = -3750763034362895579L;
            this.hash = -3750763034362895579L;
            this.hash_lower ^= (long)(this.ch >= 'A' && this.ch <= 'Z' ? this.ch + 32 : this.ch);
            this.hash_lower *= 1099511628211L;
            this.hash ^= (long)this.ch;
            this.hash *= 1099511628211L;
            this.mark = this.pos;
            this.bufPos = 1;
            char ch = 0;

            while(true) {
               char c0 = ch;
               ch = this.charAt(++this.pos);
               if (!CharTypes.isIdentifierChar(ch)) {
                  if (ch != '（' && ch != '）' || c0 <= 256) {
                     this.ch = this.charAt(this.pos);
                     if (this.bufPos == 1) {
                        switch (first) {
                           case '（':
                              this.token = Token.LPAREN;
                              return;
                           case '）':
                              this.token = Token.RPAREN;
                              return;
                           default:
                              this.token = Token.IDENTIFIER;
                              this.stringVal = CharTypes.valueOf(first);
                              if (this.stringVal == null) {
                                 this.stringVal = Character.toString(first);
                              }

                              return;
                        }
                     } else {
                        Token tok = this.keywords.getKeyword(this.hash_lower);
                        if (tok != null) {
                           this.token = tok;
                           if (this.token == Token.IDENTIFIER) {
                              this.stringVal = SymbolTable.global.addSymbol(this.text, this.mark, this.bufPos, this.hash);
                           } else {
                              this.stringVal = null;
                           }
                        } else {
                           this.token = Token.IDENTIFIER;
                           this.stringVal = SymbolTable.global.addSymbol(this.text, this.mark, this.bufPos, this.hash);
                        }

                        return;
                     }
                  }

                  ++this.bufPos;
               } else {
                  this.hash_lower ^= (long)(ch >= 'A' && ch <= 'Z' ? ch + 32 : ch);
                  this.hash_lower *= 1099511628211L;
                  this.hash ^= (long)ch;
                  this.hash *= 1099511628211L;
                  ++this.bufPos;
               }
            }
         }
      }
   }

   public void scanNumber() {
      this.mark = this.pos;
      this.numberSale = 0;
      this.numberExp = false;
      this.bufPos = 0;
      if (this.ch == '0' && this.charAt(this.pos + 1) == 'b' && this.dbType != DbType.odps) {
         int i = 2;
         int mark = this.pos + 2;

         while(true) {
            char ch = this.charAt(this.pos + i);
            if (ch != '0' && ch != '1') {
               if (ch < '2' || ch > '9') {
                  this.bufPos += i;
                  this.pos += i;
                  this.stringVal = this.subString(mark, i - 2);
                  this.ch = this.charAt(this.pos);
                  this.token = Token.BITS;
                  return;
               }
               break;
            }

            ++i;
         }
      }

      if (this.ch == '-') {
         ++this.bufPos;
         this.ch = this.charAt(++this.pos);
      }

      while(this.ch >= '0' && this.ch <= '9') {
         ++this.bufPos;
         this.ch = this.charAt(++this.pos);
      }

      if (this.ch == '.') {
         if (this.charAt(this.pos + 1) == '.') {
            this.token = Token.LITERAL_INT;
            return;
         }

         ++this.bufPos;
         this.ch = this.charAt(++this.pos);

         for(this.numberSale = 0; this.ch >= '0' && this.ch <= '9'; ++this.numberSale) {
            ++this.bufPos;
            this.ch = this.charAt(++this.pos);
         }

         this.numberExp = true;
      }

      if ((this.ch == 'e' || this.ch == 'E') && (this.isDigit(this.charAt(this.pos + 1)) || isDigit2(this.charAt(this.pos + 1)) && isDigit2(this.charAt(this.pos + 2)))) {
         this.numberExp = true;
         ++this.bufPos;
         this.ch = this.charAt(++this.pos);
         if (this.ch == '+' || this.ch == '-') {
            ++this.bufPos;
            this.ch = this.charAt(++this.pos);
         }

         while(this.ch >= '0' && this.ch <= '9') {
            ++this.bufPos;
            this.ch = this.charAt(++this.pos);
         }

         if (this.ch >= 'a' && this.ch <= 'z' || this.ch >= 'A' && this.ch <= 'Z') {
            this.numberExp = false;
         }
      }

      if (this.numberSale <= 0 && !this.numberExp) {
         if (CharTypes.isFirstIdentifierChar(this.ch) && this.ch != '）' && (this.ch != 'b' || this.bufPos != 1 || this.charAt(this.pos - 1) != '0' || this.dbType == DbType.odps)) {
            ++this.bufPos;

            while(true) {
               char c0 = this.ch;
               this.ch = this.charAt(++this.pos);
               if (!CharTypes.isIdentifierChar(this.ch)) {
                  if (this.ch != '（' && (this.ch != '）' || c0 <= 256)) {
                     this.stringVal = this.addSymbol();
                     this.hash_lower = FnvHash.hashCode64(this.stringVal);
                     this.token = Token.IDENTIFIER;
                     break;
                  }

                  ++this.bufPos;
               } else {
                  ++this.bufPos;
               }
            }
         } else {
            this.token = Token.LITERAL_INT;
         }
      } else {
         this.token = Token.LITERAL_FLOAT;
      }

   }

   public void scanHexaDecimal() {
      this.mark = this.pos;
      if (this.ch == '-') {
         ++this.bufPos;
         this.ch = this.charAt(++this.pos);
      }

      while(CharTypes.isHex(this.ch)) {
         ++this.bufPos;
         this.ch = this.charAt(++this.pos);
      }

      this.token = Token.LITERAL_HEX;
   }

   public String hexString() {
      return this.subString(this.mark, this.bufPos);
   }

   public final boolean isDigit(char ch) {
      return ch >= '0' && ch <= '9';
   }

   protected static final boolean isDigit2(char ch) {
      return ch == '+' || ch == '-' || ch >= '0' && ch <= '9';
   }

   protected final void putChar(char ch) {
      if (this.bufPos == this.buf.length) {
         char[] newsbuf = new char[this.buf.length * 2];
         System.arraycopy(this.buf, 0, newsbuf, 0, this.buf.length);
         this.buf = newsbuf;
      }

      this.buf[this.bufPos++] = ch;
   }

   public final int pos() {
      return this.pos;
   }

   public final String stringVal() {
      if (this.stringVal == null) {
         this.stringVal = this.subString(this.mark, this.bufPos);
      }

      return this.stringVal;
   }

   private final void stringVal(StringBuffer out) {
      if (this.stringVal != null) {
         out.append(this.stringVal);
      } else {
         out.append(this.text, this.mark, this.mark + this.bufPos);
      }
   }

   public final boolean identifierEquals(String text) {
      if (this.token != Token.IDENTIFIER) {
         return false;
      } else {
         if (this.stringVal == null) {
            this.stringVal = this.subString(this.mark, this.bufPos);
         }

         return text.equalsIgnoreCase(this.stringVal);
      }
   }

   public final boolean identifierEquals(long hash_lower) {
      if (this.token != Token.IDENTIFIER) {
         return false;
      } else {
         if (this.hash_lower == 0L) {
            if (this.stringVal == null) {
               this.stringVal = this.subString(this.mark, this.bufPos);
            }

            this.hash_lower = FnvHash.fnv1a_64_lower(this.stringVal);
         }

         return this.hash_lower == hash_lower;
      }
   }

   public final long hash_lower() {
      if (this.hash_lower == 0L) {
         if (this.stringVal == null) {
            this.stringVal = this.subString(this.mark, this.bufPos);
         }

         this.hash_lower = FnvHash.fnv1a_64_lower(this.stringVal);
      }

      return this.hash_lower;
   }

   public final List<String> readAndResetComments() {
      List<String> comments = this.comments;
      this.comments = null;
      return comments;
   }

   protected boolean isOperator(char ch) {
      switch (ch) {
         case '!':
         case '%':
         case '&':
         case '*':
         case '+':
         case '-':
         case ';':
         case '<':
         case '=':
         case '>':
         case '^':
         case '|':
         case '~':
            return true;
         default:
            return false;
      }
   }

   public final boolean isNegativeIntegerValue() {
      return this.charAt(this.mark) == '-';
   }

   public final Number integerValue() {
      long result = 0L;
      boolean negative = false;
      int i = this.mark;
      int max = this.mark + this.bufPos;
      long limit;
      if (this.charAt(this.mark) == '-') {
         negative = true;
         limit = Long.MIN_VALUE;
         ++i;
      } else {
         limit = -9223372036854775807L;
      }

      long multmin = negative ? -922337203685477580L : -922337203685477580L;
      if (i < max) {
         int digit = this.charAt(i++) - 48;
         result = (long)(-digit);
      }

      while(i < max) {
         int digit = this.charAt(i++) - 48;
         if (result < multmin) {
            return new BigInteger(this.numberString());
         }

         result *= 10L;
         if (result < limit + (long)digit) {
            return new BigInteger(this.numberString());
         }

         result -= (long)digit;
      }

      if (negative) {
         if (i > this.mark + 1) {
            if (result >= -2147483648L) {
               return (int)result;
            } else {
               return result;
            }
         } else {
            throw new NumberFormatException(this.numberString());
         }
      } else {
         result = -result;
         if (result <= 2147483647L) {
            return (int)result;
         } else {
            return result;
         }
      }
   }

   public int bp() {
      return this.pos;
   }

   public char current() {
      return this.ch;
   }

   public void reset(int mark, char markChar, Token token) {
      this.pos = mark;
      this.ch = markChar;
      this.token = token;
   }

   public final String numberString() {
      return this.text.substring(this.mark, this.mark + this.bufPos);
   }

   public BigDecimal decimalValue() {
      if (this.numberSale > 0 && !this.numberExp) {
         int len = this.bufPos;
         if (len < 20) {
            long unscaleVal = 0L;
            boolean negative = false;
            int i = 0;
            char first = this.text.charAt(this.mark);
            if (first == '+') {
               ++i;
            } else if (first == '-') {
               ++i;
               negative = true;
            }

            for(; i < len; ++i) {
               char ch = this.text.charAt(this.mark + i);
               if (ch != '.') {
                  int digit = ch - 48;
                  unscaleVal = unscaleVal * 10L + (long)digit;
               }
            }

            return BigDecimal.valueOf(negative ? -unscaleVal : unscaleVal, this.numberSale);
         }
      }

      char[] value = this.sub_chars(this.mark, this.bufPos);
      if (!StringUtils.isNumber(value)) {
         throw new ParserException(value + " is not a number! " + this.info());
      } else {
         return new BigDecimal(value);
      }
   }

   public SQLNumberExpr numberExpr() {
      char[] value = this.sub_chars(this.mark, this.bufPos);
      if (!StringUtils.isNumber(value)) {
         throw new ParserException(value + " is not a number! " + this.info());
      } else {
         return new SQLNumberExpr(value);
      }
   }

   public SQLNumberExpr numberExpr(SQLObject parent) {
      char[] value = this.sub_chars(this.mark, this.bufPos);
      if (!StringUtils.isNumber(value)) {
         throw new ParserException(value + " is not a number! " + this.info());
      } else {
         return new SQLNumberExpr(value, parent);
      }
   }

   public SQLNumberExpr numberExpr(boolean negate) {
      char[] value = this.sub_chars(this.mark, this.bufPos);
      if (!StringUtils.isNumber(value)) {
         throw new ParserException(value + " is not a number! " + this.info());
      } else if (negate) {
         char[] chars = new char[value.length + 1];
         chars[0] = '-';
         System.arraycopy(value, 0, chars, 1, value.length);
         return new SQLNumberExpr(chars);
      } else {
         return new SQLNumberExpr(value);
      }
   }

   public boolean hasComment() {
      return this.comments != null;
   }

   public int getCommentCount() {
      return this.commentCount;
   }

   public void skipToEOF() {
      this.pos = this.text.length();
      this.token = Token.EOF;
   }

   public boolean isEndOfComment() {
      return this.endOfComment;
   }

   protected boolean isSafeComment(String comment) {
      if (comment == null) {
         return true;
      } else {
         comment = comment.toLowerCase();
         return comment.indexOf("select") == -1 && comment.indexOf("delete") == -1 && comment.indexOf("insert") == -1 && comment.indexOf("update") == -1 && comment.indexOf("into") == -1 && comment.indexOf("where") == -1 && comment.indexOf("or") == -1 && comment.indexOf("and") == -1 && comment.indexOf("union") == -1 && comment.indexOf(39) == -1 && comment.indexOf(61) == -1 && comment.indexOf(62) == -1 && comment.indexOf(60) == -1 && comment.indexOf(38) == -1 && comment.indexOf(124) == -1 && comment.indexOf(94) == -1;
      }
   }

   protected void addComment(String comment) {
      if (this.comments == null) {
         this.comments = new ArrayList(2);
      }

      this.comments.add(comment);
   }

   public List<String> getComments() {
      return this.comments;
   }

   public int getLine() {
      return this.line;
   }

   public void computeRowAndColumn() {
      int line = 1;
      int column = 1;

      for(int i = 0; i < this.startPos; ++i) {
         char ch = this.text.charAt(i);
         if (ch == '\n') {
            column = 0;
            ++line;
         } else {
            ++column;
         }
      }

      this.posLine = line;
      this.posColumn = column;
   }

   public int getPosLine() {
      return this.posLine;
   }

   public int getPosColumn() {
      return this.posColumn;
   }

   public void config(SQLParserFeature feature, boolean state) {
      this.features = SQLParserFeature.config(this.features, feature, state);
      if (feature == SQLParserFeature.OptimizedForParameterized) {
         this.optimizedForParameterized = state;
      } else if (feature == SQLParserFeature.KeepComments) {
         this.keepComments = state;
      } else if (feature == SQLParserFeature.KeepSourceLocation) {
         this.keepSourceLocation = state;
      } else if (feature == SQLParserFeature.SkipComments) {
         this.skipComment = state;
      }

   }

   public TimeZone getTimeZone() {
      return this.timeZone;
   }

   public void setTimeZone(TimeZone timeZone) {
      this.timeZone = timeZone;
   }

   public final boolean isEnabled(SQLParserFeature feature) {
      return SQLParserFeature.isEnabled(this.features, feature);
   }

   public static String parameterize(String sql, DbType dbType) {
      Lexer lexer = SQLParserUtils.createLexer(sql, dbType);
      lexer.optimizedForParameterized = true;
      lexer.nextToken();
      StringBuffer buf = new StringBuffer();

      while(true) {
         Token token = lexer.token;
         switch (token) {
            case EOF:
               return buf.toString();
            case ERROR:
               return sql;
            case TABLE:
            case VIEW:
            case FUNCTION:
            case LPAREN:
            case WITH:
            case BY:
            default:
               if (buf.length() != 0) {
                  buf.append(' ');
               }

               lexer.stringVal(buf);
               break;
            case SELECT:
               buf.append("SELECT");
               break;
            case COMMA:
               buf.append(',');
               break;
            case LITERAL_ALIAS:
            case LITERAL_FLOAT:
            case LITERAL_CHARS:
            case LITERAL_INT:
            case LITERAL_NCHARS:
            case LITERAL_HEX:
            case VARIANT:
               if (buf.length() != 0) {
                  buf.append(' ');
               }

               buf.append('?');
               break;
            case EQ:
               buf.append('=');
               break;
            case UPDATE:
               buf.append("UPDATE");
         }

         lexer.nextToken();
      }
   }

   public String getSource() {
      return this.text;
   }

   static {
      for(int i = 48; i <= 57; ++i) {
         digits[i] = i - 48;
      }

   }

   public static class SavePoint {
      int bp;
      int sp;
      int np;
      char ch;
      long hash;
      long hash_lower;
      public Token token;
      String stringVal;
   }

   public interface CommentHandler {
      boolean handle(Token var1, String var2);
   }
}
