Open Source Repository

Home /hibernate/hibernate-3.2.4.ga | Repository Home


org/hibernate/hql/ast/tree/FromElement.java
// $Id: FromElement.java 10851 2006-11-21 17:38:43Z [email protected] $
package org.hibernate.hql.ast.tree;

import java.util.LinkedList;
import java.util.List;

import org.hibernate.QueryException;
import org.hibernate.engine.JoinSequence;
import org.hibernate.hql.QueryTranslator;
import org.hibernate.hql.CollectionProperties;
import org.hibernate.hql.antlr.SqlTokenTypes;
import org.hibernate.hql.ast.util.ASTUtil;
import org.hibernate.hql.ast.HqlSqlWalker;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.util.StringHelper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Represents a single mapped class mentioned in an HQL FROM clause.  Each
 * class reference will have the following symbols:
 <ul>
 <li>A class name - This is the name of the Java class that is mapped by Hibernate.</li>
 <li>[optional] an HQL alias for the mapped class.</li>
 <li>A table name - The name of the table that is mapped to the Java class.</li>
 <li>A table alias - The alias for the table that will be used in the resulting SQL.</li>
 </ul>
 <br>
 * User: josh<br>
 * Date: Dec 6, 2003<br>
 * Time: 10:28:17 AM<br>
 */
