Open Source Repository

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


org/hibernate/hql/ast/tree/IndexNode.java
// $Id: IndexNode.java 15770 2009-01-12 15:37:37Z [email protected] $
package org.hibernate.hql.ast.tree;

import java.util.List;
import java.util.Iterator;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.hibernate.QueryException;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.engine.JoinSequence;
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.hql.ast.SqlGenerator;
import org.hibernate.hql.ast.util.SessionFactoryHelper;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.type.CollectionType;
import org.hibernate.type.Type;

import antlr.RecognitionException;
import antlr.SemanticException;
import antlr.collections.AST;

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

/**
 * Represents the [] operator and provides it's semantics.
 *
 @author josh Aug 14, 2004 7:07:10 AM
 */
public class IndexNode extends FromReferenceNode {

  private static final Log log = LogFactory.getLogIndexNode.class );

  public void setScalarColumnText(int ithrows SemanticException {
    throw new UnsupportedOperationException"An IndexNode cannot generate column text!" );
  }

  public void prepareForDot(String propertyNamethrows SemanticException {
    FromElement fromElement = getFromElement();
    if fromElement == null ) {
      throw new IllegalStateException"No FROM element for index operator!" );
    }
    QueryableCollection queryableCollection = fromElement.getQueryableCollection();
    if queryableCollection != null && !queryableCollection.isOneToMany() ) {

      FromReferenceNode collectionNode = FromReferenceNode getFirstChild();
      String path = collectionNode.getPath() "[]." + propertyName;
      if log.isDebugEnabled() ) {
        log.debug"Creating join for many-to-many elements for " + path );
      }
      FromElementFactory factory = new FromElementFactoryfromElement.getFromClause(), fromElement, path );
      // This will add the new from element to the origin.
      FromElement elementJoin = factory.createElementJoinqueryableCollection );
      setFromElementelementJoin );
    }
  }

  public void resolveIndex(AST parentthrows SemanticException {
    throw new UnsupportedOperationException();
  }

  public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent
  throws SemanticException {
    if isResolved() ) {
      return;
    }
    FromReferenceNode collectionNode = FromReferenceNode getFirstChild();
    SessionFactoryHelper sessionFactoryHelper = getSessionFactoryHelper();
    collectionNode.resolveIndexthis );    // Fully resolve the map reference, create implicit joins.

    Type type = collectionNode.getDataType();
    if !type.isCollectionType() ) {
      throw new SemanticException"The [] operator cannot be applied to type " + type.toString() );
    }
    String collectionRole = ( ( CollectionType type ).getRole();
    QueryableCollection queryableCollection = sessionFactoryHelper.requireQueryableCollectioncollectionRole );
    if !queryableCollection.hasIndex() ) {
      throw new QueryException"unindexed fromElement before []: " + collectionNode.getPath() );
    }

    // Generate the inner join -- The elements need to be joined to the collection they are in.
    FromElement fromElement = collectionNode.getFromElement();
    String elementTable = fromElement.getTableAlias();
    FromClause fromClause = fromElement.getFromClause();
    String path = collectionNode.getPath();

    FromElement elem = fromClause.findCollectionJoinpath );
    if elem == null ) {
      FromElementFactory factory = new FromElementFactoryfromClause, fromElement, path );
      elem = factory.createCollectionElementsJoinqueryableCollection, elementTable );
      if log.isDebugEnabled() ) {
        log.debug"No FROM element found for the elements of collection join path " + path
            ", created " + elem );
      }
    }
    else {
      if log.isDebugEnabled() ) {
        log.debug"FROM element found for collection join path " + path );
      }
    }

    // The 'from element' that represents the elements of the collection.
    setFromElementfromElement );

    // Add the condition to the join sequence that qualifies the indexed element.
    AST selector = collectionNode.getNextSibling();
    if selector == null ) {
      throw new QueryException"No index value!" );
    }

    // Sometimes use the element table alias, sometimes use the... umm... collection table alias (many to many)
    String collectionTableAlias = elementTable;
    if elem.getCollectionTableAlias() != null ) {
      collectionTableAlias = elem.getCollectionTableAlias();
    }

    // TODO: get SQL rendering out of here, create an AST for the join expressions.
    // Use the SQL generator grammar to generate the SQL text for the index expression.
    JoinSequence joinSequence = fromElement.getJoinSequence();
    String[] indexCols = queryableCollection.getIndexColumnNames();
    if indexCols.length != ) {
      throw new QueryException"composite-index appears in []: " + collectionNode.getPath() );
    }
    SqlGenerator gen = new SqlGeneratorgetSessionFactoryHelper().getFactory() );
    try {
      gen.simpleExprselector )//TODO: used to be exprNoParens! was this needed?
    }
    catch RecognitionException e ) {
      throw new QueryExceptione.getMessage(), e );
    }
    String selectorExpression = gen.getSQL();
    joinSequence.addConditioncollectionTableAlias + '.' + indexCols[0" = " + selectorExpression );
    List paramSpecs = gen.getCollectedParameters();
    if paramSpecs != null ) {
      switch paramSpecs.size() ) {
        case :
          // nothing to do
          break;
        case :
          ParameterSpecification paramSpec = ParameterSpecification paramSpecs.get);
          paramSpec.setExpectedTypequeryableCollection.getIndexType() );
          fromElement.setIndexCollectionSelectorParamSpecparamSpec );
          break;
        default:
          fromElement.setIndexCollectionSelectorParamSpec(
              new AggregatedIndexCollectionSelectorParameterSpecificationsparamSpecs )
          );
          break;
      }
    }

    // Now, set the text for this node.  It should be the element columns.
    String[] elementColumns = queryableCollection.getElementColumnNameselementTable );
    setTextelementColumns[0] );
    setResolved();
  }

  /**
   * In the (rare?) case where the index selector contains multiple parameters...
   */
  private static class AggregatedIndexCollectionSelectorParameterSpecifications implements ParameterSpecification {
    private final List paramSpecs;

    public AggregatedIndexCollectionSelectorParameterSpecifications(List paramSpecs) {
      this.paramSpecs = paramSpecs;
    }

    public int bind(PreparedStatement statement, QueryParameters qp, SessionImplementor session, int position)
    throws SQLException {
      int bindCount = 0;
      Iterator itr = paramSpecs.iterator();
      while itr.hasNext() ) {
        final ParameterSpecification paramSpec = ParameterSpecification itr.next();
        bindCount += paramSpec.bindstatement, qp, session, position + bindCount );
      }
      return bindCount;
    }

    public Type getExpectedType() {
      return null;
    }

    public void setExpectedType(Type expectedType) {
    }

    public String renderDisplayInfo() {
      return "index-selector [" + collectDisplayInfo() "]" ;
    }

    private String collectDisplayInfo() {
      StringBuffer buffer = new StringBuffer();
      Iterator itr = paramSpecs.iterator();
      while itr.hasNext() ) {
        buffer.append( ( ( ParameterSpecification itr.next() ).renderDisplayInfo() );
      }
      return buffer.toString();
    }
  }
}