package com.alibaba.druid.sql.dialect.oracle.parser;

import com.alibaba.druid.sql.parser.CharTypes;
import com.alibaba.druid.sql.parser.Keywords;
import com.alibaba.druid.sql.parser.Lexer;
import com.alibaba.druid.sql.parser.NotAllowCommentException;
import com.alibaba.druid.sql.parser.ParserException;
import com.alibaba.druid.sql.parser.SQLParserFeature;
import com.alibaba.druid.sql.parser.Token;
import java.util.HashMap;
import java.util.Map;

public class OracleLexer extends Lexer {
   public static final Keywords DEFAULT_ORACLE_KEYWORDS;

   public OracleLexer(char[] input, int inputLength, boolean skipComment) {
      super(input, inputLength, skipComment);
      super.keywords = DEFAULT_ORACLE_KEYWORDS;
   }

   public OracleLexer(String input) {
      super(input);
      this.skipComment = true;
      this.keepComments = true;
      super.keywords = DEFAULT_ORACLE_KEYWORDS;
   }

   public OracleLexer(String input, SQLParserFeature... features) {
      super(input);
      this.skipComment = true;
      this.keepComments = true;
      super.keywords = DEFAULT_ORACLE_KEYWORDS;

      for(SQLParserFeature feature : features) {
         this.config(feature, true);
      }

   }

   public void scanVariable() {
      char c0 = this.ch;
      if (c0 != ':' && c0 != '#' && c0 != '$') {
         throw new ParserException("illegal variable. " + this.info());
      } else {
         this.mark = this.pos;
         this.bufPos = 1;
         boolean quoteFlag = false;
         boolean mybatisFlag = false;
         char c1 = this.charAt(this.pos + 1);
         if (c0 == ':' && c1 == ' ') {
            ++this.pos;
            this.bufPos = 2;
            c1 = this.charAt(this.pos + 1);
         }

         if (c1 == '"') {
            ++this.pos;
            ++this.bufPos;
            quoteFlag = true;
         } else if (c1 == '{') {
            ++this.pos;
            ++this.bufPos;
            mybatisFlag = true;
         }

         char ch;
         if (c0 == ':' && c1 >= '0' && c1 <= '9') {
            while(true) {
               ch = this.charAt(++this.pos);
               if (ch < '0' || ch > '9') {
                  break;
               }

               ++this.bufPos;
            }
         } else {
            while(true) {
               ch = this.charAt(++this.pos);
               if (!CharTypes.isIdentifierChar(ch) && ch != ':') {
                  break;
               }

               ++this.bufPos;
            }
         }

         if (quoteFlag) {
            if (ch != '"') {
               throw new ParserException("syntax error. " + this.info());
            }

            ++this.pos;
            ++this.bufPos;
         } else if (mybatisFlag) {
            if (ch != '}') {
               throw new ParserException("syntax error" + this.info());
            }

            ++this.pos;
            ++this.bufPos;
         }

         this.ch = this.charAt(this.pos);
         this.stringVal = this.addSymbol();
         Token tok = this.keywords.getKeyword(this.stringVal);
         if (tok != null) {
            this.token = tok;
         } else {
            this.token = Token.VARIANT;
         }

      }
   }

   protected void scanVariable_at() {
      this.scanChar();
      if (this.ch == '@') {
         this.scanChar();
         this.token = Token.MONKEYS_AT_AT;
      } else {
         this.token = Token.MONKEYS_AT;
      }

   }

   public void scanComment() {
      if (this.ch != '/' && this.ch != '-') {
         throw new IllegalStateException();
      } else {
         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;
            }

            while(!this.isEOF()) {
               if (this.ch == '*' && this.charAt(this.pos + 1) == '/') {
                  this.bufPos += 2;
                  this.scanChar();
                  this.scanChar();
                  break;
               }

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

            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.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.bufPos += 2;
                     this.scanChar();
                  } else {
                     ++this.bufPos;
                  }
                  break;
               }

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

