FileDocCategorySizeDatePackage
HqlParser.javaAPI DocHibernate 3.2.510856Thu Oct 27 06:43:20 BST 2005org.hibernate.hql.ast

HqlParser

public final class HqlParser extends org.hibernate.hql.antlr.HqlBaseParser
Implements the semantic action methods defined in the HQL base parser to keep the grammar source file a little cleaner. Extends the parser class generated by ANTLR.
author
Joshua Davis (pgmjsd@sourceforge.net)

Fields Summary
private static final Log
log
A logger for this class.
private ParseErrorHandler
parseErrorHandler
private org.hibernate.hql.ast.util.ASTPrinter
printer
Constructors Summary
private HqlParser(antlr.TokenStream lexer)

		super( lexer );
		initialize();
	
Methods Summary
private antlr.collections.ASTcreateIsNullParent(antlr.collections.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 antlr.collections.ASTcreateSubquery(antlr.collections.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;
	
private static org.hibernate.hql.ast.util.ASTPrintergetASTPrinter()


	    
		return new ASTPrinter( org.hibernate.hql.antlr.HqlTokenTypes.class );
	
public static org.hibernate.hql.ast.HqlParsergetInstance(java.lang.String hql)

        // [jsd] The fix for HHH-558...
        HqlLexer lexer = new HqlLexer( new StringReader( hql ) );
		return new HqlParser( lexer );
	
public ParseErrorHandlergetParseErrorHandler()

		return parseErrorHandler;
	
public voidhandleDotIdent()

        // 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.
            HqlToken t = (HqlToken)LT(2);
            if (t.isPossibleID())
            {
                // Set it!
                LT( 2 ).setType( IDENT );
                if ( log.isDebugEnabled() ) {
                    log.debug( "handleDotIdent() : new LT(2) token - " + LT( 1 ) );
                }
            }
        }
    
public antlr.collections.ASThandleIdentifierError(antlr.Token token, antlr.RecognitionException ex)
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.

		// 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 == HqlTokenTypes.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( HqlTokenTypes.WEIRD_IDENT );
					astFactory.addASTChild( currentAST, astFactory.create( token ) );
					consume();
					AST identifierAST = currentAST.root;
					return identifierAST;
				}
			} // if
		} // if
		// Otherwise, handle the error normally.
		return super.handleIdentifierError( token, ex );
	
private voidinitialize()

		// Initialize the error handling delegate.
		parseErrorHandler = new ErrorCounter();
		setASTFactory(new HqlASTFactory());	// Create nodes that track line and column number.
	
public antlr.collections.ASTnegateNode(antlr.collections.AST x)
Returns an equivalent tree for (NOT (a relop b) ), for example:
(NOT (GT a b) ) => (LE a b)

param
x The sub tree to transform, the parent is assumed to be NOT.
return
AST - The equivalent sub-tree.

		//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.
		}
	
public static voidpanic()

		//overriden to avoid System.exit
		throw new QueryException("Parser: panic");
	
public antlr.collections.ASTprocessEqualityExpression(antlr.collections.AST x)
Post process equality expressions, clean up the subtree.

param
x The equality expression.
return
AST - The clean sub-tree.

		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 antlr.collections.ASTprocessIsEmpty(antlr.collections.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;
	
public voidprocessMemberOf(antlr.Token n, antlr.collections.AST p, antlr.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 );
	
public voidreportError(antlr.RecognitionException e)

		parseErrorHandler.reportError( e ); // Use the delegate.
	
public voidreportError(java.lang.String s)

		parseErrorHandler.reportError( s ); // Use the delegate.
	
public voidreportWarning(java.lang.String s)

		parseErrorHandler.reportWarning( s );
	
public voidshowAst(antlr.collections.AST ast, java.io.PrintStream out)

		showAst( ast, new PrintWriter( out ) );
	
private voidshowAst(antlr.collections.AST ast, java.io.PrintWriter pw)

		printer.showAst( ast, pw );
	
public voidweakKeywords()


		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;
		}