Package org.hibernate.hql.internal.ast.tree

Source Code of org.hibernate.hql.internal.ast.tree.DotNode$IllegalCollectionDereferenceExceptionBuilder

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA  02110-1301  USA
*
*/
package org.hibernate.hql.internal.ast.tree;

import java.util.Set;

import antlr.SemanticException;
import antlr.collections.AST;
import org.jboss.logging.Logger;

import org.hibernate.QueryException;
import org.hibernate.engine.internal.JoinSequence;
import org.hibernate.hql.internal.CollectionProperties;
import org.hibernate.hql.internal.antlr.SqlTokenTypes;
import org.hibernate.hql.internal.ast.util.ASTUtil;
import org.hibernate.hql.internal.ast.util.ColumnHelper;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.JoinType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;

/**
* Represents a reference to a property or alias expression.  This should duplicate the relevant behaviors in
* PathExpressionParser.
*
* @author Joshua Davis
*/
public class DotNode extends FromReferenceNode implements DisplayableNode, SelectExpression {

  ///////////////////////////////////////////////////////////////////////////
  // USED ONLY FOR REGRESSION TESTING!!!!
  //
  // todo : obviously get rid of all this junk ;)
  ///////////////////////////////////////////////////////////////////////////
  public static boolean useThetaStyleImplicitJoins = false;
  public static boolean REGRESSION_STYLE_JOIN_SUPPRESSION = false;
  public static interface IllegalCollectionDereferenceExceptionBuilder {
    public QueryException buildIllegalCollectionDereferenceException(String collectionPropertyName, FromReferenceNode lhs);
  }
  public static final IllegalCollectionDereferenceExceptionBuilder DEF_ILLEGAL_COLL_DEREF_EXCP_BUILDER = new IllegalCollectionDereferenceExceptionBuilder() {
    public QueryException buildIllegalCollectionDereferenceException(String propertyName, FromReferenceNode lhs) {
      String lhsPath = ASTUtil.getPathText( lhs );
      return new QueryException( "illegal attempt to dereference collection [" + lhsPath + "] with element property reference [" + propertyName + "]" );
    }
  };
  public static IllegalCollectionDereferenceExceptionBuilder ILLEGAL_COLL_DEREF_EXCP_BUILDER = DEF_ILLEGAL_COLL_DEREF_EXCP_BUILDER;
  ///////////////////////////////////////////////////////////////////////////

