From: <hib...@li...> - 2006-05-02 14:29:35
|
Author: ste...@jb... Date: 2006-05-02 10:26:06 -0400 (Tue, 02 May 2006) New Revision: 9851 Removed: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/RangeNode.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ResolverContext.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ResolverContextImpl.java Modified: branches/HQL_ANTLR_2/Hibernate3/grammar/hql-resolve.g branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/CollectionPersisterReference.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/EntityPersisterReference.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/HqlResolver.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/HqlResolverASTFactory.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/JoinNode.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/PersisterReference.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/PropertyReference.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/StatementNode.java branches/HQL_ANTLR_2/Hibernate3/test/org/hibernate/test/hql/HqlResolverTest.java Log: further join structure work plus property-reference stuff Modified: branches/HQL_ANTLR_2/Hibernate3/grammar/hql-resolve.g =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/grammar/hql-resolve.g 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/grammar/hql-resolve.g 2006-05-02 14:26:06 UTC (rev 9851) @@ -4,8 +4,6 @@ package org.hibernate.hql.antlr; import java.util.*; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; } /** @@ -41,34 +39,50 @@ // -- Declarations -- { - private static Log log = LogFactory.getLog( HqlBaseResolver.class ); - // Semantic action methods, overridden in subclasses for clarity. + // Statement node BEGIN/END handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - protected void pushContext(AST outputAst,AST inputAst) { } + protected void pushStatement(AST statementNode) { + } - protected void popContext() { } + protected void popStatement() { + } - protected void defineRange(AST range,String path,AST alias, AST fetch) { } + // persister reference handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + protected AST buildEntityPersisterReference(String entityName, AST alias) { return null; } - protected AST buildThetaJoinNode(AST persisterReference) { - return null; + protected void handleExplicitPropertyJoin( + AST propertyReference, + AST alias, + AST joinType, + AST fetch, + AST propertyFetch, + AST withClause) { } - protected AST buildExplicitPropertyJoinNode(AST propertyReference, AST alias, AST joinType, AST fetch, AST propertyFetch, AST withClause) { - return null; + protected void handleAdHocJoinNode( + AST persisterReference, + AST joinType, + AST onClause) { } - protected AST buildAdHocJoinNode(AST persisterReference, AST joinType, AST onClause) { + protected boolean isEntityName(String test) { + return false; + } + + + // property reference handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + protected AST resolveAtomicPropertyReference(AST propertyIdent) { return null; } - protected boolean isEntityName(String test) { - return false; + protected AST resolveCompoundPropertyReference(AST dotStructure) { + return null; } } @@ -84,22 +98,19 @@ ; updateStatement - : #(UPDATE (VERSIONED)? fromClause setClause (whereClause)? - { popContext(); } ) + : #(UPDATE { pushStatement( #updateStatement ); } (VERSIONED)? fromClause setClause (whereClause)? { popStatement(); }) ; deleteStatement - : #(DELETE fromClause (whereClause)? - { popContext(); } ) + : #(DELETE { pushStatement( #deleteStatement ); } fromClause (whereClause)? { popStatement(); }) ; insertStatement - : #(INSERT intoClause query - { popContext(); } ) + : #(INSERT { pushStatement( #insertStatement ); } intoClause query { popStatement(); }) ; query - : #(QUERY + : #(QUERY { pushStatement( #query ); } // The first phase places the FROM first to make processing the SELECT simpler. #(SELECT_FROM fromClause @@ -107,8 +118,9 @@ ) (whereClause)? (groupClause)? - (orderClause)? - { popContext(); } + (orderClause)? { + popStatement(); + } ) ; @@ -120,8 +132,7 @@ fromClause -// : #( f:FROM { pushContext(#fromClause, f); } entityPersisterReference ( thetaJoin | explicitJoin )* ) - : #( f:FROM { pushContext(#fromClause, f); } range ( thetaJoin | explicitJoin )* ) + : #( f:FROM ( range | explicitJoin )* ) ; range! @@ -138,28 +149,28 @@ } ; -thetaJoin! -// : #( COMMA e:entityPersisterReference ) { -// #thetaJoin = buildThetaJoinNode( e ); -// } - : r:range { - #thetaJoin = buildThetaJoinNode( #r ); - } - ; - explicitJoin! : #(JOIN (jt:joinType)? joinRhs[jt] ) ; -joinRhs [AST joinType] +joinRhs! [AST joinType] : { isEntityName( pathAsString( _t ) ) }? e:entityPersisterReference (on:ON)? { - buildAdHocJoinNode( #e, joinType, on ); + handleAdHocJoinNode( #e, joinType, on ); } - | (f:FETCH)? ref:propertyRef[true] (a:ALIAS)? (pf:FETCH)? (with:WITH)? { - buildExplicitPropertyJoinNode( #ref, a, joinType, f, pf, with ); + | (f:FETCH)? prop:joinedProperty (a:ALIAS)? (pf:FETCH)? (with:WITH)? { + handleExplicitPropertyJoin( #prop, a, joinType, f, pf, with ); } ; +joinedProperty! + : i:identifier { + #joinedProperty = #( [DOT, "."], i ); + } + | d:dotRootStructure { + #joinedProperty = #d; + } + ; + // TODO : need to add cross joins joinType : ( (LEFT | RIGHT) (OUTER)? ) @@ -168,7 +179,7 @@ ; intoClause - : #(i:INTO { pushContext(#intoClause,i); } (subtree)* ) + : #(i:INTO (subtree)* ) ; whereClause @@ -232,8 +243,8 @@ ; collectionFunction - : #(ELEMENTS propertyRef [true] ) - | #(INDICES propertyRef [true] ) + : #( ELEMENTS propertyRef ) + | #( INDICES propertyRef ) ; count @@ -270,7 +281,7 @@ ; addrExpr - : propertyRef [true] + : propertyRef | #(INDEX_OP addrExprLhs expr) ; @@ -303,27 +314,62 @@ | #(AGGREGATE aggregateExpr ) ; -propertyRef! [ boolean root ] +// property references come in 3 basic flavors: +// (1) "simple" (either qualified or unqualified) : represents a reference to +// a value-type property +// (2) "nested" (either qualified or unqualified) : represents a series of +// property references which result in "implicit joins" being +// generated +// (3) "indexed" (either qualified or unqualified) : represents indexed access +// into an indexed collection such as a map or list +// +// an additional consideration when dealing with property references is the question +// of qualified vs unqualified references. Qualified references are "rooted" at an +// alias pointing to a particular persister reference, while unqualified references +// do not start with any such alias. Thus the "root" of the property reference +// structure may or may not begin with an explicit reference to a persister +// (via an alias). If qualified, the appropriate persister reference is simply looked +// up based on the alias. If unqualified, all non-aliased persister references are +// searched for one containing the indicated property; if one (and only one) is found +// then it is assumed to be the referenced persister; all other cases here are an error. +// +// Both "nested" and "indexed" property references may result in additional joins +// being added to the current from-clause structure implicitly. +propertyRef! : i:identifier { - if (root) { // Make the tree very regular, property refs always have a child. - #propertyRef = #([PROPERTY_REF,#i.getText()],#i); - } - else { // Otherwise, just construct the tree. - #propertyRef = #i; - } + #propertyRef = resolveAtomicPropertyReference( #i ); } - | #(d:DOT lhs:propertyRef [ false ] rhs:propertyName ) { - // Flatten nested DOTs... + | d:dotRootStructure { + #propertyRef = resolveCompoundPropertyReference( #d ); + } + ; + +dotRootStructure! + : #( d:DOT lhs:dotSubStructure rhs:dotStructureAtomic ) { AST first = (#lhs.getType() == DOT) ? #lhs.getFirstChild() : #lhs; - if (root) { // If this is the root of the path tree, then make a property ref node. - #propertyRef = #([PROPERTY_REF,#d.getText()],first,#rhs); - } - else { // If this is not the root of the path tree, then just flatten it. - #propertyRef = #(#d,first,#rhs); - } + #dotRootStructure = #( [DOT, "."], first, #rhs ); } ; +// reuseable rule to provide DOT structure flattening; +// eventually, this will get moved to the first phase as DOT +// structures are recognized there, but that needs to wait till +// we integrate this phase into the overall translation... +dotSubStructure! + : i:identifier { + // indicates we are at the base IDENT of the dot structure + #dotSubStructure = #i; + } + | #( d:DOT lhs:dotSubStructure rhs:dotStructureAtomic ) { + AST first = (#lhs.getType() == DOT) ? #lhs.getFirstChild() : #lhs; + #dotSubStructure = #( #d, first, #rhs ); + } + ; + +dotStructureAtomic + : propertyName + ; + propertyName : identifier | CLASS Modified: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/CollectionPersisterReference.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/CollectionPersisterReference.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/CollectionPersisterReference.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -1,34 +1,108 @@ package org.hibernate.hql.ast.resolve; import org.hibernate.type.AssociationType; -import org.hibernate.hql.ast.tree.Node; +import org.hibernate.type.EntityType; +import org.hibernate.type.Type; +import org.hibernate.type.ComponentType; +import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.Queryable; +import org.hibernate.hql.CollectionProperties; +import org.hibernate.QueryException; /** * @author Steve Ebersole */ -public class CollectionPersisterReference extends Node implements PersisterReference { - // TODO : implement +public class CollectionPersisterReference extends EntityPersisterReference implements ResolverAware { + + private HqlResolver resolver; + private String role; + private String alias; + private QueryableCollection persister; + + public void initialize(String role, String alias) { + this.role = role; + this.alias = alias; + } + + public QueryableCollection getCollectionPersister() { + if ( persister == null ) { + persister = ( QueryableCollection ) resolver.getSessionFactory().getCollectionPersister( role ); + } + return persister; + } + + public Queryable getEntityPersister() { + if ( getCollectionPersister().getElementType().isEntityType() ) { + EntityType elementEntityType = ( EntityType ) getCollectionPersister().getElementType(); + return ( Queryable ) elementEntityType.getAssociatedJoinable( resolver.getSessionFactory() ); + } + else { + throw new QueryException( "not a collection of entities" ); + } + } + public String getName() { - return null; //To change body of implemented methods use File | Settings | File Templates. + return role; } public String getAlias() { - return null; //To change body of implemented methods use File | Settings | File Templates. + return alias; } public AssociationType getPersisterType() { - return null; //To change body of implemented methods use File | Settings | File Templates. + return ( AssociationType ) getEntityPersister().getType(); } + public Type getPropertyType(String propertyName) { + if ( CollectionProperties.isAnyCollectionProperty( propertyName ) ) { + // todo + return null; + } + else { + Type elementType = getCollectionPersister().getElementType(); + if ( elementType.isAssociationType() ) { + // a collection of entities + EntityType elementEntityType = ( EntityType ) elementType; + try { + Queryable elementEntityPersister = ( Queryable ) elementEntityType + .getAssociatedJoinable( resolver.getSessionFactory() ); + return elementEntityPersister.getPropertyType( propertyName ); + } + catch( Throwable t ) { + // ignore + } + } + else if ( elementType.isComponentType() ) { + ComponentType elementComponentType = ( ComponentType ) elementType; + String[] subPropertyNames = elementComponentType.getPropertyNames(); + for ( int i = 0; i < subPropertyNames.length; i++ ) { + if ( subPropertyNames[i].equals( propertyName ) ) { + return elementComponentType.getSubtypes()[i]; + } + } + } + } + return null; + } + public boolean containsProperty(String propertyName) { - return false; //To change body of implemented methods use File | Settings | File Templates. + // this should not be called for "collection properties" (i.e., size, index, etc) + // so we make that assumption here... + try { + return getPropertyType( propertyName ) != null; + } + catch( Throwable t ) { + // ignore + } + return false; } - public PropertyReference retrievePropertyReference(String propertyName) { - return null; //To change body of implemented methods use File | Settings | File Templates. + public void setHqlResolver(HqlResolver resolver) { + this.resolver = resolver; } - public String getDisplayText() { - return null; //To change body of implemented methods use File | Settings | File Templates. + public String toString() { + return "CollectionPersisterReference {role=" + getName() + ", alias=" + getAlias() + + ", element-type=" + getCollectionPersister().getElementType() + "}"; } } Modified: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/EntityPersisterReference.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/EntityPersisterReference.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/EntityPersisterReference.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -1,39 +1,34 @@ package org.hibernate.hql.ast.resolve; import org.hibernate.type.AssociationType; +import org.hibernate.type.Type; import org.hibernate.persister.entity.Queryable; -import org.hibernate.hql.antlr.HqlRTokenTypes; -import org.hibernate.hql.ast.tree.Node; -import org.hibernate.engine.SessionFactoryImplementor; /** * @author Steve Ebersole */ -public class EntityPersisterReference extends Node implements PersisterReference, SessionFactoryAwareNode { +public class EntityPersisterReference extends PersisterReference implements ResolverAware { private String entityName; private String alias; - private SessionFactoryImplementor sessionFactory; - private transient Queryable persister; - public EntityPersisterReference() { - super(); - super.setType( HqlRTokenTypes.ENTITY_PERSISTER_REF ); - } + private HqlResolver resolver; + private transient Queryable persister; + public void initialize(String entityName, String alias) { this.entityName = entityName; this.alias = alias; } - public Queryable getPersister() { + public Queryable getEntityPersister() { if ( persister == null ) { - persister = ( Queryable ) sessionFactory.getEntityPersister( entityName ); + persister = ( Queryable ) resolver.getSessionFactory().getEntityPersister( entityName ); } return persister; } public String getName() { - return alias == null ? entityName : entityName + " (" + alias + ")"; + return entityName; } public String getAlias() { @@ -41,34 +36,27 @@ } public AssociationType getPersisterType() { - return ( AssociationType ) persister.getType(); + return ( AssociationType ) getEntityPersister().getType(); } + public Type getPropertyType(String propertyName) { + return getEntityPersister().getPropertyType( propertyName ); + } + public boolean containsProperty(String propertyName) { try { - return persister.getPropertyType( propertyName ) != null; + return getPropertyType( propertyName ) != null; } catch( Throwable t ) { return false; } } - public PropertyReference retrievePropertyReference(String propertyName) { - return null; - } - - public String getDisplayText() { - return "{" + - "entityName='" + entityName + '\'' + - ", alias='" + alias + '\'' + - '}'; - } - public String toString() { - return "EntityPersisterReference" + getDisplayText(); + return "EntityPersisterReference {entity-name=" + entityName + ", alias=" + alias + "}"; } - public void setSessionFactory(SessionFactoryImplementor sessionFactory) { - this.sessionFactory = sessionFactory; + public void setHqlResolver(HqlResolver resolver) { + this.resolver = resolver; } } Modified: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/HqlResolver.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/HqlResolver.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/HqlResolver.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -1,128 +1,187 @@ package org.hibernate.hql.ast.resolve; import org.hibernate.hql.antlr.HqlBaseResolver; +import org.hibernate.hql.CollectionProperties; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.QueryException; +import org.hibernate.type.Type; +import org.hibernate.type.ComponentType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import java.util.List; -import java.util.ArrayList; import antlr.collections.AST; /** - * Implements the methods for the HqlBaseResolver. - * <br>User: Joshua Davis - * Date: Apr 1, 2006 - * Time: 7:40:42 AM + * Adds specific semantic action behavior needed to perform useful resolution. + * + * @author Joshua Davis + * @author Steve Ebersole */ public class HqlResolver extends HqlBaseResolver { private static Log log = LogFactory.getLog( HqlResolver.class ); private final SessionFactoryImplementor sessionFactory; - private final ResolverContext resolverContext; + private StatementNode currentStatement; - private int level = 0; - private List inputContext = new ArrayList(); - private List outputContext = new ArrayList(); - public HqlResolver(SessionFactoryImplementor sessionFactory) { super(); this.sessionFactory = sessionFactory; - setASTFactory( new HqlResolverASTFactory( sessionFactory ) ); - this.resolverContext = new ResolverContextImpl( sessionFactory, getASTFactory() ); + setASTFactory( new HqlResolverASTFactory( this ) ); } + public SessionFactoryImplementor getSessionFactory() { + return sessionFactory; + } - protected void pushContext(AST outputAst,AST inputAst) { - level++; - inputContext.add(inputAst); - outputContext.add(outputAst); - if (log.isDebugEnabled()) { - log.debug("Pushed context: level = " + level + " input is " + inputAst.toStringTree()); - } + public PersisterReferenceContext getPersisterReferenceContext() { + return currentStatement; } - protected void popContext() { - level--; - AST n = (AST)inputContext.remove(inputContext.size() - 1); - outputContext.remove(outputContext.size() - 1); - if (log.isDebugEnabled()) { - log.debug("Popped context: level = " + level + " input was " + n.toStringTree()); + protected void pushStatement(AST statementNode) { + log.trace( "pushing new statement context : " + currentStatement + " -> " + statementNode ); + StatementNode statement = ( StatementNode ) statementNode; + if ( currentStatement != null ) { + currentStatement.pushChild( statement ); } + currentStatement = statement; } - protected AST getCurrentContext(boolean input) { - return (AST)((input) ? inputContext.get(inputContext.size() - 1) - : outputContext.get(outputContext.size() - 1)); + protected void popStatement() { + log.trace( "popping statement context : " + currentStatement + " -> " + currentStatement.getParentStatement() ); + currentStatement = currentStatement.getParentStatement(); } - protected int getLevel() { return level; } - - protected void defineRange(AST range, String path, AST alias, AST fetch) { - RangeNode r = (RangeNode) range; - r.setPath(path); - r.setPersisterReference( resolverContext.getEntityPersisterReference( path, alias == null ? null : alias.getText() ) ); - r.setFetch(fetch != null); - r.setAlias(alias != null ? alias.getText() : null); - } - protected AST buildEntityPersisterReference(String entityName, AST alias) { - return resolverContext.getEntityPersisterReference( entityName, alias == null ? null : alias.getText() ); + return currentStatement.buildEntityPersisterReference( entityName, alias == null ? null : alias.getText() ); } - protected AST buildThetaJoinNode(AST persisterReference) { - JoinNode join = createJoinNode( JoinType.FULL, JoinSource.THETA, false ); - join.setFirstChild( persisterReference ); - return join; - } - - private JoinNode createJoinNode(JoinType type, JoinSource source, boolean fetch) { + private JoinNode createJoinNode(JoinType type, JoinSource source, boolean fetch, PersisterReference rhs) { JoinNode node = ( JoinNode ) getASTFactory().create( JOIN, "join" ); - node.initialize( type, source, fetch ); + node.initialize( type, source, fetch, rhs ); + rhs.addChild( node ); return node; } - protected AST buildExplicitPropertyJoinNode( - AST propertyReference, - AST alias, - AST joinType, - AST fetch, - AST propertyFetch, - AST withClause) { - JoinNode join = createJoinNode( resolveJoinType( joinType ), JoinSource.EXPLICIT, fetch != null ); - join.setFirstChild( propertyReference ); - if ( withClause != null ) { - join.addChild( withClause ); + /** + * Parser has recognized an explicit join based on a "property join". + * <p/> + * For example, something like: <tt>from Animal a join a.mother</tt> or + * <tt>from Animal a join a.mother.mother as gm</tt> or even + * <tt>from Animal join mother</tt> + * <p/> + * Here, we will need to resolve the explicit join, as well as handle any + * implied joins. + * </p> + * Note that any implicit joins here are handled rather differently than + * implicit joins in any clause other than an explicit join. See + * {@link #resolveCompoundPropertyReference} for the differences. Mainly, the + * differences are:<ul> + * </ul> + * + * @param propertyPath The property(s) being joined to, which may indicate + * additional implicit joins are intended. + * @param aliasNode The alias to be applied to the generated persister reference + * @param joinTypeNode The type of join indicated + * @param fetchNode Was relation fetching specified? + * @param propertyFetchNode Was property fetching specified? + * @param withClauseNode Any with clause. + */ + protected void handleExplicitPropertyJoin( + AST propertyPath, + AST aliasNode, + AST joinTypeNode, + AST fetchNode, + AST propertyFetchNode, + AST withClauseNode) { + JoinType joinType = resolveJoinType( joinTypeNode ); + String alias = aliasNode == null ? null : aliasNode.getText(); + boolean relationFetch = fetchNode != null; + boolean propertyFetch = propertyFetchNode != null; + + // propertyPath should be a "flattened" dot node structure, which always have at least one child + AST next = null; + String firstPathExpression = propertyPath.getFirstChild().getText(); + EntityPersisterReference root = resolveRootAsAlias( firstPathExpression ); + if ( root != null ) { + // the root of the path expression is an alias referencing a persister reference, + // so the alias itself has essentially been resolved already, so "consume it" + next = propertyPath.getFirstChild().getNextSibling(); } - return join; + else { + // otherwise, the expectation is that the root of the path expression is an + // unqualified property reference, in which case we essentially use the + // root as a look ahead, and now need to actually resolve it + root = resolveRootAsUnqualified( firstPathExpression ); + if ( root == null ) { + throw new QueryException( "unable to determine root of path expression [" + reconstitutePathString( propertyPath ) + "]" ); + } + next = propertyPath.getFirstChild(); + } + + // At this point, we know the persister which acts as the root or base (root) of the + // path structure (propertyPath), as well as the first actual path expression which + // needs resolving (next). So now, we need to start iterating all the path + // expressions and performing the resolutions. + PersisterReference lhs = root; + JoinSource joinSource = JoinSource.EXPLICIT; + boolean firstPass = true; + while ( next != null ) { + // todo : components? + boolean isLast = next.getNextSibling() == null; + + JoinNode joinNode = createJoinNode( joinType, joinSource, relationFetch, lhs ); + PersisterReference rhs = currentStatement.getPersisterReference( lhs, next.getText(), isLast ? alias : null ); + joinNode.addChild( rhs ); + + if ( isLast && withClauseNode != null ) { + joinNode.addChild( withClauseNode ); + } + + if ( firstPass ) { + firstPass = false; + joinSource = JoinSource.IMPLICIT; + } + + lhs = rhs; + next = next.getNextSibling(); + } + } - protected AST buildAdHocJoinNode(AST persisterReference, AST joinType, AST onClause) { - JoinNode join = createJoinNode( resolveJoinType( joinType ), JoinSource.AD_HOC, false ); + protected void handleAdHocJoinNode(AST persisterReference, AST joinType, AST onClause) { + // todo : need to be able to resolve the lhs join operand + EntityPersisterReference other = null; + JoinNode join = createJoinNode( resolveJoinType( joinType ), JoinSource.AD_HOC, false, other ); join.setFirstChild( persisterReference ); if ( onClause != null ) { join.addChild( onClause ); } - return join; } private JoinType resolveJoinType(AST joinType) { - if ( joinType.getType() == LEFT ) { - return JoinType.LEFT; + int joinTypeType = joinType == null ? INNER : joinType.getType(); + switch ( joinTypeType ) { + case INNER: + return JoinType.INNER; + case LEFT: + return JoinType.LEFT; + case RIGHT: + return JoinType.RIGHT; + case FULL: + return JoinType.FULL; } - else if ( joinType.getType() == RIGHT ) { - return JoinType.RIGHT; - } - else if ( joinType.getType() == FULL ) { - return JoinType.FULL; - } - else { - throw new QueryException( "Unrecognized join type [" + joinType.getText() + "]" ); - } + // if no match found, throw exception + throw new QueryException( "Unrecognized join type [" + joinType.getText() + "]" ); } + public PersisterReference buildPropertyJoin(PersisterReference source, String propertyName, String alias) { + JoinNode join = createJoinNode( JoinType.INNER, JoinSource.IMPLICIT, false, source ); + PersisterReference persisterReference = currentStatement.getPersisterReference( source, propertyName, alias ); + join.addChild( persisterReference ); + return persisterReference; + } + protected boolean isEntityName(String test) { try { return sessionFactory.getEntityPersister( test ) != null; @@ -133,4 +192,277 @@ return false; } + protected String reconstitutePathString(AST propertyReference) { + AST child = propertyReference.getFirstChild(); + String prefix = ""; + StringBuffer buffer = new StringBuffer(); + while ( child != null ) { + buffer.append( prefix ).append( child.getText() ); + prefix = "."; + child = child.getNextSibling(); + } + return buffer.toString(); + } + + protected AST resolveAtomicPropertyReference(AST propertyNode) { + EntityPersisterReference persisterReference = resolveRootAsUnqualified( propertyNode.getText() ); + return generatePropertyReference( persisterReference, propertyNode.getText() ); + } + + private PropertyReference generatePropertyReference(PersisterReference persisterReference, String propertyName) { + PropertyReference propertyReferenceNode = ( PropertyReference ) getASTFactory() + .create( PROPERTY_REF, persisterReference.getAlias() + "." + propertyName ); + + AST aliasNode = getASTFactory().create( ALIAS, persisterReference.getAlias() ); + propertyReferenceNode.addChild( aliasNode ); + + AST propertyNameNode = getASTFactory().create( IDENT, propertyName ); + propertyReferenceNode.addChild( propertyNameNode ); + + return propertyReferenceNode; + } + + protected AST resolveCompoundPropertyReference(AST dotStructure) { + PersisterReference persisterReference; + AST next; + + // first, resolve root origin + String firstPathExpression = dotStructure.getFirstChild().getText(); + if ( dotStructure.getNumberOfChildren() == 1 ) { + // can only really represent an unqualified simple property ref + EntityPersisterReference match = resolveRootAsUnqualified( firstPathExpression ); + if ( match == null ) { + throw new QueryException( "unable to resolve property [" + firstPathExpression + "] as unqualified reference" ); + } + persisterReference = match; + // the root of the path expression is an alias referencing a persister reference, + // so the alias itself has essentially been resolved already, so "consume it" + next = dotStructure.getFirstChild(); + } + else { + // need to decide whether 'firstPathExpression' refers to a from-clause alias, or + // an unqualified property name. from-clause alias has a higher precedence + // for matching purposes, so try to resolve as alias first + EntityPersisterReference match = resolveRootAsAlias( firstPathExpression ); + if ( match != null ) { + // the root of the path expression is an alias referencing a persister reference, + // so the alias itself has essentially been resolved already, so "consume it" + next = dotStructure.getFirstChild().getNextSibling(); + } + else { + // then try as unqualified + match = resolveRootAsUnqualified( firstPathExpression ); + if ( match != null ) { + // the root of the path expression is an unqualified property reference, + // in which case we essentially use the root as a look ahead, and now + // need to actually resolve it + next = dotStructure.getFirstChild(); + } + else { + throw new QueryException( "unable to determine root of path expression [" + reconstitutePathString( dotStructure ) + "]" ); + } + } + persisterReference = match; + } + + PropertyPathPart propertySource = new PropertyPathRoot( ( EntityPersisterReference ) persisterReference ); + String pathSoFar = persisterReference.getAlias(); + + while ( next != null && next.getNextSibling() != null ) { + String propertyName = next.getText(); + pathSoFar += ( "." + propertyName ); + + PersisterReference built = currentStatement.locatePersisterReferenceByPath( pathSoFar ); + if ( built != null ) { + propertySource = new PropertyPathRoot( ( EntityPersisterReference ) built ); + } + else { + propertySource = propertySource.handleIntermediatePathPart( propertyName ); + } + + next = next.getNextSibling(); + } + + if ( next == null ) { + throw new QueryException( "illegal parser state" ); + } + return propertySource.handleLeafPathPart( next.getText() ); + } + + private EntityPersisterReference resolveRootAsAlias(String firstPathExpression) { + return ( EntityPersisterReference ) currentStatement.locatePersisterReferenceByAlias( firstPathExpression ); + } + + private EntityPersisterReference resolveRootAsUnqualified(String firstPathExpression) { + return currentStatement.locatePersisterReferenceExposingProperty( firstPathExpression ); + } + + private PropertyPathPart determineAppropriatePartType(EntityPersisterReference origin, String propertyName) { + Type propertyType = origin.getPropertyType( propertyName ); + if ( propertyType.isComponentType() ) { + return new ComponentPropertyReference( origin, propertyName, ( ComponentType ) propertyType ); + } + else if ( propertyType.isEntityType() ) { + return new EntityPropertyReference( origin, propertyName, false ); + } + else if ( propertyType.isCollectionType() ) { + return new CollectionPropertyReference( origin, propertyName ); + } + else { + return new SimplePropertyReference( origin, propertyName ); + } + } + + private int locateComponentPropertyIndex(ComponentType componentType, String subPropertyName) { + String[] componentPropertyNames = componentType.getPropertyNames(); + for ( int i = 0; i < componentPropertyNames.length; i++ ) { + if ( componentPropertyNames[i].equals( subPropertyName ) ) { + return i; + } + } + throw new QueryException( "could not locate component property [" + subPropertyName + "]" ); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // todo : this stuff needs to be finished up. + // specific things needing to be completed: + // (1) only dereference an actual entity association property into a + // join when it is further dereferenced (already handled) or when + // occuring in select clause + // (2) properly handle "collection properties" + // (3) properly handle components + // (4) properly handle index operations + // + // todo : look at utilizing this from handleExplicitPropertyJoin(...) also + + /** + * Contract for handling delegation for a particular portion of an implicit + * join structure (aka property path). + */ + private interface PropertyPathPart { + public PropertyPathPart handleIntermediatePathPart(String name); + public PropertyReference handleLeafPathPart(String name); + } + + private class PropertyPathRoot implements PropertyPathPart { + private final EntityPersisterReference persisterReference; + + public PropertyPathRoot(EntityPersisterReference persisterReference) { + this.persisterReference = persisterReference; + } + + public PropertyPathPart handleIntermediatePathPart(String name) { + return determineAppropriatePartType( persisterReference, name ); + } + + public PropertyReference handleLeafPathPart(String name) { + // todo : this really needs to consider whether a join might be needed + // based on the property type and type of clause + return generatePropertyReference( persisterReference, name ); + } + + } + + private class SimplePropertyReference implements PropertyPathPart { + private final EntityPersisterReference origin; + private final String propertyName; + + public SimplePropertyReference(EntityPersisterReference origin, String propertyName) { + this.origin = origin; + this.propertyName = propertyName; + } + + public PropertyPathPart handleIntermediatePathPart(String name) { + throw new QueryException( "cannot perform implicit join based on simple property" ); + } + + public PropertyReference handleLeafPathPart(String name) { + throw new QueryException( "cannot perform implicit join based on simple property" ); + } + } + + private class ComponentPropertyReference implements PropertyPathPart { + private final EntityPersisterReference origin; + private final String componentPropertyName; + private final ComponentType componentType; + + public ComponentPropertyReference(EntityPersisterReference origin, String componentPropertyName) { + this( origin, componentPropertyName, ( ComponentType ) origin.getPropertyType( componentPropertyName ) ); + } + + public ComponentPropertyReference(EntityPersisterReference origin, String componentPropertyName, ComponentType componentType) { + this.origin = origin; + this.componentPropertyName = componentPropertyName; + this.componentType = componentType; + } + + public PropertyPathPart handleIntermediatePathPart(String propertyName) { + int index = locateComponentPropertyIndex( componentType, propertyName ); + String path = buildDerefPath( propertyName ); + Type propertyType = componentType.getSubtypes()[index]; + if ( propertyType.isComponentType() ) { + return new ComponentPropertyReference( origin, path, ( ComponentType ) propertyType ); + } + else if ( propertyType.isEntityType() ) { + return new EntityPropertyReference( origin, path, false ); + } + else { + return new SimplePropertyReference( origin, path ); + } + } + + public PropertyReference handleLeafPathPart(String name) { + return generatePropertyReference( origin, buildDerefPath( name ) ); + } + + private String buildDerefPath(String subPropertyName) { + return componentPropertyName + "." + subPropertyName; + } + } + + private class EntityPropertyReference implements PropertyPathPart { + private final EntityPersisterReference origin; + private final String propertyName; + + private boolean joined; + + public EntityPropertyReference(EntityPersisterReference origin, String propertyName, boolean joined) { + this.origin = origin; + this.propertyName = propertyName; + this.joined = joined; + } + + public PropertyPathPart handleIntermediatePathPart(String name) { + EntityPersisterReference joinedPersister = ( EntityPersisterReference ) buildPropertyJoin( origin, propertyName, null ); + return determineAppropriatePartType( joinedPersister, name ); + } + + public PropertyReference handleLeafPathPart(String name) { + // not always needed (i.e. : .id) + EntityPersisterReference joinedPersister = ( EntityPersisterReference ) buildPropertyJoin( origin, propertyName, null ); + return generatePropertyReference( joinedPersister, name ); + } + } + + private class CollectionPropertyReference implements PropertyPathPart { + private final EntityPersisterReference origin; + private final String collectionPropertyName; + + public CollectionPropertyReference(EntityPersisterReference origin, String collectionPropertyName) { + this.origin = origin; + this.collectionPropertyName = collectionPropertyName; + } + + public PropertyPathPart handleIntermediatePathPart(String name) { + throw new QueryException( "illegal attempt to perform implicit join across collection property" ); + } + + public PropertyReference handleLeafPathPart(String name) { + if ( CollectionProperties.isAnyCollectionProperty( name ) ) { + CollectionPersisterReference joinedPersister = ( CollectionPersisterReference ) buildPropertyJoin( origin, collectionPropertyName, null ); + return generatePropertyReference( joinedPersister, name ); + } + throw new QueryException( "illegal attempt to perform implicit join across collection property" ); + } + } } Modified: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/HqlResolverASTFactory.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/HqlResolverASTFactory.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/HqlResolverASTFactory.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -13,30 +13,33 @@ * Time: 7:58:16 AM */ public class HqlResolverASTFactory extends HqlASTFactory implements HqlRTokenTypes { - private final SessionFactoryImplementor factory; + private final HqlResolver resolver; - public HqlResolverASTFactory(SessionFactoryImplementor factory) { - this.factory = factory; + public HqlResolverASTFactory(HqlResolver resolver) { + this.resolver = resolver; } public Class getASTNodeType(int tokenType) { // Statement nodes: switch (tokenType) { - case QUERY : + case QUERY: + return SelectStatementNode.class; case UPDATE: + return UpdateStatementNode.class; case DELETE: + return DeleteStatementNode.class; case INSERT: - return StatementNode.class; - case RANGE: - return RangeNode.class; + return InsertStatementNode.class; case ENTITY_PERSISTER_REF: return EntityPersisterReference.class; case COLLECTION_PERSISTER_REF: return CollectionPersisterReference.class; case JOIN: return JoinNode.class; + case PROPERTY_REF: + return PropertyReference.class; } - return super.getASTNodeType(tokenType); + return super.getASTNodeType( tokenType ); } protected AST createUsingCtor(Token token, String string) { @@ -53,7 +56,10 @@ private void prepare(AST node) { if ( node instanceof SessionFactoryAwareNode ) { - ( ( SessionFactoryAwareNode ) node ).setSessionFactory( factory ); + ( ( SessionFactoryAwareNode ) node ).setSessionFactory( resolver.getSessionFactory() ); } + if ( node instanceof ResolverAware ) { + ( ( ResolverAware ) node ).setHqlResolver( resolver ); + } } } Modified: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/JoinNode.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/JoinNode.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/JoinNode.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -20,19 +20,22 @@ private JoinType joinType; private JoinSource source; private boolean fetch; + private PersisterReference lhs; public JoinNode() { + super.setText( "join" ); } public JoinNode(Token tok) { super( tok ); + super.setText( "join" ); } - public void initialize(JoinType joinType, JoinSource source, boolean fetch) { + public void initialize(JoinType joinType, JoinSource source, boolean fetch, PersisterReference lhs) { this.joinType = joinType; this.source = source; this.fetch = fetch; - super.setText( "join" ); + this.lhs = lhs; } public JoinType getJoinType() { @@ -60,10 +63,10 @@ } public String getDisplayText() { - return "{" + "type=" + joinType + ", source=" + source + "fetch=" + fetch + "}"; + return "{" + "type=" + joinType + ", source=" + source + ", fetch=" + fetch + ", lhs=" + lhs.getAlias() + "}"; } public String toString() { - return "JoinNode" + getDisplayText(); + return "JoinNode " + getDisplayText(); } } Modified: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/PersisterReference.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/PersisterReference.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/PersisterReference.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -1,7 +1,8 @@ package org.hibernate.hql.ast.resolve; import org.hibernate.type.AssociationType; -import org.hibernate.hql.ast.tree.DisplayableNode; +import org.hibernate.type.Type; +import org.hibernate.hql.ast.tree.Node; /** * Represents a reference to a persister (either entity or collection) within @@ -9,11 +10,13 @@ * * @author Steve Ebersole */ -public interface PersisterReference extends DisplayableNode { - public String getName(); - public String getAlias(); - public AssociationType getPersisterType(); +public abstract class PersisterReference extends Node { + public abstract String getName(); + public abstract String getAlias(); + public abstract AssociationType getPersisterType(); + public abstract Type getPropertyType(String propertyName); - public boolean containsProperty(String propertyName); - public PropertyReference retrievePropertyReference(String propertyName); + public String getText() { + return getName() + " (" + getAlias() + ")"; + } } Modified: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/PropertyReference.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/PropertyReference.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/PropertyReference.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -1,16 +1,57 @@ package org.hibernate.hql.ast.resolve; import org.hibernate.type.Type; -import org.hibernate.hql.ast.tree.DisplayableNode; +import org.hibernate.hql.ast.tree.Node; /** - * Represents a reference to a particular persister-managed property; also - * some special cases such as a collection property reference (e.g., size). + * Represents a reference to a particular property. + * <p/> + * The sub-tree structure of this node is such that its first child is an ALIAS + * node representing an alias pointing to the specific persister reference from + * which the property reference originates. The other child is the property name, + * as an IDENT * * @author Steve Ebersole */ -public interface PropertyReference extends DisplayableNode { - public String getPropertyName(); - public Type getPropertyType(); - public PersisterReference getPersisterReference(); +public class PropertyReference extends Node implements ResolverAware { + + private HqlResolver resolver; + + // caches + private String alias; + private String propertyName; + private Type propertyType; + private PersisterReference origin; + + public String getPersisterReferenceAlias() { + if ( alias == null ) { + alias = getFirstChild().getText(); + } + return alias; + } + + public String getPropertyName() { + if ( propertyName == null ) { + propertyName = getFirstChild().getNextSibling().getText(); + } + return propertyName; + } + + public PersisterReference getPropertyOrigination() { + if ( origin == null ) { + origin = resolver.getPersisterReferenceContext().locatePersisterReferenceByAlias( getPersisterReferenceAlias() ); + } + return origin; + } + + public Type getPropertyType() { + if ( propertyType == null ) { + propertyType = getPropertyOrigination().getPropertyType( getPropertyName() ); + } + return propertyType; + } + + public void setHqlResolver(HqlResolver resolver) { + this.resolver = resolver; + } } Deleted: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/RangeNode.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/RangeNode.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/RangeNode.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -1,69 +0,0 @@ -package org.hibernate.hql.ast.resolve; - -import org.hibernate.hql.ast.tree.Node; -import org.hibernate.hql.ast.tree.DisplayableNode; - -/** - * Represents a "top-level" element in a FROM clause (e.g. "from Animal a"). These - * "top-level" nodes are then contained in a RANGE node within the FROM node. - * - * - * @author Joshua Davis - */ -public class RangeNode extends Node implements DisplayableNode { - // TODO : would like to remove this range concept; - // a "range" is really just a series of full joins (unless further - // qualified in the where-clause, in which case they'd become inner - // joins) specified using old "theta join" syntax from SQL, so - // represent them as join structures - private String path; - private String alias; - private EntityPersisterReference persisterReference; - private boolean fetch = false; - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public EntityPersisterReference getPersisterReference() { - return persisterReference; - } - - public void setPersisterReference(EntityPersisterReference persisterReference) { - this.persisterReference = persisterReference; - } - - public boolean isFetch() { - return fetch; - } - - public void setFetch(boolean fetch) { - this.fetch = fetch; - } - - public String getAlias() { - return alias; - } - - public void setAlias(String alias) { - this.alias = alias; - } - - - public String toString() { - return "RangeNode{" + - "path='" + path + '\'' + - ", alias='" + alias + '\'' + - ", reference=" + persisterReference + - ", fetch=" + fetch + - '}'; - } - - public String getDisplayText() { - return toString(); - } -} Deleted: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ResolverContext.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ResolverContext.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ResolverContext.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -1,19 +0,0 @@ -package org.hibernate.hql.ast.resolve; - -import org.hibernate.persister.entity.EntityPersister; - -import java.util.List; - -/** - * Looks up persisters and other things by name. - * <br>User: Joshua Davis - * Date: Apr 12, 2006 - * Time: 7:34:11 AM - */ -public interface ResolverContext { - public EntityPersister lookupPersister(String path); - public PersisterReference locatePersisterReferenceByAlias(String alias); - public List collectPersisterReferences(); - public EntityPersisterReference getEntityPersisterReference(String entityName, String alias); - public CollectionPersisterReference getCollectionPersisterReference(String collectionRole, String alias); -} Deleted: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ResolverContextImpl.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ResolverContextImpl.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ResolverContextImpl.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -1,74 +0,0 @@ -package org.hibernate.hql.ast.resolve; - -import org.hibernate.engine.SessionFactoryImplementor; -import org.hibernate.MappingException; -import org.hibernate.hql.antlr.HqlRTokenTypes; -import org.hibernate.persister.entity.EntityPersister; - -import java.util.Map; -import java.util.HashMap; -import java.util.List; -import java.util.ArrayList; -import java.util.Collections; - -import antlr.ASTFactory; - -/** - * Implements the resolver's context with a session factory. - * <br>User: Joshua Davis - * Date: Apr 12, 2006 - * Time: 7:35:31 AM - */ -public class ResolverContextImpl implements ResolverContext { - private Map persisterReferencesByAlias = new HashMap(); - private List persisterReferences = new ArrayList(); - private final SessionFactoryImplementor sessionFactory; - private final ASTFactory astFactory; - - public ResolverContextImpl(SessionFactoryImplementor sessionFactory, ASTFactory astFactory) { - this.sessionFactory = sessionFactory; - this.astFactory = astFactory; - } - - public EntityPersister lookupPersister(String name) { - // First, try to get the persister using the class name directly. - try { - return sessionFactory.getEntityPersister( name ); - } - catch ( MappingException ignore ) { - // unable to locate it using this name - } - - // If that didn't work, try using the 'import' name. - String importedClassName = sessionFactory.getImportedClassName( name ); - if ( importedClassName == null ) { - return null; - } - return sessionFactory.getEntityPersister( importedClassName ); - } - - public PersisterReference locatePersisterReferenceByAlias(String alias) { - return ( PersisterReference ) persisterReferencesByAlias.get( alias ); - } - - public List collectPersisterReferences() { - return Collections.unmodifiableList( persisterReferences ); - } - - public EntityPersisterReference getEntityPersisterReference(String entityName, String alias) { - EntityPersister persister = lookupPersister( entityName ); - EntityPersisterReference persisterReference = ( EntityPersisterReference ) astFactory.create( HqlRTokenTypes.ENTITY_PERSISTER_REF, persister.getEntityName() ); - persisterReference.initialize( persister.getEntityName(), alias ); - persisterReferences.add( persisterReference ); - if ( alias != null ) { - persisterReferencesByAlias.put( alias, persisterReference ); - } - return persisterReference; - } - - public CollectionPersisterReference getCollectionPersisterReference(String collectionRole, String alias) { - // TODO : implement; for now returns null... - return null; - } - -} Modified: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/StatementNode.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/StatementNode.java 2006-05-02 11:07:06 UTC (rev 9850) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/StatementNode.java 2006-05-02 14:26:06 UTC (rev 9851) @@ -1,12 +1,220 @@ package org.hibernate.hql.ast.resolve; import org.hibernate.hql.ast.tree.Node; +import org.hibernate.hql.antlr.HqlRTokenTypes; +import org.hibernate.util.EmptyIterator; +import org.hibernate.QueryException; +import org.hibernate.MappingException; +import org.hibernate.type.Type; +import org.hibernate.type.EntityType; +import org.hibernate.type.CollectionType; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.engine.SessionFactoryImplementor; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; + +import antlr.ASTFactory; + /** - * Represents a statement (SELECT, UPDATE, INSERT, DELETE) in the resolved HQL tree. - * <br>User: Joshua Davis - * Date: Apr 3, 2006 - * Time: 8:00:55 AM + * Base node class for hql statements (SELECT, DELETE, INSERT, UPDATE). + * + * @author Joshua Davis + * @author Steve Ebersole */ -public class StatementNode extends Node { +public abstract class StatementNode extends Node implements ResolverAware, PersisterReferenceContext { + + private static final Log log = LogFactory.getLog( StatementNode.class ); + + private StatementNode parentStatement; + private List childStatements; + private HqlResolver resolver; + + public StatementNode getParentStatement() { + return parentStatement; + } + + public Iterator iterateChildStatements() { + return childStatements == null ? EmptyIterator.INSTANCE : childStatements.iterator(); + } + + public void pushChild(StatementNode childStatement) { + childStatement.setParent( this ); + if ( childStatements == null ) { + childStatements = new ArrayList(); + } + childStatements.add( childStatement ); + } + + private void setParent(StatementNode parentStatement) { + if ( this.parentStatement != null ) { + throw new QueryException( "statement already had an associated parent" ); + } + this.parentStatement = parentStatement; + } + + public void setHqlResolver(HqlResolver resolver) { + this.resolver = resolver; + // todo : temp (see below) + this.sessionFactory = resolver.getSessionFactory(); + this.astFactory = resolver.getASTFactory(); + } + + + // todo : temp impl of PersisterReferenceContext ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // still need to handle hierarchical lookups as well as proper scoping rules between + // statement types + + private int unaliasedCount = 0; + private Map persisterReferencesByAlias = new HashMap(); + private Map persisterReferencesByPath = new HashMap(); + private List persisterReferences = new ArrayList(); + private SessionFactoryImplementor sessionFactory; + private ASTFactory astFactory; + + public EntityPersister lookupPersister(String name) { + // First, try to get the persister using the class name directly. + try { + return sessionFactory.getEntityPersister( name ); + } + catch ( MappingException ignore ) { + // unable to locate it using this name + } + + // If that didn't work, try using the 'import' name. + String importedClassName = sessionFactory.getImportedClassName( name ); + if ( importedClassName == null ) { + return null; + } + return sessionFactory.getEntityPersister( importedClassName ); + } + + public void registerPersisterReferenceAgainstPath(String path, PersisterReference persisterReference) { + if ( persisterReferencesByPath.containsKey( path ) ) { + throw new QueryException( "persister reference already registered for path [" + path + "]" ); + } + } + + public PersisterReference locatePersisterReferenceByPath(String path) { + return ( PersisterReference ) persisterReferencesByPath.get( path ); + } + + public PersisterReference locatePersisterReferenceByAlias(String alias) { + return ( PersisterReference ) persisterReferencesByAlias.get( alias ); + } + + public EntityPersisterReference locatePersisterReferenceExposingProperty(String firstPathExpression) { + log.trace( "trying to locate persister exposing property [" + firstPathExpression + "]" ); + EntityPersisterReference match = null; + Iterator itr = persisterReferences.iterator(); + while( itr.hasNext() ) { + final EntityPersisterReference test = ( EntityPersisterReference ) itr.next(); + if ( test.containsProperty( firstPathExpression ) ) { + if ( match != null ) { + throw new QueryException( "multiple referenced persisters contained property [" + firstPathExpression + "]" ); + } + match = test; + } + } + return match; + } + + public EntityPersisterReference buildEntityPersisterReference(String entityName, String alias) { + EntityPersister persister = lookupPersister( entityName ); + alias = determineAlias( alias ); + EntityPersisterReference persisterReference = ( EntityPersisterReference ) astFactory.create( HqlRTokenTypes.ENTITY_PERSISTER_REF, persister.getEntityName() ); + persisterReference.initialize( persister.getEntityName(), alias ); + persisterReferenceBuilt( alias, persisterReference ); + return persisterReference; + } + + private String determineAlias(String alias) { + if ( alias == null ) { + alias = "<gen:" + unaliasedCount++ + ">"; + } + return alias; + } + + public PersisterReference getPersisterReference(PersisterReference source, String propertyName, String alias) { + alias = determineAlias( alias ); + if ( source.getPersisterType().isEntityType() ) { + return buildPersisterReferenceFromEntity( ( EntityPersisterReference ) source, propertyName, alias ); + } + else { + return buildPersisterReferenceFromCollection( ( CollectionPersisterReference ) source, propertyName, alias ); + } + } + + private PersisterReference buildPersisterReferenceFromCollection( + CollectionPersisterReference collectionPersisterReference, + String propertyName, + String alias) { + // todo : we may need two different forms of CollectionPersisterReference, + // the distinction being which "properties" are available from each... + // (1) represents nodes built from explicit joins (properties from + // the underlying collection elements are available + // (2) represents nodes built from implicit joins (only + // "collection properties" are available (size, index, etc) + return null; + } + + private PersisterReference buildPersisterReferenceFromEntity( + EntityPersisterReference source, + String propertyName, + String alias ) { + Type type = null; + PersisterReference ref = null; + try { + type = source.getEntityPersister().getPropertyType( propertyName ); + } + catch( Throwable t ) { + throw new QueryException( "could not resolve property [" + propertyName + "] on " + source.getEntityPersister().getEntityName() ); + } + + if ( type.isEntityType() ) { + EntityType entityType = ( EntityType ) type; + EntityPersisterReference entityPersisterReference = ( EntityPersisterReference ) + astFactory.create( HqlRTokenTypes.ENTITY_PERSISTER_REF, entityType.getAssociatedEntityName() ); + entityPersisterReference.initialize( entityType.getAssociatedEntityName(), alias ); + ref = entityPersisterReference; + } + else if ( type.isCollectionType() ) { + CollectionType collectionType = ( CollectionType ) type; + CollectionPersisterReference collectionPersisterReference = ( CollectionPersisterReference ) + astFactory.create( HqlRTokenTypes.COLLECTION_PERSISTER_REF, "" ); + collectionPersisterReference.initialize( collectionType.getRole(), alias ); + ref = collectionPersisterReference; + } + else { + throw new QueryException( + "cannot dereference property [" + propertyName + + "] from persister [" + source.getEntityPersister().getEntityName() + + "] as part of path expression" + ); + } + + persisterReferenceBuilt( alias, ref ); + return ref; + } + + private void persisterReferenceBuilt(String alias, PersisterReference reference) { + persisterReferen... [truncated message content] |