package util.sqlparse.visitor.common.scope;

import bean.Column;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.SQLOrderBy;
import com.alibaba.druid.sql.ast.SQLOver;
import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLCaseExpr;
import com.alibaba.druid.sql.ast.expr.SQLExistsExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLNullExpr;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.expr.SQLQueryExpr;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectQuery;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource;
import com.alibaba.druid.sql.ast.statement.SQLUnionQuery;
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleSysdateExpr;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import util.sqlparse.visitor.common.Context;
import util.sqlparse.visitor.common.ID;
import util.sqlparse.visitor.common.Owner;
import util.sqlparse.visitor.common.ScopeDialector;
import util.sqlparse.visitor.common.bean.SQLResult;
import util.sqlparse.visitor.common.memo.FieldMemo;
import util.sqlparse.visitor.common.memo.TableMemo;
import util.sqlparse.visitor.common.memo.ValueMemo;
import util.sqlparse.visitor.common.utils.SeqMap;

public class Scope extends ID {
   public static final int SUCCESS_CODE = 0;
   private static final String ALL_COLUMN_START = "*";
   public static final int ERROR_CODE = -1;
   public List<SQLResult> results = new ArrayList();
   public Context context;
   protected Scope current = this;
   public Scope parent;
   public SQLObject ref;
   protected List<Scope> children = new ArrayList();
   protected Set<String> schemas = new HashSet(6);
   private boolean fieldState;
   public boolean fromField;
   public boolean top = false;
   public boolean join = false;
   public ScopeDialector dialector;
   protected SeqMap<String, FieldMemo> fieldMap = new SeqMap<String, FieldMemo>();
   protected SeqMap<String, TableMemo> tableMap = new SeqMap<String, TableMemo>();
   protected List<ValueMemo> values = new ArrayList();
   private boolean debug = false;
   public boolean union = false;
   public boolean isolated = true;
   public boolean isCheckIsolated = false;
   public ReplaceInfo replaceInfo;

   public boolean isFieldState() {
      return this.fieldState;
   }

   public void openFieldState() {
      this.fieldState = true;
   }

   public void closeFieldState() {
      this.fieldState = false;
   }

   public void addTable(TableMemo table) {
      String qualified = table.getQualifiedName();
      if (!this.tableMap.containsKey(qualified)) {
         this.tableMap.put(qualified, table);
      }

   }

   public void addTableCheckConflict(TableMemo table) {
      this.removeTableConflict(table);
      this.addTable(table);
   }

   public void removeTableConflict(TableMemo table) {
      String qualified = table.getQualifiedName();
      if (this.tableMap.containsKey(qualified)) {
         TableMemo existTable = (TableMemo)this.tableMap.get(qualified);
         if (!existTable.atom) {
            return;
         }

         if (this.isEqual(existTable.name, existTable.alias) && !this.isEqual(table.name, table.alias)) {
            this.tableMap.remove(qualified);
            this.removeTableScopeRelation(qualified);
            if (existTable.atom) {
               this.current.context.getTableMap().remove(existTable.getAtomName());
            }
         }
      }

   }

   private void removeTableScopeRelation(String qualified) {
      TableScope tableScope = this.current.getNearestParentTableScope();
      if (tableScope != null) {
         TableMemo parentTable = tableScope.table;
         if (parentTable != null) {
            for(int i = 0; i < parentTable.getChildren().size(); ++i) {
               TableMemo temp = (TableMemo)parentTable.getChildren().get(i);
               if (this.isEqual(temp.getQualifiedName(), qualified)) {
                  parentTable.getChildren().remove(i);
               }
            }
         }
      }

   }

   public FieldMemo addField(FieldMemo field) {
      field.scope = this;
      new FieldMemo();
      if (this.dialector.addField(field)) {
         return field;
      } else {
         String qualifiedName = field.getQualifiedName();
         if (!this.fieldMap.containsKey(qualifiedName)) {
            this.fieldMap.put(qualifiedName, field);
            return field;
         } else {
            FieldMemo fieldInfo = (FieldMemo)this.fieldMap.get(qualifiedName);
            if (field.isSelectItem) {
               fieldInfo.isSelectItem = true;
               fieldInfo.id = field.id;
            }

            if (field.atom) {
               fieldInfo.atom = field.atom;
            }

            fieldInfo.exprs.addAll(field.exprs);
            fieldInfo.aliasFields.addAll(field.aliasFields);
            field.children.remove(fieldInfo);
            this.mergeChildren(field, fieldInfo);
            return fieldInfo;
         }
      }
   }

   private void mergeChildren(FieldMemo field, FieldMemo fieldInfo) {
      for(FieldMemo temp : field.children) {
         FieldMemo find = null;

         for(FieldMemo x : fieldInfo.children) {
            if (this.isEqual(x.getQualifiedName(), temp.getQualifiedName())) {
               find = x;
               break;
            }
         }

         if (find == null && fieldInfo != temp && !this.isEqual(fieldInfo.getQualifiedName(), temp.getQualifiedName())) {
            fieldInfo.children.add(temp);
         }
      }

   }

