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.sql.SQLUtils;
  import com.alibaba.druid.sql.ast.*;
  import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr;
  import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
  import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
  import com.alibaba.druid.sql.repository.SchemaObject;
  import com.alibaba.druid.sql.visitor.SQLASTVisitor;
  import com.alibaba.druid.util.FnvHash;
  import com.alibaba.druid.util.StringUtils;
  
  import java.util.ArrayList;
  import java.util.List;
  
  public class SQLExprTableSource extends SQLTableSourceImpl implements SQLReplaceable {
      protected SQLExpr expr;
      protected List<SQLName> partitions;
      protected SQLTableSampling sampling;
      protected SchemaObject schemaObject;
  
      protected List<SQLName> columns;
  
      public SQLExprTableSource() {
      }
  
      public SQLExprTableSource(String tableName) {
          this(SQLUtils.toSQLExpr(tableName), null);
      }
  
      public SQLExprTableSource(SQLExpr expr) {
          this(expr, null);
      }
  
      public SQLExprTableSource(SQLExpr expr, String alias) {
          this.setExpr(expr);
          this.setAlias(alias);
      }
  
      public SQLExpr getExpr() {
          return this.expr;
      }
  
      public void setExpr(SQLExpr x) {
          if (x != null) {
              x.setParent(this);
          }
          this.expr = x;
      }
  
      public void setExpr(String name) {
          this.setExpr(new SQLIdentifierExpr(name));
      }
  
      public SQLTableSampling getSampling() {
          return sampling;
      }
  
      public void setSampling(SQLTableSampling x) {
          if (x != null) {
              x.setParent(this);
          }
          this.sampling = x;
      }
  
      public SQLName getName() {
          if (expr instanceof SQLName) {
              return (SQLName) expr;
          }
          return null;
      }
  
      public String getTableName(boolean normalize) {
          String tableName = getTableName();
          if (normalize) {
              return SQLUtils.normalize(tableName);
          }
          return tableName;
      }
  
      public String getTableName() {
          if (expr == null) {
              return null;
          }
  
          if (expr instanceof SQLIdentifierExpr) {
              return ((SQLIdentifierExpr) expr).getName();
          }
  
          if (expr instanceof SQLPropertyExpr) {
              return ((SQLPropertyExpr) expr).getSimpleName();
          }
  
          return null;
      }
  
      public String getSchema() {
          if (expr == null) {
              return null;
          }
  
          if (expr instanceof SQLPropertyExpr) {
              SQLExpr owner = ((SQLPropertyExpr) expr).getOwner();
              if (owner instanceof SQLIdentifierExpr) {
                  return ((SQLIdentifierExpr) owner).getName();
              }
  
              if (owner instanceof SQLPropertyExpr) {
                  return ((SQLPropertyExpr) owner).getSimpleName();
              }
  
              if (owner instanceof SQLAllColumnExpr) {
                  return "*";
              }
  
              return null;
          }
  
          return null;
      }
  
      public String getCatalog() {
          if (expr instanceof SQLPropertyExpr) {
              SQLExpr owner = ((SQLPropertyExpr) expr).getOwner();
  
              if (owner instanceof SQLPropertyExpr) {
                  SQLExpr catalogExpr = ((SQLPropertyExpr) owner).getOwner();
                  if (catalogExpr instanceof SQLIdentifierExpr) {
                      return ((SQLIdentifierExpr) catalogExpr).getName();
                  }
              }
  
              return null;
          }
  
          return null;
      }
  
      public boolean setCatalog(String catalog) {
          if (expr instanceof SQLPropertyExpr) {
              SQLPropertyExpr propertyExpr = (SQLPropertyExpr) expr;
              SQLExpr owner = propertyExpr.getOwner();
              if (owner instanceof SQLIdentifierExpr) {
                  if (catalog == null) {
                      return false;
                  }
  
                  propertyExpr.setOwner(
                          new SQLPropertyExpr(
                                  catalog,
                                  ((SQLIdentifierExpr) owner).getName()
                          )
                  );
                  return true;
              } else if (owner instanceof SQLPropertyExpr) {
                  SQLPropertyExpr propertyOwner = (SQLPropertyExpr) owner;
                  final SQLExpr propertyOwnerOwner = propertyOwner.getOwner();
                  if (propertyOwnerOwner instanceof SQLIdentifierExpr) {
                      if (catalog == null) {
                          propertyExpr.setOwner(((SQLIdentifierExpr) propertyOwnerOwner).getName());
                      } else {
                          propertyOwner.setOwner(
                                  new SQLIdentifierExpr(catalog));
                      }
                      return true;
                  }
              }
          }
  
          return false;
      }
  
      public void setCatalog(String catalog, String schema) {
          if (catalog == null) {
              throw new IllegalArgumentException("catalog is null.");
          }
  
          if (schema == null) {
              throw new IllegalArgumentException("schema is null.");
          }
  
          setSchema(schema);
          setCatalog(catalog);
      }
  
      public void setSchema(String schema) {
          if (expr instanceof SQLPropertyExpr) {
              SQLPropertyExpr propertyExpr = (SQLPropertyExpr) expr;
              if (StringUtils.isEmpty(schema)) {
                  this.setExpr(new SQLIdentifierExpr(propertyExpr.getName()));
              } else {
                  propertyExpr.setOwner(schema);
              }
          } else {
              if (StringUtils.isEmpty(schema)) {
                  return;
              }
  
              String ident = ((SQLIdentifierExpr) expr).getName();
              this.setExpr(new SQLPropertyExpr(schema, ident));
          }
      }
  
      public void setSimpleName(String name) {
          if (StringUtils.isEmpty(name)) {
              throw new IllegalArgumentException("schema is empty.");
          }
          if (expr == null) {
              expr = new SQLIdentifierExpr(name);
          } else if (expr instanceof SQLPropertyExpr) {
              ((SQLPropertyExpr) expr).setName(name);
          } else {
              expr = new SQLIdentifierExpr(name);
          }
      }
  
      public List<SQLName> getPartitions() {
          if (this.partitions == null) {
              this.partitions = new ArrayList<SQLName>(2);
          }
  
          return partitions;
      }
  
      public int getPartitionSize() {
          if (this.partitions == null) {
              return 0;
          }
          return this.partitions.size();
      }
  
      public void addPartition(SQLName partition) {
          if (partition != null) {
              partition.setParent(this);
          }
  
          if (this.partitions == null) {
              this.partitions = new ArrayList<SQLName>(2);
          }
          this.partitions.add(partition);
      }
  
      @Override
      protected void accept0(SQLASTVisitor visitor) {
          if (visitor.visit(this)) {
              acceptChild(visitor, expr);
              acceptChild(visitor, sampling);
              acceptChild(visitor, partitions);
              acceptChild(visitor, columns);
              super.accept0(visitor);
          }
          visitor.endVisit(this);
      }
  
      public String computeAlias() {
          String alias = this.getAlias();
  
          if (alias == null) {
              if (expr instanceof SQLName) {
                  alias = ((SQLName) expr).getSimpleName();
              }
          }
  
          return SQLUtils.normalize(alias);
      }
  
      public SQLExprTableSource clone() {
          SQLExprTableSource x = new SQLExprTableSource();
          cloneTo(x);
          return x;
      }
  
      public void cloneTo(SQLExprTableSource x) {
          x.alias = alias;
  
          if (expr != null) {
              x.setExpr(expr.clone());
          }
  
          if (hints != null) {
              for (SQLHint p : hints) {
                  SQLHint p1 = p.clone();
                  x.getHints().add(p1);
              }
          }
          if (partitions != null) {
              for (SQLName p : partitions) {
                  SQLName p1 = p.clone();
                  x.addPartition(p1);
              }
          }
  
          if (schemaObject != null) {
              x.setSchemaObject(schemaObject);
          }
  
          if (columns != null) {
              x.columns = new ArrayList<SQLName>(columns.size());
  
              for (SQLName column : columns) {
                  SQLName clonedColumn = column.clone();
                  clonedColumn.setParent(x);
                  x.columns.add(clonedColumn);
              }
          }
      }
  
      public List<SQLName> getColumns() {
          if (columns == null) {
              columns = new ArrayList<SQLName>(2);
          }
          return columns;
      }
  
      public List<SQLName> getColumnsDirect() {
          return columns;
      }
  
      public SchemaObject getSchemaObject() {
          return schemaObject;
      }
  
      public void setSchemaObject(SchemaObject schemaObject) {
          this.schemaObject = schemaObject;
      }
  
      public boolean containsAlias(String alias) {
          long hashCode64 = FnvHash.hashCode64(alias);
  
          return containsAlias(hashCode64);
      }
  
      public boolean containsAlias(long aliasHash) {
          if (this.aliasHashCode64() == aliasHash) {
              return true;
          }
  
          if (expr instanceof SQLPropertyExpr) {
              long exprNameHash = ((SQLPropertyExpr) expr).hashCode64();
              if (exprNameHash == aliasHash) {
                  return true;
              }
          }
  
          if (expr instanceof SQLName) {
              long exprNameHash = ((SQLName) expr).nameHashCode64();
              return exprNameHash == aliasHash;
          }
  
          return false;
      }
  
      public SQLColumnDefinition findColumn(String columnName) {
          if (columnName == null) {
              return null;
          }
  
          long hash = FnvHash.hashCode64(columnName);
          return findColumn(hash);
      }
  
      public SQLColumnDefinition findColumn(long columnNameHash) {
          SQLObject object = resolveColum(columnNameHash);
          if (object instanceof SQLColumnDefinition) {
              return (SQLColumnDefinition) object;
          }
          return null;
      }
  
      public SQLObject resolveColumn(long columnNameHash) {
          if (schemaObject != null) {
              SQLStatement stmt = schemaObject.getStatement();
              if (stmt instanceof SQLCreateTableStatement) {
                  SQLCreateTableStatement createTableStmt = (SQLCreateTableStatement) stmt;
                  return createTableStmt.findColumn(columnNameHash);
              }
          }
  
          SQLObject resolvedOwnerObject = null;
          if (expr instanceof SQLIdentifierExpr) {
              resolvedOwnerObject = ((SQLIdentifierExpr) expr).getResolvedOwnerObject();
          }
  
          if (resolvedOwnerObject == null) {
              return resolvedOwnerObject;
          }
  
          if (resolvedOwnerObject instanceof SQLWithSubqueryClause.Entry) {
              final SQLSelect subQuery = ((SQLWithSubqueryClause.Entry) resolvedOwnerObject)
                      .getSubQuery();
              if (subQuery == null) {
                  return null;
              }
  
              final SQLSelectQueryBlock firstQueryBlock = subQuery.getFirstQueryBlock();
              if (firstQueryBlock == null) {
                  return null;
              }
  
              SQLSelectItem selectItem = firstQueryBlock.findSelectItem(columnNameHash);
              if (selectItem != null) {
                  return selectItem;
              }
          }
          return null;
      }
  
      public SQLTableSource findTableSourceWithColumn(String columnName) {
          if (columnName == null) {
              return null;
          }
  
          long hash = FnvHash.hashCode64(columnName);
          return findTableSourceWithColumn(hash, columnName, 0);
      }
  
      public SQLTableSource findTableSourceWithColumn(long columnName_hash, String name, int option) {
          if (schemaObject != null) {
              SQLStatement stmt = schemaObject.getStatement();
              if (stmt instanceof SQLCreateTableStatement) {
                  SQLCreateTableStatement createTableStmt = (SQLCreateTableStatement) stmt;
                  if (createTableStmt.findColumn(columnName_hash) != null) {
                      return this;
                  }
              }
          }
  
          if (expr instanceof SQLIdentifierExpr) {
              SQLTableSource tableSource = ((SQLIdentifierExpr) expr).getResolvedTableSource();
              if (tableSource != null) {
                  return this;
              }
          }
  
          return null;
      }
  
      public SQLTableSource findTableSource(long alias_hash) {
          if (alias_hash == 0) {
              return null;
          }
  
          if (aliasHashCode64() == alias_hash) {
              return this;
          }
  
          if (expr instanceof SQLName) {
              long exprNameHash = ((SQLName) expr).nameHashCode64();
              if (exprNameHash == alias_hash) {
                  return this;
              }
          }
  
          if (expr instanceof SQLPropertyExpr) {
              long hash = ((SQLPropertyExpr) expr).hashCode64();
              if (hash == alias_hash) {
                  return this;
              }
          }
  
          return null;
      }
  
      @Override
      public boolean replace(SQLExpr expr, SQLExpr target) {
          if (expr == this.expr) {
              this.setExpr(target);
              return true;
          }
  
          if (partitions != null) {
              for (int i = 0; i < partitions.size(); i++) {
                  if (partitions.get(i) == expr) {
                      target.setParent(this);
                      partitions.set(i, (SQLName) target);
                      return true;
                  }
              }
          }
  
          return false;
      }
  
      public long aliasHashCode64() {
          if (alias != null) {
              if (aliasHashCode64 == 0) {
                  aliasHashCode64 = FnvHash.hashCode64(alias);
              }
              return aliasHashCode64;
          }
  
          if (expr instanceof SQLName) {
              return ((SQLName) expr).nameHashCode64();
          }
  
          return 0;
      }
  
      @Override
      public boolean equals(Object o) {
          if (this == o) {
              return true;
          }
          if (o == null || getClass() != o.getClass()) {
              return false;
          }
          if (!super.equals(o)) {
              return false;
          }
  
          SQLExprTableSource that = (SQLExprTableSource) o;
  
          if (expr != null ? !expr.equals(that.expr) : that.expr != null) {
              return false;
          }
          if (partitions != null ? !partitions.equals(that.partitions) : that.partitions != null) {
              return false;
          }
          if (sampling != null ? !sampling.equals(that.sampling) : that.sampling != null) {
              return false;
          }
          if (schemaObject != null ? !schemaObject.equals(that.schemaObject) : that.schemaObject != null) {
              return false;
          }
          return columns != null ? columns.equals(that.columns) : that.columns == null;
      }
  
      @Override
      public int hashCode() {
          int result = super.hashCode();
          result = 31 * result + (expr != null ? expr.hashCode() : 0);
          result = 31 * result + (partitions != null ? partitions.hashCode() : 0);
          result = 31 * result + (sampling != null ? sampling.hashCode() : 0);
          result = 31 * result + (schemaObject != null ? schemaObject.hashCode() : 0);
          result = 31 * result + (columns != null ? columns.hashCode() : 0);
          return result;
      }
  }
  