KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > triactive > jdo > store > JDOQLQuery


1 /*
2  * Copyright 2002 (C) TJDO.
3  * All rights reserved.
4  *
5  * This software is distributed under the terms of the TJDO License version 1.0.
6  * See the terms of the TJDO License in the documentation provided with this software.
7  *
8  * $Id: JDOQLQuery.java,v 1.10 2004/01/18 05:46:55 jackknifebarber Exp $
9  */

10
11 package com.triactive.jdo.store;
12
13 import com.triactive.jdo.PersistenceManager;
14 import java.math.BigInteger JavaDoc;
15 import java.math.BigDecimal JavaDoc;
16 import java.sql.Connection JavaDoc;
17 import java.sql.PreparedStatement JavaDoc;
18 import java.sql.ResultSet JavaDoc;
19 import java.sql.SQLException JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.StringTokenizer JavaDoc;
25 import javax.jdo.Extent;
26 import javax.jdo.JDODataStoreException;
27 import javax.jdo.JDOFatalInternalException;
28 import javax.jdo.JDOUnsupportedOptionException;
29 import javax.jdo.JDOUserException;
30 import org.apache.log4j.Category;
31
32
33 /**
34  * A JDO query that uses the default JQOQL language.
35  *
36  * @author <a HREF="mailto:mmartin5@austin.rr.com">Mike Martin</a>
37  * @version $Revision: 1.10 $
38  *
39  * @see Query
40  */

