KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > hql > classic > WhereParser


1 //$Id: WhereParser.java,v 1.9 2005/03/30 16:51:18 oneovthafew Exp $
2
package org.hibernate.hql.classic;
3
4 import java.util.HashMap JavaDoc;
5 import java.util.HashSet JavaDoc;
6 import java.util.LinkedList JavaDoc;
7 import java.util.Map JavaDoc;
8 import java.util.Set JavaDoc;
9 import java.util.StringTokenizer JavaDoc;
10
11 import org.hibernate.MappingException;
12 import org.hibernate.QueryException;
13 import org.hibernate.engine.JoinSequence;
14 import org.hibernate.hql.QueryTranslator;
15 import org.hibernate.persister.collection.CollectionPropertyNames;
16 import org.hibernate.persister.entity.Queryable;
17 import org.hibernate.sql.InFragment;
18 import org.hibernate.type.EntityType;
19 import org.hibernate.type.LiteralType;
20 import org.hibernate.type.Type;
21 import org.hibernate.type.TypeFactory;
22 import org.hibernate.util.ReflectHelper;
23 import org.hibernate.util.StringHelper;
24
25 /**
26  * Parses the where clause of a hibernate query and translates it to an
27  * SQL where clause.
28  */

29
30 // We should reengineer this class so that, rather than the current ad -
31
// hoc linear approach to processing a stream of tokens, we instead
32
// build up a tree of expressions.
33

34 // We would probably refactor to have LogicParser (builds a tree of simple
35
// expressions connected by and, or, not), ExpressionParser (translates
36
// from OO terms like foo, foo.Bar, foo.Bar.Baz to SQL terms like
37
// FOOS.ID, FOOS.BAR_ID, etc) and PathExpressionParser (which does much
38
// the same thing it does now)
39

