KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > josql > Query


1 /*
2  * Copyright 2004-2005 Gary Bentley
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may
5  * not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */

15 package org.josql;
16
17 import java.io.File JavaDoc;
18 import java.io.StringReader JavaDoc;
19 import java.io.BufferedReader JavaDoc;
20
21 import java.util.Map JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.SortedMap JavaDoc;
24 import java.util.LinkedHashMap JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.StringTokenizer JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.Collections JavaDoc;
30 import java.util.Arrays JavaDoc;
31 import java.util.Comparator JavaDoc;
32 import java.util.Set JavaDoc;
33 import java.util.LinkedHashSet JavaDoc;
34 import java.util.Collection JavaDoc;
35
36 import org.josql.parser.JoSQLParser;
37
38 import org.josql.expressions.*;
39
40 import org.josql.functions.*;
41
42 import org.josql.internal.*;
43
44 import org.josql.events.*;
45
46 /**
47  * This class provides the ability for a developer to apply an arbitrary SQL statement
48  * (using suitable syntax) to a collection of Java objects.
49  * <p>
50  * Basic usage:
51  * <pre>
52  * Query q = new Query ();
53  * q.parse (myStatement);
54  * List results = q.execute (myObjects);
55  * </pre>
56  * <p>
57  * An example statement would look like:
58  * <pre>
59  * SELECT lastModified,
60  * name
61  * FROM java.io.File
62  * WHERE name LIKE '%.html'
63  * </pre>
64  * <p>
65  * The JoSQL functionality is large and complex, whilst basic queries like the one above are
66  * perfectly possible, very complex queries are also possible, for example:
67  * <pre>
68  * SELECT name,
69  * formatDate(lastModified),
70  * formatNumber(length),
71  * formatNumber(length - @avg_length),
72  * formatTimeDuration(@max_last_modified - lastModified)
73  * FROM java.io.File
74  * WHERE lastModified > @avg_last_modified
75  * AND length > @avg_length
76  * AND lower(name) LIKE '%.html'
77  * GROUP BY path
78  * ORDER BY name, lastModified DESC
79  * EXECUTE ON ALL avg (:_allobjs, length) avg_length,
80  * avg (:_allobjs, lastModified) avg_last_modified,
81  * max (:_allobjs, lastModified) max_last_modified
82  * </pre>
83  * <p>
84  * Note: the "EXECUTE ON ALL" syntax is an extension used by JoSQL because it has no notion
85  * of "aggregate functions".
86  * <p>
87  * For full details of how a query works and what is possible, see the
88  * <a HREF="http://josql.sourceforge.net/manual/index.html">JoSQL User Manual</a>.
89  * <p>
90  * Please note that the package structure for JoSQL is deliberate, JoSQL is designed to be a
91  * black box and lightweight thus only the <code>Query</code> object and associated exceptions
92  * are exposed in the main package. Also, the class structure for JoSQL is not designed to
93  * exactly represent the SQL statement passed to it, rather the classes are optimised for
94  * ease of execution of the statement. If you wish to have a completely accurate Java object
95  * view of ANY SQL statement then please see:
96  * <a HREF="http://sourceforge.net/projects/jsqlparser">JSqlParser</a>
97  * which will provide what you need.
98  */

