content: /*
   * Copyright 1999-2017 Alibaba Group Holding Ltd.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.alibaba.druid.sql.ast.statement;
  
  import com.alibaba.druid.DbType;
  import com.alibaba.druid.sql.ast.*;
  import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr;
  import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObject;
  import com.alibaba.druid.sql.visitor.SQLASTVisitor;
  import com.alibaba.druid.util.FnvHash;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  
  public class SQLSelect extends SQLObjectImpl implements SQLDbTypedObject {
      protected SQLWithSubqueryClause withSubQuery;
      protected SQLSelectQuery query;
      protected SQLOrderBy orderBy;
      protected SQLLimit limit;
  
      protected List<SQLHint> hints;
  
      protected SQLObject restriction;
  
      protected boolean forBrowse;
      protected List<String> forXmlOptions;
      protected SQLExpr xmlPath;
  
      protected SQLExpr rowCount;
      protected SQLExpr offset;
  
      private SQLHint headHint;
  
      public SQLSelect() {
      }
  
      public List<SQLHint> getHints() {
          if (hints == null) {
              hints = new ArrayList<SQLHint>(2);
          }
          return hints;
      }
  
      public int getHintsSize() {
          if (hints == null) {
              return 0;
          }
          return hints.size();
      }
  
      public SQLSelect(SQLSelectQuery query) {
          this.setQuery(query);
      }
  
      public SQLWithSubqueryClause getWithSubQuery() {
          return withSubQuery;
      }
  
      public SQLWithSubqueryClause.Entry findWithSubQueryEntry(String alias) {
          return withSubQuery == null ? null : withSubQuery.findEntry(alias);
      }
  
      public SQLDataType findSelectItemAndComputeDataType(String ident) {
          if (ident == null) {
              return null;
          }
  
          long hash = FnvHash.hashCode64(ident);
          return findSelectItemAndComputeDataType(hash);
      }
  
      public SQLDataType findSelectItemAndComputeDataType(long identHash) {
          SQLSelectQueryBlock queryBlock = getFirstQueryBlock();
          return queryBlock != null ? queryBlock.findSelectItemAndComputeDataType(identHash) : null;
      }
  
      public void setWithSubQuery(SQLWithSubqueryClause x) {
          if (x != null) {
              x.setParent(this);
          }
          this.withSubQuery = x;
      }
  
      public SQLSelectQuery getQuery() {
          return this.query;
      }
  
      public void setQuery(SQLSelectQuery query) {
          if (query != null) {
              query.setParent(this);
          }
          this.query = query;
      }
  
      public SQLSelectQueryBlock getQueryBlock() {
          if (query instanceof SQLSelectQueryBlock) {
              return (SQLSelectQueryBlock) query;
          }
  
          return null;
      }
  
      public SQLOrderBy getOrderBy() {
          return this.orderBy;
      }
  
      public void setOrderBy(SQLOrderBy orderBy) {
          if (orderBy != null) {
              orderBy.setParent(this);
          }
          this.orderBy = orderBy;
      }
  
      protected void accept0(SQLASTVisitor v) {
          if (v.visit(this)) {
              if (withSubQuery != null) {
                  withSubQuery.accept0(v);
              }
  
              if (this.query != null) {
                  this.query.accept(v);
              }
  
              if (this.restriction != null) {
                  this.restriction.accept(v);
              }
  
              if (this.orderBy != null) {
                  this.orderBy.accept(v);
              }
  
              if (this.hints != null) {
                  for (SQLHint hint : hints) {
                      hint.accept(v);
                  }
              }
  
              if (this.offset != null) {
                  this.offset.accept(v);
              }
  
              if (this.rowCount != null) {
                  this.rowCount.accept(v);
              }
  
              if (this.headHint != null) {
                  this.headHint.accept(v);
              }
          }
  
          v.endVisit(this);
      }
  
      public DbType getDbType() {
          DbType dbType = null;
  
          SQLObject parent = this.getParent();
          if (parent instanceof SQLStatement) {
              dbType = ((SQLStatement) parent).getDbType();
          }
  
          if (dbType == null && parent instanceof OracleSQLObject) {
              dbType = DbType.oracle;
          }
  
          if (dbType == null && query instanceof SQLSelectQueryBlock) {
              dbType = ((SQLSelectQueryBlock) query).dbType;
          }
  
          return dbType;
      }
  
      public SQLSelect clone() {
          SQLSelect x = new SQLSelect();
  
          if (withSubQuery != null) {
              x.withSubQuery = withSubQuery.clone();
          }
  
          if (query != null) {
              x.setQuery(query.clone());
          }
  
          if (orderBy != null) {
              x.setOrderBy(this.orderBy.clone());
          }
          if (restriction != null) {
              x.setRestriction(restriction.clone());
          }
  
          if (this.hints != null) {
              for (SQLHint hint : this.hints) {
                  x.getHints().add(hint);
              }
          }
  
          x.forBrowse = forBrowse;
  
          if (forXmlOptions != null) {
              x.forXmlOptions = new ArrayList<String>(forXmlOptions);
          }
  
          if (xmlPath != null) {
              x.setXmlPath(xmlPath.clone());
          }
  
          if (rowCount != null) {
              x.setRowCount(rowCount.clone());
          }
  
          if (offset != null) {
              x.setOffset(offset.clone());
          }
  
          if (headHint != null) {
              x.setHeadHint(headHint.clone());
          }
  
          return x;
      }
  
      public boolean isSimple() {
          return withSubQuery == null
                  && (hints == null || hints.isEmpty())
                  && restriction == null
                  && (!forBrowse)
                  && (forXmlOptions == null || forXmlOptions.isEmpty())
                  && xmlPath == null
                  && rowCount == null
                  && offset == null;
      }
  
      public SQLObject getRestriction() {
          return this.restriction;
      }
  
      public void setRestriction(SQLObject restriction) {
          if (restriction != null) {
              restriction.setParent(this);
          }
          this.restriction = restriction;
      }
  
      public boolean isForBrowse() {
          return forBrowse;
      }
  
      public void setForBrowse(boolean forBrowse) {
          this.forBrowse = forBrowse;
      }
  
      public List<String> getForXmlOptions() {
          if (forXmlOptions == null) {
              forXmlOptions = new ArrayList<String>(4);
          }
  
          return forXmlOptions;
      }
  
      public int getForXmlOptionsSize() {
          if (forXmlOptions == null) {
              return 0;
          }
          return forXmlOptions.size();
      }
  
      public SQLExpr getRowCount() {
          return rowCount;
      }
  
      public void setRowCount(SQLExpr rowCount) {
          if (rowCount != null) {
              rowCount.setParent(this);
          }
  
          this.rowCount = rowCount;
      }
  
      public SQLHint getHeadHint() {
          return headHint;
      }
  
      public void setHeadHint(SQLHint headHint) {
          this.headHint = headHint;
      }
  
      public SQLExpr getOffset() {
          return offset;
      }
  
      public void setOffset(SQLExpr offset) {
          if (offset != null) {
              offset.setParent(this);
          }
          this.offset = offset;
      }
  
      public SQLExpr getXmlPath() {
          return xmlPath;
      }
  
      public void setXmlPath(SQLExpr xmlPath) {
          if (xmlPath != null) {
              xmlPath.setParent(this);
          }
          this.xmlPath = xmlPath;
      }
  
      public List<String> computeSelecteListAlias() {
          SQLSelectQueryBlock firstQuery = getFirstQueryBlock();
          if (firstQuery != null) {
              return firstQuery.computeSelecteListAlias();
          }
  
          return Collections.emptyList();
      }
  
      public SQLSelectQueryBlock getFirstQueryBlock() {
          if (query instanceof SQLSelectQueryBlock) {
              return (SQLSelectQueryBlock) query;
          }
  
          if (query instanceof SQLUnionQuery) {
              SQLUnionQuery union = (SQLUnionQuery) query;
              while (union.getLeft() instanceof SQLUnionQuery) {
                  union = (SQLUnionQuery) union.getLeft();
              }
              return union.getFirstQueryBlock();
          }
  
          return null;
      }
  
      @Override
      public boolean equals(Object o) {
          if (this == o) {
              return true;
          }
          if (o == null || getClass() != o.getClass()) {
              return false;
          }
  
          SQLSelect sqlSelect = (SQLSelect) o;
  
          if (forBrowse != sqlSelect.forBrowse) {
              return false;
          }
          if (withSubQuery != null ? !withSubQuery.equals(sqlSelect.withSubQuery) : sqlSelect.withSubQuery != null) {
              return false;
          }
          if (query != null ? !query.equals(sqlSelect.query) : sqlSelect.query != null) {
              return false;
          }
          if (orderBy != null ? !orderBy.equals(sqlSelect.orderBy) : sqlSelect.orderBy != null) {
              return false;
          }
          if (limit != null ? !limit.equals(sqlSelect.limit) : sqlSelect.limit != null) {
              return false;
          }
          if (hints != null ? !hints.equals(sqlSelect.hints) : sqlSelect.hints != null) {
              return false;
          }
          if (restriction != null ? !restriction.equals(sqlSelect.restriction) : sqlSelect.restriction != null) {
              return false;
          }
          if (forXmlOptions != null ? !forXmlOptions.equals(sqlSelect.forXmlOptions) : sqlSelect.forXmlOptions != null) {
              return false;
          }
          if (xmlPath != null ? !xmlPath.equals(sqlSelect.xmlPath) : sqlSelect.xmlPath != null) {
              return false;
          }
          if (rowCount != null ? !rowCount.equals(sqlSelect.rowCount) : sqlSelect.rowCount != null) {
              return false;
          }
          if (offset != null ? !offset.equals(sqlSelect.offset) : sqlSelect.offset != null) {
              return false;
          }
          return headHint != null ? headHint.equals(sqlSelect.headHint) : sqlSelect.headHint == null;
      }
  
      @Override
      public int hashCode() {
          int result = withSubQuery != null ? withSubQuery.hashCode() : 0;
          result = 31 * result + (query != null ? query.hashCode() : 0);
          result = 31 * result + (orderBy != null ? orderBy.hashCode() : 0);
          result = 31 * result + (limit != null ? limit.hashCode() : 0);
          result = 31 * result + (hints != null ? hints.hashCode() : 0);
          result = 31 * result + (restriction != null ? restriction.hashCode() : 0);
          result = 31 * result + (forBrowse ? 1 : 0);
          result = 31 * result + (forXmlOptions != null ? forXmlOptions.hashCode() : 0);
          result = 31 * result + (xmlPath != null ? xmlPath.hashCode() : 0);
          result = 31 * result + (rowCount != null ? rowCount.hashCode() : 0);
          result = 31 * result + (offset != null ? offset.hashCode() : 0);
          result = 31 * result + (headHint != null ? headHint.hashCode() : 0);
          return result;
      }
  
      public boolean addWhere(SQLExpr where) {
          if (where == null) {
              return false;
          }
  
          if (query instanceof SQLSelectQueryBlock) {
              ((SQLSelectQueryBlock) query).addWhere(where);
              return true;
          }
  
          if (query instanceof SQLUnionQuery) {
              SQLSelectQueryBlock queryBlock = new SQLSelectQueryBlock(getDbType());
              queryBlock.setFrom(new SQLSelect(query), "u");
              queryBlock.addSelectItem(new SQLAllColumnExpr());
              queryBlock.setParent(queryBlock);
              query = queryBlock;
              return true;
          }
  
          return false;
      }
  
      public boolean replace(SQLSelectQuery cmp, SQLSelectQuery target) {
          if (cmp == query) {
              setQuery(target);
              return true;
          }
  
          return false;
      }
  
      public SQLLimit getLimit() {
          return limit;
      }
  
      public void setLimit(SQLLimit x) {
          if (x != null) {
              x.setParent(this);
          }
          this.limit = x;
      }
  }
  