   public void enterScope(Scope scope) {
      this.current.append(scope);
      if (this.debug) {
         System.out.println("[E]" + this.current.id + "->" + scope.id + "     " + scope.ref.getClass().toString() + "\n     " + scope.ref);
      }

      this.current = scope;
      if (this.dialector != null) {
         this.current.dialector = this.dialector.clone(this.current);
      }

   }

   public void reset() {
      this.current = this;
   }

   public void enterScope(SQLObject sqlObject) {
      for(Scope child : this.current.children) {
         if (child.ref.equals(sqlObject)) {
            if (this.debug) {
               System.out.println("[E]" + this.current.id + "->" + child.id + "     " + child.ref.getClass().toString() + "\n     " + child.ref);
            }

            this.current = child;
            return;
         }
      }

   }

   public Scope getScope(SQLObject sqlObject) {
      for(Scope child : this.current.children) {
         if (child.ref.equals(sqlObject)) {
            return child;
         }
      }

      return null;
   }

   public void exitScope() {
      if (this.debug) {
         if (this.current.parent != null) {
            System.out.println("[X]" + this.current.id + "->" + this.current.parent.id + "     " + this.current.ref.getClass().toString());
         } else {
            System.out.println("[X]" + this.current.id + "->root");
         }
      }

      this.current = this.current.parent;
      if (this.current == null) {
         this.current = this;
      }

   }

   public void append(Scope child) {
      this.children.add(child);
      child.context = this.context;
      child.parent = this.current;
      child.isCheckIsolated = this.isCheckIsolated;
   }

   public Scope getCurrent() {
      return this.current;
   }

   public String getDefaultSchema() {
      return this.context.getSchema();
   }

   public boolean isPrimitive(SQLExpr expr) {
      if (expr instanceof SQLSubqueryTableSource) {
         return false;
      } else {
         if (expr instanceof SQLMethodInvokeExpr) {
            List<SQLExpr> exprs = ((SQLMethodInvokeExpr)expr).getArguments();
            if (exprs.size() > 0) {
               for(SQLExpr sqlExpr : exprs) {
                  if (!this.isPrimitive(sqlExpr)) {
                     return false;
                  }
               }
            }
         }

         if (expr instanceof SQLCaseExpr) {
            SQLCaseExpr caseExpr = (SQLCaseExpr)expr;

            for(SQLCaseExpr.Item item : caseExpr.getItems()) {
               if (!this.isPrimitive(item.getConditionExpr()) && this.isPrimitive(item.getValueExpr())) {
                  return false;
               }
            }
         }

         if (!(expr instanceof SQLBinaryOpExpr)) {
            if (expr instanceof SQLInSubQueryExpr) {
               return false;
            } else if (expr instanceof SQLExistsExpr) {
               return false;
            } else {
               return !(expr instanceof SQLQueryExpr);
            }
         } else {
            SQLBinaryOpExpr opExpr = (SQLBinaryOpExpr)expr;
            SQLExpr left = opExpr.getLeft();
            SQLExpr right = opExpr.getRight();
            return this.isPrimitive(left) && this.isPrimitive(right);
         }
      }
   }

