package com.alibaba.druid.wall.spi;

import com.alibaba.druid.sql.PagerUtils;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLCommentHint;
import com.alibaba.druid.sql.ast.SQLLimit;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.TDDLHint;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLInListExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLCallStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement;
import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLSetStatement;
import com.alibaba.druid.sql.ast.statement.SQLShowCreateTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLUnionQuery;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.alibaba.druid.wall.Violation;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallContext;
import com.alibaba.druid.wall.WallProvider;
import com.alibaba.druid.wall.WallSqlTableStat;
import com.alibaba.druid.wall.WallUpdateCheckItem;
import com.alibaba.druid.wall.WallVisitor;
import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation;
import java.util.ArrayList;
import java.util.List;

public abstract class WallVisitorBase implements WallVisitor {
   protected final WallConfig config;
   protected final WallProvider provider;
   protected final List<Violation> violations = new ArrayList();
   protected boolean sqlModified = false;
   protected boolean sqlEndOfComment = false;
   protected List<WallUpdateCheckItem> updateCheckItems;

   public WallVisitorBase(WallProvider provider) {
      this.config = provider.getConfig();
      this.provider = provider;
   }

   public boolean isSqlModified() {
      return this.sqlModified;
   }

   public void setSqlModified(boolean sqlModified) {
      this.sqlModified = sqlModified;
   }

   public WallProvider getProvider() {
      return this.provider;
   }

   public WallConfig getConfig() {
      return this.config;
   }

   public void addViolation(Violation violation) {
      this.violations.add(violation);
   }

   public List<Violation> getViolations() {
      return this.violations;
   }

   public boolean isSqlEndOfComment() {
      return this.sqlEndOfComment;
   }

   public void setSqlEndOfComment(boolean sqlEndOfComment) {
      this.sqlEndOfComment = sqlEndOfComment;
   }

   public void addWallUpdateCheckItem(WallUpdateCheckItem item) {
      if (this.updateCheckItems == null) {
         this.updateCheckItems = new ArrayList();
      }

      this.updateCheckItems.add(item);
   }

   public List<WallUpdateCheckItem> getUpdateCheckItems() {
      return this.updateCheckItems;
   }

   public boolean isDenyTable(String name) {
      if (!this.config.isTableCheck()) {
         return false;
      } else {
         return !this.provider.checkDenyTable(name);
      }
   }

   public String toSQL(SQLObject obj) {
      return SQLUtils.toSQLString(obj, this.getDbType());
   }

   public boolean visit(SQLPropertyExpr x) {
      WallVisitorUtils.check(this, (SQLPropertyExpr)x);
      return true;
   }

   public boolean visit(SQLInListExpr x) {
      WallVisitorUtils.check(this, (SQLInListExpr)x);
      return true;
   }

   public boolean visit(SQLBinaryOpExpr x) {
      return WallVisitorUtils.check(this, (SQLBinaryOpExpr)x);
   }

   public boolean visit(SQLMethodInvokeExpr x) {
      WallVisitorUtils.checkFunction(this, x);
      return true;
   }

   public boolean visit(SQLSelectQueryBlock x) {
      WallVisitorUtils.checkSelelct(this, x);
      return true;
   }

   public boolean visit(SQLSelectGroupByClause x) {
      WallVisitorUtils.checkHaving(this, x.getHaving());
      return true;
   }

   public boolean visit(SQLSelectItem x) {
      WallVisitorUtils.check(this, (SQLSelectItem)x);
      return true;
   }

   public boolean visit(SQLJoinTableSource x) {
      WallVisitorUtils.check(this, (SQLJoinTableSource)x);
      return true;
   }

   public boolean visit(SQLCreateTableStatement x) {
      WallVisitorUtils.check(this, (SQLCreateTableStatement)x);
      return true;
   }

   public boolean visit(SQLAlterTableStatement x) {
      WallVisitorUtils.check(this, (SQLAlterTableStatement)x);
      return true;
   }