public class FromElement extends HqlSqlWalkerNode implements DisplayableNode {
  private static final Log log = LogFactory.getLogFromElement.class );

  private String className;
  private String classAlias;
  private String tableAlias;
  private String collectionTableAlias;
  private FromClause fromClause;
  private boolean includeSubclasses = true;
  private boolean collectionJoin = false;
  private FromElement origin;
  private String[] columns;
  private String role;
  private boolean fetch;
  private boolean isAllPropertyFetch;
  private boolean filter = false;
  private int sequence = -1;
  private boolean useFromFragment = false;
  private boolean initialized = false;
  private FromElementType elementType;
  private boolean useWhereFragment = true;
  private List destinations = new LinkedList();
  private boolean manyToMany = false;
  private String withClauseFragment = null;
  private String withClauseJoinAlias;
  private boolean dereferencedBySuperclassProperty;
  private boolean dereferencedBySubclassProperty;

  public FromElement() {
  }

  public String getCollectionSuffix() {
    return elementType.getCollectionSuffix();
  }

  public void setCollectionSuffix(String suffix) {
    elementType.setCollectionSuffix(suffix);
  }

  public void initializeCollection(FromClause fromClause, String classAlias, String tableAlias) {
    doInitializefromClause, tableAlias, null, classAlias, null, null );
    initialized = true;
  }

  public void initializeEntity(
          FromClause fromClause,
          String className,
          EntityPersister persister,
          EntityType type,
          String classAlias,
          String tableAlias) {
    doInitializefromClause, tableAlias, className, classAlias, persister, type );
    this.sequence = fromClause.nextFromElementCounter();
    initialized = true;
  }

  private void doInitialize(FromClause fromClause, String tableAlias, String className, String classAlias,
                EntityPersister persister, EntityType type) {
    if initialized ) {
      throw new IllegalStateException"Already initialized!!" );
    }
    this.fromClause = fromClause;
    this.tableAlias = tableAlias;
    this.className = className;
    this.classAlias = classAlias;
    this.elementType = new FromElementTypethis, persister, type );
    // Register the FromElement with the FROM clause, now that we have the names and aliases.
    fromClause.registerFromElementthis );
    if log.isDebugEnabled() ) {
      log.debugfromClause + " :  " + className + " ("
          classAlias == null "no alias" : classAlias ") -> " + tableAlias );
    }
  }

  public EntityPersister getEntityPersister() {
    return elementType.getEntityPersister();
  }

  public Type getDataType() {
    return elementType.getDataType();
  }

  public Type getSelectType() {
    return elementType.getSelectType();
  }

  public Queryable getQueryable() {
    return elementType.getQueryable();
  }

  public String getClassName() {
    return className;
  }

  public String getClassAlias() {
    return classAlias;
    //return classAlias == null ? className : classAlias;
  }

  private String getTableName() {
    Queryable queryable = getQueryable();
    return queryable != null ? queryable.getTableName() "{none}";
  }

  public String getTableAlias() {
    return tableAlias;
  }

  /**
   * Render the identifier select, but in a 'scalar' context (i.e. generate the column alias).
   *
   @param i the sequence of the returned type
   @return the identifier select with the column alias.
   */
  String renderScalarIdentifierSelect(int i) {
    return elementType.renderScalarIdentifierSelect);
  }

  void checkInitialized() {
    if !initialized ) {
      throw new IllegalStateException"FromElement has not been initialized!" );
    }
  }

  /**
   * Returns the identifier select SQL fragment.
   *
   @param size The total number of returned types.
   @param k    The sequence of the current returned type.
   @return the identifier select SQL fragment.
   */
  String renderIdentifierSelect(int size, int k) {
    return elementType.renderIdentifierSelectsize, k );
  }

  /**
   * Returns the property select SQL fragment.
   *
   @param size The total number of returned types.
   @param k    The sequence of the current returned type.
   @return the property select SQL fragment.
   */
  String renderPropertySelect(int size, int k) {
    return elementType.renderPropertySelectsize, k, isAllPropertyFetch );
  }

  String renderCollectionSelectFragment(int size, int k) {
    return elementType.renderCollectionSelectFragmentsize, k );
  }

  String renderValueCollectionSelectFragment(int size, int k) {
    return elementType.renderValueCollectionSelectFragmentsize, k );
  }

  public FromClause getFromClause() {
    return fromClause;
  }

  /**
   * Returns true if this FromElement was implied by a path, or false if this FROM element is explicitly declared in
   * the FROM clause.
   *
   @return true if this FromElement was implied by a path, or false if this FROM element is explicitly declared
   */
  public boolean isImplied() {
    return false;  // This is an explicit FROM element.
  }

  /**
   * Returns additional display text for the AST node.
   *
   @return String - The additional display text.
   */
  public String getDisplayText() {
    StringBuffer buf = new StringBuffer();
    buf.append"FromElement{" );
    appendDisplayTextbuf );
    buf.append"}" );
    return buf.toString();
  }

  protected void appendDisplayText(StringBuffer buf) {
    buf.appendisImplied() (
        isImpliedInFromClause() "implied in FROM clause" "implied" )
        "explicit" );
    buf.append"," ).appendisCollectionJoin() "collection join" "not a collection join" );
    buf.append"," ).appendfetch ? "fetch join" "not a fetch join" );
    buf.append"," ).appendisAllPropertyFetch ? "fetch all properties" "fetch non-lazy properties" );
    buf.append",classAlias=" ).appendgetClassAlias() );
    buf.append",role=" ).appendrole );
    buf.append",tableName=" ).appendgetTableName() );
    buf.append",tableAlias=" ).appendgetTableAlias() );
    FromElement origin = getRealOrigin();
    buf.append",origin=" ).appendorigin == null "null" : origin.getText() );
    buf.append",colums={" );
    if columns != null ) {
      for int i = 0; i < columns.length; i++ ) {
        buf.appendcolumns[i] );
        if i < columns.length ) {
          buf.append" " );
        }
      }
    }
    buf.append",className=" ).appendclassName );
    buf.append"}" );
  }

  public int hashCode() {
    return super.hashCode();
  }

  public boolean equals(Object obj) {
    return super.equalsobj );
  }


  public void setJoinSequence(JoinSequence joinSequence) {
    elementType.setJoinSequencejoinSequence );
  }

  public JoinSequence getJoinSequence() {
    return elementType.getJoinSequence();
  }

  public void setIncludeSubclasses(boolean includeSubclasses) {
    if isDereferencedBySuperclassOrSubclassProperty() ) {
      if !includeSubclasses && log.isTraceEnabled() ) {
        log.trace"attempt to disable subclass-inclusions"new Exception"stack-trace source" ) );
      }
    }
    this.includeSubclasses = includeSubclasses;
  }

  public boolean isIncludeSubclasses() {
    return includeSubclasses;
  }

  public boolean isDereferencedBySuperclassOrSubclassProperty() {
    return dereferencedBySubclassProperty || dereferencedBySuperclassProperty;
  }

  public String getIdentityColumn() {
    checkInitialized();
    String table = getTableAlias();
    if table == null ) {
      throw new IllegalStateException"No table alias for node " this );
    }
    String[] cols;
    String propertyName;
    if getEntityPersister() != null && getEntityPersister().getEntityMetamodel() != null
        && getEntityPersister().getEntityMetamodel().hasNonIdentifierPropertyNamedId() ) {
      propertyName = getEntityPersister().getIdentifierPropertyName();
    }
    else {
      propertyName = EntityPersister.ENTITY_ID;
    }
    if getWalker().getStatementType() == HqlSqlWalker.SELECT ) {
      cols = getPropertyMappingpropertyName ).toColumnstable, propertyName );
    }
    else {
      cols = getPropertyMappingpropertyName ).toColumnspropertyName );
    }
    String result = StringHelper.join", ", cols );
    return  cols.length == ? result : "(" + result + ")";
  }

  public void setCollectionJoin(boolean collectionJoin) {
    this.collectionJoin = collectionJoin;
  }

  public boolean isCollectionJoin() {
    return collectionJoin;
  }

  public void setRole(String role) {
    this.role = role;
  }

  public void setQueryableCollection(QueryableCollection queryableCollection) {
    elementType.setQueryableCollectionqueryableCollection );
  }

  public QueryableCollection getQueryableCollection() {
    return elementType.getQueryableCollection();
  }

  public void setColumns(String[] columns) {
    this.columns = columns;
  }

  public void setOrigin(FromElement origin, boolean manyToMany) {
    this.origin = origin;
    this.manyToMany = manyToMany;
    origin.addDestinationthis );
    if origin.getFromClause() == this.getFromClause() ) {
      // TODO: Figure out a better way to get the FROM elements in a proper tree structure.
      // If this is not the destination of a many-to-many, add it as a child of the origin.
      if manyToMany ) {
        ASTUtil.appendSiblingorigin, this );
      }
      else {
        if !getWalker().isInFrom() && !getWalker().isInSelect() ) {
          getFromClause().addChildthis );
        }
        else {
          origin.addChildthis );
        }
      }
    }
    else if !getWalker().isInFrom() ) {
      // HHH-276 : implied joins in a subselect where clause - The destination needs to be added
      // to the destination's from clause.
      getFromClause().addChildthis );  // Not sure if this is will fix everything, but it works.
    }
    else {
      // Otherwise, the destination node was implied by the FROM clause and the FROM clause processor
      // will automatically add it in the right place.
    }
  }

  public boolean isManyToMany() {
    return manyToMany;
  }

  private void addDestination(FromElement fromElement) {
    destinations.addfromElement );
  }

  public List getDestinations() {
    return destinations;
  }

  public FromElement getOrigin() {
    return origin;
  }

  public FromElement getRealOrigin() {
    if origin == null ) {
      return null;
    }
    if origin.getText() == null || "".equalsorigin.getText() ) ) {
      return origin.getRealOrigin();
    }
    return origin;
  }

  public Type getPropertyType(String propertyName, String propertyPath) {
    return elementType.getPropertyTypepropertyName, propertyPath );
  }

  public String[] toColumns(String tableAlias, String path, boolean inSelect) {
    return elementType.toColumnstableAlias, path, inSelect );
  }

  public String[] toColumns(String tableAlias, String path, boolean inSelect, boolean forceAlias) {
    return elementType.toColumnstableAlias, path, inSelect, forceAlias );
  }

  public PropertyMapping getPropertyMapping(String propertyName) {
    return elementType.getPropertyMappingpropertyName );
  }

  public void setFetch(boolean fetch) {
    this.fetch = fetch;
    // Fetch can't be used with scroll() or iterate().
    if fetch && getWalker().isShallowQuery() ) {
      throw new QueryExceptionQueryTranslator.ERROR_CANNOT_FETCH_WITH_ITERATE );
    }
  }

  public boolean isFetch() {
    return fetch;
  }

  public int getSequence() {
    return sequence;
  }

  public void setFilter(boolean b) {
    filter = b;
  }

  public boolean isFilter() {
    return filter;
  }

  public boolean useFromFragment() {
    checkInitialized();
    // If it's not implied or it is implied and it's a many to many join where the target wasn't found.
    return !isImplied() || this.useFromFragment;
  }

  public void setUseFromFragment(boolean useFromFragment) {
    this.useFromFragment = useFromFragment;
  }

  public boolean useWhereFragment() {
    return useWhereFragment;
  }

  public void setUseWhereFragment(boolean b) {
    useWhereFragment = b;
  }


  public void setCollectionTableAlias(String collectionTableAlias) {
    this.collectionTableAlias = collectionTableAlias;
  }

  public String getCollectionTableAlias() {
    return collectionTableAlias;
  }

  public boolean isCollectionOfValuesOrComponents() {
    return elementType.isCollectionOfValuesOrComponents();
  }

  public boolean isEntity() {
    return elementType.isEntity();
  }

  public void setImpliedInFromClause(boolean flag) {
    throw new UnsupportedOperationException"Explicit FROM elements can't be implied in the FROM clause!" );
  }

  public boolean isImpliedInFromClause() {
    return false;  // Since this is an explicit FROM element, it can't be implied in the FROM clause.
  }

  public void setInProjectionList(boolean inProjectionList) {
    // Do nothing, eplicit from elements are *always* in the projection list.
  }

  public boolean inProjectionList() {
    return !isImplied() && isFromOrJoinFragment();
  }

  public boolean isFromOrJoinFragment() {
    return getType() == SqlTokenTypes.FROM_FRAGMENT || getType() == SqlTokenTypes.JOIN_FRAGMENT;
  }
  
  public boolean isAllPropertyFetch() {
    return isAllPropertyFetch;
  }
  
  public void setAllPropertyFetch(boolean fetch) {
    isAllPropertyFetch = fetch;
  }

  public String getWithClauseFragment() {
    return withClauseFragment;
  }

  public String getWithClauseJoinAlias() {
    return withClauseJoinAlias;
  }

  public void setWithClauseFragment(String withClauseJoinAlias, String withClauseFragment) {
    this.withClauseJoinAlias = withClauseJoinAlias;
    this.withClauseFragment = withClauseFragment;
  }

  public boolean hasCacheablePersister() {
    if getQueryableCollection() != null ) {
      return getQueryableCollection().hasCache();
    }
    else {
      return getQueryable().hasCache();
    }
  }

  public void handlePropertyBeingDereferenced(Type propertySource, String propertyName) {
    if getQueryableCollection() != null && CollectionProperties.isCollectionPropertypropertyName ) ) {
      // propertyName refers to something like collection.size...
      return;
    }
    if propertySource.isComponentType() ) {
      // property name is a sub-path of a component...
      return;
    }

    Queryable persister = getQueryable();
    if persister != null ) {
      try {
        Queryable.Declarer propertyDeclarer = persister.getSubclassPropertyDeclarerpropertyName );
        if log.isTraceEnabled() ) {
          log.trace"handling property dereference [" + persister.getEntityName() " (" + getClassAlias() ") -> " + propertyName + " (" + propertyDeclarer + ")]" );
        }
        if propertyDeclarer == Queryable.Declarer.SUBCLASS ) {
          dereferencedBySubclassProperty = true;
          includeSubclasses = true;
        }
        else if propertyDeclarer == Queryable.Declarer.SUPERCLASS ) {
          dereferencedBySuperclassProperty = true;
        }
      }
      catchQueryException ignore ) {
        // ignore it; the incoming property could not be found so we
        // cannot be sure what to do here.  At the very least, the
        // safest is to simply not apply any dereference toggling...

      }
    }
  }

  public boolean isDereferencedBySuperclassProperty() {
    return dereferencedBySuperclassProperty;
  }

  public boolean isDereferencedBySubclassProperty() {
    return dereferencedBySubclassProperty;
  }
}