    private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, DotNode.class.getName());

  private static final int DEREF_UNKNOWN = 0;
  private static final int DEREF_ENTITY = 1;
  private static final int DEREF_COMPONENT = 2;
  private static final int DEREF_COLLECTION = 3;
  private static final int DEREF_PRIMITIVE = 4;
  private static final int DEREF_IDENTIFIER = 5;
  private static final int DEREF_JAVA_CONSTANT = 6;

  /**
   * The identifier that is the name of the property.
   */
  private String propertyName;
  /**
   * The full path, to the root alias of this dot node.
   */
  private String path;
  /**
   * The unresolved property path relative to this dot node.
   */
  private String propertyPath;

  /**
   * The column names that this resolves to.
   */
  private String[] columns;

  /**
   * The type of join to create.   Default is an inner join.
   */
  private JoinType joinType = JoinType.INNER_JOIN;

  /**
   * Fetch join or not.
   */
  private boolean fetch = false;

  /**
   * The type of dereference that hapened (DEREF_xxx).
   */
  private int dereferenceType = DEREF_UNKNOWN;

  private FromElement impliedJoin;

  /**
   * Sets the join type for this '.' node structure.
   *
   * @param joinType The type of join to use.
   * @see JoinFragment
   */
  public void setJoinType(JoinType joinType) {
    this.joinType = joinType;
  }

  private String[] getColumns() throws QueryException {
    if ( columns == null ) {
      // Use the table fromElement and the property name to get the array of column names.
      String tableAlias = getLhs().getFromElement().getTableAlias();
      columns = getFromElement().toColumns( tableAlias, propertyPath, false );
    }
    return columns;
  }

  @Override
    public String getDisplayText() {
    StringBuilder buf = new StringBuilder();
    FromElement fromElement = getFromElement();
    buf.append( "{propertyName=" ).append( propertyName );
    buf.append( ",dereferenceType=" ).append( getWalker().getASTPrinter().getTokenTypeName( dereferenceType ) );
    buf.append( ",getPropertyPath=" ).append( propertyPath );
    buf.append( ",path=" ).append( getPath() );
    if ( fromElement != null ) {
      buf.append( ",tableAlias=" ).append( fromElement.getTableAlias() );
      buf.append( ",className=" ).append( fromElement.getClassName() );
      buf.append( ",classAlias=" ).append( fromElement.getClassAlias() );
    }
    else {
      buf.append( ",no from element" );
    }
    buf.append( '}' );
    return buf.toString();
  }

  /**
   * Resolves the left hand side of the DOT.
   *
   * @throws SemanticException
   */
  @Override
    public void resolveFirstChild() throws SemanticException {
    FromReferenceNode lhs = ( FromReferenceNode ) getFirstChild();
    SqlNode property = ( SqlNode ) lhs.getNextSibling();

    // Set the attributes of the property reference expression.
    String propName = property.getText();
    propertyName = propName;
    // If the uresolved property path isn't set yet, just use the property name.
    if ( propertyPath == null ) {
      propertyPath = propName;
    }
    // Resolve the LHS fully, generate implicit joins.  Pass in the property name so that the resolver can
    // discover foreign key (id) properties.
    lhs.resolve( true, true, null, this );
    setFromElement( lhs.getFromElement() );      // The 'from element' that the property is in.

    checkSubclassOrSuperclassPropertyReference( lhs, propName );
  }

  @Override
    public void resolveInFunctionCall(boolean generateJoin, boolean implicitJoin) throws SemanticException {
    if ( isResolved() ) {
      return;
    }
    Type propertyType = prepareLhs();      // Prepare the left hand side and get the data type.
    if ( propertyType!=null && propertyType.isCollectionType() ) {
      resolveIndex(null);
    }
    else {
      resolveFirstChild();
      super.resolve(generateJoin, implicitJoin);
    }
  }


  public void resolveIndex(AST parent) throws SemanticException {
    if ( isResolved() ) {
      return;
    }
    Type propertyType = prepareLhs();      // Prepare the left hand side and get the data type.
    dereferenceCollection( ( CollectionType ) propertyType, true, true, null, parent );
  }

  public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent)
  throws SemanticException {
    // If this dot has already been resolved, stop now.
    if ( isResolved() ) {
      return;
    }
    Type propertyType = prepareLhs(); // Prepare the left hand side and get the data type.

    // If there is no data type for this node, and we're at the end of the path (top most dot node), then
    // this might be a Java constant.
    if ( propertyType == null ) {
      if ( parent == null ) {
        getWalker().getLiteralProcessor().lookupConstant( this );
      }
      // If the propertyType is null and there isn't a parent, just
      // stop now... there was a problem resolving the node anyway.
      return;
    }

    if ( propertyType.isComponentType() ) {
      // The property is a component...
      checkLhsIsNotCollection();
      dereferenceComponent( parent );
      initText();
    }
    else if ( propertyType.isEntityType() ) {
      // The property is another class..
      checkLhsIsNotCollection();
      dereferenceEntity( ( EntityType ) propertyType, implicitJoin, classAlias, generateJoin, parent );
      initText();
    }
    else if ( propertyType.isCollectionType() ) {
      // The property is a collection...
      checkLhsIsNotCollection();
      dereferenceCollection( ( CollectionType ) propertyType, implicitJoin, false, classAlias, parent );
    }
    else {
      // Otherwise, this is a primitive type.
      if ( ! CollectionProperties.isAnyCollectionProperty( propertyName ) ) {
        checkLhsIsNotCollection();
      }
      dereferenceType = DEREF_PRIMITIVE;
      initText();
    }
    setResolved();
  }

  private void initText() {
    String[] cols = getColumns();
    String text = StringHelper.join( ", ", cols );
    if ( cols.length > 1 && getWalker().isComparativeExpressionClause() ) {
      text = "(" + text + ")";
    }
    setText( text );
  }

  private Type prepareLhs() throws SemanticException {
    FromReferenceNode lhs = getLhs();
    lhs.prepareForDot( propertyName );
    return getDataType();
  }

  private void dereferenceCollection(CollectionType collectionType, boolean implicitJoin, boolean indexed, String classAlias, AST parent)
  throws SemanticException {

    dereferenceType = DEREF_COLLECTION;
    String role = collectionType.getRole();

    //foo.bars.size (also handles deprecated stuff like foo.bars.maxelement for backwardness)
    boolean isSizeProperty = getNextSibling()!=null &&
      CollectionProperties.isAnyCollectionProperty( getNextSibling().getText() );

    if ( isSizeProperty ) indexed = true; //yuck!

    QueryableCollection queryableCollection = getSessionFactoryHelper().requireQueryableCollection( role );
    String propName = getPath();
    FromClause currentFromClause = getWalker().getCurrentFromClause();

    // determine whether we should use the table name or table alias to qualify the column names...
    // we need to use the table-name when:
    //    1) the top-level statement is not a SELECT
    //    2) the LHS FromElement is *the* FromElement from the top-level statement
    //
    // there is a caveat here.. if the update/delete statement are "multi-table" we should continue to use
    // the alias also, even if the FromElement is the root one...
    //
    // in all other cases, we should use the table alias
    final FromElement lhsFromElement = getLhs().getFromElement();
    if ( getWalker().getStatementType() != SqlTokenTypes.SELECT ) {
      if ( isFromElementUpdateOrDeleteRoot( lhsFromElement ) ) {
        // at this point we know we have the 2 conditions above,
        // lets see if we have the mentioned "multi-table" caveat...
        boolean useAlias = false;
        if ( getWalker().getStatementType() != SqlTokenTypes.INSERT ) {
          final Queryable persister = lhsFromElement.getQueryable();
          if ( persister.isMultiTable() ) {
            useAlias = true;
          }
        }
        if ( ! useAlias ) {
          final String lhsTableName = lhsFromElement.getQueryable().getTableName();
          columns = getFromElement().toColumns( lhsTableName, propertyPath, false, true );
        }
      }
    }

    // We do not look for an existing join on the same path, because
    // it makes sense to join twice on the same collection role
    FromElementFactory factory = new FromElementFactory(
            currentFromClause,
            getLhs().getFromElement(),
            propName,
        classAlias,
            getColumns(),
            implicitJoin
    );
    FromElement elem = factory.createCollection( queryableCollection, role, joinType, fetch, indexed );

    LOG.debugf( "dereferenceCollection() : Created new FROM element for %s : %s", propName, elem );

    setImpliedJoin( elem );
    setFromElement( elem )// This 'dot' expression now refers to the resulting from element.

    if ( isSizeProperty ) {
      elem.setText("");
      elem.setUseWhereFragment(false);
    }

    if ( !implicitJoin ) {
      EntityPersister entityPersister = elem.getEntityPersister();
      if ( entityPersister != null ) {
        getWalker().addQuerySpaces( entityPersister.getQuerySpaces() );
      }
    }
    getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() )// Always add the collection's query spaces.
  }

  private void dereferenceEntity(EntityType entityType, boolean implicitJoin, String classAlias, boolean generateJoin, AST parent) throws SemanticException {
    checkForCorrelatedSubquery( "dereferenceEntity" );
    // three general cases we check here as to whether to render a physical SQL join:
    // 1) is our parent a DotNode as well?  If so, our property reference is
    //     being further de-referenced...
    // 2) is this a DML statement
    // 3) we were asked to generate any needed joins (generateJoins==true) *OR*
    //    we are currently processing a select or from clause
    // (an additional check is the REGRESSION_STYLE_JOIN_SUPPRESSION check solely intended for the test suite)
    //
    // The REGRESSION_STYLE_JOIN_SUPPRESSION is an additional check
    // intended solely for use within the test suite.  This forces the
    // implicit join resolution to behave more like the classic parser.
    // The underlying issue is that classic translator is simply wrong
    // about its decisions on whether or not to render an implicit join
    // into a physical SQL join in a lot of cases.  The piece it generally
    // tends to miss is that INNER joins effect the results by further
    // restricting the data set!  A particular manifestation of this is
    // the fact that the classic translator will skip the physical join
    // for ToOne implicit joins *if the query is shallow*; the result
    // being that Query.list() and Query.iterate() could return
    // different number of results!
    DotNode parentAsDotNode = null;
    String property = propertyName;
    final boolean joinIsNeeded;

    if ( isDotNode( parent ) ) {
      // our parent is another dot node, meaning we are being further dereferenced.
      // thus we need to generate a join unless the parent refers to the associated
      // entity's PK (because 'our' table would know the FK).
      parentAsDotNode = ( DotNode ) parent;
      property = parentAsDotNode.propertyName;
      joinIsNeeded = generateJoin && !isReferenceToPrimaryKey( parentAsDotNode.propertyName, entityType );
    }
    else if ( ! getWalker().isSelectStatement() ) {
      // in non-select queries, the only time we should need to join is if we are in a subquery from clause
      joinIsNeeded = getWalker().getCurrentStatementType() == SqlTokenTypes.SELECT && getWalker().isInFrom();
    }
    else if ( REGRESSION_STYLE_JOIN_SUPPRESSION ) {
      // this is the regression style determination which matches the logic of the classic translator
      joinIsNeeded = generateJoin && ( !getWalker().isInSelect() || !getWalker().isShallowQuery() );
    }
    else {
      joinIsNeeded = generateJoin || ( getWalker().isInSelect() || getWalker().isInFrom() );
    }

    if ( joinIsNeeded ) {
      dereferenceEntityJoin( classAlias, entityType, implicitJoin, parent );
    }
    else {
      dereferenceEntityIdentifier( property, parentAsDotNode );
    }

  }

  private boolean isDotNode(AST n) {
    return n != null && n.getType() == SqlTokenTypes.DOT;
  }

  private void dereferenceEntityJoin(String classAlias, EntityType propertyType, boolean impliedJoin, AST parent)
  throws SemanticException {
    dereferenceType = DEREF_ENTITY;
        if (LOG.isDebugEnabled()) LOG.debugf("dereferenceEntityJoin() : generating join for %s in %s (%s) parent = %s",
                                             propertyName,
                                             getFromElement().getClassName(),
                                             classAlias == null ? "<no alias>" : classAlias,
                                             ASTUtil.getDebugString(parent));
    // Create a new FROM node for the referenced class.
    String associatedEntityName = propertyType.getAssociatedEntityName();
    String tableAlias = getAliasGenerator().createName( associatedEntityName );

    String[] joinColumns = getColumns();
    String joinPath = getPath();

    if ( impliedJoin && getWalker().isInFrom() ) {
      joinType = getWalker().getImpliedJoinType();
    }

    FromClause currentFromClause = getWalker().getCurrentFromClause();
    FromElement elem = currentFromClause.findJoinByPath( joinPath );

///////////////////////////////////////////////////////////////////////////////
//
// This is the piece which recognizes the condition where an implicit join path
// resolved earlier in a correlated subquery is now being referenced in the
// outer query.  For 3.0final, we just let this generate a second join (which
// is exactly how the old parser handles this).  Eventually we need to add this
// logic back in and complete the logic in FromClause.promoteJoin; however,
// FromClause.promoteJoin has its own difficulties (see the comments in
// FromClause.promoteJoin).
//
//    if ( elem == null ) {
//      // see if this joinPath has been used in a "child" FromClause, and if so
//      // promote that element to the outer query
//      FromClause currentNodeOwner = getFromElement().getFromClause();
//      FromClause currentJoinOwner = currentNodeOwner.locateChildFromClauseWithJoinByPath( joinPath );
//      if ( currentJoinOwner != null && currentNodeOwner != currentJoinOwner ) {
//        elem = currentJoinOwner.findJoinByPathLocal( joinPath );
//        if ( elem != null ) {
//          currentFromClause.promoteJoin( elem );
//          // EARLY EXIT!!!
//          return;
//        }
//      }
//    }
//
///////////////////////////////////////////////////////////////////////////////

    boolean found = elem != null;
    // even though we might find a pre-existing element by join path, for FromElements originating in a from-clause
    // we should only ever use the found element if the aliases match (null != null here).  Implied joins are
    // always (?) ok to reuse.
    boolean useFoundFromElement = found && ( elem.isImplied() || areSame( classAlias, elem.getClassAlias() ) );

    if ( ! useFoundFromElement ) {
      // If this is an implied join in a from element, then use the impled join type which is part of the
      // tree parser's state (set by the gramamar actions).
      JoinSequence joinSequence = getSessionFactoryHelper()
        .createJoinSequence( impliedJoin, propertyType, tableAlias, joinType, joinColumns );

      // If the lhs of the join is a "component join", we need to go back to the
      // first non-component-join as the origin to properly link aliases and
      // join columns
      FromElement lhsFromElement = getLhs().getFromElement();
      while ( lhsFromElement != null &&  ComponentJoin.class.isInstance( lhsFromElement ) ) {
        lhsFromElement = lhsFromElement.getOrigin();
      }
      if ( lhsFromElement == null ) {
        throw new QueryException( "Unable to locate appropriate lhs" );
      }
     
      String role = lhsFromElement.getClassName() + "." + propertyName;

      FromElementFactory factory = new FromElementFactory(
              currentFromClause,
          lhsFromElement,
          joinPath,
          classAlias,
          joinColumns,
          impliedJoin
      );
      elem = factory.createEntityJoin(
          associatedEntityName,
          tableAlias,
          joinSequence,
          fetch,
          getWalker().isInFrom(),
          propertyType,
          role,
          joinPath
      );
    }
    else {
      // NOTE : addDuplicateAlias() already performs nullness checks on the alias.
      currentFromClause.addDuplicateAlias( classAlias, elem );
    }
    setImpliedJoin( elem );
    getWalker().addQuerySpaces( elem.getEntityPersister().getQuerySpaces() );
    setFromElement( elem )// This 'dot' expression now refers to the resulting from element.
  }

  private boolean areSame(String alias1, String alias2) {
    // again, null != null here
    return !StringHelper.isEmpty( alias1 ) && !StringHelper.isEmpty( alias2 ) && alias1.equals( alias2 );
  }

  private void setImpliedJoin(FromElement elem) {
    this.impliedJoin = elem;
    if ( getFirstChild().getType() == SqlTokenTypes.DOT ) {
      DotNode dotLhs = ( DotNode ) getFirstChild();
      if ( dotLhs.getImpliedJoin() != null ) {
        this.impliedJoin = dotLhs.getImpliedJoin();
      }
    }
  }

  @Override
    public FromElement getImpliedJoin() {
    return impliedJoin;
  }

  /**
   * Is the given property name a reference to the primary key of the associated
   * entity construed by the given entity type?
   * <p/>
   * For example, consider a fragment like order.customer.id
   * (where order is a from-element alias).  Here, we'd have:
   * propertyName = "id" AND
   * owningType = ManyToOneType(Customer)
   * and are being asked to determine whether "customer.id" is a reference
   * to customer's PK...
   *
   * @param propertyName The name of the property to check.
   * @param owningType The type represeting the entity "owning" the property
   * @return True if propertyName references the entity's (owningType->associatedEntity)
   * primary key; false otherwise.
   */
  private boolean isReferenceToPrimaryKey(String propertyName, EntityType owningType) {
    EntityPersister persister = getSessionFactoryHelper()
        .getFactory()
        .getEntityPersister( owningType.getAssociatedEntityName() );
    if ( persister.getEntityMetamodel().hasNonIdentifierPropertyNamedId() ) {
      // only the identifier property field name can be a reference to the associated entity's PK...
      return propertyName.equals( persister.getIdentifierPropertyName() ) && owningType.isReferenceToPrimaryKey();
    }
        // here, we have two possibilities:
        // 1) the property-name matches the explicitly identifier property name
        // 2) the property-name matches the implicit 'id' property name
        // the referenced node text is the special 'id'
        if (EntityPersister.ENTITY_ID.equals(propertyName)) return owningType.isReferenceToPrimaryKey();
        String keyPropertyName = getSessionFactoryHelper().getIdentifierOrUniqueKeyPropertyName(owningType);
        return keyPropertyName != null && keyPropertyName.equals(propertyName) && owningType.isReferenceToPrimaryKey();
  }

  private void checkForCorrelatedSubquery(String methodName) {
    if ( isCorrelatedSubselect() ) {
      LOG.debugf( "%s() : correlated subquery", methodName );
    }
  }

  private boolean isCorrelatedSubselect() {
    return getWalker().isSubQuery() &&
      getFromElement().getFromClause() != getWalker().getCurrentFromClause();
  }

  private void checkLhsIsNotCollection() throws SemanticException {
    if ( getLhs().getDataType() != null && getLhs().getDataType().isCollectionType() ) {
      throw ILLEGAL_COLL_DEREF_EXCP_BUILDER.buildIllegalCollectionDereferenceException( propertyName, getLhs() );
    }
  }
  private void dereferenceComponent(AST parent) {
    dereferenceType = DEREF_COMPONENT;
    setPropertyNameAndPath( parent );
  }

  private void dereferenceEntityIdentifier(String propertyName, DotNode dotParent) {
    // special shortcut for id properties, skip the join!
    // this must only occur at the _end_ of a path expression
    if ( LOG.isDebugEnabled() ) {
      LOG.debugf( "dereferenceShortcut() : property %s in %s does not require a join.",
          propertyName,
          getFromElement().getClassName() );
    }

    initText();
    setPropertyNameAndPath( dotParent ); // Set the unresolved path in this node and the parent.
    // Set the text for the parent.
    if ( dotParent != null ) {
      dotParent.dereferenceType = DEREF_IDENTIFIER;
      dotParent.setText( getText() );
      dotParent.columns = getColumns();
    }
  }

  private void setPropertyNameAndPath(AST parent) {
    if ( isDotNode( parent ) ) {
      DotNode dotNode = ( DotNode ) parent;
      AST lhs = dotNode.getFirstChild();
      AST rhs = lhs.getNextSibling();
      propertyName = rhs.getText();
      propertyPath = propertyPath + "." + propertyName; // Append the new property name onto the unresolved path.
      dotNode.propertyPath = propertyPath;
      LOG.debugf( "Unresolved property path is now '%s'", dotNode.propertyPath );
    }
    else {
      LOG.debugf( "Terminal getPropertyPath = [%s]", propertyPath );
    }
  }

  @Override
    public Type getDataType() {
    if ( super.getDataType() == null ) {
      FromElement fromElement = getLhs().getFromElement();
      if ( fromElement == null ) return null;
      // If the lhs is a collection, use CollectionPropertyMapping
      Type propertyType = fromElement.getPropertyType( propertyName, propertyPath );
      LOG.debugf( "getDataType() : %s -> %s", propertyPath, propertyType );
      super.setDataType( propertyType );
    }
    return super.getDataType();
  }

  public void setPropertyPath(String propertyPath) {
    this.propertyPath = propertyPath;
  }

  public String getPropertyPath() {
    return propertyPath;
  }

  public FromReferenceNode getLhs() {
    FromReferenceNode lhs = ( ( FromReferenceNode ) getFirstChild() );
    if ( lhs == null ) {
      throw new IllegalStateException( "DOT node with no left-hand-side!" );
    }
    return lhs;
  }

  /**
   * Returns the full path of the node.
   *
   * @return the full path of the node.
   */
  @Override
    public String getPath() {
    if ( path == null ) {
      FromReferenceNode lhs = getLhs();
      if ( lhs == null ) {
        path = getText();
      }
      else {
        SqlNode rhs = ( SqlNode ) lhs.getNextSibling();
        path = lhs.getPath() + "." + rhs.getOriginalText();
      }
    }
    return path;
  }

  public void setFetch(boolean fetch) {
    this.fetch = fetch;
  }

  public void setScalarColumnText(int i) throws SemanticException {
    String[] sqlColumns = getColumns();
    ColumnHelper.generateScalarColumns( this, sqlColumns, i );
  }

  /**
   * Special method to resolve expressions in the SELECT list.
   *
   * @throws SemanticException if this cannot be resolved.
   */
  public void resolveSelectExpression() throws SemanticException {
    if ( getWalker().isShallowQuery() || getWalker().getCurrentFromClause().isSubQuery() ) {
      resolve(false, true);
    }
    else {
      resolve(true, false);
      Type type = getDataType();
      if ( type.isEntityType() ) {
        FromElement fromElement = getFromElement();
        fromElement.setIncludeSubclasses( true ); // Tell the destination fromElement to 'includeSubclasses'.
        if ( useThetaStyleImplicitJoins ) {
          fromElement.getJoinSequence().setUseThetaStyle( true )// Use theta style (for regression)
          // Move the node up, after the origin node.
          FromElement origin = fromElement.getOrigin();
          if ( origin != null ) {
            ASTUtil.makeSiblingOfParent( origin, fromElement );
          }
        }
      }
    }

    FromReferenceNode lhs = getLhs();
    while ( lhs != null ) {
      checkSubclassOrSuperclassPropertyReference( lhs, lhs.getNextSibling().getText() );
      lhs = ( FromReferenceNode ) lhs.getFirstChild();
    }
  }

  public void setResolvedConstant(String text) {
    path = text;
    dereferenceType = DEREF_JAVA_CONSTANT;
    setResolved(); // Don't resolve the node again.
  }

  private boolean checkSubclassOrSuperclassPropertyReference(FromReferenceNode lhs, String propertyName) {
    if ( lhs != null && !( lhs instanceof IndexNode ) ) {
      final FromElement source = lhs.getFromElement();
      if ( source != null ) {
        source.handlePropertyBeingDereferenced( lhs.getDataType(), propertyName );
      }
    }
    return false;
  }
}
TOP

Related Classes of org.hibernate.hql.internal.ast.tree.DotNode$IllegalCollectionDereferenceExceptionBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.