   public boolean visit(SQLDropTableStatement x) {
      WallVisitorUtils.check(this, (SQLDropTableStatement)x);
      return true;
   }

   public boolean visit(SQLUpdateStatement x) {
      WallVisitorUtils.initWallTopStatementContext();
      WallVisitorUtils.checkUpdate(this, x);
      return true;
   }

   public void endVisit(SQLUpdateStatement x) {
      WallVisitorUtils.clearWallTopStatementContext();
   }

   public boolean visit(SQLInsertStatement x) {
      WallVisitorUtils.initWallTopStatementContext();
      WallVisitorUtils.checkInsert(this, x);
      return true;
   }

   public void endVisit(SQLInsertStatement x) {
      WallVisitorUtils.clearWallTopStatementContext();
   }

   public boolean visit(SQLDeleteStatement x) {
      WallVisitorUtils.checkDelete(this, x);
      return true;
   }

   public void preVisit(SQLObject x) {
      WallVisitorUtils.preVisitCheck(this, x);
   }

   public boolean visit(SQLSelectStatement x) {
      WallConfig config = this.getConfig();
      if (!config.isSelectAllow()) {
         this.getViolations().add(new IllegalSQLObjectViolation(1002, "select not allow", this.toSQL(x)));
         return false;
      } else {
         WallVisitorUtils.initWallTopStatementContext();
         int selectLimit = config.getSelectLimit();
         if (selectLimit >= 0) {
            SQLSelect select = x.getSelect();
            PagerUtils.limit((SQLSelect)select, this.getDbType(), 0, selectLimit, true);
            this.setSqlModified(true);
         }

         return true;
      }
   }

   public void endVisit(SQLSelectStatement x) {
      WallVisitorUtils.clearWallTopStatementContext();
   }

   public boolean visit(SQLExprTableSource x) {
      WallVisitorUtils.check(this, (SQLExprTableSource)x);
      return !(x.getExpr() instanceof SQLName);
   }

   public boolean visit(SQLIdentifierExpr x) {
      WallConfig config = this.getConfig();
      String name = x.getName();
      name = WallVisitorUtils.form(name);
      if (config.isVariantCheck() && config.getDenyVariants().contains(name)) {
         this.getViolations().add(new IllegalSQLObjectViolation(2003, "variable not allow : " + name, this.toSQL(x)));
      }

      return true;
   }

   public boolean visit(SQLUnionQuery x) {
      return WallVisitorUtils.checkUnion(this, x);
   }

   public void endVisit(SQLDeleteStatement x) {
      WallVisitorUtils.clearWallTopStatementContext();
   }

   public boolean visit(SQLLimit x) {
      if (x.getRowCount() instanceof SQLNumericLiteralExpr) {
         WallContext context = WallContext.current();
         int rowCount = ((SQLNumericLiteralExpr)x.getRowCount()).getNumber().intValue();
         if (rowCount == 0) {
            if (context != null) {
               context.incrementWarnings();
            }

            if (!this.getProvider().getConfig().isLimitZeroAllow()) {
               this.getViolations().add(new IllegalSQLObjectViolation(2200, "limit row 0", this.toSQL(x)));
            }
         }
      }

      return true;
   }

   public boolean visit(SQLCreateTriggerStatement x) {
      return false;
   }

   public boolean visit(SQLSetStatement x) {
      return false;
   }

   public boolean visit(SQLCallStatement x) {
      return false;
   }

   public boolean visit(SQLCommentHint x) {
      if (x instanceof TDDLHint) {
         return false;
      } else {
         WallVisitorUtils.check(this, (SQLCommentHint)x);
         return true;
      }
   }

   public boolean visit(SQLShowCreateTableStatement x) {
      String tableName = x.getName().getSimpleName();
      WallContext context = WallContext.current();
      if (context != null) {
         WallSqlTableStat tableStat = context.getTableStat(tableName);
         if (tableStat != null) {
            tableStat.incrementShowCount();
         }
      }

      return false;
   }
}