41
42 public class JDOQLQuery extends Query
43 {
44     private static final Category LOG = Category.getInstance(JDOQLQuery.class);
45     private static final Category CLOG = Category.getInstance(Compiler JavaDoc.class);
46
47     private transient Queryable candidates = null;
48
49     private transient QueryStatement queryStmt = null;
50     private transient Query.ResultObjectFactory rof = null;
51
52
53     /**
54      * Constructs a new query instance having no persistence manager. The
55      * resulting query cannot be executed, but can be used to create a new
56      * query instance having the same criteria.
57      */

58
59     public JDOQLQuery()
60     {
61         this(null, null);
62     }
63
64
65     /**
66      * Constructs a new query instance that uses the given persistence manager.
67      *
68      * @param pm the associated persistence manager for this query.
69      */

70
71     public JDOQLQuery(PersistenceManager pm, StoreManager storeMgr)
72     {
73         this(pm, storeMgr, null);
74     }
75
76
77     /**
78      * Constructs a new query instance having the same criteria as the given
79      * query.
80      *
81      * @param q the query from which to copy criteria.
82      */

83
84     public JDOQLQuery(PersistenceManager pm, StoreManager storeMgr, JDOQLQuery q)
85     {
86         super(pm, storeMgr);
87
88         if (q == null)
89         {
90             candidateClass = null;
91             filter = null;
92             imports = null;
93             variables = null;
94             parameters = null;
95             ordering = null;
96         }
97         else
98         {
99             candidateClass = q.candidateClass;
100             filter = q.filter;
101             imports = q.imports;
102             variables = q.variables;
103             parameters = q.parameters;
104             ordering = q.ordering;
105         }
106     }
107
108
109     protected void discardCompiled()
110     {
111         super.discardCompiled();
112
113         queryStmt = null;
114         rof = null;
115     }
116
117
118     public boolean equals(Object JavaDoc obj)
119     {
120         if (obj == this)
121             return true;
122
123         if (!(obj instanceof JDOQLQuery) || !super.equals(obj))
124             return false;
125
126         return true;
127     }
128
129
130     /**
131      * Set the candidate Extent to query.
132      *
133      * @param pcs the Candidate Extent.
134      *
135      * @see javax.jdo.Query#setCandidates(javax.jdo.Extent)
136      */

137
138     public void setCandidates(Extent pcs)
139     {
140         setCandidatesInternal(pcs);
141     }
142
143
144     /**
145      * Set the candidate Collection to query.
146      *
147      * @param pcs the Candidate collection.
148      *
149      * @see javax.jdo.Query#setCandidates(java.util.Collection)
150      */

151
152     public void setCandidates(Collection JavaDoc pcs)
153     {
154         setCandidatesInternal(pcs);
155     }
156
157
158     private void setCandidatesInternal(Object JavaDoc pcs)
159     {
160         if (!(pcs instanceof Queryable))
161             throw new JDOUnsupportedOptionException("Object not queryable: " + pcs.getClass().getName());
162
163         discardCompiled();
164
165         candidates = (Queryable)pcs;
166
167         if (candidateClass == null)
168             candidateClass = candidates.getCandidateClass();
169     }
170
171
172     /**
173      * Verify the elements of the query and provide a hint to the query to
174      * prepare and optimize an execution plan.
175      *
176      * @see javax.jdo.Query#compile
177      */

178
179     public void compile()
180     {
181         super.compile();
182         isCompiled = true;
183     }
184
185
186     private void compile(Map JavaDoc parameters)
187     {
188         discardCompiled();
189         super.compile();
190
191         try
192         {
193             if (CLOG.isDebugEnabled())
194             {
195                 CLOG.debug("Filter = " + filter);
196                 CLOG.debug("Ordering = " + ordering);
197             }
198
199             long startTime = System.currentTimeMillis();
200
201             Compiler JavaDoc c = new Compiler JavaDoc(parameters);
202
203             queryStmt = c.compileQueryStatement();
204             rof = candidates.newResultObjectFactory(queryStmt);
205
206             if (CLOG.isDebugEnabled())
207                 CLOG.debug("Compile time = " + (System.currentTimeMillis() - startTime) + " ms: " + queryStmt);
208
209             isCompiled = true;
210         }
211         finally
212         {
213             if (!isCompiled)
214                 discardCompiled();
215         }
216     }
217
218
219     /**
220      * Execute the query and return the filtered Collection.
221      *
222      * @param parameters the Map containing all of the parameters.
223      *
224      * @return the filtered Collection.
225      *
226      * @see javax.jdo.Query#executeWithMap(Map)
227      * @see #executeWithArray(Object[] parameters)
228      */

229
230     public Object JavaDoc executeWithMap(Map JavaDoc parameters)
231     {
232         compile(parameters);
233
234         QueryResult qr = null;
235
236         try
237         {
238             Connection JavaDoc conn = pm.getConnection(false);
239
240             try
241             {
242                 PreparedStatement JavaDoc ps = queryStmt.toStatementText().prepareStatement(pm, conn);
243
244                 try
245                 {
246                     long startTime = System.currentTimeMillis();
247
248                     ResultSet JavaDoc rs = ps.executeQuery();
249
250                     if (LOG.isDebugEnabled())
251                         LOG.debug("Time = " + (System.currentTimeMillis() - startTime) + " ms: " + queryStmt);
252
253                     try
254                     {
255                         qr = new QueryResult(this, rof, rs);
256                     }
257                     finally
258                     {
259                         if (qr == null)
260                             rs.close();
261                     }
262                 }
263                 finally
264                 {
265                     if (qr == null)
266                         ps.close();
267                 }
268             }
269             finally
270             {
271                 pm.releaseConnection(conn);
272             }
273         }
274         catch (SQLException JavaDoc e)
275         {
276             throw dba.newDataStoreException("Error executing query: " + queryStmt, e);
277         }
278
279         queryResults.add(qr);
280
281         return qr;
282     }
283
284
285     public class Compiler
286     {
287         private final Map JavaDoc parameters;
288         private final HashMap JavaDoc expressionsByVariableName = new HashMap JavaDoc();
289         private QueryStatement qs = null;
290         private Parser p = null;
291
292
293         public Compiler(Map JavaDoc parameters)
294         {
295             if (parameters != null && parameters.size() != parameterNames.size())
296                 throw new JDOUserException("Incorrect number of parameters: " + parameters.size() + ", s/b " + parameterNames.size());
297
298             this.parameters = parameters;
299         }
300
301
302         public void bindVariable(String JavaDoc name, SQLExpression expr)
303         {
304             SQLExpression previousExpr = (SQLExpression)expressionsByVariableName.put(name, expr);
305
306             if (previousExpr != null)
307                 throw new JDOFatalInternalException("Error binding " + name + " to " + expr + ", previously bound to " + previousExpr);
308         }
309
310
311         public QueryStatement compileQueryStatement()
312         {
313             if (candidates == null)
314             {
315                 if (candidateClass == null)
316                     throw new JDOUserException("No candidate class provided");
317
318                 candidates = (Queryable)pm.getExtent(candidateClass, true);
319             }
320
321             qs = candidates.newQueryStatement(candidateClass);
322
323             if (filter != null && filter.length() > 0)
324             {
325                 p = new Parser(filter, parsedImports);
326                 SQLExpression expr = compileExpression();
327
328                 if (!p.parseEOS())
329                     throw new ExpressionSyntaxException("Invalid expression");
330
331                 if (!(expr instanceof BooleanExpression))
332                     throw new JDOUserException("Filter expression does not yield boolean result: " + filter);
333
334                 qs.andCondition((BooleanExpression)expr);
335             }
336
337             if (ordering != null && ordering.length() > 0)
338             {
339                 StringTokenizer JavaDoc t1 = new StringTokenizer JavaDoc(ordering, ",");
340
341                 int n = t1.countTokens();
342                 SQLExpression[] orderExprs = new SQLExpression[n];
343                 boolean[] descending = new boolean[n];
344
345                 for (n = 0; t1.hasMoreTokens(); ++n)
346                 {
347                     StringTokenizer JavaDoc t2 = new StringTokenizer JavaDoc(t1.nextToken(), " ");
348
349                     if (t2.countTokens() != 2)
350                         throw new JDOUserException("Invalid order specification: " + ordering);
351
352                     String JavaDoc expression = t2.nextToken();
353                     String JavaDoc direction = t2.nextToken();
354
355                     p = new Parser(expression, parsedImports);
356                     orderExprs[n] = compileExpression();
357
358                     if (!p.parseEOS())
359                         throw new ExpressionSyntaxException("Invalid expression");
360
361                     if (direction.equals("ascending"))
362                         descending[n] = false;
363                     else if (direction.equals("descending"))
364                         descending[n] = true;
365                     else
366                         throw new JDOUserException("Invalid order direction: " + ordering);
367                 }
368
369                 qs.setOrdering(orderExprs, descending);
370             }
371
372             return qs;
373         }
374
375
376         private SQLExpression compileExpression()
377         {
378             return compileConditionalOrExpression();
379         }
380
381
382         private SQLExpression compileConditionalOrExpression()
383         {
384             SQLExpression expr = compileConditionalAndExpression();
385
386             while (p.parseString("||"))
387                 expr = expr.ior(compileConditionalAndExpression());
388
389             return expr;
390         }
391
392
393         private SQLExpression compileConditionalAndExpression()
394         {
395             SQLExpression expr = compileInclusiveOrExpression();
396
397             while (p.parseString("&&"))
398                 expr = expr.and(compileInclusiveOrExpression());
399
400             return expr;
401         }
402
403
404         private SQLExpression compileInclusiveOrExpression()
405         {
406             SQLExpression expr = compileExclusiveOrExpression();
407
408             while (p.parseChar('|', '|'))
409                 expr = expr.ior(compileExclusiveOrExpression());
410
411             return expr;
412         }
413
414
415         private SQLExpression compileExclusiveOrExpression()
416         {
417             SQLExpression expr = compileAndExpression();
418
419             while (p.parseChar('^'))
420                 expr = expr.eor(compileExclusiveOrExpression());
421
422             return expr;
423         }
424
425
426         private SQLExpression compileAndExpression()
427         {
428             SQLExpression expr = compileEqualityExpression();
429
430             while (p.parseChar('&', '&'))
431                 expr = expr.and(compileEqualityExpression());
432
433             return expr;
434         }
435
436
437         private SQLExpression compileEqualityExpression()
438         {
439             SQLExpression expr = compileRelationalExpression();
440
441             for (;;)
442             {
443                 if (p.parseString("=="))
444                     expr = expr.eq(compileRelationalExpression());
445                 else if (p.parseString("!="))
446                     expr = expr.noteq(compileRelationalExpression());
447                 else
448                     break;
449             }
450
451             return expr;
452         }
453
454
455         private SQLExpression compileRelationalExpression()
456         {
457             SQLExpression expr = compileAdditiveExpression();
458
459             for (;;)
460             {
461                 if (p.parseString("<="))
462                     expr = expr.lteq(compileAdditiveExpression());
463                 else if (p.parseString(">="))
464                     expr = expr.gteq(compileAdditiveExpression());
465                 else if (p.parseChar('<'))
466                     expr = expr.lt(compileAdditiveExpression());
467                 else if (p.parseChar('>'))
468                     expr = expr.gt(compileAdditiveExpression());
469                 else
470                     break;
471             }
472
473             return expr;
474         }
475
476
477         private SQLExpression compileAdditiveExpression()
478         {
479             SQLExpression expr = compileMultiplicativeExpression();
480
481             for (;;)
482             {
483                 if (p.parseChar('+'))
484                     expr = expr.add(compileMultiplicativeExpression());
485                 else if (p.parseChar('-'))
486                     expr = expr.sub(compileMultiplicativeExpression());
487                 else
488                     break;
489             }
490
491             return expr;
492         }
493
494
495         private SQLExpression compileMultiplicativeExpression()
496         {
497             SQLExpression expr = compileUnaryExpression();
498
499             for (;;)
500             {
501                 if (p.parseChar('*'))
502                     expr = expr.mul(compileUnaryExpression());
503                 else if (p.parseChar('/'))
504                     expr = expr.div(compileUnaryExpression());
505                 else if (p.parseChar('%'))
506                     expr = expr.mod(compileUnaryExpression());
507                 else
508                     break;
509             }
510
511             return expr;
512         }
513
514
515         private SQLExpression compileUnaryExpression()
516         {
517             SQLExpression expr;
518
519             if (p.parseChar('+'))
520                 expr = compileUnaryExpression();
521             else if (p.parseChar('-'))
522                 expr = compileUnaryExpression().neg();
523             else
524                 expr = compileUnaryExpressionNotPlusMinus();
525
526             return expr;
527         }
528
529
530         private SQLExpression compileUnaryExpressionNotPlusMinus()
531         {
532             SQLExpression expr;
533
534             if (p.parseChar('~'))
535                 expr = compileUnaryExpression().com();
536             else if (p.parseChar('!'))
537                 expr = compileUnaryExpression().not();
538             else if ((expr = compileCastExpression()) == null)
539                 expr = compilePrimary();
540
541             return expr;
542         }
543
544
545         private SQLExpression compileCastExpression()
546         {
547             Class JavaDoc type;
548
549             if ((type = p.parseCast()) == null)
550                 return null;
551
552             return compileUnaryExpression().cast(type);
553         }
554
555
556         private SQLExpression compilePrimary()
557         {
558             SQLExpression expr = compileLiteral();
559
560             if (expr == null)
561             {
562                 if (p.parseChar('('))
563                 {
564                     expr = compileExpression();
565
566                     if (!p.parseChar(')'))
567                         throw new ExpressionSyntaxException("')' expected");
568                 }
569                 else
570                     expr = compileIdentifier();
571
572                 while (p.parseChar('.'))
573                 {
574                     String JavaDoc id = p.parseIdentifier();
575
576                     if (id == null)
577                         throw new ExpressionSyntaxException("Identifier expected");
578
579                     if (p.parseChar('('))
580                     {
581                         ArrayList JavaDoc args = new ArrayList JavaDoc();
582
583                         if (!p.parseChar(')'))
584                         {
585                             do
586                             {
587                                 args.add(compileExpression());
588                             } while (p.parseChar(','));
589
590                             if (!p.parseChar(')'))
591                                 throw new ExpressionSyntaxException("')' expected");
592                         }
593
594                         expr = expr.callMethod(id, args);
595                     }
596                     else
597                         expr = expr.accessField(id);
598                 }
599             }
600
601             return expr;
602         }
603
604
605         private SQLExpression compileLiteral()
606         {
607             Class JavaDoc litType;
608             Object JavaDoc litValue;
609
610             String JavaDoc sLiteral;
611             BigDecimal JavaDoc fLiteral;
612             BigInteger JavaDoc iLiteral;
613             Character JavaDoc cLiteral;
614             Boolean JavaDoc bLiteral;
615
616             if ((sLiteral = p.parseStringLiteral()) != null)
617             {
618                 litType = String JavaDoc.class;
619                 litValue = sLiteral;
620             }
621             else if ((fLiteral = p.parseFloatingPointLiteral()) != null)
622             {
623                 litType = BigDecimal JavaDoc.class;
624                 litValue = fLiteral;
625             }
626             else if ((iLiteral = p.parseIntegerLiteral()) != null)
627             {
628                 litType = Long JavaDoc.class;
629                 litValue = new Long JavaDoc(iLiteral.longValue());
630             }
631             else if ((cLiteral = p.parseCharacterLiteral()) != null)
632             {
633                 litType = Character JavaDoc.class;
634                 litValue = cLiteral;
635             }
636             else if ((bLiteral = p.parseBooleanLiteral()) != null)
637             {
638                 litType = Boolean JavaDoc.class;
639                 litValue = bLiteral;
640             }
641             else if (p.parseNullLiteral())
642                 return new NullLiteral(qs);
643             else
644                 return null;
645
646             Mapping m = dba.getMapping(litType);
647             return m.newSQLLiteral(qs, litValue);
648         }
649
650
651         private SQLExpression compileIdentifier()
652         {
653             SQLExpression expr;
654             String JavaDoc id = p.parseIdentifier();
655
656             if (id == null)
657                 throw new ExpressionSyntaxException("Identifier expected");
658
659             if (parameterNames.contains(id))
660             {
661                 Mapping m = dba.getMapping((Class JavaDoc)parameterTypesByName.get(id));
662
663                 if (!parameters.containsKey(id))
664                     throw new JDOUserException("Required parameter " + id + " not provided");
665
666                 Object JavaDoc parameterValue = parameters.get(id);
667
668                 if (parameterValue == null)
669                     expr = new NullLiteral(qs);
670                 else
671                     expr = m.newSQLLiteral(qs, parameterValue);
672             }
673             else if (variableNames.contains(id))
674             {
675                 expr = (SQLExpression)expressionsByVariableName.get(id);
676
677                 if (expr == null)
678                     expr = new UnboundVariable(qs, id, (Class JavaDoc)variableTypesByName.get(id), this);
679             }
680             else
681                 expr = qs.getDefaultTableExpression().newFieldExpression(id);
682
683             return expr;
684         }
685
686
687         private class ExpressionSyntaxException extends JDOUserException
688         {
689             public ExpressionSyntaxException(String JavaDoc msg)
690             {
691                 super(msg + " at character " + (p.getIndex() + 1) + " in \"" + p.getInput() + '"');
692             }
693         }
694     }
695 }
696
Popular Tags