KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > hql > ast > HqlParser


1 // $Id: HqlParser.java,v 1.30 2005/07/16 17:28:40 epbernard Exp $
2
package org.hibernate.hql.ast;
3
4 import java.io.PrintStream JavaDoc;
5 import java.io.PrintWriter JavaDoc;
6 import java.io.StringReader JavaDoc;
7
8 import antlr.ASTPair;
9 import antlr.MismatchedTokenException;
10 import antlr.RecognitionException;
11 import antlr.Token;
12 import antlr.TokenStream;
13 import antlr.TokenStreamException;
14 import antlr.collections.AST;
15 import org.apache.commons.logging.Log;
16 import org.apache.commons.logging.LogFactory;
17 import org.hibernate.hql.antlr.HqlBaseParser;
18 import org.hibernate.hql.antlr.HqlTokenTypes;
19 import org.hibernate.hql.ast.util.ASTPrinter;
20 import org.hibernate.hql.ast.util.ASTUtil;
21 import org.hibernate.QueryException;
22
23 /**
24  * Implements the semantic action methods defined in the HQL base parser to keep the grammar
25  * source file a little cleaner. Extends the parser class generated by ANTLR.
26  *
27  * @author Joshua Davis (pgmjsd@sourceforge.net)
28  */

29 public final class HqlParser extends HqlBaseParser {
30     /**
31      * A logger for this class.
32      */

33     private static final Log log = LogFactory.getLog( HqlParser.class );
34
35     private ParseErrorHandler parseErrorHandler;
36     private ASTPrinter printer = getASTPrinter();
37
38     private static ASTPrinter getASTPrinter() {
39         return new ASTPrinter( org.hibernate.hql.antlr.HqlTokenTypes.class );
40     }
41
42     public static HqlParser getInstance(String JavaDoc hql) {
43 // HqlLexer lexer = new HqlLexer( new DataInputStream( new ByteArrayInputStream( hql.getBytes() ) ) );
44
// [jsd] The fix for HHH-558...
45
HqlLexer lexer = new HqlLexer( new StringReader JavaDoc( hql ) );
46         HqlParser parser = new HqlParser( lexer );
47         return parser;
48     }
49
50     private HqlParser(TokenStream lexer) {
51         super( lexer );
52         initialize();
53     }
54
55     public void reportError(RecognitionException e) {
56         parseErrorHandler.reportError( e ); // Use the delegate.
57
}
58
59     public void reportError(String JavaDoc s) {
60         parseErrorHandler.reportError( s ); // Use the delegate.
61
}
62
63     public void reportWarning(String JavaDoc s) {
64         parseErrorHandler.reportWarning( s );
65     }
66
67     public ParseErrorHandler getParseErrorHandler() {
68         return parseErrorHandler;
69     }
70
71     /**
72      * Overrides the base behavior to retry keywords as identifiers.
73      *
74      * @param token The token.
75      * @param ex The recognition exception.
76      * @return AST - The new AST.
77      * @throws antlr.RecognitionException if the substitution was not possible.
78      * @throws antlr.TokenStreamException if the substitution was not possible.
79      */

80     public AST handleIdentifierError(Token token, RecognitionException ex) throws RecognitionException, TokenStreamException {
81         // If the token can tell us if it could be an identifier...
82
if ( token instanceof HqlToken ) {
83             HqlToken hqlToken = ( HqlToken ) token;
84             // ... and the token could be an identifer and the error is
85
// a mismatched token error ...
86
if ( hqlToken.isPossibleID() && ( ex instanceof MismatchedTokenException ) ) {
87                 MismatchedTokenException mte = ( MismatchedTokenException ) ex;
88                 // ... and the expected token type was an identifier, then:
89
if ( mte.expecting == HqlTokenTypes.IDENT ) {
90                     // Use the token as an identifier.
91
reportWarning( "Keyword '"
92                             + token.getText()
93                             + "' is being interpreted as an identifier due to: " + mte.getMessage() );
94                     // Add the token to the AST.
95
ASTPair currentAST = new ASTPair();
96                     token.setType( HqlTokenTypes.WEIRD_IDENT );
97                     astFactory.addASTChild( currentAST, astFactory.create( token ) );
98                     consume();
99                     AST identifierAST = currentAST.root;
100                     return identifierAST;
101                 }
102             } // if
103
} // if
104
// Otherwise, handle the error normally.
105
return super.handleIdentifierError( token, ex );
106     }
107
108     /**
109      * Returns an equivalent tree for (NOT (a relop b) ), for example:<pre>
110      * (NOT (GT a b) ) => (LE a b)
111      * </pre>
112      *
113      * @param x The sub tree to transform, the parent is assumed to be NOT.
114      * @return AST - The equivalent sub-tree.
115      */

116     public AST negateNode(AST x) {
117 // if ( log.isDebugEnabled() )
118
// log.debug( printer.showAsString( x, "negateNode()") );
119
switch ( x.getType() ) {
120             case OR:
121                 x.setType(AND);
122                 x.setText("{and}");
123                 negateNode( x.getFirstChild() );
124                 negateNode( x.getFirstChild().getNextSibling() );
125                 return x;
126             case AND:
127                 x.setType(OR);
128                 x.setText("{or}");
129                 negateNode( x.getFirstChild() );
130                 negateNode( x.getFirstChild().getNextSibling() );
131                 return x;
132             case EQ:
133                 x.setType( NE );
134                 x.setText( "{not}" + x.getText() );
135                 return x; // (NOT (EQ a b) ) => (NE a b)
136
case NE:
137                 x.setType( EQ );
138                 x.setText( "{not}" + x.getText() );
139                 return x; // (NOT (NE a b) ) => (EQ a b)
140
case GT:
141                 x.setType( LE );
142                 x.setText( "{not}" + x.getText() );
143                 return x; // (NOT (GT a b) ) => (LE a b)
144
case LT:
145                 x.setType( GE );
146                 x.setText( "{not}" + x.getText() );
147                 return x; // (NOT (LT a b) ) => (GE a b)
148
case GE:
149                 x.setType( LT );
150                 x.setText( "{not}" + x.getText() );
151                 return x; // (NOT (GE a b) ) => (LT a b)
152
case LE:
153                 x.setType( GT );
154                 x.setText( "{not}" + x.getText() );
155                 return x; // (NOT (LE a b) ) => (GT a b)
156
case LIKE:
157                 x.setType( NOT_LIKE );
158                 x.setText( "{not}" + x.getText() );
159                 return x; // (NOT (LIKE a b) ) => (NOT_LIKE a b)
160
case NOT_LIKE:
161                 x.setType( LIKE );
162                 x.setText( "{not}" + x.getText() );
163                 return x; // (NOT (NOT_LIKE a b) ) => (LIKE a b)
164
case IS_NULL:
165                 x.setType( IS_NOT_NULL );
166                 x.setText( "{not}" + x.getText() );
167                 return x; // (NOT (IS_NULL a b) ) => (IS_NOT_NULL a b)
168
case IS_NOT_NULL:
169                 x.setType( IS_NULL );
170                 x.setText( "{not}" + x.getText() );
171                 return x; // (NOT (IS_NOT_NULL a b) ) => (IS_NULL a b)
172
case BETWEEN:
173                 x.setType( NOT_BETWEEN );
174                 x.setText( "{not}" + x.getText() );
175                 return x; // (NOT (BETWEEN a b) ) => (NOT_BETWEEN a b)
176
case NOT_BETWEEN:
177                 x.setType( BETWEEN );
178                 x.setText( "{not}" + x.getText() );
179                 return x; // (NOT (NOT_BETWEEN a b) ) => (BETWEEN a b)
180
/* This can never happen because this rule will always eliminate the child NOT.
181             case NOT:
182                 return x.getFirstChild(); // (NOT (NOT x) ) => (x)
183 */

184             default:
185                 return super.negateNode( x ); // Just add a 'not' parent.
186
}
187     }
188
189     /**
190      * Post process equality expressions, clean up the subtree.
191      *
192      * @param x The equality expression.
193      * @return AST - The clean sub-tree.
194      */

195     public AST processEqualityExpression(AST x) {
196         if ( x == null ) {
197             log.warn( "processEqualityExpression() : No expression to process!" );
198             return null;
199         }
200
201         int type = x.getType();
202         if ( type == EQ || type == NE ) {
203             boolean negated = type == NE;
204             if ( x.getNumberOfChildren() == 2 ) {
205                 AST a = x.getFirstChild();
206                 AST b = a.getNextSibling();
207                 // (EQ NULL b) => (IS_NULL b)
208
if ( a.getType() == NULL && b.getType() != NULL ) {
209                     return createIsNullParent( b, negated );
210                 }
211                 // (EQ a NULL) => (IS_NULL a)
212
else if ( b.getType() == NULL && a.getType() != NULL ) {
213                     return createIsNullParent( a, negated );
214                 }
215                 else if ( b.getType() == EMPTY ) {
216                     return processIsEmpty( a, negated );
217                 }
218                 else {
219                     return x;
220                 }
221             }
222             else {
223                 return x;
224             }
225         }
226         else {
227             return x;
228         }
229     }
230
231     private AST createIsNullParent(AST node, boolean negated) {
232         node.setNextSibling( null );
233         int type = negated ? IS_NOT_NULL : IS_NULL;
234         String JavaDoc text = negated ? "is not null" : "is null";
235         return ASTUtil.createParent( astFactory, type, text, node );
236     }
237
238     private AST processIsEmpty(AST node, boolean negated) {
239         node.setNextSibling( null );
240         // NOTE: Because we're using ASTUtil.createParent(), the tree must be created from the bottom up.
241
// IS EMPTY x => (EXISTS (QUERY (SELECT_FROM (FROM x) ) ) )
242
AST ast = createSubquery( node );
243         ast = ASTUtil.createParent( astFactory, EXISTS, "exists", ast );
244         // Add NOT if it's negated.
245
if ( !negated ) {
246             ast = ASTUtil.createParent( astFactory, NOT, "not", ast );
247         }
248         return ast;
249     }
250
251     private AST createSubquery(AST node) {
252         AST ast = ASTUtil.createParent( astFactory, RANGE, "RANGE", node );
253         ast = ASTUtil.createParent( astFactory, FROM, "from", ast );
254         ast = ASTUtil.createParent( astFactory, SELECT_FROM, "SELECT_FROM", ast );
255         ast = ASTUtil.createParent( astFactory, QUERY, "QUERY", ast );
256         return ast;
257     }
258
259     public void showAst(AST ast, PrintStream JavaDoc out) {
260         showAst( ast, new PrintWriter JavaDoc( out ) );
261     }
262
263     private void showAst(AST ast, PrintWriter JavaDoc pw) {
264         printer.showAst( ast, pw );
265     }
266
267     private void initialize() {
268         // Initialize the error handling delegate.
269
parseErrorHandler = new ErrorCounter();
270     }
271
272     public void weakKeywords() throws TokenStreamException {
273
274         int t = LA( 1 );
275         switch ( t ) {
276             case ORDER:
277             case GROUP:
278                 // Case 1: Multi token keywords GROUP BY and ORDER BY
279
// The next token ( LT(2) ) should be 'by'... otherwise, this is just an ident.
280
if ( LA( 2 ) != LITERAL_by ) {
281                     LT( 1 ).setType( IDENT );
282                     if ( log.isDebugEnabled() ) {
283                         log.debug( "weakKeywords() : new LT(1) token - " + LT( 1 ) );
284                     }
285                 }
286                 break;
287             default:
288                 // Case 2: The current token is after FROM and before '.'.
289
if (LA(0) == FROM && t != IDENT && LA(2) == DOT) {
290                     HqlToken hqlToken = (HqlToken)LT(1);
291                     if (hqlToken.isPossibleID()) {
292                         hqlToken.setType(IDENT);
293                         if ( log.isDebugEnabled() ) {
294                             log.debug( "weakKeywords() : new LT(1) token - " + LT( 1 ) );
295                         }
296                     }
297                 }
298                 break;
299         }
300     }
301
302     public void handleDotIdent() throws TokenStreamException {
303         // This handles HHH-354, where there is a strange property name in a where clause.
304
// If the lookahead contains a DOT then something that isn't an IDENT...
305
if (LA(1) == DOT && LA(2) != IDENT) {
306             // See if the second lookahed token can be an identifier.
307
HqlToken t = (HqlToken)LT(2);
308             if (t.isPossibleID())
309             {
310                 // Set it!
311
LT( 2 ).setType( IDENT );
312                 if ( log.isDebugEnabled() ) {
313                     log.debug( "handleDotIdent() : new LT(2) token - " + LT( 1 ) );
314                 }
315             }
316         }
317     }
318
319     public void processMemberOf(Token n, AST p, ASTPair currentAST) {
320         AST inAst = n == null ? astFactory.create( IN, "in" ) : astFactory.create( NOT_IN, "not in" );
321         astFactory.makeASTRoot( currentAST, inAst );
322         AST ast = createSubquery( p );
323         ast = ASTUtil.createParent( astFactory, IN_LIST, "inList", ast );
324         inAst.addChild( ast );
325     }
326
327     static public void panic() {
328         //overriden to avoid System.exit
329
throw new QueryException("Parser: panic");
330     }
331 }
332
Popular Tags