   public boolean isAllColumn(SQLExpr x) {
      if (x instanceof SQLAllColumnExpr) {
         return true;
      } else {
         if (x instanceof SQLPropertyExpr) {
            SQLPropertyExpr prop = (SQLPropertyExpr)x;
            if (prop.getName() == null) {
               return false;
            }

            if (prop.getName().equals("*")) {
               return true;
            }
         }

         if (x instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr)x;
            if (identifierExpr.getName().equals("*")) {
               return true;
            }
         }

         return false;
      }
   }

   public TableMemo getTable(SQLObject x) {
      List<Scope> feet = new ArrayList();
      TableMemo table = this.getTableRecursive(x, feet);
      if (table == null) {
         if (this.tableMap.size() <= 0) {
            return this.getTableDownStream(x, feet);
         }

         Iterator var4 = this.tableMap.entrySet().iterator();
         if (var4.hasNext()) {
            Map.Entry<String, TableMemo> stringTableMemoEntry = (Map.Entry)var4.next();
            return (TableMemo)stringTableMemoEntry.getValue();
         }
      }

      return table;
   }

   private TableMemo getTableDownStream(SQLObject x, List<Scope> feet) {
      for(Scope child : this.children) {
         TableMemo table = child.getTable(x);
         if (table != null) {
            return table;
         }

         table = this.getTableDownStream(x, feet);
         if (table != null) {
            return null;
         }
      }

      return null;
   }

   private TableMemo getTableRecursive(SQLObject x, List<Scope> feet) {
      String name = null;
      SQLExpr ownerExpr = null;
      if (x instanceof SQLIdentifierExpr) {
         SQLIdentifierExpr idExpr = (SQLIdentifierExpr)x;
         name = idExpr.getName();
      } else if (x instanceof SQLPropertyExpr) {
         SQLPropertyExpr propExpr = (SQLPropertyExpr)x;
         name = propExpr.getName();
         ownerExpr = propExpr.getOwner();
      } else if (x instanceof SQLNullExpr) {
         SQLNullExpr nullExpr = (SQLNullExpr)x;
         name = nullExpr.toString();
      }

      Owner owner = this.getOwner(ownerExpr);
      if (owner.table != null) {
         String ns = owner.getQualifiedName();
         TableMemo table = null;

         for(TableMemo z : this.tableMap.values()) {
            if (this.isEqual(z.alias, owner.table)) {
               table = z;
               break;
            }
         }

         if (table != null) {
            return table;
         } else {
            List<Scope> subScopes = new ArrayList();

            for(Scope z : this.children) {
               if (!z.fromField) {
                  subScopes.add(z);
               }
            }

            for(Scope sub : subScopes) {
               if ((!sub.isCheckIsolated || !sub.isolated) && sub.tableMap.containsKey(ns)) {
                  return (TableMemo)sub.tableMap.get(ns);
               }
            }

            if (this.parent != null) {
               return this.parent.getTableRecursive(x, feet);
            } else {
               return null;
            }
         }
      } else {
         for(TableMemo table : this.tableMap.values()) {
            if (table.atom) {
               for(Column column : this.context.getColumns(table.schema, table.name)) {
                  if (this.isEqual(column.getColumnName(), name)) {
                     return table;
                  }
               }
            }

            if (table.refTable != null && table.refTable.scope != null) {
               FieldMemo fieldRef = table.refTable.scope.getFieldByAlias(name);
               if (fieldRef != null) {
                  return table;
               }
            }
         }

         if (this.children != null && this.children.size() > 0) {
            List<TableMemo> path = new ArrayList();
            Feet ft = this.getFieldByAliasBranch(name, path);
            if (ft != null && ft.path.size() > 0) {
               return (TableMemo)ft.path.get(0);
            }
         }

         if (this.parent != null) {
            return this.parent.getTableRecursive(x, feet);
         } else {
            return null;
         }
      }
   }

   private Feet getFieldByAliasBranch(String name, List<TableMemo> path) {
      for(Scope child : this.children) {
         if (!child.top && !child.fromField && (!child.isCheckIsolated || !child.isolated)) {
            if (child.fieldMap.size() > 0) {
               FieldMemo field = child.getFieldByAliasInternal(name);
               if (field != null) {
                  Feet feet = new Feet();
                  feet.field = field;
                  feet.path = path;
                  return feet;
               }
            } else if (child.children.size() > 0) {
               List<TableMemo> paths = new ArrayList(path);
               if (child instanceof TableScope) {
                  paths.add(((TableScope)child).table);
               }

               Feet feet = child.getFieldByAliasBranch(name, paths);
               if (feet != null) {
                  return feet;
               }
            }
         }
      }

      return null;
   }

   private FieldMemo getFieldByAliasInternal(String name) {
      if (name == null) {
         return null;
      } else {
         for(FieldMemo value : this.fieldMap.values()) {
            if (this.isEqual(name, value.alias)) {
               return value;
            }
         }

         return null;
      }
   }

   public ValueMemo getFieldValueInfo(SQLExpr expr) {
      if (expr == null) {
         return null;
      } else {
         FieldMemo field = this.getFieldByExpr(expr);
         if (field != null) {
            ValueMemo value = new ValueMemo();
            value.field = field;
            value.ref = expr;
            return value;
         } else {
            return null;
         }
      }
   }

   public List<SQLExpr> getSubFieldExprs(SQLExpr x) {
      List<SQLExpr> exprs = new ArrayList();
      this.getSubFieldExprs(x, exprs);
      return exprs;
   }

   private void getSubFieldExprs(SQLObject x, List<SQLExpr> exprs) {
      if (x != null) {
         if (!(x instanceof SQLSelectQueryBlock)) {
            if (!(x instanceof SQLInSubQueryExpr)) {
               if (!(x instanceof SQLExistsExpr)) {
                  if (!(x instanceof SQLSelectQuery)) {
                     if (x instanceof SQLPropertyExpr) {
                        exprs.add((SQLPropertyExpr)x);
                     } else if (x instanceof SQLIdentifierExpr) {
                        exprs.add((SQLIdentifierExpr)x);
                     } else if (x instanceof OracleSysdateExpr) {
                        exprs.add((OracleSysdateExpr)x);
                     } else if (x instanceof SQLOver) {
                        SQLOver over = (SQLOver)x;
                        this.getSubFieldExprsByExprs(over.getPartitionBy(), exprs);
                        this.getSQLOrderExprs(over.getOrderBy(), exprs);
                        this.getSQLOrderExprs(over.getDistributeBy(), exprs);
                        this.getSQLOrderExprs(over.getSortBy(), exprs);
                        this.getSubFieldExprs(over.getWindowingBetweenBegin());
                        this.getSubFieldExprs(over.getWindowingBetweenEnd());
                     } else if (x instanceof SQLCaseExpr.Item) {
                        SQLCaseExpr.Item item = (SQLCaseExpr.Item)x;
                        this.getSubFieldExprs(item.getConditionExpr(), exprs);
                        this.getSubFieldExprs(item.getValueExpr(), exprs);
                     } else if (x instanceof SQLExpr) {
                        SQLExpr expr = (SQLExpr)x;
                        if (expr.getChildren() != null && expr.getChildren().size() > 0) {
                           for(SQLObject sqlObject : expr.getChildren()) {
                              this.getSubFieldExprs(sqlObject, exprs);
                           }
                        }
                     }

                  }
               }
            }
         }
      }
   }

   private void getSQLOrderExprs(SQLOrderBy order, List<SQLExpr> exprs) {
      if (order != null) {
         for(SQLSelectOrderByItem item : order.getItems()) {
            exprs.addAll(this.getSubFieldExprs(item.getExpr()));
         }
      }

   }

   private void getSubFieldExprsByExprs(List<SQLExpr> sqlExprs, List<SQLExpr> exprs) {
      if (sqlExprs != null) {
         for(SQLExpr sqlExpr : sqlExprs) {
            this.getSubFieldExprs(sqlExpr, exprs);
         }
      }

   }

   public Owner getOwner(SQLObject x) {
      Owner owner = new Owner();
      if (x == null) {
         owner.schema = this.getDefaultSchema();
      }

      if (x instanceof SQLIdentifierExpr) {
         SQLIdentifierExpr idExpr = (SQLIdentifierExpr)x;
         owner.table = this.dialector.wrap(idExpr.getName());
         owner.schema = this.dialector.wrap(this.getDefaultSchema());
      } else if (x instanceof SQLPropertyExpr) {
         SQLPropertyExpr propExpr = (SQLPropertyExpr)x;
         owner.table = this.dialector.wrap(propExpr.getName());
         owner.schema = this.dialector.wrap(propExpr.getOwnerName());
      } else if (x instanceof SQLAllColumnExpr) {
         owner.table = null;
         owner.schema = this.dialector.wrap(this.getDefaultSchema());
      }

      return owner;
   }

   public Owner getAllOwner(SQLExpr e) {
      Owner owner = new Owner();
      owner.schema = this.getDefaultSchema();
      if (e instanceof SQLAllColumnExpr) {
         owner.schema = this.context.getSchema();
      } else if (e instanceof SQLPropertyExpr) {
         SQLPropertyExpr propExpr = (SQLPropertyExpr)e;
         SQLExpr expr = propExpr.getOwner();
         if (expr instanceof SQLIdentifierExpr) {
            owner.alias = ((SQLIdentifierExpr)expr).getName();
         } else {
            owner.alias = ((SQLPropertyExpr)expr).getName();
            owner.schema = ((SQLPropertyExpr)expr).getOwnerName();
         }
      }

      return owner;
   }

   public List<FieldMemo> expandAllColumns(TableMemo table, boolean isSub) {
      List<FieldMemo> fields = new FieldNameList();
      if (this.dialector != null) {
         boolean continueExpand = this.dialector.expandAllColumns(table, isSub, fields);
         if (!continueExpand) {
            return fields;
         }
      }

      if (table == null) {
         List<TableMemo> topTables = new ArrayList();

         for(TableMemo x : this.tableMap.orderValues()) {
            if (x.refTable != null) {
               topTables.add(x);
            }
         }

         if (topTables.size() > 0) {
            for(TableMemo topTable : topTables) {
               List<FieldMemo> subFields = this.cloneFields(this.expandAllColumns(topTable.refTable, topTable.refTable.isSub), topTable, true);
               fields.addAll(subFields);
            }
         }

         List<TableMemo> memTables = new ArrayList();

         for(TableMemo x : this.tableMap.orderValues()) {
            if (!x.isSub && x.refTable == null) {
               memTables.add(x);
            }
         }

         if (memTables.size() > 0) {
            for(TableMemo memTable : memTables) {
               List<FieldMemo> subFields = this.cloneFields(this.expandAllColumns(memTable, memTable.isSub), memTable, false);
               fields.addAll(subFields);
            }
         }

         for(Scope child : this.children) {
            if (!child.fromField) {
               child.expandScopeColumns(fields);
            }
         }
      } else if (!table.isSub) {
         if (table.refTable != null) {
            for(TableMemo childTable : table.refTable.getChildren()) {
               if (childTable.topTable) {
                  List<FieldMemo> subFields = new ArrayList();
                  List<FieldMemo> cloneFields = new ArrayList();

                  for(FieldMemo x : childTable.scope.fieldMap.orderValues()) {
                     if (x.isSelectItem) {
                        cloneFields.add(x);
                     }
                  }

                  for(FieldMemo field : cloneFields) {
                     FieldMemo cloneField = field.copy();
                     cloneField.alias = field.alias;
                     cloneField.table = table;
                     cloneField.refField = field;
                     subFields.add(cloneField);
                  }

                  fields.addAll(subFields);
                  return fields;
               }
            }
         } else {
            if (!table.atom && table.scope.fieldMap.size() > 0) {
               ArrayList<FieldMemo> fieldMemos = new ArrayList();

               for(FieldMemo x : table.scope.fieldMap.orderValues()) {
                  if (x.isSelectItem) {
                     fieldMemos.add(x);
                  }
               }

               fields.addAll(fieldMemos);
               return fields;
            }

            List<FieldMemo> tbFields = this.dialector.getColumns(table);
            if (tbFields != null && tbFields.size() > 0) {
               for(FieldMemo tbField : tbFields) {
                  fields.add(tbField);
               }
            } else {
               List<Column> columns = this.context.getColumns(table.schema, table.name);
               if (columns != null && columns.size() > 0) {
                  for(Column column : columns) {
                     FieldMemo field = new FieldMemo();
                     field.table = table;
                     field.alias = this.current.dialector.wrapColumn(table.schema, table.name, column.getColumnName());
                     field.name = this.current.dialector.wrapColumn(table.schema, table.name, column.getColumnName());
                     field.isSelectItem = true;
                     fields.add(field);
                  }
               }
            }
         }
      } else if (table.topTable) {
         List<FieldMemo> branchFields = this.getParentTableFields(table, table.scope);
         fields.addAll(branchFields);
      } else {
         for(Scope child : table.scope.children) {
            child.expandScopeColumns(fields);
            if (table.scope.union) {
               break;
            }
         }
      }

      return fields;
   }

   private List<FieldMemo> cloneFields(List<FieldMemo> fields, TableMemo table, boolean isRefField) {
      if (fields != null && fields.size() != 0) {
         List<FieldMemo> cloneFields = new ArrayList();

         for(FieldMemo fieldMemo : fields) {
            FieldMemo cloneField = fieldMemo.copy();
            cloneField.alias = fieldMemo.alias;
            cloneField.table = table;
            if (isRefField) {
               cloneField.refField = fieldMemo;
            }

            cloneFields.add(cloneField);
         }

         return cloneFields;
      } else {
         return new ArrayList();
      }
   }

   private void expandScopeColumns(List<FieldMemo> fields) {
      if (this.fieldMap.size() > 0) {
         TableScope scope = this.getNearestParentTableScope();
         List<FieldMemo> selfFields = new ArrayList();

         for(FieldMemo x : this.fieldMap.orderValues()) {
            if (x.isSelectItem) {
               selfFields.add(x);
            }
         }

         if (scope != null) {
            List<FieldMemo> subFields = this.cloneFields(selfFields, scope.table, false);
            fields.addAll(subFields);
            return;
         }
      }

      for(Scope child : this.children) {
         child.expandScopeColumns(fields);
         if (this.union) {
            break;
         }
      }

   }

   public TableMemo getParentTable(String schemaName, String table) {
      boolean childrenTop = false;
      boolean parentChildrenTop = false;
      if (this.parent != null) {
         for(Scope x : this.parent.children) {
            if (x.top) {
               parentChildrenTop = true;
               break;
            }
         }

         for(Scope x : this.children) {
            if (x.top) {
               childrenTop = true;
               break;
            }
         }

         String name = this.dialector.wrap(schemaName + "." + table);
         if (this.parent.tableMap.containsKey(name)) {
            return (TableMemo)this.parent.tableMap.get(name);
         }

         if (parentChildrenTop) {
            TableMemo memo = this.parent.getParentTopTable(name);
            if (memo != null && memo.topTable) {
               return memo;
            }
         } else if (childrenTop) {
            TableMemo memo = this.current.getParentTopTable(name);
            if (memo != null && memo.topTable) {
               return memo;
            }
         }

         if (this.parent.parent != null) {
            return this.parent.getParentTable(schemaName, table);
         }
      }

      return null;
   }

   private TableMemo getParentTopTable(String name) {
      boolean childrenTop = false;

      for(Scope child : this.children) {
         if (child.top) {
            for(Scope x : child.children) {
               if (x.top) {
                  childrenTop = true;
               }
            }

            if (child.tableMap.size() == 0) {
               if (childrenTop) {
                  TableMemo memo = child.getParentTopTable(name);
                  if (memo != null && memo.topTable) {
                     return memo;
                  }
               }
            } else {
               TableMemo memo = (TableMemo)child.tableMap.get(name);
               if (memo != null && memo.topTable) {
                  return memo;
               }
            }

            childrenTop = false;
         }
      }

      return null;
   }

   private List<FieldMemo> getBranchTableFields(TableMemo table, Scope parent) {
      List<FieldMemo> fields = new ArrayList();

      for(Scope child : parent.children) {
         if (child.fieldMap.size() <= 0) {
            return this.getBranchTableFields(table, child);
         }

         for(FieldMemo branchField : child.fieldMap.orderValues()) {
            if (branchField.isSelectItem && (branchField.table != null && branchField.table.equals(table) || branchField.tables.contains(table))) {
               FieldMemo field = branchField.copy();
               field.alias = branchField.alias;
               field.table = table;
               fields.add(field);
            }
         }
      }

      return fields;
   }

   private List<FieldMemo> getParentTableFields(TableMemo table, Scope scope) {
      List<FieldMemo> fields = new ArrayList();
      if (scope == null) {
         return fields;
      } else {
         if (scope.fieldMap.size() > 0) {
            for(FieldMemo branchField : scope.fieldMap.orderValues()) {
               if (branchField.table != null && branchField.table.equals(table) || branchField.tables.contains(table)) {
                  FieldMemo field = branchField.copy();
                  field.children.addAll(branchField.children);
                  field.isSelectItem = true;
                  field.refField = branchField;
                  field.table = table;
                  fields.add(field);
               }
            }
         } else if (scope.parent != null) {
            fields.addAll(this.getParentTableFields(table, scope.parent));
         }

         return fields;
      }
   }

   public List<TableMemo> getBranchTables() {
      List<TableMemo> tables = new ArrayList();
      if (this.tableMap.size() > 0) {
         tables.addAll(this.tableMap.values());
      } else {
         for(Scope child : this.children) {
            tables.addAll(child.getBranchTables());
         }
      }

      return tables;
   }

   public List<FieldMemo> getBranchAllFields(TableMemo table) {
      List<FieldMemo> fields = new ArrayList();

      for(Scope child : this.children) {
         if (child.fieldMap.size() > 0) {
            for(FieldMemo value : child.fieldMap.values()) {
               if (table == null || value.tables.contains(table)) {
                  FieldMemo field = value.copy();
                  field.table = table;
                  fields.add(field);
               }
            }
         } else {
            fields.addAll(child.getBranchAllFields(table));
         }
      }

      return fields;
   }

   public TableScope getNearestParentTableScope() {
      for(Scope cursor = this; cursor != null; cursor = cursor.parent) {
         if (cursor instanceof TableScope) {
            return (TableScope)cursor;
         }
      }

      return null;
   }

   public void getSubBranchFields(List<FieldMemo> fields) {
      if (this.children.size() != 0) {
         for(Scope child : this.children) {
            if (child instanceof ClauseScope) {
               fields.addAll(child.getFieldMap().values());
            } else {
               child.getSubBranchFields(fields);
            }
         }

      }
   }

   public FieldMemo getFieldByExpr(SQLExpr expr) {
      FieldMemo field = null;

      for(FieldMemo value : this.fieldMap.values()) {
         boolean found = false;

         for(SQLObject sqlObject : value.exprs) {
            if (sqlObject == expr) {
               found = true;
               break;
            }
         }

         if (found) {
            field = value;
            break;
         }
      }

      if (field != null) {
         return field;
      } else {
         return this.parent != null ? this.parent.getFieldByExpr(expr) : null;
      }
   }

   public FieldMemo getFieldByName(String name) {
      FieldMemo field = this.getFieldByNameInternal(name);
      if (field != null) {
         return field;
      } else {
         field = this.getFieldByNameBranch(name);
         if (field != null) {
            return field;
         } else {
            if (this.parent != null) {
               field = this.getFieldByNameTop(name);
            }

            return field;
         }
      }
   }

   public FieldMemo getFieldByAlias(String name) {
      if (name != null && name.length() != 0) {
         for(FieldMemo x : this.fieldMap.values()) {
            if (x.alias != null && this.isEqual(x.alias, name)) {
               return x;
            }
         }

         return null;
      } else {
         return null;
      }
   }

   private FieldMemo getFieldByNameTop(String name) {
      Scope child = null;

      for(Scope x : this.parent.children) {
         if (x.top) {
            child = x;
            break;
         }
      }

      if (child != null) {
         FieldMemo field = child.getFieldByNameTopBranch(name);
         if (field != null) {
            return field;
         }
      }

      return this.parent.parent != null ? this.parent.getFieldByNameTop(name) : null;
   }

   private FieldMemo getFieldByNameTopBranch(String name) {
      if (!this.top) {
         return null;
      } else {
         FieldMemo field = this.getFieldByNameInternal(name);
         if (field != null) {
            return field;
         } else {
            if (this.fieldMap.size() == 0 && this.children.size() > 0) {
               for(Scope child : this.children) {
                  if (child.top) {
                     field = child.getFieldByNameTopBranch(name);
                     if (field != null) {
                        return field;
                     }
                  }
               }
            }

            return null;
         }
      }
   }

   private FieldMemo getFieldByNameInternal(String name) {
      for(FieldMemo x : this.fieldMap.values()) {
         if (x.isSelectItem && this.isEqual(x.name, name)) {
            return x;
         }
      }

      return null;
   }

   private FieldMemo getFieldByNameBranch(String name) {
      FieldMemo field = null;

      for(Scope child : this.children) {
         if (!child.top) {
            if (child.fieldMap.size() > 0) {
               field = child.getFieldByNameInternal(name);
               if (field != null) {
                  return field;
               }
            } else if (child.children.size() > 0) {
               field = child.getFieldByNameBranch(name);
               if (field != null) {
                  return field;
               }
            }
         }
      }

      return null;
   }

   public void getFieldsByExprs(List<SQLExpr> exprs, List<FieldMemo> fields) {
      for(SQLExpr expr : exprs) {
         FieldMemo field = this.getFieldByExpr(expr);
         if (field == null) {
            if (this.debug) {
               System.out.println("null" + expr.getParent().toString());
            }
         } else {
            fields.add(field);
         }
      }

   }

   public List<FieldMemo> getSubRelationFields(FieldMemo field) {
      List<FieldMemo> relations = this.getSubRelationFieldsInternal(field);
      if (field.table != null && field.table.refTable != null) {
         FieldMemo refField = field.table.refTable.scope.getFieldByNameTopBranch(field.alias);
         if (refField != null) {
            relations.add(refField);
         }
      }

      if (relations != null) {
         for(FieldMemo relation : relations) {
            relation.outside = true;
         }
      }

      return relations;
   }

   private List<FieldMemo> getSubRelationFieldsInternal(FieldMemo field) {
      if (field.table == null) {
         return new ArrayList();
      } else if (!field.table.isSub && !field.complex) {
         return new ArrayList();
      } else {
         List<FieldMemo> list = new ArrayList();
         if (!field.complex) {
            if (field.table == null) {
               return new ArrayList();
            }

            if (field.table.scope.isCheckIsolated && field.table.scope.join) {
               Scope tableScope = this.getTableScope(field);
               if (tableScope != null) {
                  return this.getJoinSubRelationFieldsInteral(tableScope, field);
               }
            }

            if (field.table.scope.isCheckIsolated) {
               Scope tableScope = this.getTableScope(field);
               if (tableScope != null && tableScope.union) {
                  this.getUnionSubRelationFieldsInteral(tableScope, field, list);
                  return list;
               }
            }

            list = this.getSubRelationFieldsInteral(field.table.scope, field);
         } else {
            for(Scope child : this.children) {
               if (!child.isCheckIsolated || !child.isolated) {
                  if (child.fieldMap.size() == 0) {
                     list.addAll(child.getSubRelationFieldsInternal(field));
                  } else {
                     for(FieldMemo value : child.fieldMap.values()) {
                        if (value.alias.equals(field.name)) {
                           list.add(value);
                        }
                     }
                  }
               }
            }
         }

         return list;
      }
   }

   private Scope getTableScope(FieldMemo field) {
      Scope tableScope = null;

      for(Scope child : field.table.scope.children) {
         if (field.table.ref != null && child.ref != null && this.isEqual(field.table.ref.toString(), child.ref.toString())) {
            tableScope = child;
         }
      }

      return tableScope;
   }

   private List<FieldMemo> getSubRelationFieldsInteral(Scope scope, FieldMemo field) {
      List<FieldMemo> list = new ArrayList();

      for(Scope child : scope.children) {
         if (!child.isCheckIsolated || !child.isolated) {
            if (child.isCheckIsolated && child.union) {
               this.getUnionSubRelationFieldsInteral(child, field, list);
            } else if (child.fieldMap.size() == 0) {
               list.addAll(child.getSubRelationFieldsInteral(child, field));
            } else {
               for(FieldMemo value : child.fieldMap.values()) {
                  if (field.name != null && scope.dialector.isColumnEqual(field.name, value.alias) && value.isSelectItem) {
                     list.add(value);
                  }
               }
            }
         }
      }

      return list;
   }

   private List<FieldMemo> getJoinSubRelationFieldsInteral(Scope scope, FieldMemo field) {
      List<FieldMemo> list = new ArrayList();

      for(Scope child : scope.children) {
         if (!child.isCheckIsolated || !child.isolated) {
            if (child.union) {
               this.getUnionSubRelationFieldsInteral(child, field, list);
            } else if (child.fieldMap.size() == 0) {
               list.addAll(child.getJoinSubRelationFieldsInteral(child, field));
            } else {
               for(FieldMemo value : child.fieldMap.values()) {
                  if (field.name != null && field.name.equals(value.alias) && value.isSelectItem) {
                     list.add(value);
                  }
               }
            }

            if (list.size() > 0) {
               return list;
            }
         }
      }

      return list;
   }

   private int getUnionSubRelationFieldsInteral(Scope scope, FieldMemo field, List<FieldMemo> list) {
      int loop = 0;
      int index = 0;

      while(scope.children.size() != loop) {
         Scope child = (Scope)scope.children.get(loop);
         if (child.isCheckIsolated && child.isolated) {
            ++loop;
         } else {
            if (child.fieldMap.size() == 0) {
               index = child.getUnionSubRelationFieldsInteral(child, field, list);
               if (index < 0) {
                  return index;
               }
            } else {
               List<FieldMemo> fields = new ArrayList(child.fieldMap.values());
               if (loop == 0) {
                  index = this.getIndex(field, list, fields);
                  if (index < 0) {
                     return index;
                  }
               } else if (fields.size() > index) {
                  list.add(fields.get(index));
               }
            }

            ++loop;
         }
      }

      return index;
   }

   private int getIndex(FieldMemo field, List<FieldMemo> list, List<FieldMemo> fields) {
      for(int j = 0; j < fields.size(); ++j) {
         FieldMemo value = (FieldMemo)fields.get(j);
         if (field.name != null && field.name.equals(value.alias) && value.isSelectItem) {
            list.add(value);
            return j;
         }
      }

      return -1;
   }

   public boolean getSubFieldsForWith(List<FieldMemo> fields) {
      for(Scope child : this.children) {
         if (child.getFieldMap().size() > 0) {
            List<FieldMemo> childFields = new ArrayList();

            for(FieldMemo x : child.getFieldMap().orderValues()) {
               if (x.isSelectItem) {
                  childFields.add(x);
               }
            }

            fields.addAll(childFields);
            return true;
         }
      }

      for(Scope child : this.children) {
         if (child.getSubFieldsForWith(fields)) {
            return true;
         }
      }

      return false;
   }

   public void getUnionFieldsForWithRecursive(List<List<FieldMemo>> list) {
      for(Scope child : this.children) {
         if (child.ref instanceof SQLUnionQuery) {
            child.getUnionFieldsForWithRecursive(list);
         } else if (child.getFieldMap().size() > 0) {
            List<FieldMemo> childFields = new ArrayList();

            for(FieldMemo x : child.getFieldMap().orderValues()) {
               if (x.isSelectItem) {
                  childFields.add(x);
               }
            }

            list.add(childFields);
         }
      }

   }

   public void getUnionFieldsForWith(List<List<FieldMemo>> unionFieldlist) {
      if (this.children.size() > 0) {
         Scope tableScopChild = (Scope)this.children.get(0);
         if (tableScopChild != null && tableScopChild.union) {
            tableScopChild.getUnionFieldsForWithRecursive(unionFieldlist);
         }
      }

   }

   public boolean isFromField(SQLObject x) {
      for(Scope pCursor = this.parent; pCursor != null; pCursor = pCursor.parent) {
         if (pCursor.fromField) {
            return true;
         }
      }

      for(SQLObject cursor = x.getParent(); cursor != null; cursor = cursor.getParent()) {
         if (cursor instanceof SQLSelectItem) {
            return true;
         }
      }

      return false;
   }

   public final List<ValueMemo> getValues() {
      return this.values;
   }

   public SeqMap<String, FieldMemo> getFieldMap() {
      return this.fieldMap;
   }

   public SeqMap<String, TableMemo> getTableMap() {
      return this.tableMap;
   }

   public final List<Scope> getChildren() {
      return this.children;
   }

   public boolean isCaseSensitive() {
      return this.context.getDataBase() != null ? this.context.getDataBase().isCaseSensitive() : false;
   }

   private boolean isEqual(String name1, String name2) {
      return this.dialector.isEqual(name1, name2);
   }

   public ReplaceInfo getReplaceInfo() {
      return this.replaceInfo;
   }

   public void setReplaceInfo(ReplaceInfo replaceInfo) {
      this.replaceInfo = replaceInfo;
   }

   class Feet {
      FieldMemo field;
      List<TableMemo> path;
   }

   class FieldNameList extends ArrayList<FieldMemo> {
      Set<String> names = new HashSet();

      public boolean add(FieldMemo field) {
         if (!this.names.contains(field.alias)) {
            super.add(field);
            this.names.add(field.alias);
         }

         return true;
      }

      public boolean addAll(Collection<? extends FieldMemo> fields) {
         if (fields != null && fields.size() != 0) {
            for(FieldMemo field : fields) {
               this.add(field);
            }

            return true;
         } else {
            return true;
         }
      }
   }
}
