/** Return the expression from the attribute dervied from this expression. */
 @Override
 public Expression get(String attributeName, boolean forceInnerJoin) {
   ObjectExpression result = derivedExpressionNamed(attributeName);
   if (forceInnerJoin) {
     result.doNotUseOuterJoin();
   }
   return result;
 }
 /** Defines a join between this expression and the target expression based on the ON clause. */
 @Override
 public Expression join(Expression target, Expression onClause) {
   if (target instanceof ObjectExpression) {
     ((ObjectExpression) target).setJoinSource(this);
     ((ObjectExpression) target).setOnClause(onClause);
   } else {
     throw new IllegalArgumentException();
   }
   return this;
 }
  @Override
  public Expression getAllowingNull(String attributeName) {
    ObjectExpression exp = existingDerivedExpressionNamed(attributeName);

    // The same (aliased) table cannot participate in a normal join and an outer join.
    // To help enforce this, if the node already exists
    if (exp != null) {
      return exp;
    }
    ObjectExpression result = derivedExpressionNamed(attributeName);
    result.doUseOuterJoin();
    return result;
  }
  /**
   * INTERNAL: This method is used when computing the nested queries for batch read mappings. It
   * recurses computing the nested mapping queries.
   */
  protected void computeNestedQueriesForBatchReadExpressions(Vector batchReadExpressions) {
    for (int index = 0; index < batchReadExpressions.size(); index++) {
      ObjectExpression objectExpression = (ObjectExpression) batchReadExpressions.get(index);

      // Expression may not have been initialized.
      ExpressionBuilder builder = objectExpression.getBuilder();
      builder.setSession(getSession().getRootSession(null));
      builder.setQueryClass(getReferenceClass());

      // PERF: Cache join attribute names.
      ObjectExpression baseExpression = objectExpression;
      while (!baseExpression.getBaseExpression().isExpressionBuilder()) {
        baseExpression = (ObjectExpression) baseExpression.getBaseExpression();
      }
      this.batchReadAttributes.add(baseExpression.getName());

      // Ignore nested
      if (objectExpression.getBaseExpression().isExpressionBuilder()) {
        DatabaseMapping mapping = objectExpression.getMapping();
        if ((mapping != null) && mapping.isForeignReferenceMapping()) {
          // A nested query must be built to pass to the descriptor that looks like the real query
          // execution would.
          ReadQuery nestedQuery = ((ForeignReferenceMapping) mapping).prepareNestedBatchQuery(this);
          // Register the nested query to be used by the mapping for all the objects.
          getBatchReadMappingQueries().put(mapping, nestedQuery);
        }
      }
    }
  }
 /**
  * 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;
 }
  /**
   * INTERNAL: For CR#2456 if this is part of an objExp.equal(objExp), do not need to add additional
   * expressions to normalizer both times, and the foreign key join replaces the equal expression.
   */
  public Expression normalize(ExpressionNormalizer normalizer, Vector foreignKeyJoinPointer) {
    if (hasBeenNormalized()) {
      return this;
    }
    super.normalize(normalizer);

    setHasBeenNormalized(true);
    if ((getMapping() != null) && getMapping().isDirectToXMLTypeMapping()) {
      normalizer.getStatement().setRequiresAliases(true);
    }

    // Check if any joins need to be added.
    if (isAttribute()) {
      return this;
    }

    // If the mapping is 'ref' or 'structure', no join needed.
    if ((getMapping() != null)
        && (getMapping().isReferenceMapping() || getMapping().isStructureMapping())) {
      normalizer.getStatement().setRequiresAliases(true);
      return this;
    }

    // Compute if a distinct is required during normalization.
    if (shouldQueryToManyRelationship()
        && (!normalizer.getStatement().isDistinctComputed())
        && (!normalizer.getStatement().isAggregateSelect())) {
      normalizer.getStatement().useDistinct();
    }

    // Turn off DISTINCT if nestedTableMapping is used (not supported by Oracle 8.1.5).
    if ((getMapping() != null) && getMapping().isNestedTableMapping()) {
      // There are two types of nested tables, one used by clients, one used by mappings, do nothing
      // in the mapping case.
      if (!shouldQueryToManyRelationship()) {
        return this;
      }
      normalizer.getStatement().dontUseDistinct();
    }

    Expression mappingExpression = mappingCriteria();
    if (mappingExpression != null) {
      mappingExpression = mappingExpression.normalize(normalizer);
    }
    if (mappingExpression != null) {
      // If the join was an outer join we must not add the join criteria to the where clause,
      // if the platform prints the join in the from clause.
      if (shouldUseOuterJoin() && (getSession().getPlatform().isInformixOuterJoin())) {
        normalizer.getStatement().getOuterJoinExpressions().addElement(this);
        normalizer.getStatement().getOuterJoinedMappingCriteria().addElement(mappingExpression);
        normalizer.addAdditionalExpression(mappingExpression.and(additionalExpressionCriteria()));
        return this;
      } else if ((shouldUseOuterJoin() || isUsingOuterJoinForMultitableInheritance())
          && (!getSession().getPlatform().shouldPrintOuterJoinInWhereClause())) {
        if (shouldUseOuterJoin()) {
          normalizer.getStatement().getOuterJoinExpressions().addElement(this);
          normalizer.getStatement().getOuterJoinedMappingCriteria().addElement(mappingExpression);
          normalizer
              .getStatement()
              .getOuterJoinedAdditionalJoinCriteria()
              .addElement(additionalExpressionCriteriaMap());
          normalizer.getStatement().getDescriptorsForMultitableInheritanceOnly().add(null);
          return this;
        } else {
          if (isUsingOuterJoinForMultitableInheritance()) {
            normalizer.getStatement().getOuterJoinExpressions().addElement(null);
            normalizer.getStatement().getOuterJoinedMappingCriteria().addElement(null);
            normalizer
                .getStatement()
                .getOuterJoinedAdditionalJoinCriteria()
                .addElement(additionalExpressionCriteriaMap());
            normalizer
                .getStatement()
                .getDescriptorsForMultitableInheritanceOnly()
                .add(getMapping().getReferenceDescriptor());
            // fall through to the main case
          }
        }
      }

      // This must be added even if outer. Actually it should be converted to use a right outer
      // join, but that gets complex
      // so we do not support this current which is a limitation in some cases.
      if (foreignKeyJoinPointer != null) {
        // If this expression is right side of an objExp.equal(objExp), one
        // need not add additionalExpressionCriteria twice.
        // Also the join will replace the original objExp.equal(objExp).
        // For CR#2456.
        foreignKeyJoinPointer.add(mappingExpression);
      } else {
        normalizer.addAdditionalExpression(mappingExpression.and(additionalExpressionCriteria()));
      }
    }

    // For bug 2900974 special code for DirectCollectionMappings moved to printSQL.
    return this;
  }
 /** INTERNAL: Used for cloning. */
 protected void postCopyIn(Map alreadyDone) {
   super.postCopyIn(alreadyDone);
   if (index != null) {
     index = (IndexExpression) index.copiedVersionFrom(alreadyDone);
   }
 }
 /** Defines a join between this expression and the target expression based on the ON clause. */
 @Override
 public Expression leftJoin(Expression target, Expression onClause) {
   join(target, onClause);
   ((ObjectExpression) target).doUseOuterJoin();
   return this;
 }