40 public class WhereParser implements Parser {
41
42     private final PathExpressionParser pathExpressionParser;
43
44     {
45         pathExpressionParser = new PathExpressionParser();
46         pathExpressionParser.setUseThetaStyleJoin( true ); //Need this, since join condition can appear inside parens!
47
}
48
49     private static final Set JavaDoc EXPRESSION_TERMINATORS = new HashSet JavaDoc(); //tokens that close a sub expression
50
private static final Set JavaDoc EXPRESSION_OPENERS = new HashSet JavaDoc(); //tokens that open a sub expression
51
private static final Set JavaDoc BOOLEAN_OPERATORS = new HashSet JavaDoc(); //tokens that would indicate a sub expression is a boolean expression
52
private static final Map JavaDoc NEGATIONS = new HashMap JavaDoc();
53
54     static {
55         EXPRESSION_TERMINATORS.add( "and" );
56         EXPRESSION_TERMINATORS.add( "or" );
57         EXPRESSION_TERMINATORS.add( ")" );
58         //expressionTerminators.add(","); // deliberately excluded
59

60         EXPRESSION_OPENERS.add( "and" );
61         EXPRESSION_OPENERS.add( "or" );
62         EXPRESSION_OPENERS.add( "(" );
63         //expressionOpeners.add(","); // deliberately excluded
64

65         BOOLEAN_OPERATORS.add( "<" );
66         BOOLEAN_OPERATORS.add( "=" );
67         BOOLEAN_OPERATORS.add( ">" );
68         BOOLEAN_OPERATORS.add( "#" );
69         BOOLEAN_OPERATORS.add( "~" );
70         BOOLEAN_OPERATORS.add( "like" );
71         BOOLEAN_OPERATORS.add( "ilike" );
72         BOOLEAN_OPERATORS.add( "regexp" );
73         BOOLEAN_OPERATORS.add( "rlike" );
74         BOOLEAN_OPERATORS.add( "is" );
75         BOOLEAN_OPERATORS.add( "in" );
76         BOOLEAN_OPERATORS.add( "any" );
77         BOOLEAN_OPERATORS.add( "some" );
78         BOOLEAN_OPERATORS.add( "all" );
79         BOOLEAN_OPERATORS.add( "exists" );
80         BOOLEAN_OPERATORS.add( "between" );
81         BOOLEAN_OPERATORS.add( "<=" );
82         BOOLEAN_OPERATORS.add( ">=" );
83         BOOLEAN_OPERATORS.add( "=>" );
84         BOOLEAN_OPERATORS.add( "=<" );
85         BOOLEAN_OPERATORS.add( "!=" );
86         BOOLEAN_OPERATORS.add( "<>" );
87         BOOLEAN_OPERATORS.add( "!#" );
88         BOOLEAN_OPERATORS.add( "!~" );
89         BOOLEAN_OPERATORS.add( "!<" );
90         BOOLEAN_OPERATORS.add( "!>" );
91         BOOLEAN_OPERATORS.add( "is not" );
92         BOOLEAN_OPERATORS.add( "not like" );
93         BOOLEAN_OPERATORS.add( "not ilike" );
94         BOOLEAN_OPERATORS.add( "not regexp" );
95         BOOLEAN_OPERATORS.add( "not rlike" );
96         BOOLEAN_OPERATORS.add( "not in" );
97         BOOLEAN_OPERATORS.add( "not between" );
98         BOOLEAN_OPERATORS.add( "not exists" );
99
100         NEGATIONS.put( "and", "or" );
101         NEGATIONS.put( "or", "and" );
102         NEGATIONS.put( "<", ">=" );
103         NEGATIONS.put( "=", "<>" );
104         NEGATIONS.put( ">", "<=" );
105         NEGATIONS.put( "#", "!#" );
106         NEGATIONS.put( "~", "!~" );
107         NEGATIONS.put( "like", "not like" );
108         NEGATIONS.put( "ilike", "not ilike" );
109         NEGATIONS.put( "regexp", "not regexp" );
110         NEGATIONS.put( "rlike", "not rlike" );
111         NEGATIONS.put( "is", "is not" );
112         NEGATIONS.put( "in", "not in" );
113         NEGATIONS.put( "exists", "not exists" );
114         NEGATIONS.put( "between", "not between" );
115         NEGATIONS.put( "<=", ">" );
116         NEGATIONS.put( ">=", "<" );
117         NEGATIONS.put( "=>", "<" );
118         NEGATIONS.put( "=<", ">" );
119         NEGATIONS.put( "!=", "=" );
120         NEGATIONS.put( "<>", "=" );
121         NEGATIONS.put( "!#", "#" );
122         NEGATIONS.put( "!~", "~" );
123         NEGATIONS.put( "!<", "<" );
124         NEGATIONS.put( "!>", ">" );
125         NEGATIONS.put( "is not", "is" );
126         NEGATIONS.put( "not like", "like" );
127         NEGATIONS.put( "not ilike", "ilike" );
128         NEGATIONS.put( "not regexp", "regexp" );
129         NEGATIONS.put( "not rlike", "rlike" );
130         NEGATIONS.put( "not in", "in" );
131         NEGATIONS.put( "not between", "between" );
132         NEGATIONS.put( "not exists", "exists" );
133
134     }
135     // Handles things like:
136
// a and b or c
137
// a and ( b or c )
138
// not a and not b
139
// not ( a and b )
140
// x between y and z (overloaded "and")
141
// x in ( a, b, c ) (overloaded brackets)
142
// not not a
143
// a is not null (overloaded "not")
144
// etc......
145
// and expressions like
146
// foo = bar (maps to: foo.id = bar.id)
147
// foo.Bar = 'foo' (maps to: foo.bar = 'foo')
148
// foo.Bar.Baz = 1.0 (maps to: foo.bar = bar.id and bar.baz = 1.0)
149
// 1.0 = foo.Bar.Baz (maps to: bar.baz = 1.0 and foo.Bar = bar.id)
150
// foo.Bar.Baz = a.B.C (maps to: bar.Baz = b.C and foo.Bar = bar.id and a.B = b.id)
151
// foo.Bar.Baz + a.B.C (maps to: bar.Baz + b.C and foo.Bar = bar.id and a.B = b.id)
152
// ( foo.Bar.Baz + 1.0 ) < 2.0 (maps to: ( bar.Baz + 1.0 ) < 2.0 and foo.Bar = bar.id)
153

154     private boolean betweenSpecialCase = false; //Inside a BETWEEN ... AND ... expression
155
private boolean negated = false;
156
157     private boolean inSubselect = false;
158     private int bracketsSinceSelect = 0;
159     private StringBuffer JavaDoc subselect;
160
161     private boolean expectingPathContinuation = false;
162     private int expectingIndex = 0;
163
164     // The following variables are stacks that keep information about each subexpression
165
// in the list of nested subexpressions we are currently processing.
166

167     private LinkedList JavaDoc nots = new LinkedList JavaDoc(); //were an odd or even number of NOTs encountered
168
private LinkedList JavaDoc joins = new LinkedList JavaDoc(); //the join string built up by compound paths inside this expression
169
private LinkedList JavaDoc booleanTests = new LinkedList JavaDoc(); //a flag indicating if the subexpression is known to be boolean
170

171     private String JavaDoc getElementName(PathExpressionParser.CollectionElement element, QueryTranslatorImpl q) throws QueryException {
172         String JavaDoc name;
173         if ( element.isOneToMany ) {
174             name = element.alias;
175         }
176         else {
177             Type type = element.elementType;
178             if ( type.isEntityType() ) { //ie. a many-to-many
179
String JavaDoc entityName = ( ( EntityType ) type ).getAssociatedEntityName();
180                 name = pathExpressionParser.continueFromManyToMany( entityName, element.elementColumns, q );
181             }
182             else {
183                 throw new QueryException( "illegally dereferenced collection element" );
184             }
185         }
186         return name;
187     }
188
189     public void token(String JavaDoc token, QueryTranslatorImpl q) throws QueryException {
190
191         String JavaDoc lcToken = token.toLowerCase();
192
193         //Cope with [,]
194
if ( token.equals( "[" ) && !expectingPathContinuation ) {
195             expectingPathContinuation = false;
196             if ( expectingIndex == 0 ) throw new QueryException( "unexpected [" );
197             return;
198         }
199         else if ( token.equals( "]" ) ) {
200             expectingIndex--;
201             expectingPathContinuation = true;
202             return;
203         }
204
205         //Cope with a continued path expression (ie. ].baz)
206
if ( expectingPathContinuation ) {
207             boolean pathExpressionContinuesFurther = continuePathExpression( token, q );
208             if ( pathExpressionContinuesFurther ) return; //NOTE: early return
209
}
210
211         //Cope with a subselect
212
if ( !inSubselect && ( lcToken.equals( "select" ) || lcToken.equals( "from" ) ) ) {
213             inSubselect = true;
214             subselect = new StringBuffer JavaDoc( 20 );
215         }
216         if ( inSubselect && token.equals( ")" ) ) {
217             bracketsSinceSelect--;
218
219             if ( bracketsSinceSelect == -1 ) {
220                 QueryTranslatorImpl subq = new QueryTranslatorImpl(
221                         subselect.toString(),
222                         q.getEnabledFilters(),
223                         q.getFactory()
224                 );
225                 try {
226                     subq.compile( q );
227                 }
228                 catch ( MappingException me ) {
229                     throw new QueryException( "MappingException occurred compiling subquery", me );
230                 }
231                 appendToken( q, subq.getSQLString() );
232                 inSubselect = false;
233                 bracketsSinceSelect = 0;
234             }
235         }
236         if ( inSubselect ) {
237             if ( token.equals( "(" ) ) bracketsSinceSelect++;
238             subselect.append( token ).append( ' ' );
239             return;
240         }
241
242         //Cope with special cases of AND, NOT, ()
243
specialCasesBefore( lcToken );
244
245         //Close extra brackets we opened
246
if ( !betweenSpecialCase && EXPRESSION_TERMINATORS.contains( lcToken ) ) {
247             closeExpression( q, lcToken );
248         }
249
250         //take note when this is a boolean expression
251
if ( BOOLEAN_OPERATORS.contains( lcToken ) ) {
252             booleanTests.removeLast();
253             booleanTests.addLast( Boolean.TRUE );
254         }
255
256         if ( lcToken.equals( "not" ) ) {
257             nots.addLast( new Boolean JavaDoc( !( ( Boolean JavaDoc ) nots.removeLast() ).booleanValue() ) );
258             negated = !negated;
259             return; //NOTE: early return
260
}
261
262         //process a token, mapping OO path expressions to SQL expressions
263
doToken( token, q );
264
265         //Open any extra brackets we might need.
266
if ( !betweenSpecialCase && EXPRESSION_OPENERS.contains( lcToken ) ) {
267             openExpression( q, lcToken );
268         }
269
270         //Cope with special cases of AND, NOT, )
271
specialCasesAfter( lcToken );
272
273     }
274
275     public void start(QueryTranslatorImpl q) throws QueryException {
276         token( "(", q );
277     }
278
279     public void end(QueryTranslatorImpl q) throws QueryException {
280         if ( expectingPathContinuation ) {
281             expectingPathContinuation = false;
282             PathExpressionParser.CollectionElement element = pathExpressionParser.lastCollectionElement();
283             if ( element.elementColumns.length != 1 ) throw new QueryException( "path expression ended in composite collection element" );
284             appendToken( q, element.elementColumns[0] );
285             addToCurrentJoin( element );
286         }
287         token( ")", q );
288     }
289
290     private void closeExpression(QueryTranslatorImpl q, String JavaDoc lcToken) {
291         if ( ( ( Boolean JavaDoc ) booleanTests.removeLast() ).booleanValue() ) { //it was a boolean expression
292

293             if ( booleanTests.size() > 0 ) {
294                 // the next one up must also be
295
booleanTests.removeLast();
296                 booleanTests.addLast( Boolean.TRUE );
297             }
298
299             // Add any joins
300
appendToken( q, ( joins.removeLast() ).toString() );
301
302         }
303         else {
304             StringBuffer JavaDoc join = ( StringBuffer JavaDoc ) joins.removeLast();
305             ( ( StringBuffer JavaDoc ) joins.getLast() ).append( join.toString() );
306         }
307
308         if ( ( ( Boolean JavaDoc ) nots.removeLast() ).booleanValue() ) negated = !negated;
309
310         if ( !")".equals( lcToken ) ) appendToken( q, ")" );
311     }
312
313     private void openExpression(QueryTranslatorImpl q, String JavaDoc lcToken) {
314         nots.addLast( Boolean.FALSE );
315         booleanTests.addLast( Boolean.FALSE );
316         joins.addLast( new StringBuffer JavaDoc() );
317         if ( !"(".equals( lcToken ) ) appendToken( q, "(" );
318     }
319
320     private void preprocess(String JavaDoc token, QueryTranslatorImpl q) throws QueryException {
321         // ugly hack for cases like "elements(foo.bar.collection)"
322
// (multi-part path expression ending in elements or indices)
323
String JavaDoc[] tokens = StringHelper.split( ".", token, true );
324         if (
325                 tokens.length > 5 &&
326                 ( CollectionPropertyNames.COLLECTION_ELEMENTS.equals( tokens[tokens.length - 1] )
327                 || CollectionPropertyNames.COLLECTION_INDICES.equals( tokens[tokens.length - 1] ) )
328         ) {
329             pathExpressionParser.start( q );
330             for ( int i = 0; i < tokens.length - 3; i++ ) {
331                 pathExpressionParser.token( tokens[i], q );
332             }
333             pathExpressionParser.token( null, q );
334             pathExpressionParser.end( q );
335             addJoin( pathExpressionParser.getWhereJoin(), q );
336             pathExpressionParser.ignoreInitialJoin();
337         }
338     }
339
340     private void doPathExpression(String JavaDoc token, QueryTranslatorImpl q) throws QueryException {
341
342         preprocess( token, q );
343
344         StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc( token, ".", true );
345         pathExpressionParser.start( q );
346         while ( tokens.hasMoreTokens() ) {
347             pathExpressionParser.token( tokens.nextToken(), q );
348         }
349         pathExpressionParser.end( q );
350         if ( pathExpressionParser.isCollectionValued() ) {
351             openExpression( q, "" );
352             appendToken( q, pathExpressionParser.getCollectionSubquery( q.getEnabledFilters() ) );
353             closeExpression( q, "" );
354             // this is ugly here, but needed because its a subquery
355
q.addQuerySpaces( q.getCollectionPersister( pathExpressionParser.getCollectionRole() ).getCollectionSpaces() );
356         }
357         else {
358             if ( pathExpressionParser.isExpectingCollectionIndex() ) {
359                 expectingIndex++;
360             }
361             else {
362                 addJoin( pathExpressionParser.getWhereJoin(), q );
363                 appendToken( q, pathExpressionParser.getWhereColumn() );
364             }
365         }
366     }
367
368     private void addJoin(JoinSequence joinSequence, QueryTranslatorImpl q) throws QueryException {
369         //JoinFragment fromClause = q.createJoinFragment(true);
370
//fromClause.addJoins( join.toJoinFragment().toFromFragmentString(), StringHelper.EMPTY_STRING );
371
q.addFromJoinOnly( pathExpressionParser.getName(), joinSequence );
372         try {
373             addToCurrentJoin( joinSequence.toJoinFragment( q.getEnabledFilters(), true ).toWhereFragmentString() );
374         }
375         catch ( MappingException me ) {
376             throw new QueryException( me );
377         }
378     }
379
380     private void doToken(String JavaDoc token, QueryTranslatorImpl q) throws QueryException {
381         if ( q.isName( StringHelper.root( token ) ) ) { //path expression
382
doPathExpression( q.unalias( token ), q );
383         }
384         else if ( token.startsWith( ParserHelper.HQL_VARIABLE_PREFIX ) ) { //named query parameter
385
q.addNamedParameter( token.substring( 1 ) );
386             appendToken( q, "?" );
387         }
388         else {
389             Queryable persister = q.getEntityPersisterUsingImports( token );
390             if ( persister != null ) { // the name of a class
391
final String JavaDoc discrim = persister.getDiscriminatorSQLValue();
392                 if ( InFragment.NULL.equals(discrim) || InFragment.NOT_NULL.equals(discrim) ) {
393                     throw new QueryException( "subclass test not allowed for null or not null discriminator" );
394                 }
395                 else {
396                     appendToken( q, discrim );
397                 }
398             }
399             else {
400                 Object JavaDoc constant;
401                 if (
402                         token.indexOf( '.' ) > -1 &&
403                         ( constant = ReflectHelper.getConstantValue( token ) ) != null
404                 ) {
405                     Type type;
406                     try {
407                         type = TypeFactory.heuristicType( constant.getClass().getName() );
408                     }
409                     catch ( MappingException me ) {
410                         throw new QueryException( me );
411                     }
412                     if ( type == null ) throw new QueryException( QueryTranslator.ERROR_CANNOT_DETERMINE_TYPE + token );
413                     try {
414                         appendToken( q, ( ( LiteralType ) type ).objectToSQLString( constant ) );
415                     }
416                     catch ( Exception JavaDoc e ) {
417                         throw new QueryException( QueryTranslator.ERROR_CANNOT_FORMAT_LITERAL + token, e );
418                     }
419                 }
420                 else { //anything else
421

422                     String JavaDoc negatedToken = negated ? ( String JavaDoc ) NEGATIONS.get( token.toLowerCase() ) : null;
423                     if ( negatedToken != null && ( !betweenSpecialCase || !"or".equals( negatedToken ) ) ) {
424                         appendToken( q, negatedToken );
425                     }
426                     else {
427                         appendToken( q, token );
428                     }
429                 }
430             }
431         }
432     }
433
434     private void addToCurrentJoin(String JavaDoc sql) {
435         ( ( StringBuffer JavaDoc ) joins.getLast() ).append( sql );
436     }
437
438     private void addToCurrentJoin(PathExpressionParser.CollectionElement ce)
439             throws QueryException {
440         try {
441             addToCurrentJoin( ce.joinSequence.toJoinFragment().toWhereFragmentString() + ce.indexValue.toString() );
442         }
443         catch ( MappingException me ) {
444             throw new QueryException( me );
445         }
446     }
447
448     private void specialCasesBefore(String JavaDoc lcToken) {
449         if ( lcToken.equals( "between" ) || lcToken.equals( "not between" ) ) {
450             betweenSpecialCase = true;
451         }
452     }
453
454     private void specialCasesAfter(String JavaDoc lcToken) {
455         if ( betweenSpecialCase && lcToken.equals( "and" ) ) {
456             betweenSpecialCase = false;
457         }
458     }
459
460     void appendToken(QueryTranslatorImpl q, String JavaDoc token) {
461         if ( expectingIndex > 0 ) {
462             pathExpressionParser.setLastCollectionElementIndexValue( token );
463         }
464         else {
465             q.appendWhereToken( token );
466         }
467     }
468
469     private boolean continuePathExpression(String JavaDoc token, QueryTranslatorImpl q) throws QueryException {
470
471         expectingPathContinuation = false;
472
473         PathExpressionParser.CollectionElement element = pathExpressionParser.lastCollectionElement();
474
475         if ( token.startsWith( "." ) ) { // the path expression continues after a ]
476

477             doPathExpression( getElementName( element, q ) + token, q ); // careful with this!
478

479             addToCurrentJoin( element );
480             return true; //NOTE: EARLY EXIT!
481

482         }
483
484         else { // the path expression ends at the ]
485
if ( element.elementColumns.length != 1 ) {
486                 throw new QueryException( "path expression ended in composite collection element" );
487             }
488             appendToken( q, element.elementColumns[0] );
489             addToCurrentJoin( element );
490             return false;
491         }
492     }
493 }
494
Popular Tags