From: <hib...@li...> - 2006-06-30 05:58:32
|
Author: ste...@jb... Date: 2006-06-30 01:55:55 -0400 (Fri, 30 Jun 2006) New Revision: 10070 Added: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/HqlParser.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/ParseLexer.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/PathCollector.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/AbstractImplicitJoinContext.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/FromClauseImplicitJoinContext.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ImplicitJoinContext.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/NormalImplicitJoinContext.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/PersisterReferenceBuilder.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/tree/ASTFactoryAwareNode.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/tree/PersisterReferenceContextAwareNode.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/tree/SessionFactoryAwareNode.java Removed: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ResolverAware.java branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/SessionFactoryAwareNode.java 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/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/PersisterReferenceContext.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/src/org/hibernate/util/ReflectHelper.java Log: redid parse phase and simplified portions of resolve phase Added: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/HqlParser.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/HqlParser.java 2006-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/HqlParser.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -0,0 +1,384 @@ +package org.hibernate.hql.ast.parse; + +import org.hibernate.hql.antlr.GeneratedHqlParser; +import org.hibernate.hql.antlr.ParseTokenTypes; +import org.hibernate.hql.ast.util.ASTUtil; +import org.hibernate.hql.ast.util.ASTPrinter; +import org.hibernate.hql.ast.HqlToken; +import org.hibernate.hql.ast.ErrorCounter; +import org.hibernate.hql.ast.HqlASTFactory; +import org.hibernate.hql.ast.ParseErrorHandler; +import org.hibernate.QueryException; +import org.hibernate.util.ReflectHelper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.StringReader; +import java.io.PrintStream; +import java.io.PrintWriter; + +import antlr.collections.AST; +import antlr.RecognitionException; +import antlr.TokenStreamException; +import antlr.Token; +import antlr.ASTPair; +import antlr.MismatchedTokenException; + +/** + * The parser used by Hibernate to generate an AST given an input + * HQL string (a "stream parser"). The produced AST is then used + * (and mutated) by later phases/parsers to apply semantic resolution; + * this parser, however, is all about syntax resolution. + * + * @author Steve Ebersole + */ +public class HqlParser extends GeneratedHqlParser { + + public static final Log log = LogFactory.getLog( HqlParser.class ); + + private final ParseErrorHandler parseErrorHandler = new ErrorCounter(); + private final ASTPrinter printer = new ASTPrinter( ParseTokenTypes.class ); + + public HqlParser(String hql) { + super( new ParseLexer( new StringReader( hql ) ) ); + setASTFactory( new HqlASTFactory() ); + } + + + // overrides of Antlr infastructure methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public void reportError(RecognitionException e) { + parseErrorHandler.reportError( e ); + } + + public void reportError(String s) { + parseErrorHandler.reportError( s ); + } + + public void reportWarning(String s) { + parseErrorHandler.reportWarning( s ); + } + + public ParseErrorHandler getParseErrorHandler() { + return parseErrorHandler; + } + + static public void panic() { + //overriden to avoid System.exit + throw new QueryException( "Parser: panic" ); + } + + + // various AST output methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public void dumpAst(AST ast) { + dumpAst( ast, "DUMP" ); + } + + public void dumpAst(AST ast, String header) { + log.info( printer.showAsString( ast, header ) ); + } + + public void showAst(AST ast, PrintStream out) { + showAst( ast, new PrintWriter( out ) ); + } + + private void showAst(AST ast, PrintWriter pw) { + printer.showAst( ast, pw ); + } + + + // overrides of grammar semantic actions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public String extractEntityName(AST node) throws RecognitionException { + dumpAst( node, "expected entity name path" ); + return PathCollector.getPath( node ); + } + + public String extractDynamicInstantiationPojoName(AST node) { + return PathCollector.getPath( node ); + } + + public String extractJavaConstantReference(AST node) throws RecognitionException { + return PathCollector.getPath( node ); + } + + public boolean isJavaConstant() throws RecognitionException, TokenStreamException { + if ( LA( 2 ) != DOT ) { + return false; + } + String path = LT( 1 ).getText() + "." + LT( 3 ).getText(); + int currentLaPotion = 5; + while ( LA( currentLaPotion - 1 ) == DOT ) { + path += "." + LT( currentLaPotion ).getText(); + currentLaPotion += 2; + } + return isJavaConstant( path ); + } + + public boolean isJavaConstant(AST dotStructure) throws RecognitionException, TokenStreamException { + return isJavaConstant( PathCollector.getPath( dotStructure ) ); + } + + private boolean isJavaConstant(String path) { + try { + log.trace( "Testing path [" + path + "] as potential java constant" ); + Object value = ReflectHelper.getConstantValueStrictly( path ); + log.trace( "Resolved path to java constant [" + value + "]" ); + return true; + } + catch( Throwable t ) { + log.trace( "Path did not resolve to java constant : " + t ); + return false; + } + } + + /** + * Overrides the base behavior to retry keywords as identifiers. + * + * @param token The token. + * @param ex The recognition exception. + * @return AST - The new AST. + * @throws antlr.RecognitionException if the substitution was not possible. + * @throws antlr.TokenStreamException if the substitution was not possible. + */ + public AST handleIdentifierError(Token token, RecognitionException ex) throws RecognitionException, TokenStreamException { + // If the token can tell us if it could be an identifier... + if ( token instanceof HqlToken ) { + HqlToken hqlToken = ( HqlToken ) token; + // ... and the token could be an identifer and the error is + // a mismatched token error ... + if ( hqlToken.isPossibleID() && ex instanceof MismatchedTokenException ) { + MismatchedTokenException mte = ( MismatchedTokenException ) ex; + // ... and the expected token type was an identifier, then: + if ( mte.expecting == IDENT ) { + // Use the token as an identifier. + reportWarning( + "Keyword '"+ token.getText() + + "' is being interpreted as an identifier due to: " + + mte.getMessage() + ); + // Add the token to the AST. + ASTPair currentAST = new ASTPair(); + token.setType( WEIRD_IDENT ); + astFactory.addASTChild( currentAST, astFactory.create( token ) ); + consume(); + return currentAST.root; + } + } + } + return super.handleIdentifierError( token, ex ); + } + + public void handleDotIdent() throws TokenStreamException { + // This handles HHH-354, where there is a strange property name in a where clause. + // If the lookahead contains a DOT then something that isn't an IDENT... + if ( LA( 1 ) == DOT && LA( 2 ) != IDENT ) { + // See if the second lookahed token can be an identifier. + if ( ( ( HqlToken ) LT( 2 ) ).isPossibleID() ) { + // Set it! + LT( 2 ).setType( IDENT ); + if ( log.isDebugEnabled() ) { + log.debug( "handleDotIdent() : new LT(2) token - " + LT( 1 ) ); + } + } + } + } + + /** + * Returns an equivalent tree for (NOT (a relop b) ), for example:<pre> + * (NOT (GT a b) ) => (LE a b) + * </pre> + * + * @param x The sub tree to transform, the parent is assumed to be NOT. + * @return AST - The equivalent sub-tree. + */ + public AST negateNode(AST x) { + //TODO: switch statements are always evil! We already had bugs because + // of forgotten token types. Use polymorphism for this! + switch ( x.getType() ) { + case OR: + x.setType(AND); + x.setText("{and}"); + negateNode( x.getFirstChild() ); + negateNode( x.getFirstChild().getNextSibling() ); + return x; + case AND: + x.setType(OR); + x.setText("{or}"); + negateNode( x.getFirstChild() ); + negateNode( x.getFirstChild().getNextSibling() ); + return x; + case EQ: + x.setType( NE ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (EQ a b) ) => (NE a b) + case NE: + x.setType( EQ ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (NE a b) ) => (EQ a b) + case GT: + x.setType( LE ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (GT a b) ) => (LE a b) + case LT: + x.setType( GE ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (LT a b) ) => (GE a b) + case GE: + x.setType( LT ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (GE a b) ) => (LT a b) + case LE: + x.setType( GT ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (LE a b) ) => (GT a b) + case LIKE: + x.setType( NOT_LIKE ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (LIKE a b) ) => (NOT_LIKE a b) + case NOT_LIKE: + x.setType( LIKE ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (NOT_LIKE a b) ) => (LIKE a b) + case IN: + x.setType( NOT_IN ); + x.setText( "{not}" + x.getText() ); + return x; + case NOT_IN: + x.setType( IN ); + x.setText( "{not}" + x.getText() ); + return x; + case IS_NULL: + x.setType( IS_NOT_NULL ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (IS_NULL a b) ) => (IS_NOT_NULL a b) + case IS_NOT_NULL: + x.setType( IS_NULL ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (IS_NOT_NULL a b) ) => (IS_NULL a b) + case BETWEEN: + x.setType( NOT_BETWEEN ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (BETWEEN a b) ) => (NOT_BETWEEN a b) + case NOT_BETWEEN: + x.setType( BETWEEN ); + x.setText( "{not}" + x.getText() ); + return x; // (NOT (NOT_BETWEEN a b) ) => (BETWEEN a b) +/* This can never happen because this rule will always eliminate the child NOT. + case NOT: + return x.getFirstChild(); // (NOT (NOT x) ) => (x) +*/ + default: + return super.negateNode( x ); // Just add a 'not' parent. + } + } + + /** + * Post process equality expressions, clean up the subtree. + * + * @param x The equality expression. + * @return AST - The clean sub-tree. + */ + public AST processEqualityExpression(AST x) { + if ( x == null ) { + log.warn( "processEqualityExpression() : No expression to process!" ); + return null; + } + + int type = x.getType(); + if ( type == EQ || type == NE ) { + boolean negated = type == NE; + if ( x.getNumberOfChildren() == 2 ) { + AST a = x.getFirstChild(); + AST b = a.getNextSibling(); + // (EQ NULL b) => (IS_NULL b) + if ( a.getType() == NULL && b.getType() != NULL ) { + return createIsNullParent( b, negated ); + } + // (EQ a NULL) => (IS_NULL a) + else if ( b.getType() == NULL && a.getType() != NULL ) { + return createIsNullParent( a, negated ); + } + else if ( b.getType() == EMPTY ) { + return processIsEmpty( a, negated ); + } + else { + return x; + } + } + else { + return x; + } + } + else { + return x; + } + } + + private AST createIsNullParent(AST node, boolean negated) { + node.setNextSibling( null ); + int type = negated ? IS_NOT_NULL : IS_NULL; + String text = negated ? "is not null" : "is null"; + return ASTUtil.createParent( astFactory, type, text, node ); + } + + private AST processIsEmpty(AST node, boolean negated) { + node.setNextSibling( null ); + // NOTE: Because we're using ASTUtil.createParent(), the tree must be created from the bottom up. + // IS EMPTY x => (EXISTS (QUERY (SELECT_FROM (FROM x) ) ) ) + AST ast = createSubquery( node ); + ast = ASTUtil.createParent( astFactory, EXISTS, "exists", ast ); + // Add NOT if it's negated. + if ( !negated ) { + ast = ASTUtil.createParent( astFactory, NOT, "not", ast ); + } + return ast; + } + + private AST createSubquery(AST node) { + AST ast = ASTUtil.createParent( astFactory, RANGE, "RANGE", node ); + ast = ASTUtil.createParent( astFactory, FROM, "from", ast ); + ast = ASTUtil.createParent( astFactory, SELECT_FROM, "SELECT_FROM", ast ); + ast = ASTUtil.createParent( astFactory, QUERY, "QUERY", ast ); + return ast; + } + + public void weakKeywords() throws TokenStreamException { + int t = LA( 1 ); + switch ( t ) { + case ORDER: + case GROUP: + // Case 1: Multi token keywords GROUP BY and ORDER BY + // The next token ( LT(2) ) should be 'by'... otherwise, this is just an ident. + if ( LA( 2 ) != LITERAL_by ) { + LT( 1 ).setType( IDENT ); + if ( log.isDebugEnabled() ) { + log.debug( "weakKeywords() : new LT(1) token - " + LT( 1 ) ); + } + } + break; + default: + // Case 2: The current token is after FROM and before '.'. + if (LA(0) == FROM && t != IDENT && LA(2) == DOT) { + HqlToken hqlToken = (HqlToken)LT(1); + if (hqlToken.isPossibleID()) { + hqlToken.setType(IDENT); + if ( log.isDebugEnabled() ) { + log.debug( "weakKeywords() : new LT(1) token - " + LT( 1 ) ); + } + } + } + break; + } + } + + public void processMemberOf(Token n, AST p, ASTPair currentAST) { + AST inAst = n == null ? astFactory.create( IN, "in" ) : astFactory.create( NOT_IN, "not in" ); + astFactory.makeASTRoot( currentAST, inAst ); + AST ast = createSubquery( p ); + ast = ASTUtil.createParent( astFactory, IN_LIST, "inList", ast ); + inAst.addChild( ast ); + } + +} Added: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/ParseLexer.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/ParseLexer.java 2006-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/ParseLexer.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -0,0 +1,53 @@ +package org.hibernate.hql.ast.parse; + +import org.hibernate.hql.antlr.GeneratedParseLexer; +import org.hibernate.hql.ast.HqlToken; +import org.hibernate.QueryException; + +import java.io.InputStream; +import java.io.Reader; + +import antlr.Token; + +/** + * The HQL parse lexer + * + * @author Steve Ebersole + */ +public class ParseLexer extends GeneratedParseLexer { + private boolean possibleID = false; + + public ParseLexer(InputStream in) { + super( in ); + } + + public ParseLexer(Reader in) { + super( in ); + } + + public void setTokenObjectClass(String cl) { + // Ignore the token class name parameter, and use a specific token class. + super.setTokenObjectClass( HqlToken.class.getName() ); + } + + protected void setPossibleID(boolean possibleID) { + this.possibleID = possibleID; + } + + protected Token makeToken(int i) { + HqlToken token = ( HqlToken ) super.makeToken( i ); + token.setPossibleID( possibleID ); + possibleID = false; + return token; + } + + public void panic() { + //overriden to avoid System.exit + panic( "CharScanner: panic" ); + } + + public void panic(String s) { + //overriden to avoid System.exit + throw new QueryException( s ); + } +} Added: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/PathCollector.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/PathCollector.java 2006-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/parse/PathCollector.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -0,0 +1,34 @@ +package org.hibernate.hql.ast.parse; + +import org.hibernate.hql.ast.util.NodeTraverser; +import org.hibernate.hql.antlr.ParseTokenTypes; +import antlr.collections.AST; + +/** + * Utilizes a NodeTraverser in order to collect a path from + * a (expecting dot-structure) an AST. + * + * @author Steve Ebersole + */ +public class PathCollector implements NodeTraverser.VisitationStrategy, ParseTokenTypes { + private String path = ""; + + public void visit(AST node) { + if ( node.getType() != DOT ) { + path += "." + node.getText(); + } + } + + private PathCollector() { + } + + public static String getPath(AST dotStructure) { + if ( dotStructure.getType() == IDENT ) { + return dotStructure.getText(); + } + PathCollector collector = new PathCollector(); + NodeTraverser walker = new NodeTraverser( collector ); + walker.traverseDepthFirst( dotStructure ); + return collector.path.substring( 1 ); + } +} Added: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/AbstractImplicitJoinContext.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/AbstractImplicitJoinContext.java 2006-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/AbstractImplicitJoinContext.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -0,0 +1,49 @@ +package org.hibernate.hql.ast.resolve; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Provides basic templating around how the two {@link org.hibernate.hql.ast.resolve.ImplicitJoinContext} + * method calls need to be interpreted and handled in different scenarios. + * + * @author Steve Ebersole + */ +public abstract class AbstractImplicitJoinContext implements ImplicitJoinContext { + + public static final Log log = LogFactory.getLog( AbstractImplicitJoinContext.class ); + + private PropertyPathPart prior; + + protected abstract PropertyPathPart handleRoot(String rootPathPart); + protected abstract PropertyReference handleRootAsTerminus(String pathPart); + + public final void handleIntermediatePathPart(String pathPart) { + if ( prior == null ) { + prior = handleRoot( pathPart ); + } + else { + prior = prior.handleIntermediatePathPart( pathPart ); + } + } + + public final PropertyReference handleTerminalPathPart(String pathPart) { + try { + if ( prior == null ) { + return handleRootAsTerminus( pathPart ); + } + else { + return prior.handleTerminalPathPart( pathPart ); + } + } + finally { + // clear our processing state in preparation for any future path expression + prior = null; + } + } + + protected static interface PropertyPathPart { + public PropertyPathPart handleIntermediatePathPart(String name); + public PropertyReference handleTerminalPathPart(String name); + } +} 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-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/CollectionPersisterReference.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -12,9 +12,8 @@ /** * @author Steve Ebersole */ -public class CollectionPersisterReference extends EntityPersisterReference implements ResolverAware { +public class CollectionPersisterReference extends EntityPersisterReference { - private HqlResolver resolver; private String role; private String alias; private QueryableCollection persister; @@ -26,7 +25,7 @@ public QueryableCollection getCollectionPersister() { if ( persister == null ) { - persister = ( QueryableCollection ) resolver.getSessionFactory().getCollectionPersister( role ); + persister = ( QueryableCollection ) getSessionFactory().getCollectionPersister( role ); } return persister; } @@ -34,7 +33,7 @@ public Queryable getEntityPersister() { if ( getCollectionPersister().getElementType().isEntityType() ) { EntityType elementEntityType = ( EntityType ) getCollectionPersister().getElementType(); - return ( Queryable ) elementEntityType.getAssociatedJoinable( resolver.getSessionFactory() ); + return ( Queryable ) elementEntityType.getAssociatedJoinable( getSessionFactory() ); } else { throw new QueryException( "not a collection of entities" ); @@ -65,7 +64,7 @@ EntityType elementEntityType = ( EntityType ) elementType; try { Queryable elementEntityPersister = ( Queryable ) elementEntityType - .getAssociatedJoinable( resolver.getSessionFactory() ); + .getAssociatedJoinable( getSessionFactory() ); return elementEntityPersister.getPropertyType( propertyName ); } catch( Throwable t ) { @@ -97,12 +96,9 @@ return false; } - public void setHqlResolver(HqlResolver resolver) { - this.resolver = resolver; - } - public String toString() { return "CollectionPersisterReference {role=" + getName() + ", alias=" + getAlias() + ", element-type=" + getCollectionPersister().getElementType() + "}"; } -} + +} \ No newline at end of file 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-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/EntityPersisterReference.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -3,26 +3,29 @@ import org.hibernate.type.AssociationType; import org.hibernate.type.Type; import org.hibernate.persister.entity.Queryable; +import org.hibernate.engine.SessionFactoryImplementor; +import org.hibernate.hql.ast.tree.SessionFactoryAwareNode; /** * @author Steve Ebersole */ -public class EntityPersisterReference extends PersisterReference implements ResolverAware { +public class EntityPersisterReference extends PersisterReference implements SessionFactoryAwareNode { private String entityName; private String alias; + private boolean propertyFetch; - private HqlResolver resolver; - + private SessionFactoryImplementor sessionFactory; private transient Queryable persister; - public void initialize(String entityName, String alias) { + public void initialize(String entityName, String alias, boolean propertyFetch) { this.entityName = entityName; this.alias = alias; + this.propertyFetch = propertyFetch; } public Queryable getEntityPersister() { if ( persister == null ) { - persister = ( Queryable ) resolver.getSessionFactory().getEntityPersister( entityName ); + persister = ( Queryable ) sessionFactory.getEntityPersister( entityName ); } return persister; } @@ -35,6 +38,10 @@ return alias; } + public boolean isPropertyFetch() { + return propertyFetch; + } + public AssociationType getPersisterType() { return ( AssociationType ) getEntityPersister().getType(); } @@ -56,7 +63,11 @@ return "EntityPersisterReference {entity-name=" + entityName + ", alias=" + alias + "}"; } - public void setHqlResolver(HqlResolver resolver) { - this.resolver = resolver; + public void setSessionFactory(SessionFactoryImplementor sessionFactory) { + this.sessionFactory = sessionFactory; } + + protected SessionFactoryImplementor getSessionFactory() { + return sessionFactory; + } } Added: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/FromClauseImplicitJoinContext.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/FromClauseImplicitJoinContext.java 2006-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/FromClauseImplicitJoinContext.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -0,0 +1,98 @@ +package org.hibernate.hql.ast.resolve; + +import org.hibernate.QueryException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * todo: describe FromClauseImplicitJoinContext + * + * @author Steve Ebersole + */ +public class FromClauseImplicitJoinContext extends AbstractImplicitJoinContext { + + public static final Log log = LogFactory.getLog( FromClauseImplicitJoinContext.class ); + + private final PersisterReferenceContext persisterReferenceContext; + private final PersisterReferenceBuilder persisterReferenceBuilder; + + private final JoinType joinType; + private final String alias; + private final boolean fetching; + private final boolean propertyFetching; + + public FromClauseImplicitJoinContext( + PersisterReferenceContext persisterReferenceContext, + PersisterReferenceBuilder persisterReferenceBuilder, + JoinType joinType, + String alias, + boolean fetching, + boolean propertyFetching) { + this.persisterReferenceContext = persisterReferenceContext; + this.persisterReferenceBuilder = persisterReferenceBuilder; + this.joinType = joinType; + this.alias = alias; + this.fetching = fetching; + this.propertyFetching = propertyFetching; + } + + protected PropertyPathPart handleRoot(String rootPathPart) { + log.debug( "attempting to resolve [" + rootPathPart + "] as alias" ); + PersisterReference ref = persisterReferenceContext.locatePersisterReferenceByAlias( rootPathPart ); + if ( ref == null ) { + log.debug( "attempting to resolve [" + rootPathPart + "] as unqualified property reference" ); + ref = persisterReferenceContext.locatePersisterReferenceExposingProperty( rootPathPart ); + if ( ref == null ) { + throw new QueryException( "unable to resolve path expression root [" + rootPathPart + "]" ); + } + else { + ref = ( PersisterReference ) persisterReferenceBuilder + .buildPropertyJoin( ref, rootPathPart, joinType, null, fetching, false ) + .getFirstChild(); + } + } + return new PathPart( ref ); + } + + protected PropertyReference handleRootAsTerminus(String pathPart) { + // this should only ever mean that we have a simple unqualified property reference + log.debug( "attempting to resolve [" + pathPart + "] as unqualified property reference" ); + PersisterReference ref = persisterReferenceContext.locatePersisterReferenceExposingProperty( pathPart ); + if ( ref == null ) { + throw new QueryException( "unable to resolve unqualified property reference [" + pathPart + "]" ); + } + persisterReferenceBuilder.buildPropertyJoin( ref, pathPart, joinType, alias, fetching, propertyFetching ); + + // for joins in the from clause, we dont care about the property ref... + return null; + } + + private class PathPart implements PropertyPathPart { + private final PersisterReference persisterReference; + + public PathPart(PersisterReference persisterReference) { + this.persisterReference = persisterReference; + } + + public PropertyPathPart handleIntermediatePathPart(String name) { + return new PathPart( ( PersisterReference ) buildJoin( name, null, false ).getFirstChild() ); + } + + public PropertyReference handleTerminalPathPart(String name) { + buildJoin( name, alias, propertyFetching ); + // for joins in the from clause, we dont care about the property ref... + return null; + } + + private JoinNode buildJoin(String name, String alias, boolean propertyFetching) { + return persisterReferenceBuilder.buildPropertyJoin( + persisterReference, + name, + joinType, + alias, + fetching, + propertyFetching + ); + } + } +} 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-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/HqlResolver.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -1,35 +1,42 @@ package org.hibernate.hql.ast.resolve; -import org.hibernate.hql.antlr.HqlBaseResolver; -import org.hibernate.hql.CollectionProperties; +import org.hibernate.hql.antlr.GeneratedHqlResolver; 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 antlr.collections.AST; +import java.util.LinkedList; + /** - * Adds specific semantic action behavior needed to perform useful resolution. + * Actual {@link GeneratedHqlResolver} implementation used in the query translator + * providing semantic action implementation. * * @author Joshua Davis * @author Steve Ebersole */ -public class HqlResolver extends HqlBaseResolver { +public class HqlResolver extends GeneratedHqlResolver implements HqlResolverASTFactory.Context { private static Log log = LogFactory.getLog( HqlResolver.class ); private final SessionFactoryImplementor sessionFactory; + private final PersisterReferenceBuilder persisterReferenceBuilder; + private StatementNode currentStatement; + private ImplicitJoinContextTracker implicitJoinContextTracker = new ImplicitJoinContextTracker(); public HqlResolver(SessionFactoryImplementor sessionFactory) { super(); this.sessionFactory = sessionFactory; setASTFactory( new HqlResolverASTFactory( this ) ); + persisterReferenceBuilder = new PersisterReferenceBuilder( getASTFactory(), sessionFactory ); } + + // HqlResolverASTFactory.Context implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + public SessionFactoryImplementor getSessionFactory() { return sessionFactory; } @@ -38,115 +45,94 @@ return currentStatement; } + + // semantic action implementations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + /** + * Semantic action called whenever we start a new statement (i.e top-level statement vs. subquery) + * + * @param statementNode The statement we are starting to process. + */ protected void pushStatement(AST statementNode) { log.trace( "pushing new statement context : " + currentStatement + " -> " + statementNode ); StatementNode statement = ( StatementNode ) statementNode; + statement.setPersisterReferenceBuilder( persisterReferenceBuilder ); + implicitJoinContextTracker.push( + new NormalImplicitJoinContext( statement, persisterReferenceBuilder, getASTFactory() ) + ); if ( currentStatement != null ) { currentStatement.pushChild( statement ); } currentStatement = statement; } + /** + * Semantic action called whenever we complete processing a statement. + */ protected void popStatement() { log.trace( "popping statement context : " + currentStatement + " -> " + currentStatement.getParentStatement() ); currentStatement = currentStatement.getParentStatement(); + implicitJoinContextTracker.pop(); } - protected AST buildEntityPersisterReference(String entityName, AST alias) { - return currentStatement.buildEntityPersisterReference( entityName, alias == null ? null : alias.getText() ); + protected void pushExplicitJoinContext(AST joinTypeNode, AST fetch, AST alias, AST propertyFetch) { + log.debug( "pushing explicit (from-clause) implicit join context" ); + implicitJoinContextTracker.push( + new FromClauseImplicitJoinContext( + currentStatement, + persisterReferenceBuilder, + resolveJoinType( joinTypeNode ), + textOrNull( alias ), + fetch != null, + propertyFetch != null + ) + ); } - private JoinNode createJoinNode(JoinType type, JoinSource source, boolean fetch, PersisterReference rhs) { - JoinNode node = ( JoinNode ) getASTFactory().create( JOIN, "join" ); - node.initialize( type, source, fetch, rhs ); - rhs.addChild( node ); - return node; + protected void popExplicitJoinContext() { + log.debug( "popping implicit join context" ); + implicitJoinContextTracker.pop(); } /** - * 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> + * Semantic action called to perform generation of an {@link EntityPersisterReference} + * representing a "root" persister reference. * - * @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. + * @param entityName The name of the entity. + * @param alias An (optional) alias for later qualification-resolution to the generated + * persister reference. + * @param propertyFetch Was property fetching explicitly specified. + * @return The generated reference. */ - 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; + protected AST buildEntityPersisterReference(AST entityName, AST alias, AST propertyFetch) { + return buildEntityPersisterReference( entityName.getText(), textOrNull( alias ), propertyFetch != 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(); - } - 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(); - } + private EntityPersisterReference buildEntityPersisterReference(String entityName, String alias, boolean propertyFetching) { +// return currentStatement.buildEntityPersisterReference( entityName, alias, propertyFetching ); + return persisterReferenceBuilder.buildEntityPersisterReference( entityName, alias, propertyFetching ); + } - // 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; + private String textOrNull(AST node) { + return node == null ? null : node.getText(); + } - JoinNode joinNode = createJoinNode( joinType, joinSource, relationFetch, lhs ); - PersisterReference rhs = currentStatement.getPersisterReference( lhs, next.getText(), isLast ? alias : null ); - joinNode.addChild( rhs ); + private JoinNode createJoinNode(JoinType type, JoinSource source, boolean fetch, PersisterReference rhs) { + JoinNode node = ( JoinNode ) getASTFactory().create( JOIN, "join" ); + node.initialize( type, source, fetch, rhs ); + rhs.addChild( node ); + return node; + } - if ( isLast && withClauseNode != null ) { - joinNode.addChild( withClauseNode ); - } - if ( firstPass ) { - firstPass = false; - joinSource = JoinSource.IMPLICIT; - } + protected void handleIntermediatePathPart(AST name) { + log.debug( "handling intermediate path part [" + name.getText() + "]" ); + implicitJoinContextTracker.getCurrent().handleIntermediatePathPart( name.getText() ); + } - lhs = rhs; - next = next.getNextSibling(); - } - + protected AST handleTerminalPathPart(AST name) { + log.debug( "handling terminal path part [" + name.getText() + "]" ); + return implicitJoinContextTracker.getCurrent().handleTerminalPathPart( name.getText() ); } protected void handleAdHocJoinNode(AST persisterReference, AST joinType, AST onClause) { @@ -175,23 +161,6 @@ 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; - } - catch( Throwable t ) { - // ignore it... - } - return false; - } - protected String reconstitutePathString(AST propertyReference) { AST child = propertyReference.getFirstChild(); String prefix = ""; @@ -204,265 +173,22 @@ return buffer.toString(); } - protected AST resolveAtomicPropertyReference(AST propertyNode) { - EntityPersisterReference persisterReference = resolveRootAsUnqualified( propertyNode.getText() ); - return generatePropertyReference( persisterReference, propertyNode.getText() ); - } + private class ImplicitJoinContextTracker { + private LinkedList stack = new LinkedList(); - 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(); + public ImplicitJoinContextTracker() { } - 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(); + public void push(ImplicitJoinContext context) { + stack.addFirst( context ); } - if ( next == null ) { - throw new QueryException( "illegal parser state" ); + public void pop() { + stack.removeFirst(); } - 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 ); + public ImplicitJoinContext getCurrent() { + return ( ImplicitJoinContext ) stack.getFirst(); } - 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-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/HqlResolverASTFactory.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -1,24 +1,33 @@ package org.hibernate.hql.ast.resolve; -import org.hibernate.hql.antlr.HqlRTokenTypes; +import org.hibernate.hql.antlr.ResolveTokenTypes; import org.hibernate.hql.ast.HqlASTFactory; +import org.hibernate.hql.ast.tree.SessionFactoryAwareNode; +import org.hibernate.hql.ast.tree.PersisterReferenceContextAwareNode; +import org.hibernate.hql.ast.tree.ASTFactoryAwareNode; import org.hibernate.engine.SessionFactoryImplementor; import antlr.collections.AST; import antlr.Token; /** * AST factory for the resolver phase. - * <br>User: Joshua Davis - * Date: Apr 3, 2006 - * Time: 7:58:16 AM + * + * @author Joshua Davis + * @author Steve Ebersole */ -public class HqlResolverASTFactory extends HqlASTFactory implements HqlRTokenTypes { - private final HqlResolver resolver; +public class HqlResolverASTFactory extends HqlASTFactory implements ResolveTokenTypes { - public HqlResolverASTFactory(HqlResolver resolver) { - this.resolver = resolver; + public static interface Context { + public SessionFactoryImplementor getSessionFactory(); + public PersisterReferenceContext getPersisterReferenceContext(); } + private final Context context; + + public HqlResolverASTFactory(Context context) { + this.context = context; + } + public Class getASTNodeType(int tokenType) { // Statement nodes: switch (tokenType) { @@ -55,11 +64,14 @@ } private void prepare(AST node) { - if ( node instanceof SessionFactoryAwareNode ) { - ( ( SessionFactoryAwareNode ) node ).setSessionFactory( resolver.getSessionFactory() ); + if ( node instanceof org.hibernate.hql.ast.tree.SessionFactoryAwareNode ) { + ( ( SessionFactoryAwareNode ) node ).setSessionFactory( context.getSessionFactory() ); } - if ( node instanceof ResolverAware ) { - ( ( ResolverAware ) node ).setHqlResolver( resolver ); + if ( node instanceof PersisterReferenceContextAwareNode ) { + ( ( PersisterReferenceContextAwareNode ) node ).setPersisterReferenceContext( context.getPersisterReferenceContext() ); } + if ( node instanceof ASTFactoryAwareNode ) { + ( ( ASTFactoryAwareNode ) node ).setASTFactory( this ); + } } } Added: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ImplicitJoinContext.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ImplicitJoinContext.java 2006-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/ImplicitJoinContext.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -0,0 +1,11 @@ +package org.hibernate.hql.ast.resolve; + +/** + * Contract for how implicit joins are handled. + * + * @author Steve Ebersole + */ +public interface ImplicitJoinContext { + public void handleIntermediatePathPart(String pathPart); + public PropertyReference handleTerminalPathPart(String pathPart); +} Added: branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/NormalImplicitJoinContext.java =================================================================== --- branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/NormalImplicitJoinContext.java 2006-06-30 05:54:36 UTC (rev 10069) +++ branches/HQL_ANTLR_2/Hibernate3/src/org/hibernate/hql/ast/resolve/NormalImplicitJoinContext.java 2006-06-30 05:55:55 UTC (rev 10070) @@ -0,0 +1,236 @@ +package org.hibernate.hql.ast.resolve; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.hql.antlr.ResolveTokenTypes; +import org.hibernate.hql.CollectionProperties; +import org.hibernate.QueryException; +import org.hibernate.type.Type; +import org.hibernate.type.ComponentType; +import antlr.ASTFactory; +import antlr.collections.AST; + +/** + * Defines the behavior of how implicit joins are normally handled. + * <p/> + * All other implementations of {@link org.hibernate.hql.ast.resolve.ImplicitJoinContext} are + * considered special cases. + * + * @author Steve Ebersole + */ +public class NormalImplicitJoinContext extends AbstractImplicitJoinContext { + + public static final Log log = LogFactory.getLog( NormalImplicitJoinContext.class ); + + private final PersisterReferenceContext persisterReferenceContext; + private final PersisterReferenceBuilder persisterReferenceBuilder; + private final ASTFactory astFactory; + + private PropertyPathPart prior; + + public NormalImplicitJoinContext( + PersisterReferenceContext persisterReferenceContext, + PersisterReferenceBuilder persisterReferenceBuilder, + ASTFactory astFactory) { + this.persisterReferenceContext = persisterReferenceContext; + this.persisterReferenceBuilder = persisterReferenceBuilder; + this.astFactory = astFactory; + } + + protected PropertyPathPart handleRoot(String pathPart) { + PersisterReference persisterReference = resolveAsAlias( pathPart ); + if ( persisterReference != null ) { + return new PropertyPathRoot( ( EntityPersisterReference ) persisterReference ); + } + + persisterReference = resolveAsUnqualified( pathPart ); + if ( persisterReference != null ) { + return new EntityPropertyReference( ( EntityPersisterReference ) persisterReference, pathPart, false ); + } + + throw new QueryException( "unable to resolve path expression root [" + pathPart + "]" ); + } + + protected PropertyReference handleRootAsTerminus(String pathPart) { + PersisterReference ref = resolveAsUnqualified( pathPart ); + return generatePropertyReference( ref, pathPart ); + } + + + + + private PropertyReference generatePropertyReference(PersisterReference persisterReference, String propertyName) { + PropertyReference propertyReferenceNode = ( PropertyReference ) astFactory.create( ResolveTokenTypes.PROPERTY_REF, persisterReference.getAlias() + "." + propertyName ); + + AST aliasNode = astFactory.create( ResolveTokenTypes.ALIAS, persisterReference.getAlias() ); + propertyReferenceNode.addChild( aliasNode ); + + AST propertyNameNode = astFactory.create( ResolveTokenTypes.IDENT, propertyName ); + propertyReferenceNode.addChild( propertyNameNode ); + + return propertyReferenceNode; + } + + private EntityPersisterReference resolveAsAlias(String name) { + return ( EntityPersisterReference ) persisterReferenceContext.locatePersisterReferenceByAlias( name ); + } + + private EntityPersisterReference resolveAsUnqualified(String firstPathExpression) { + return persisterReferenceContext.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 + "]" ); + } + + 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 handleTerminalPathPart(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 handleTerminalPathPart(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.compone... [truncated message content] |