public List<Expression> copyDerivedExpressions(Map alreadyDone) {
   if (this.derivedExpressions == null) {
     return null;
   }
   List<Expression> derivedExpressionsCopy;
   synchronized (this) {
     derivedExpressionsCopy = new ArrayList(this.derivedExpressions);
   }
   List<Expression> result = new ArrayList(derivedExpressionsCopy.size());
   for (Expression exp : derivedExpressionsCopy) {
     result.add(exp.copiedVersionFrom(alreadyDone));
   }
   return result;
 }
  /**
   * INTERNAL: Used in case outer joins should be printed in FROM clause. Each of the additional
   * tables mapped to expressions that joins it.
   */
  public Map additionalExpressionCriteriaMap() {
    if (getDescriptor() == null) {
      return null;
    }

    HashMap tablesJoinExpressions = new HashMap();
    Vector tables = getDescriptor().getTables();
    // skip the main table - start with i=1
    int tablesSize = tables.size();
    if (shouldUseOuterJoin()) {
      for (int i = 1; i < tablesSize; i++) {
        DatabaseTable table = (DatabaseTable) tables.elementAt(i);
        Expression joinExpression =
            (Expression) getDescriptor().getQueryManager().getTablesJoinExpressions().get(table);
        joinExpression = getBaseExpression().twist(joinExpression, this);
        tablesJoinExpressions.put(table, joinExpression);
      }
    }
    if (isUsingOuterJoinForMultitableInheritance()) {
      List childrenTables = getDescriptor().getInheritancePolicy().getChildrenTables();
      tablesSize = childrenTables.size();
      for (int i = 0; i < tablesSize; i++) {
        DatabaseTable table = (DatabaseTable) childrenTables.get(i);
        Expression joinExpression =
            (Expression)
                getDescriptor()
                    .getInheritancePolicy()
                    .getChildrenTablesJoinExpressions()
                    .get(table);
        joinExpression = getBaseExpression().twist(joinExpression, this);
        tablesJoinExpressions.put(table, joinExpression);
      }
    }

    return tablesJoinExpressions;
  }
  /**
   * INTERNAL: Used in case outer joins should be printed in FROM clause. Each of the additional
   * tables mapped to expressions that joins it.
   */
  public Map additionalExpressionCriteriaMap() {
    if (getDescriptor() == null) {
      return null;
    }

    HashMap tablesJoinExpressions = null;
    if (isUsingOuterJoinForMultitableInheritance()) {
      tablesJoinExpressions = new HashMap();
      List childrenTables = getDescriptor().getInheritancePolicy().getChildrenTables();
      for (int i = 0; i < childrenTables.size(); i++) {
        DatabaseTable table = (DatabaseTable) childrenTables.get(i);
        Expression joinExpression =
            getDescriptor().getInheritancePolicy().getChildrenTablesJoinExpressions().get(table);
        if (getBaseExpression() != null) {
          joinExpression = getBaseExpression().twist(joinExpression, this);
        } else {
          joinExpression = twist(joinExpression, this);
        }
        tablesJoinExpressions.put(table, joinExpression);
      }
    }

    return tablesJoinExpressions;
  }
 /**
  * INTERNAL: Parses an expression to return the first non-AggregateObjectMapping expression after
  * the base ExpressionBuilder. This is used by joining and batch fetch to get the list of mappings
  * that really need to be processed (non-aggregates).
  *
  * @param aggregateMappingsEncountered - collection of aggregateObjectMapping expressions
  *     encountered in the returned expression between the first expression and the
  *     ExpressionBuilder
  * @return first non-AggregateObjectMapping expression after the base ExpressionBuilder from the
  *     fullExpression
  */
 public ObjectExpression getFirstNonAggregateExpressionAfterExpressionBuilder(
     List aggregateMappingsEncountered) {
   boolean done = false;
   ObjectExpression baseExpression = this;
   ObjectExpression prevExpression = this;
   while (!baseExpression.getBaseExpression().isExpressionBuilder() && !done) {
     baseExpression = (ObjectExpression) baseExpression.getBaseExpression();
     while (!baseExpression.isExpressionBuilder()
         && baseExpression.getMapping().isAggregateObjectMapping()) {
       aggregateMappingsEncountered.add(baseExpression.getMapping());
       baseExpression = (ObjectExpression) baseExpression.getBaseExpression();
     }
     if (baseExpression.isExpressionBuilder()) {
       done = true;
       // use the one closest to the expression builder that wasn't an aggregate
       baseExpression = prevExpression;
     } else {
       prevExpression = baseExpression;
     }
   }
   return baseExpression;
 }