               if (this.ch == '\n') {
                  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();
         }
      }
   }

   public void scanNumber() {
      this.mark = this.pos;
      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);
      }

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

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

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

      if ((this.ch == 'e' || this.ch == 'E') && isDigit2(this.charAt(this.pos + 1))) {
         ++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);
         }

         isDouble = true;
      }

      if (this.ch != 'f' && this.ch != 'F') {
         if (this.ch != 'd' && this.ch != 'D') {
            if (isDouble) {
               this.token = Token.LITERAL_FLOAT;
            } else {
               this.token = Token.LITERAL_INT;
            }

         } else {
            this.token = Token.BINARY_DOUBLE;
            this.scanChar();
         }
      } else {
         this.token = Token.BINARY_FLOAT;
         this.scanChar();
      }
   }

   static {
      Map<String, Token> map = new HashMap();
      map.putAll(Keywords.DEFAULT_KEYWORDS.getKeywords());
      map.put("BEGIN", Token.BEGIN);
      map.put("COMMENT", Token.COMMENT);
      map.put("COMMIT", Token.COMMIT);
      map.put("CONNECT", Token.CONNECT);
      map.put("CONTINUE", Token.CONTINUE);
      map.put("CROSS", Token.CROSS);
      map.put("CURSOR", Token.CURSOR);
      map.put("DECLARE", Token.DECLARE);
      map.put("ERRORS", Token.ERRORS);
      map.put("EXCEPTION", Token.EXCEPTION);
      map.put("EXCLUSIVE", Token.EXCLUSIVE);
      map.put("EXTRACT", Token.EXTRACT);
      map.put("GOTO", Token.GOTO);
      map.put("IF", Token.IF);
      map.put("ELSIF", Token.ELSIF);
      map.put("LIMIT", Token.LIMIT);
      map.put("LOOP", Token.LOOP);
      map.put("MATCHED", Token.MATCHED);
      map.put("MERGE", Token.MERGE);
      map.put("MODE", Token.MODE);
      map.put("NOWAIT", Token.NOWAIT);
      map.put("OF", Token.OF);
      map.put("PRIOR", Token.PRIOR);
      map.put("REJECT", Token.REJECT);
      map.put("RETURN", Token.RETURN);
      map.put("RETURNING", Token.RETURNING);
      map.put("SAVEPOINT", Token.SAVEPOINT);
      map.put("SESSION", Token.SESSION);
      map.put("SHARE", Token.SHARE);
      map.put("START", Token.START);
      map.put("SYSDATE", Token.SYSDATE);
      map.put("UNLIMITED", Token.UNLIMITED);
      map.put("USING", Token.USING);
      map.put("WAIT", Token.WAIT);
      map.put("WITH", Token.WITH);
      map.put("PCTFREE", Token.PCTFREE);
      map.put("INITRANS", Token.INITRANS);
      map.put("MAXTRANS", Token.MAXTRANS);
      map.put("SEGMENT", Token.SEGMENT);
      map.put("CREATION", Token.CREATION);
      map.put("IMMEDIATE", Token.IMMEDIATE);
      map.put("DEFERRED", Token.DEFERRED);
      map.put("STORAGE", Token.STORAGE);
      map.put("NEXT", Token.NEXT);
      map.put("MINEXTENTS", Token.MINEXTENTS);
      map.put("MAXEXTENTS", Token.MAXEXTENTS);
      map.put("MAXSIZE", Token.MAXSIZE);
      map.put("PCTINCREASE", Token.PCTINCREASE);
      map.put("FLASH_CACHE", Token.FLASH_CACHE);
      map.put("CELL_FLASH_CACHE", Token.CELL_FLASH_CACHE);
      map.put("NONE", Token.NONE);
      map.put("LOB", Token.LOB);
      map.put("STORE", Token.STORE);
      map.put("ROW", Token.ROW);
      map.put("CHUNK", Token.CHUNK);
      map.put("CACHE", Token.CACHE);
      map.put("NOCACHE", Token.NOCACHE);
      map.put("LOGGING", Token.LOGGING);
      map.put("NOCOMPRESS", Token.NOCOMPRESS);
      map.put("KEEP_DUPLICATES", Token.KEEP_DUPLICATES);
      map.put("EXCEPTIONS", Token.EXCEPTIONS);
      map.put("PURGE", Token.PURGE);
      map.put("INITIALLY", Token.INITIALLY);
      map.put("FETCH", Token.FETCH);
      map.put("TABLESPACE", Token.TABLESPACE);
      map.put("PARTITION", Token.PARTITION);
      map.put("TRUE", Token.TRUE);
      map.put("FALSE", Token.FALSE);
      map.put("，", Token.COMMA);
      map.put("（", Token.LPAREN);
      map.put("）", Token.RPAREN);
      map.put("LIKEC", Token.LIKEC);
      map.put("LIKE2", Token.LIKE2);
      map.put("LIKE4", Token.LIKE4);
      map.put("NATURAL", Token.NATURAL);
      map.put("PERCENT", Token.PERCENT);
      map.put("APPLY", Token.APPLY);
      map.put("FIRST", Token.FIRST);
      map.put("DOCUMENT", Token.DOCUMENT);
      map.put("CONTENT", Token.CONTENT);
      map.put("WELLFORMED", Token.WELLFORMED);
      map.put("EXCEPT", Token.EXCEPT);
      map.put("MULTISET", Token.MULTISET);
      map.put("KEY", Token.KEY);
      map.put("VALUE", Token.VALUE);
      map.put("ABSENT", Token.ABSENT);
      map.put("FORMAT", Token.FORMAT);
      DEFAULT_ORACLE_KEYWORDS = new Keywords(map);
   }
}
