/** INTERNAL: Print SQL onto the stream, using the ExpressionPrinter for context */
  public void printSQL(ExpressionSQLPrinter printer) {
    if (isAttribute()) {
      printer.printField(getAliasedField());
    }

    // If the mapping is a direct collection then this falls into a gray area.
    // It must be treated as an attribute at this moment for it has a direct field.
    // However it is not an attribute in the sense that it also represents a foreign
    // reference and a mapping criteria has been added.
    // For bug 2900974 these are now handled as non-attributes during normalize but
    // as attributes when printing SQL.
    //
    if ((!isAttribute()) && (getMapping() != null) && getMapping().isDirectCollectionMapping()) {
      DirectCollectionMapping directCollectionMapping = (DirectCollectionMapping) getMapping();

      // The aliased table comes for free as it was a required part of the join criteria.
      TableExpression table =
          (TableExpression) getTable(directCollectionMapping.getReferenceTable());
      DatabaseTable aliasedTable = table.aliasForTable(table.getTable());
      DatabaseField aliasedField = (DatabaseField) directCollectionMapping.getDirectField().clone();
      aliasedField.setTable(aliasedTable);
      printer.printField(aliasedField);
    }

    if ((getMapping() != null) && getMapping().isNestedTableMapping()) {
      DatabaseTable tableAlias = aliasForTable(new NestedTable(this));
      printer.printString(tableAlias.getName());
    }
  }
  /** INTERNAL: Find the alias for a given table */
  public DatabaseTable aliasForTable(DatabaseTable table) {
    DatabaseMapping mapping = getMapping();
    if (isAttribute()
        || ((mapping != null)
            && (mapping.isAggregateObjectMapping() || mapping.isTransformationMapping()))) {
      return ((DataExpression) getBaseExpression()).aliasForTable(table);
    }

    // "ref" and "structure" mappings, no table printed in the FROM clause, need to get the table
    // alias form the parent table
    if ((mapping != null) && (mapping.isReferenceMapping() || mapping.isStructureMapping())) {
      DatabaseTable alias =
          getBaseExpression().aliasForTable(mapping.getDescriptor().getTables().firstElement());
      alias.setName(alias.getName() + "." + mapping.getField().getName());
      return alias;
    }

    // For direct-collection mappings the alias is store on the table expression.
    if ((mapping != null) && (mapping.isDirectCollectionMapping())) {
      if (tableAliases != null) {
        DatabaseTable aliasedTable = tableAliases.keyAtValue(table);
        if (aliasedTable != null) {
          return aliasedTable;
        }
      }
      return getTable(table).aliasForTable(table);
    }

    return super.aliasForTable(table);
  }