99 public class Query
100 {
101
102     public static String JavaDoc QUERY_BIND_VAR_NAME = "_query";
103     public static String JavaDoc PARENT_BIND_VAR_NAME = "_parent";
104     public static String JavaDoc CURR_OBJ_VAR_NAME = "_currobj";
105     public static String JavaDoc ALL_OBJS_VAR_NAME = "_allobjs";
106     public static String JavaDoc GRPBY_OBJ_VAR_NAME = "_grpby";
107     public static String JavaDoc GRPBY_OBJ_VAR_NAME_SYNONYM = "_groupby";
108     public static final String JavaDoc INT_BIND_VAR_PREFIX = "^^^";
109
110     public static final String JavaDoc ALL = "ALL";
111     public static final String JavaDoc RESULTS = "RESULTS";
112     public static final String JavaDoc GROUP_BY_RESULTS = "GROUP_BY_RESULTS";
113     public static final String JavaDoc WHERE_RESULTS = "WHERE_RESULTS";
114     public static final String JavaDoc HAVING_RESULTS = "HAVING_RESULTS";
115
116     public static final String JavaDoc ORDER_BY_ASC = "ASC";
117     public static final String JavaDoc ORDER_BY_DESC = "DESC";
118
119     public static final List JavaDoc nullQueryList = new ArrayList JavaDoc ();
120
121     static
122     {
123
124     Query.nullQueryList.add (new Object JavaDoc ());
125
126     }
127
128     private List JavaDoc bfhs = new ArrayList JavaDoc ();
129     private Map JavaDoc bfhsMap = new HashMap JavaDoc ();
130
131     private char wildcardChar = '%';
132
133     private Map JavaDoc aliases = new HashMap JavaDoc ();
134     private List JavaDoc groupBys = null;
135     private Comparator JavaDoc orderByComp = null;
136     private Comparator JavaDoc groupOrderByComp = null;
137     private Grouper grouper = null;
138     private List JavaDoc orderBys = null;
139     private List JavaDoc groupOrderBys = null;
140     private List JavaDoc cols = null;
141     private boolean retObjs = false;
142     private Expression where = null;
143     private Expression having = null;
144     private Map JavaDoc bindVars = null;
145     private String JavaDoc query = null;
146     private boolean wantTimings = false;
147     private List JavaDoc functionHandlers = null;
148     private int anonVarIndex = 1;
149     private Expression from = null;
150     private Class JavaDoc objClass = null;
151     private Limit limit = null;
152     private Limit groupByLimit = null;
153     private Map JavaDoc executeOn = null;
154     private boolean isParsed = false;
155     private boolean distinctResults = false;
156     private ClassLoader JavaDoc classLoader = null;
157     private Query parent = null;
158     private Map JavaDoc listeners = new HashMap JavaDoc ();
159
160     // Execution data.
161
private transient Object JavaDoc currentObject = null;
162     private transient List JavaDoc allObjects = null;
163     private transient List JavaDoc currGroupBys = null;
164
165     private QueryResults qd = null;
166
167     /**
168      * Return the WHERE clause expression.
169      *
170      * @return The WHERE clause as an expression.
171      */

172     public Expression getWhereClause ()
173     {
174
175     return this.where;
176
177     }
178
179     /**
180      * Return the HAVING clause expression.
181      *
182      * @return The HAVING clause as an expression.
183      */

184     public Expression getHavingClause ()
185     {
186
187     return this.having;
188
189     }
190
191     /**
192      * Return the {@link Comparator} we will use to do the ordering of the results, may be null.
193      *
194      * @return The Comparator.
195      */

196     public Comparator JavaDoc getOrderByComparator ()
197     {
198
199     return this.orderByComp;
200
201     }
202
203     public FunctionHandler getFunctionHandler (String JavaDoc id)
204     {
205
206     if (this.parent != null)
207     {
208
209         return this.parent.getFunctionHandler (id);
210
211     }
212
213     return (FunctionHandler) this.bfhsMap.get (id);
214
215     }
216
217     private void initFunctionHandlers ()
218     {
219
220     FunctionHandler o = new CollectionFunctions ();
221     o.setQuery (this);
222
223     this.bfhsMap.put (CollectionFunctions.HANDLER_ID,
224               o);
225
226     this.bfhs.add (o);
227
228     o = new StringFunctions ();
229     o.setQuery (this);
230
231     this.bfhsMap.put (StringFunctions.HANDLER_ID,
232               o);
233
234     this.bfhs.add (o);
235
236     o = new ConversionFunctions ();
237     o.setQuery (this);
238
239     this.bfhsMap.put (ConversionFunctions.HANDLER_ID,
240               o);
241
242     this.bfhs.add (o);
243
244     o = new FormattingFunctions ();
245     o.setQuery (this);
246
247     this.bfhsMap.put (FormattingFunctions.HANDLER_ID,
248               o);
249
250     this.bfhs.add (o);
251
252     o = new GroupingFunctions ();
253     o.setQuery (this);
254
255     this.bfhsMap.put (GroupingFunctions.HANDLER_ID,
256               o);
257
258     this.bfhs.add (o);
259
260     o = new MiscellaneousFunctions ();
261     o.setQuery (this);
262
263     this.bfhsMap.put (MiscellaneousFunctions.HANDLER_ID,
264               o);
265
266     this.bfhs.add (o);
267
268     }
269
270     public Map JavaDoc getExecuteOnFunctions ()
271     {
272
273     return this.executeOn;
274
275     }
276
277     public void setExecuteOnFunctions (Map JavaDoc ex)
278     {
279
280     this.executeOn = ex;
281
282     }
283
284     public String JavaDoc getAnonymousBindVariableName ()
285     {
286
287     if (this.parent != null)
288     {
289
290         return this.parent.getAnonymousBindVariableName ();
291
292     }
293
294     String JavaDoc n = Query.INT_BIND_VAR_PREFIX + this.anonVarIndex;
295
296     this.anonVarIndex++;
297
298     return n;
299
300     }
301
302     public List JavaDoc getDefaultFunctionHandlers ()
303     {
304
305     if (this.parent != null)
306     {
307
308         return this.parent.getDefaultFunctionHandlers ();
309
310     }
311
312     return new ArrayList JavaDoc (this.bfhs);
313
314     }
315
316     public List JavaDoc getFunctionHandlers ()
317     {
318
319     if (this.parent != null)
320     {
321
322         return this.parent.getFunctionHandlers ();
323
324     }
325
326     return this.functionHandlers;
327
328     }
329
330     public void addFunctionHandler (Object JavaDoc o)
331     {
332
333     if (this.parent != null)
334     {
335
336         this.parent.addFunctionHandler (o);
337
338     }
339
340     if (this.functionHandlers == null)
341     {
342
343         this.functionHandlers = new ArrayList JavaDoc ();
344
345     }
346
347     if (o instanceof FunctionHandler)
348     {
349
350         FunctionHandler fh = (FunctionHandler) o;
351
352         fh.setQuery (this);
353
354     }
355
356     this.functionHandlers.add (o);
357
358     }
359
360     public void setFrom (Expression exp)
361     {
362
363     this.from = exp;
364
365     }
366
367     public Expression getFrom ()
368     {
369
370     return this.from;
371
372     }
373
374     public void setClassName (String JavaDoc n)
375     {
376
377     ConstantExpression ce = new ConstantExpression ();
378     this.from = ce;
379     ce.setValue (n);
380
381     }
382
383     public void setOrderByColumns (List JavaDoc cols)
384     {
385
386     this.orderBys = cols;
387
388     }
389
390     public void setGroupByLimit (Limit g)
391     {
392
393     this.groupByLimit = g;
394
395     }
396
397     public void setGroupByOrderColumns (List JavaDoc cols)
398     {
399
400     this.groupOrderBys = cols;
401
402     }
403
404     public List JavaDoc getGroupByColumns ()
405     {
406
407     return this.groupBys;
408
409     }
410
411     public void setGroupByColumns (List JavaDoc cols)
412     {
413
414     this.groupBys = cols;
415
416     }
417
418     public List JavaDoc getColumns ()
419     {
420
421     return this.cols;
422
423     }
424
425     public void setColumns (List JavaDoc cols)
426     {
427
428     this.cols = cols;
429
430     }
431
432     public void setHaving (Expression be)
433     {
434
435     this.having = be;
436
437     }
438
439     public void setWhere (Expression be)
440     {
441
442     this.where = be;
443
444     }
445
446     public Query ()
447     {
448
449     this.initFunctionHandlers ();
450
451     }
452
453     public void setWantTimings (boolean v)
454     {
455
456     this.wantTimings = v;
457
458     }
459
460     protected void addTiming (String JavaDoc id,
461                   double time)
462     {
463
464     if (this.wantTimings)
465     {
466
467         if (this.qd == null)
468         {
469
470         return;
471
472         }
473
474         if (this.qd.timings == null)
475         {
476
477         this.qd.timings = new LinkedHashMap JavaDoc ();
478
479         }
480
481         this.qd.timings.put (id,
482                  new Double JavaDoc (time));
483
484     }
485
486     }
487
488     public Object JavaDoc getVariable (int index)
489     {
490
491     if (this.parent != null)
492     {
493
494         return this.parent.getVariable (index);
495
496     }
497
498     return this.getVariable (Query.INT_BIND_VAR_PREFIX + index);
499
500     }
501
502     public Class JavaDoc getVariableClass (String JavaDoc name)
503     {
504
505     String JavaDoc n = name.toLowerCase ();
506
507     if (n.equals (Query.QUERY_BIND_VAR_NAME))
508     {
509
510         // Return the query itself!
511
return Query.class;
512
513     }
514
515     if (n.equals (Query.PARENT_BIND_VAR_NAME))
516     {
517
518         // Return the query itself!
519
return Query.class;
520
521     }
522
523     if (n.equals (Query.CURR_OBJ_VAR_NAME))
524     {
525
526         // May be null if we aren't processing a while/having expression.
527
return this.objClass;
528
529     }
530
531     if (n.equals (Query.ALL_OBJS_VAR_NAME))
532     {
533
534         // May change depending upon when it is called.
535
return List JavaDoc.class;
536
537     }
538
539     if (this.bindVars == null)
540     {
541
542         return Object JavaDoc.class;
543
544     }
545
546     if (this.parent != null)
547     {
548
549         return this.parent.getVariableClass (n);
550
551     }
552
553     Object JavaDoc v = (Object JavaDoc) this.bindVars.get (n);
554
555     if (v == null)
556     {
557
558         return Object JavaDoc.class;
559
560     }
561
562     return v.getClass ();
563
564     }
565
566     public Object JavaDoc getGroupByVariable (int ind)
567     {
568
569     // Get the current group bys.
570
if (this.currGroupBys != null)
571     {
572
573         return this.currGroupBys.get (ind - 1);
574
575     }
576
577     return null;
578
579     }
580
581     public Object JavaDoc getVariable (String JavaDoc name)
582     {
583
584     String JavaDoc n = name.toLowerCase ();
585
586     if (n.equals (Query.QUERY_BIND_VAR_NAME))
587     {
588
589         // Return the query itself!
590
return this;
591
592     }
593
594     if (n.equals (Query.PARENT_BIND_VAR_NAME))
595     {
596
597         // Return the parent query.
598
return this.parent;
599
600     }
601
602     if (n.equals (Query.CURR_OBJ_VAR_NAME))
603     {
604
605         // May be null if we aren't processing a while/having expression.
606
return this.currentObject;
607
608     }
609
610     if (n.equals (Query.ALL_OBJS_VAR_NAME))
611     {
612
613         // May change depending upon when it is called.
614
return this.allObjects;
615
616     }
617
618     if (this.bindVars == null)
619     {
620
621         return null;
622
623     }
624
625     if (this.parent != null)
626     {
627
628         return this.parent.getVariable (name);
629
630     }
631
632     return this.bindVars.get (n);
633
634     }
635
636     public void setVariable (String JavaDoc name,
637                  Object JavaDoc v)
638     {
639
640     if (this.parent != null)
641     {
642
643         this.parent.setVariable (name,
644                      v);
645
646         return;
647
648     }
649
650     if (this.bindVars == null)
651     {
652
653         this.bindVars = new HashMap JavaDoc ();
654
655     }
656
657     this.bindVars.put (name.toLowerCase (),
658                v);
659
660     }
661
662     public void setVariable (int index,
663                  Object JavaDoc v)
664     {
665
666     if (this.parent != null)
667     {
668
669         this.parent.setVariable (index,
670                      v);
671
672         return;
673
674     }
675
676     this.setVariable (Query.INT_BIND_VAR_PREFIX + index,
677               v);
678
679     }
680
681     public Map JavaDoc getVariables ()
682     {
683
684     if (this.parent != null)
685     {
686
687         return this.parent.getVariables ();
688
689     }
690
691     return this.bindVars;
692
693     }
694
695     public boolean isWhereTrue (Object JavaDoc o)
696                                 throws QueryExecutionException
697     {
698
699     if (this.where == null)
700     {
701
702         // A null where means yes!
703
return true;
704
705     }
706
707     return this.where.isTrue (o,
708                   this);
709
710     }
711
712     public void setVariables (Map JavaDoc bVars)
713     {
714
715     if (this.parent != null)
716     {
717
718         this.parent.setVariables (bVars);
719
720         return;
721
722     }
723
724     this.bindVars = bVars;
725
726     }
727
728     /**
729      * Execute all the expressions for the specified type, either: {@link #ALL} or:
730      * {@link #RESULTS}. If the expressions are aliased then the results will be
731      * available in the save results upon completion.
732      *
733      * @param l The List of objects to execute the functions on.
734      * @param t The type of expressions to execute.
735      * @throws QueryExecutionException If there is an issue with executing one of the
736      * expressions or if the Query hasn't been inited yet.
737      */

738     public void doExecuteOn (List JavaDoc l,
739                  String JavaDoc t)
740                          throws QueryExecutionException
741     {
742
743     if (!this.isParsed)
744     {
745
746         throw new QueryExecutionException ("Query has not been initialised.");
747
748     }
749
750     if (this.executeOn != null)
751     {
752
753         // Set the "all objects".
754
this.allObjects = l;
755
756         long s = System.currentTimeMillis ();
757
758         List JavaDoc fs = (List JavaDoc) this.executeOn.get (t);
759
760         if (fs != null)
761         {
762
763         // Execute each one in turn.
764
int si = fs.size ();
765
766         for (int i = 0; i < si; i++)
767         {
768
769             AliasedExpression f = (AliasedExpression) fs.get (i);
770
771             Object JavaDoc o = f.getValue (null,
772                        this);
773             
774             String JavaDoc af = f.getAlias ();
775
776             if (af != null)
777             {
778
779             this.setSaveValue (af,
780                        o);
781             
782             }
783             
784         }
785
786         this.addTiming ("Total time to execute: " + si + " expression(s) on " + t + " objects",
787                 System.currentTimeMillis () - s);
788
789         }
790
791     }
792
793     }
794
795     /**
796      * Clear all the results, it is important to call this method when you have completed
797      * execution of the Query if you are planning to re-use the Query (i.e. that it will
798      * NOT fall out of scope and be automatically cleaned up by the GC).
799      * If you don't call this method then references to your objects will be kept and
800      * prevent transient objects from being "cleaned" up by the GC until the next time
801      * that this Query is executed. You have been warned!
802      */

803     public void clearResults ()
804     {
805
806     this.qd = null;
807
808     this.currentObject = null;
809     this.allObjects = null;
810
811     }
812
813     /**
814      * Execute this query on the specified objects.
815      *
816      * @param objs The list of objects to execute the query on.
817      * @return The list of objects that match the query.
818      * @throws QueryExecutionException If the query cannot be executed.
819      */

820     public QueryResults execute (List JavaDoc objs)
821                              throws QueryExecutionException
822     {
823
824     if ((objs == null)
825         &&
826         (this.objClass != null)
827        )
828     {
829
830         throw new QueryExecutionException ("List of objects must be non-null when an object class is specified.");
831
832     }
833
834     this.qd = new QueryResults ();
835
836     if ((this.objClass == null)
837         &&
838         (objs == null)
839        )
840     {
841
842         objs = Query.nullQueryList;
843
844     }
845
846     this.allObjects = objs;
847
848     long s = System.currentTimeMillis ();
849
850     // See if we have any expressions that are to be executed on
851
// the complete set.
852
if (this.executeOn != null)
853     {
854
855         this.doExecuteOn (objs,
856                   Query.ALL);
857
858     }
859
860     s = System.currentTimeMillis ();
861
862     if (this.where != null)
863     {
864
865         int si = objs.size ();
866
867         // Create the where results with "about" half the size of the input collection.
868
// Further optimizations may be possible here if some statistics are collected
869
// about how many objects match/fail the where clause and then increase the
870
// capacity of the where results list as required, i.e. to cut down on the number
871
// of array copy and allocation operations performed. For now though half will do ;)
872
this.qd.whereResults = new ArrayList JavaDoc (si / 2);
873
874         for (int i = 0; i < si; i++)
875         {
876
877         Object JavaDoc o = objs.get (i);
878
879         this.currentObject = o;
880
881         boolean res = this.where.isTrue (o,
882                          this);
883
884         if (res)
885         {
886
887             this.qd.whereResults.add (o);
888
889         }
890
891         }
892
893     } else {
894
895         // No limiting where clause so what's passed in is what comes out.
896
this.qd.whereResults = objs;
897
898     }
899
900     double wet = (double) System.currentTimeMillis () - (double) s;
901
902     this.addTiming ("Total time to execute Where clause on all objects",
903             wet);
904     this.addTiming ("Where took average over: " + objs.size () + " objects",
905             wet / (double) objs.size ());
906
907     this.allObjects = this.qd.whereResults;
908
909     // The results here are the result of executing the where clause, if present.
910
this.qd.results = this.qd.whereResults;
911
912     // See if we have any functions that are to be executed on
913
// the results...
914
if (this.executeOn != null)
915     {
916
917         this.doExecuteOn (this.qd.results,
918                   Query.RESULTS);
919
920     }
921
922     // If we have a "having" clause execute it here...
923
if (this.having != null)
924     {
925
926         int si = this.qd.results.size ();
927
928         this.qd.havingResults = new ArrayList JavaDoc (si);
929
930         for (int i = 0; i < si; i++)
931         {
932
933         Object JavaDoc o = this.qd.results.get (i);
934
935         this.currentObject = o;
936
937         if (this.having.isTrue (o,
938                     this))
939         {
940
941             this.qd.havingResults.add (o);
942
943         }
944
945         }
946
947         this.qd.results = this.qd.havingResults;
948
949         // Future proofing...
950
this.allObjects = this.qd.results;
951
952     }
953
954     // Now perform the group by operation.
955
if (this.grouper != null)
956     {
957
958         // Need to handle the fact that this will return a Map of Lists...
959
try
960         {
961
962         s = System.currentTimeMillis ();
963
964         Map JavaDoc mres = this.grouper.group (this.qd.results);
965
966         this.qd.groupByResults = mres;
967
968         List JavaDoc grpBys = new ArrayList JavaDoc (mres.keySet ());
969
970         // Now order the group bys, if present.
971
if (this.groupOrderByComp != null)
972         {
973
974             Map JavaDoc origSvs = this.qd.saveValues;
975
976             Collections.sort (grpBys,
977                       this.groupOrderByComp);
978
979             // "Restore" the save values.
980
this.qd.saveValues = origSvs;
981
982             GroupByExpressionComparator lec = (GroupByExpressionComparator) this.groupOrderByComp;
983
984             if (lec.getException () != null)
985             {
986
987             throw new QueryExecutionException ("Unable to order group bys, remember that the current object here is a java.util.List, not the class defined in the FROM clause, you may need to use the org.josq.functions.CollectionFunctions.get(java.util.List,Number) function to get access to the relevant value from the List.",
988                                lec.getException ());
989
990             }
991
992             lec.clearCache ();
993
994         }
995
996         // Now limit the group bys, if required.
997
if (this.groupByLimit != null)
998         {
999
1000            s = System.currentTimeMillis ();
1001            
1002            grpBys = this.groupByLimit.getSubList (grpBys,
1003                               this);
1004            
1005            this.addTiming ("Total time to limit group by results size",
1006                    System.currentTimeMillis () - s);
1007
1008        }
1009
1010        long t = System.currentTimeMillis ();
1011
1012        this.addTiming ("Group operation took",
1013                (double) (t - s));
1014
1015        s = System.currentTimeMillis ();
1016
1017        // Convert the keys in the group by to a List.
1018
if ((this.orderByComp != null)
1019            ||
1020            (!this.retObjs)
1021           )
1022        {
1023
1024            Map JavaDoc origSvs = this.qd.saveValues;
1025
1026            Map JavaDoc nres = new LinkedHashMap JavaDoc ();
1027
1028            int gs = grpBys.size ();
1029
1030            for (int i = 0; i < gs; i++)
1031            {
1032
1033            List JavaDoc l = (List JavaDoc) grpBys.get (i);
1034
1035            List JavaDoc lr = (List JavaDoc) mres.get (l);
1036
1037            this.allObjects = lr;
1038            this.currGroupBys = l;
1039
1040            // Now set the save values for the group bys.
1041
if (this.qd.groupBySaveValues == null)
1042            {
1043
1044                this.qd.groupBySaveValues = new HashMap JavaDoc ();
1045
1046            }
1047
1048            this.qd.saveValues = new HashMap JavaDoc ();
1049
1050            this.qd.groupBySaveValues.put (l,
1051                               this.qd.saveValues);
1052
1053            // Now execute all (any) group by results functions.
1054
this.doExecuteOn (lr,
1055                      Query.GROUP_BY_RESULTS);
1056
1057            // Now sort these according to the order by (if any).
1058
if ((lr.size () > 1)
1059                &&
1060                (this.orderByComp != null)
1061               )
1062            {
1063
1064                Collections.sort (lr,
1065                          this.orderByComp);
1066
1067                ListExpressionComparator lec = (ListExpressionComparator) this.orderByComp;
1068
1069                if (lec.getException () != null)
1070                {
1071
1072                throw new QueryExecutionException ("Unable to order group by results",
1073                                   lec.getException ());
1074
1075                }
1076
1077                lec.clearCache ();
1078
1079            }
1080
1081            if (!this.retObjs)
1082            {
1083
1084                // Now collect the values...
1085
Collection JavaDoc res = null;
1086
1087                if (!this.distinctResults)
1088                {
1089
1090                res = new ArrayList JavaDoc ();
1091
1092                } else {
1093
1094                res = new LinkedHashSet JavaDoc ();
1095
1096                }
1097
1098                this.getColumnValues (lr,
1099                          res);
1100
1101                if (this.distinctResults)
1102                {
1103
1104                lr = new ArrayList JavaDoc (res);
1105
1106                } else {
1107
1108                lr = (List JavaDoc) res;
1109
1110                }
1111
1112            } else {
1113
1114                if (this.distinctResults)
1115                {
1116
1117                this.qd.results = ((CollectionFunctions) this.getFunctionHandler (CollectionFunctions.HANDLER_ID)).unique (this.qd.results);
1118
1119                }
1120
1121            }
1122
1123            if (this.limit != null)
1124            {
1125            
1126                lr = this.limit.getSubList (lr,
1127                            this);
1128            
1129            }
1130
1131            nres.put (l,
1132                  lr);
1133
1134            }
1135
1136            this.qd.groupByResults = nres;
1137
1138            // "Restore" the save values.
1139
this.qd.saveValues = origSvs;
1140
1141        }
1142
1143        t = System.currentTimeMillis ();
1144
1145        this.addTiming ("Group column collection and sort took",
1146                (double) (t - s));
1147
1148        this.qd.results = grpBys;
1149
1150        return this.qd;
1151
1152        } catch (Exception JavaDoc e) {
1153
1154        throw new QueryExecutionException ("Unable to perform group by operation",
1155                           e);
1156
1157        }
1158
1159    }
1160
1161    // Now perform the order by.
1162
if ((this.qd.results.size () > 1)
1163        &&
1164        (this.orderByComp != null)
1165       )
1166    {
1167
1168        s = System.currentTimeMillis ();
1169
1170        // It should be noted here that the comparator will set the
1171
// "current object" so that it can be used in the order by
1172
// clause.
1173
Collections.sort (this.qd.results,
1174                  this.orderByComp);
1175
1176        this.addTiming ("Total time to order results",
1177                System.currentTimeMillis () - s);
1178
1179    }
1180
1181    if (this.orderByComp != null)
1182    {
1183
1184        ListExpressionComparator lec = (ListExpressionComparator) this.orderByComp;
1185
1186        if (lec.getException () != null)
1187        {
1188
1189        throw new QueryExecutionException ("Unable to order results",
1190                           lec.getException ());
1191
1192        }
1193
1194        lec.clearCache ();
1195
1196    }
1197
1198    // Finally, if we have a limit clause, restrict the set of objects returned...
1199
if (this.limit != null)
1200    {
1201
1202        s = System.currentTimeMillis ();
1203
1204        this.qd.results = this.limit.getSubList (this.qd.results,
1205                             this);
1206
1207        this.addTiming ("Total time to limit results size",
1208                System.currentTimeMillis () - s);
1209
1210    }
1211
1212    boolean retNewObjs = false;
1213
1214    // See if we are a single column of new objects.
1215
if (!this.retObjs)
1216    {
1217
1218        if (this.cols.size () == 1)
1219        {
1220
1221        SelectItemExpression sei = (SelectItemExpression) this.cols.get (0);
1222        
1223        if (sei.getExpression () instanceof NewObjectExpression)
1224        {
1225
1226            retNewObjs = true;
1227            
1228        }
1229
1230        }
1231
1232    }
1233
1234    // Now get the columns if necessary, we do this here to get the minimum
1235
// set of objects required.
1236
if ((!this.retObjs)
1237        &&
1238        (!retNewObjs)
1239       )
1240    {
1241
1242        s = System.currentTimeMillis ();
1243
1244        Collection JavaDoc resC = null;
1245
1246        if (!this.distinctResults)
1247        {
1248
1249        resC = new ArrayList JavaDoc (this.qd.results.size ());
1250
1251        } else {
1252
1253        resC = new LinkedHashSet JavaDoc (this.qd.results.size ());
1254
1255        }
1256
1257        // Get the column values.
1258
this.getColumnValues (this.qd.results,
1259                  resC);
1260
1261        if (this.distinctResults)
1262        {
1263
1264        this.qd.results = new ArrayList JavaDoc (resC);
1265
1266        } else {
1267
1268        this.qd.results = (List JavaDoc) resC;
1269
1270        }
1271
1272        this.addTiming ("Collection of results took",
1273                (double) (System.currentTimeMillis () - s));
1274
1275    } else {
1276
1277        if (this.retObjs)
1278        {
1279
1280        if (this.distinctResults)
1281        {
1282
1283            s = System.currentTimeMillis ();
1284            
1285            this.qd.results = ((CollectionFunctions) this.getFunctionHandler (CollectionFunctions.HANDLER_ID)).unique (this.qd.results);
1286            
1287            this.addTiming ("Collecting unique results took",
1288                    (double) (System.currentTimeMillis () - s));
1289
1290        }
1291
1292        }
1293
1294        // If we want a single column of new objects...
1295
if (retNewObjs)
1296        {
1297
1298        this.qd.results = this.getNewObjectSingleColumnValues (this.qd.results);
1299
1300        }
1301
1302    }
1303
1304    try
1305    {
1306
1307        return this.qd;
1308
1309    } finally {
1310
1311        // Clean up ;)
1312
this.clearResults ();
1313
1314    }
1315
1316    }
1317
1318    public void setCurrentGroupByObjects (List JavaDoc objs)
1319    {
1320
1321    this.currGroupBys = objs;
1322
1323    }
1324
1325    public List JavaDoc getAllObjects ()
1326    {
1327
1328    return this.allObjects;
1329
1330    }
1331
1332    public void setAllObjects (List JavaDoc objs)
1333    {
1334
1335    this.allObjects = objs;
1336
1337    }
1338
1339    public void setCurrentObject (Object JavaDoc o)
1340    {
1341
1342    this.currentObject = o;
1343
1344    }
1345
1346    public Object JavaDoc getCurrentObject ()
1347    {
1348
1349    return this.currentObject;
1350
1351    }
1352
1353    private void getColumnValues (List JavaDoc res,
1354                  Collection JavaDoc rs)
1355                              throws QueryExecutionException
1356    {
1357
1358    int s = res.size ();
1359
1360    int cs = this.cols.size ();
1361
1362    boolean addItems = false;
1363
1364    for (int i = 0; i < s; i++)
1365    {
1366
1367        Object JavaDoc o = res.get (i);
1368
1369        this.currentObject = o;
1370
1371        List JavaDoc sRes = new ArrayList JavaDoc (cs);
1372
1373        for (int j = 0; j < cs; j++)
1374        {
1375
1376        SelectItemExpression v = (SelectItemExpression) this.cols.get (j);
1377
1378        try
1379        {
1380
1381            if (v.isAddItemsFromCollectionOrMap ())
1382            {
1383
1384            addItems = true;
1385                
1386            }
1387            
1388            // Get the value from the object...
1389
Object JavaDoc ov = v.getValue (o,
1390                        this);
1391            
1392            if (addItems)
1393            {
1394                
1395            rs.addAll (v.getAddItems (ov));
1396            
1397            } else {
1398            
1399            sRes.add (ov);
1400            
1401            }
1402            
1403            // Now since the expression can set the current object, put it
1404
// back to rights after the call...
1405
this.currentObject = o;
1406
1407        } catch (Exception JavaDoc e) {
1408
1409            throw new QueryExecutionException ("Unable to get value for column: " +
1410                               j +
1411                               " for: " +
1412                               v.toString () +
1413                               " from result: " +
1414                               i +
1415                               " (" +
1416                               o +
1417                               ")",
1418                               e);
1419                               
1420        }
1421        
1422        }
1423
1424        if (!addItems)
1425        {
1426
1427        rs.add (sRes);
1428
1429        }
1430
1431    }
1432
1433    }
1434
1435    private List JavaDoc getNewObjectSingleColumnValues (List JavaDoc rows)
1436                                             throws QueryExecutionException
1437    {
1438
1439    int s = rows.size ();
1440
1441    SelectItemExpression nsei = (SelectItemExpression) this.cols.get (0);
1442
1443    List JavaDoc res = new ArrayList JavaDoc (s);
1444
1445    for (int i = 0; i < s; i++)
1446    {
1447
1448        Object JavaDoc o = rows.get (i);
1449
1450        this.currentObject = o;
1451
1452        try
1453        {
1454
1455        res.add (nsei.getValue (o,
1456                    this));
1457
1458        // Now since the expression can set the current object, put it
1459
// back to rights after the call...
1460
this.currentObject = o;
1461
1462        } catch (Exception JavaDoc e) {
1463
1464        throw new QueryExecutionException ("Unable to get value for column: " +
1465                           1 +
1466                           " for: " +
1467                           nsei.toString () +
1468                           " from result: " +
1469                           i +
1470                           " (" +
1471                           o +
1472                           ")",
1473                           e);
1474                               
1475        }
1476
1477    }
1478
1479    return res;
1480
1481    }
1482
1483    public void setSaveValues (Map JavaDoc s)
1484    {
1485
1486    if (this.parent != null)
1487    {
1488
1489        this.parent.qd.saveValues.putAll (s);
1490
1491        return;
1492
1493    }
1494
1495    this.qd.saveValues = s;
1496
1497    }
1498
1499    public void setSaveValue (Object JavaDoc id,
1500                  Object JavaDoc value)
1501    {
1502
1503    if (this.parent != null)
1504    {
1505
1506        this.parent.setSaveValue (id,
1507                      value);
1508
1509        return;
1510
1511    }
1512
1513    if (this.qd == null)
1514    {
1515
1516        return;
1517
1518    }
1519
1520    if (id instanceof String JavaDoc)
1521    {
1522
1523        id = ((String JavaDoc) id).toLowerCase ();
1524
1525    }
1526
1527    Object JavaDoc old = this.qd.saveValues.get (id);
1528
1529    this.qd.saveValues.put (id,
1530                value);
1531
1532    if (old != null)
1533    {
1534
1535        this.fireSaveValueChangedEvent (id,
1536                        old,
1537                        value);
1538
1539    }
1540
1541    }
1542
1543    protected void fireSaveValueChangedEvent (Object JavaDoc id,
1544                          Object JavaDoc from,
1545                          Object JavaDoc to)
1546    {
1547
1548    List JavaDoc l = (List JavaDoc) this.listeners.get ("svs");
1549
1550    if ((l == null)
1551        ||
1552        (l.size () == 0)
1553       )
1554    {
1555
1556        return;
1557
1558    }
1559
1560    SaveValueChangedEvent svce = new SaveValueChangedEvent (this,
1561                                id.toString ().toLowerCase (),
1562                                from,
1563                                to);
1564
1565    for (int i = 0; i < l.size (); i++)
1566    {
1567
1568        SaveValueChangedListener svcl = (SaveValueChangedListener) l.get (i);
1569
1570        svcl.saveValueChanged (svce);
1571
1572    }
1573
1574    }
1575
1576    protected void fireBindVariableChangedEvent (String JavaDoc name,
1577                         Object JavaDoc from,
1578                         Object JavaDoc to)
1579    {
1580
1581    List JavaDoc l = (List JavaDoc) this.listeners.get ("bvs");
1582
1583    if ((l == null)
1584        ||
1585        (l.size () == 0)
1586       )
1587    {
1588
1589        return;
1590
1591    }
1592
1593    BindVariableChangedEvent bvce = new BindVariableChangedEvent (this,
1594                                      name,
1595                                      from,
1596                                      to);
1597
1598    for (int i = 0; i < l.size (); i++)
1599    {
1600
1601        BindVariableChangedListener bvcl = (BindVariableChangedListener) l.get (i);
1602
1603        bvcl.bindVariableChanged (bvce);
1604
1605    }
1606
1607    }
1608
1609    /**
1610     * Get the save value for a particular key and group by list.
1611     *
1612     * @param id The id of the save value.
1613     * @param gbs The group by list key.
1614     * @return The object the key maps to.
1615     */

1616    public Object JavaDoc getGroupBySaveValue (Object JavaDoc id,
1617                       List JavaDoc gbs)
1618    {
1619
1620    if (this.parent != null)
1621    {
1622
1623        return this.getGroupBySaveValue (id,
1624                         gbs);
1625
1626    }
1627
1628    Map JavaDoc m = this.getGroupBySaveValues (gbs);
1629
1630    if (m == null)
1631    {
1632
1633        return null;
1634
1635    }
1636
1637    return m.get (id);
1638
1639    }
1640
1641    public Map JavaDoc getGroupBySaveValues (List JavaDoc gbs)
1642    {
1643
1644    if (this.parent != null)
1645    {
1646
1647        return this.parent.getGroupBySaveValues (gbs);
1648
1649    }
1650
1651    if ((this.qd == null)
1652        ||
1653        (this.qd.groupBySaveValues == null)
1654       )
1655    {
1656
1657        return null;
1658
1659    }
1660
1661    return (Map JavaDoc) this.qd.groupBySaveValues.get (gbs);
1662
1663    }
1664
1665    /**
1666     * Get the save values for a particular key.
1667     *
1668     * @return The object the key maps to.
1669     */

1670    public Object JavaDoc getSaveValue (Object JavaDoc id)
1671    {
1672
1673    if (this.parent != null)
1674    {
1675
1676        return this.parent.getSaveValue (id);
1677
1678    }
1679
1680    if ((this.qd == null)
1681        ||
1682        (this.qd.saveValues == null)
1683       )
1684    {
1685
1686        return null;
1687
1688    }
1689
1690    if (id instanceof String JavaDoc)
1691    {
1692
1693        id = ((String JavaDoc) id).toLowerCase ();
1694
1695    }
1696
1697    return this.qd.saveValues.get (id);
1698
1699    }
1700
1701    public String JavaDoc getQuery ()
1702    {
1703
1704    return this.query;
1705
1706    }
1707
1708    /**
1709     * Will cause the order by comparator used to order the results
1710     * to be initialized. This is generally only useful if you are specifying the
1711     * the order bys yourself via: {@link #setOrderByColumns(List)}. Usage of
1712     * this method is <b>NOT</b> supported, so don't use unless you really know what
1713     * you are doing!
1714     */

1715    public void initOrderByComparator ()
1716                                   throws QueryParseException
1717    {
1718
1719    if (this.orderBys != null)
1720    {
1721        
1722        // No caching, this may need to change in the future.
1723
this.orderByComp = new ListExpressionComparator (this,
1724                                 false);
1725
1726        ListExpressionComparator lec = (ListExpressionComparator) this.orderByComp;
1727
1728        boolean allAccs = false;
1729
1730        // Need to check the type of each order by, if we have
1731
// any "column" indexes check to see if they are an accessor...
1732
int si = this.orderBys.size ();
1733
1734        for (int i = 0; i < si; i++)
1735        {
1736
1737        OrderBy ob = (OrderBy) this.orderBys.get (i);
1738
1739        // Get the expression...
1740
Expression e = (Expression) ob.getExpression ();
1741
1742        if (e == null)
1743        {
1744
1745            // Now expect an integer that refers to a column
1746
// in the select...
1747
int ci = ob.getIndex ();
1748
1749            if (ci == 0)
1750            {
1751
1752            throw new QueryParseException ("Order by column indices should start at 1.");
1753
1754            }
1755
1756            if (this.retObjs)
1757            {
1758
1759            throw new QueryParseException ("Cannot sort on a select column index when the objects are to be returned.");
1760
1761            }
1762
1763            if (ci > this.cols.size ())
1764            {
1765
1766            throw new QueryParseException ("Invalid order by column index: " +
1767                               ci +
1768                               ", only: " +
1769                               this.cols.size () +
1770                               " columns are selected to be returned.");
1771
1772            }
1773
1774            // Get the SelectItemExpression.
1775
SelectItemExpression sei = (SelectItemExpression) this.cols.get (ci - 1);
1776
1777            // Get the expression...
1778
e = sei.getExpression ();
1779
1780        } else {
1781
1782            // Init the expression...
1783
e.init (this);
1784
1785        }
1786
1787        // Check to see if the expression returns a fixed result, if so
1788
// there's no point adding it.
1789
if (!e.hasFixedResult (this))
1790        {
1791
1792            lec.addSortItem (e,
1793                     ob.getType ());
1794
1795        }
1796
1797        }
1798
1799    }
1800
1801    }
1802
1803    /**
1804     * Re-order the objects according to the columns supplied in the <b>dirs</b> Map.
1805     * The Map should be keyed on an Integer and map to a String value, the String value should
1806     * be either: {@link #ORDER_BY_ASC} for the column to be in ascending order or:
1807     * {@link #ORDER_BY_DESC} for the column to be in descending order. The Integer refers
1808     * to a column in the SELECT part of the statement.
1809     * <p>
1810     * For example:
1811     * <p>
1812     * <pre>
1813     * SELECT name,
1814     * directory,
1815     * file
1816     * length
1817     * FROM java.io.File
1818     * </pre>
1819     * Can be (re)ordered via the following code:
1820     * <pre>
1821     * Query q = new Query ();
1822     * q.parse (sql);
1823     *
1824     * Map reorderBys = new TreeMap ();
1825     * reorderBys.put (new Integer (2), Query.ORDER_BY_ASC);
1826     * reorderBys.put (new Integer (3), Query.ORDER_BY_DESC);
1827     * reorderBys.put (new Integer (1), Query.ORDER_BY_ASC);
1828     * reorderBys.put (new Integer (4), Query.ORDER_BY_DESC);
1829     *
1830     * // Note: this call will cause the entire statement to be executed.
1831     * q.reorder (myFiles,
1832     * reorderBys);
1833     * </pre>
1834     *
1835     * @param objs The objects you wish to reorder.
1836     * @param dirs The order bys.
1837     * @return The QueryResults.
1838     * @throws QueryParseException If the statement can be parsed, i.e. if any of the order by
1839     * columns is out of range.
1840     * @throws QueryExecutionException If the call to: {@link #execute(List)} fails.
1841     * @see #reorder(List,String)
1842     */

1843    public QueryResults reorder (List JavaDoc objs,
1844                 SortedMap JavaDoc dirs)
1845                             throws QueryExecutionException,
1846                    QueryParseException
1847    {
1848
1849    if (this.isWantObjects ())
1850    {
1851
1852        throw new QueryParseException ("Only SQL statements that return columns (not the objects passed in) can be re-ordered.");
1853
1854    }
1855
1856    List JavaDoc obs = new ArrayList JavaDoc ();
1857
1858    Iterator JavaDoc iter = dirs.keySet ().iterator ();
1859
1860    while (iter.hasNext ())
1861    {
1862
1863        Integer JavaDoc in = (Integer JavaDoc) iter.next ();
1864        
1865        // See if we have a column for it.
1866
if (in.intValue () > this.cols.size ())
1867        {
1868
1869        throw new QueryParseException ("Cannot reorder: " +
1870                           dirs.size () +
1871                           " columns, only: " +
1872                           this.cols.size () +
1873                           " are present in the SQL statement.");
1874
1875        }
1876
1877        String JavaDoc dir = (String JavaDoc) dirs.get (in);
1878        
1879        int d = OrderBy.ASC;
1880        
1881        if (dir.equals (Query.ORDER_BY_DESC))
1882        {
1883
1884        d = OrderBy.DESC;
1885        
1886        }
1887        
1888        OrderBy ob = new OrderBy ();
1889        ob.setIndex (in.intValue ());
1890        ob.setType (d);
1891        
1892        obs.add (ob);
1893        
1894    }
1895
1896    this.orderBys = obs;
1897    
1898    this.initOrderByComparator ();
1899
1900    // Execute the query.
1901
return this.execute (objs);
1902
1903    }
1904
1905    /**
1906     * Allows the re-ordering of the results via a textual representation of the order bys.
1907     * This is effectively like providing a new ORDER BY clause to the sql.
1908     * <p>
1909     * For example:
1910     * <p>
1911     * <pre>
1912     * SELECT name,
1913     * directory,
1914     * file
1915     * length
1916     * FROM java.io.File
1917     * </pre>
1918     * Can be (re)ordered via the following code:
1919     * <pre>
1920     * Query q = new Query ();
1921     * q.parse (sql);
1922     *
1923     * // Note: this call will cause the entire statement to be executed.
1924     * q.reorder (myFiles,
1925     * "name DESC, 3 ASC, length, 1 DESC");
1926     * </pre>
1927     *
1928     * @param objs The objects you wish to re-order.
1929     * @param orderBys The order bys.
1930     * @return The execution results.
1931     * @throws QueryParseException If the statement can be parsed, i.e. if any of the order by
1932     * columns is out of range or the order bys cannot be parsed.
1933     * @throws QueryExecutionException If the call to: {@link #execute(List)} fails.
1934     * @see #reorder(List,SortedMap)
1935     */

1936    public QueryResults reorder (List JavaDoc objs,
1937                 String JavaDoc orderBys)
1938                             throws QueryParseException,
1939                    QueryExecutionException
1940    {
1941
1942    String JavaDoc sql = "";
1943
1944    if (!orderBys.toLowerCase ().startsWith ("order by"))
1945    {
1946
1947        sql = sql + " ORDER BY ";
1948
1949    }
1950        
1951    sql = sql + orderBys;
1952
1953    BufferedReader JavaDoc sr = new BufferedReader JavaDoc (new StringReader JavaDoc (sql));
1954
1955    JoSQLParser parser = new JoSQLParser (sr);
1956
1957    List JavaDoc ors = null;
1958
1959    try
1960    {
1961
1962         ors = parser.OrderBys ();
1963
1964    } catch (Exception JavaDoc e) {
1965
1966        throw new QueryParseException ("Unable to parse order bys: " +
1967                       orderBys,
1968                       e);
1969
1970    }
1971
1972    this.orderBys = ors;
1973
1974    this.initOrderByComparator ();
1975
1976    // Execute the query.
1977
return this.execute (objs);
1978
1979    }
1980
1981    public void setClassLoader (ClassLoader JavaDoc cl)
1982    {
1983
1984    this.classLoader = cl;
1985
1986    }
1987
1988    public ClassLoader JavaDoc getClassLoader ()
1989    {
1990
1991    if (this.classLoader == null)
1992    {
1993
1994        // No custom classloader specified, use the one that loaded
1995
// this class.
1996
this.classLoader = this.getClass ().getClassLoader ();
1997
1998    }
1999
2000    return this.classLoader;
2001
2002    }
2003
2004    public Class JavaDoc loadClass (String JavaDoc name)
2005                        throws Exception JavaDoc
2006    {
2007
2008    return this.getClassLoader ().loadClass (name);
2009
2010    }
2011
2012    public void parse (String JavaDoc q)
2013                   throws QueryParseException
2014    {
2015
2016    this.query = q;
2017
2018    BufferedReader JavaDoc sr = new BufferedReader JavaDoc (new StringReader JavaDoc (q));
2019
2020    long s = System.currentTimeMillis ();
2021
2022    JoSQLParser parser = new JoSQLParser (sr);
2023
2024    this.addTiming ("Time to init josql parser object",
2025            System.currentTimeMillis () - s);
2026
2027    s = System.currentTimeMillis ();
2028
2029    try
2030    {
2031
2032        parser.parseQuery (this);
2033
2034    } catch (Exception JavaDoc e) {
2035
2036        throw new QueryParseException ("Unable to parse query: " +
2037                       q,
2038                       e);
2039
2040    }
2041
2042    this.isParsed = true;
2043
2044    this.addTiming ("Time to parse query into object form",
2045            System.currentTimeMillis () - s);
2046
2047    String JavaDoc className = null;
2048
2049    // If we don't have a parent, then there must be an explicit class name.
2050
if (this.parent == null)
2051    {
2052
2053        if (!(this.from instanceof ConstantExpression))
2054        {
2055
2056        throw new QueryParseException ("The FROM clause of the outer-most Query must be a string that denotes a fully-qualified class name, expression: " +
2057                           this.from +
2058                           " is not valid.");
2059
2060        }
2061
2062        // See if the class name is the special "null".
2063
String JavaDoc cn = null;
2064
2065        try
2066        {
2067
2068        // Should be safe to use a null value here (especially since we know
2069
// how ConstantExpression works ;)
2070
cn = (String JavaDoc) this.from.getValue (null,
2071                          this);
2072
2073        } catch (Exception JavaDoc e) {
2074        
2075        throw new QueryParseException ("Unable to determine FROM clause of the outer-most Query from expression: " +
2076                           this.from +
2077                           ", note: this exception shouldn't be able to happen, so something has gone SERIOUSLY wrong!",
2078                           e);
2079        
2080        }
2081
2082        if (!cn.equalsIgnoreCase ("null"))
2083        {
2084
2085        // Load the class that we are dealing with...
2086
try
2087        {
2088            
2089            this.objClass = this.loadClass (cn);
2090
2091        } catch (Exception JavaDoc e) {
2092            
2093            throw new QueryParseException ("Unable to load FROM class: " +
2094                           cn,
2095                           e);
2096            
2097        }
2098
2099        }
2100
2101    }
2102
2103    // Init the query.
2104
this.init ();
2105
2106    }
2107
2108    public void init ()
2109                      throws QueryParseException
2110    {
2111
2112    long s = System.currentTimeMillis ();
2113
2114    // Now if we have any columns, init those as well...
2115
if (!this.retObjs)
2116    {
2117
2118        int aic = 0;
2119
2120        int si = this.cols.size ();
2121
2122        this.aliases = new HashMap JavaDoc ();
2123
2124        for (int i = 0; i < si; i++)
2125        {
2126
2127        SelectItemExpression exp = (SelectItemExpression) this.cols.get (i);
2128
2129        exp.init (this);
2130
2131        if (exp.isAddItemsFromCollectionOrMap ())
2132        {
2133
2134            aic++;
2135
2136        }
2137
2138        String JavaDoc alias = exp.getAlias ();
2139
2140        if (alias != null)
2141        {
2142
2143            this.aliases.put (alias,
2144                      new Integer JavaDoc (i + 1));
2145
2146        }
2147
2148        this.aliases.put ((i + 1) + "",
2149                  new Integer JavaDoc (i + 1));
2150
2151        }
2152
2153        if ((aic > 0)
2154        &&
2155        (aic != si)
2156           )
2157        {
2158
2159        throw new QueryParseException ("If one or more SELECT clause columns is set to add the items returned from a: " +
2160                           Map JavaDoc.class.getName () +
2161                           " or: " +
2162                           java.util.Collection JavaDoc.class.getName () +
2163                           " then ALL columns must be marked to return the items as well.");
2164
2165        }
2166
2167    }
2168
2169    // Now init the where clause (where possible)...
2170
if (this.where != null)
2171    {
2172
2173        this.where.init (this);
2174
2175    }
2176
2177    // Now init the having clause (where possible)...
2178
if (this.having != null)
2179    {
2180
2181        this.having.init (this);
2182
2183    }
2184
2185    // See if we have order by columns, if so init the comparator.
2186
this.initOrderByComparator ();
2187
2188    // See if we have order by columns, if so init the comparator.
2189
if (this.groupBys != null)
2190    {
2191
2192        this.grouper = new Grouper (this);
2193
2194        int si = this.groupBys.size ();
2195
2196        for (int i = 0; i < si; i++)
2197        {
2198
2199        OrderBy ob = (OrderBy) this.groupBys.get (i);
2200
2201        // Get the expression...
2202
Expression e = (Expression) ob.getExpression ();
2203
2204        if (e == null)
2205        {
2206
2207            // Now expect an integer that refers to a column
2208
// in the select...
2209
int ci = ob.getIndex ();
2210
2211            if (ci == 0)
2212            {
2213
2214            throw new QueryParseException ("Order by column indices should start at 1.");
2215
2216            }
2217
2218            if (this.retObjs)
2219            {
2220
2221            throw new QueryParseException ("Cannot sort on a select column index when the objects are to be returned.");
2222
2223            }
2224
2225            if (ci > this.cols.size ())
2226            {
2227
2228            throw new QueryParseException ("Invalid order by column index: " +
2229                               ci +
2230                               ", only: " +
2231                               this.cols.size () +
2232                               " columns are selected to be returned.");
2233
2234            }
2235
2236            // Get the SelectItemExpression.
2237
SelectItemExpression sei = (SelectItemExpression) this.cols.get (ci - 1);
2238
2239            // Get the expression...
2240
e = sei.getExpression ();
2241
2242        } else {
2243
2244            // Init the expression...
2245
e.init (this);
2246
2247        }
2248
2249        this.grouper.addExpression (e);
2250
2251        }
2252
2253    }
2254
2255    if (this.groupOrderBys != null)
2256    {
2257
2258        if (this.grouper == null)
2259        {
2260
2261        throw new QueryParseException ("Group Order Bys are only valid if 1 or more Group By columns have been specified.");
2262
2263        }
2264
2265        // Here we "override" the from class because when dealing with the order bys the
2266
// current object will be a List, NOT the class defined in the FROM clause.
2267
Class JavaDoc c = this.objClass;
2268
2269        this.objClass = List JavaDoc.class;
2270
2271        // No caching, this may need to change in the future.
2272
this.groupOrderByComp = new GroupByExpressionComparator (this,
2273                                     false);
2274
2275        GroupByExpressionComparator lec = (GroupByExpressionComparator) this.groupOrderByComp;
2276
2277        List JavaDoc grouperExps = this.grouper.getExpressions ();
2278
2279        // Need to check the type of each order by, if we have
2280
// any "column" indexes check to see if they are an accessor...
2281
int si = this.groupOrderBys.size ();
2282
2283        for (int i = 0; i < si; i++)
2284        {
2285
2286        Integer JavaDoc k = new Integer JavaDoc (i);
2287
2288        OrderBy ob = (OrderBy) this.groupOrderBys.get (i);
2289
2290        if (ob.getIndex () > -1)
2291        {
2292
2293            int ci = ob.getIndex ();
2294
2295            if (ci == 0)
2296            {
2297
2298            throw new QueryParseException ("Group Order by column indices should start at 1.");
2299            
2300            }
2301
2302            if (ci > grouperExps.size ())
2303            {
2304
2305            throw new QueryParseException ("Invalid Group Order By column index: " +
2306                               ci +
2307                               ", only: " +
2308                               grouperExps.size () +
2309                               " Group By columns are selected to be returned.");
2310            
2311            }
2312
2313            lec.addSortItem (null,
2314                     // Remember the -1! Column indices start at 1 but
2315
// List indices start at 0 ;)
2316
ci - 1,
2317                     ob.getType ());
2318
2319            continue;
2320
2321        }
2322
2323        // Get the expression...
2324
Expression e = (Expression) ob.getExpression ();
2325
2326        // See if the expression is a "direct" match for any of the
2327
// group by columns.
2328
boolean cont = true;
2329
2330        for (int j = 0; j < grouperExps.size (); j++)
2331        {
2332
2333            Expression exp = (Expression) grouperExps.get (j);
2334            
2335            if (e.equals (exp))
2336            {
2337
2338            // This is a match, add to the comparator.
2339
lec.addSortItem (null,
2340                     j,
2341                     ob.getType ());
2342            
2343            cont = false;
2344
2345            }
2346
2347        }
2348
2349        if (!cont)
2350        {
2351
2352            continue;
2353
2354        }
2355
2356        if ((e instanceof Function)
2357            ||
2358            (e instanceof BindVariable)
2359            ||
2360            (e instanceof SaveValue)
2361           )
2362        {
2363
2364            e.init (this);
2365
2366            lec.addSortItem (e,
2367                     -1,
2368                     ob.getType ());
2369
2370            continue;
2371
2372        }
2373
2374        // If we are here then we haven't been able to deal with the
2375
// order by... so barf.
2376
throw new QueryParseException ("If the Group Order By: " +
2377                           ob +
2378                           " is not a function, a bind variable or a save value then it must be present in the Group By list.");
2379
2380        }
2381
2382        // Restore the FROM object class.
2383
this.objClass = c;
2384
2385    }
2386
2387    if (this.groupByLimit != null)
2388    {
2389
2390        this.groupByLimit.init (this);
2391
2392    }
2393
2394    if (this.limit != null)
2395    {
2396
2397        this.limit.init (this);
2398
2399    }
2400
2401    if (this.executeOn != null)
2402    {
2403
2404        // Get the supported types.
2405
List JavaDoc allF = (List JavaDoc) this.executeOn.get (Query.ALL);
2406
2407        if (allF != null)
2408        {
2409
2410        // We have some, so init them...
2411
int si = allF.size ();
2412
2413        for (int i = 0; i < si; i++)
2414        {
2415
2416            AliasedExpression f = (AliasedExpression) allF.get (i);
2417
2418            f.init (this);
2419
2420        }
2421
2422        }
2423
2424        List JavaDoc resultsF = (List JavaDoc) this.executeOn.get (Query.RESULTS);
2425
2426        if (resultsF != null)
2427        {
2428
2429        // We have some, so init them...
2430
int si = resultsF.size ();
2431
2432        for (int i = 0; i < si; i++)
2433        {
2434
2435            AliasedExpression f = (AliasedExpression) resultsF.get (i);
2436
2437            f.init (this);
2438
2439        }
2440
2441        }
2442
2443        resultsF = (List JavaDoc) this.executeOn.get (Query.GROUP_BY_RESULTS);
2444
2445        if (resultsF != null)
2446        {
2447
2448        // We have some, so init them...
2449
int si = resultsF.size ();
2450
2451        for (int i = 0; i < si; i++)
2452        {
2453
2454            AliasedExpression f = (AliasedExpression) resultsF.get (i);
2455
2456            f.init (this);
2457
2458        }
2459
2460        }
2461
2462    }
2463
2464    this.addTiming ("Time to init Query objects",
2465            System.currentTimeMillis () - s);
2466
2467    }
2468
2469    /**
2470     * Set the "FROM" object class. It is advised that you NEVER call this method, do so
2471     * at your own risk, dragons will swoop from the sky and crisp your innards if you do so!!!
2472     * Seriously though ;), this method should ONLY be called by those who know what they
2473     * are doing, whatever you think you know about how this method operates is irrelevant
2474     * which is why the dangers of calling this method are not documented...
2475     * <p>
2476     * YOU HAVE BEEN WARNED!!! NO BUGS WILL BE ACCEPTED THAT ARISE FROM THE CALLING OF
2477     * THIS METHOD!!!
2478     *
2479     * @param c The FROM class.
2480     */

2481    public void setFromObjectClass (Class JavaDoc c)
2482    {
2483
2484    this.objClass = c;
2485
2486    }
2487
2488    public Class JavaDoc getFromObjectClass ()
2489    {
2490
2491    return this.objClass;
2492
2493    }
2494
2495    public void removeBindVariableChangedListener (BindVariableChangedListener bvl)
2496    {
2497
2498    List JavaDoc l = (List JavaDoc) this.listeners.get ("bvs");
2499
2500    if (l == null)
2501    {
2502
2503        return;
2504
2505    }
2506
2507    l.remove (bvl);
2508
2509    }
2510
2511    public void addBindVariableChangedListener (BindVariableChangedListener bvl)
2512    {
2513
2514    List JavaDoc l = (List JavaDoc) this.listeners.get ("bvs");
2515
2516    if (l == null)
2517    {
2518
2519        l = new ArrayList JavaDoc ();
2520
2521        this.listeners.put ("bvs",
2522                l);
2523
2524    }
2525
2526    if (!l.contains (bvl))
2527    {
2528
2529        l.add (bvl);
2530
2531    }
2532
2533    }
2534
2535    public void removeSaveValueChangedListener (SaveValueChangedListener svl)
2536    {
2537
2538    List JavaDoc l = (List JavaDoc) this.listeners.get ("svs");
2539
2540    if (l == null)
2541    {
2542
2543        return;
2544
2545    }
2546
2547    l.remove (svl);
2548
2549    }
2550
2551    public void addSaveValueChangedListener (SaveValueChangedListener svl)
2552    {
2553
2554    List JavaDoc l = (List JavaDoc) this.listeners.get ("svs");
2555
2556    if (l == null)
2557    {
2558
2559        l = new ArrayList JavaDoc ();
2560
2561        this.listeners.put ("svs",
2562                l);
2563
2564    }
2565
2566    if (!l.contains (svl))
2567    {
2568
2569        l.add (svl);
2570
2571    }
2572
2573    }
2574
2575    /*
2576    public class ExecutionInfo
2577    {
2578
2579    private long timeStamp = System.currentTimeMillis ();
2580    private String message = null;
2581    private Object creator = null;
2582
2583    public ExecutionInfo (Object creator,
2584                  String message)
2585    {
2586
2587        this.message = message;
2588        this.creator = creator;
2589
2590    }
2591
2592    public String getMessage ()
2593    {
2594
2595        return this.message;
2596
2597    }
2598
2599    public Object getCreator ()
2600    {
2601
2602        return this.creator;
2603
2604    }
2605
2606    public long getTimestamp ()
2607    {
2608
2609        return this.timeStamp;
2610
2611    }
2612
2613    }
2614    */

2615
2616    public Map JavaDoc getAliases ()
2617    {
2618
2619    return this.aliases;
2620
2621    }
2622
2623    public boolean isWantObjects ()
2624    {
2625
2626    return this.retObjs;
2627
2628    }
2629
2630    public void setWantObjects (boolean v)
2631    {
2632
2633    this.retObjs = v;
2634
2635    }
2636
2637    public char getWildcardCharacter ()
2638    {
2639
2640    return this.wildcardChar;
2641
2642    }
2643
2644    public void setWildcardCharacter (char c)
2645    {
2646
2647    this.wildcardChar = c;
2648
2649    }
2650
2651    public void setLimit (Limit l)
2652    {
2653
2654    this.limit = l;
2655
2656    }
2657
2658    public Limit getLimit ()
2659    {
2660
2661    return this.limit;
2662
2663    }
2664
2665    /**
2666     * Return whether this Query object has had a statement applied to it
2667     * and has been parsed.
2668     *
2669     * @return Whether the query is associated with a statement.
2670     */

2671    public boolean parsed ()
2672    {
2673
2674    return this.isParsed;
2675
2676    }
2677
2678    /**
2679     * Indicate whether "distinct" results are required.
2680     *
2681     * @param v Set to <code>true</code> to make the results distinct.
2682     */

2683    public void setWantDistinctResults (boolean v)
2684    {
2685
2686    this.distinctResults = v;
2687
2688    }
2689
2690    public QueryResults getQueryResults ()
2691    {
2692
2693    return this.qd;
2694
2695    }
2696
2697    /**
2698     * Get the "order bys". This will return a List of {@link OrderBy} objects.
2699     * This is generally only useful when you want to {@link #reorder(List,String)}
2700     * the search and wish to get access to the textual representation of the order bys.
2701     * <p>
2702     * It is therefore possible to modify the orderbys in place, perhaps by using a different
2703     * expression or changing the direction (since the objects are not cloned before being
2704     * returned). However do so at <b>YOUR OWN RISK</b>. If you do so, then ensure you
2705     * call: {@link #setOrderByColumns(List)}, then: {@link #initOrderByComparator()}
2706     * before re-executing the statement, otherwise nothing will happen!
2707     *
2708     * @return The order bys.
2709     */

2710    public List JavaDoc getOrderByColumns ()
2711    {
2712
2713    return new ArrayList JavaDoc (this.orderBys);
2714
2715    }
2716
2717    public void setParent (Query q)
2718    {
2719
2720    this.parent = q;
2721
2722    }
2723
2724    public Query getParent ()
2725    {
2726
2727    return this.parent;
2728
2729    }
2730
2731    public Query getTopLevelQuery ()
2732    {
2733
2734    Query q = this;
2735    Query par = null;
2736
2737    while (true)
2738    {
2739
2740        par = q.getParent ();
2741
2742        if (par == null)
2743        {
2744
2745        break;
2746
2747        }
2748
2749        q = par;
2750
2751    }
2752
2753    return q;
2754
2755    }
2756
2757    public String JavaDoc toString ()
2758    {
2759
2760    StringBuffer JavaDoc buf = new StringBuffer JavaDoc ("SELECT ");
2761    
2762    if (this.distinctResults)
2763    {
2764
2765        buf.append ("DISTINCT ");
2766
2767    }
2768
2769    if (this.retObjs)
2770    {
2771
2772        buf.append ("*");
2773
2774    } else {
2775
2776        for (int i = 0; i < this.cols.size (); i++)
2777        {
2778
2779        buf.append (" ");
2780        buf.append (this.cols.get (i));
2781
2782        if (i < (this.cols.size () - 1))
2783        {
2784
2785            buf.append (",");
2786
2787        }
2788        
2789        }
2790
2791    }
2792
2793    buf.append (" FROM ");
2794    buf.append (this.from);
2795
2796    if (this.where != null)
2797    {
2798
2799        buf.append (" WHERE ");
2800    
2801        buf.append (this.where);
2802
2803    }
2804
2805    return buf.toString ();
2806
2807    }
2808
2809}
2810
Popular Tags