KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > josql > utils > JoSQLComparator


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.utils;
16
17 import java.util.List JavaDoc;
18 import java.util.Comparator JavaDoc;
19
20 import org.josql.Query;
21 import org.josql.QueryParseException;
22 import org.josql.QueryExecutionException;
23
24 import org.josql.internal.ListExpressionComparator;
25
26 /**
27  * This class allows the ORDER BY clause of a JoSQL SQL clause to be used
28  * as a Comparator. It should be noted that is the same as performing: {@link Query#execute(List)}
29  * but there are times when having a separate comparator is desirable.
30  * The EXECUTE ON ALL clause is supported but you must call: {@link #doExecuteOn(List)}
31  * first to ensure that they are executed.
32  * <p>
33  * This class is basically just a thin wrapper around using the comparator gained by
34  * calling: {@link Query#getOrderByComparator()}.
35  * <p>
36  * A note on performance, for small numbers of objects (around 1000) this comparator
37  * has (for vanilla accessors, no function calls) pretty comparable performance against a
38  * hand-coded Java Comparator that performs the same function. However start to scale the
39  * numbers of objects and performance degrades, in testing for ~34000 FileWrapper objects
40  * to order by: <code>path DESC, lastModified, name, length</code> took around: 1300ms.
41  * The hand-coded Java Comparator took around: 180ms! The upshot is, if you need flexibility
42  * and do not need to order large numbers of objects then use this kind of Comparator, if
43  * performance and numbers of objects is an issue then hand-rolling your own Comparator
44  * is probably best. As a side-note, to perform the following order by:
45  * <code>lower(path) DESC, lastModified, name, length</code> using a JoSQLComparator took:
46  * about: 1400ms. However modifying the hand-coded Comparator to use:
47  * {@link String#compareToIgnoreCase(String)} then took about 860ms! And if you using:
48  * {@link String#toLowerCase()} for each string instead, it then takes about: 1800ms!
49  * (Meaning that in certain circumstances JoSQL can be faster!)
50  * <p>
51  * <h3>Caching</h3>
52  * <p>
53  * It is not uncommon for a Comparator (even using the effecient merge-sort implementation of
54  * {@link java.util.Collections#sort(List,Comparator)}) to perform thousands (even millions!)
55  * of comparisons.<br /><br />
56  * However since JoSQL does not automatically cache the results of calls to functions and
57  * results of accessor accesses the performance of this kind of "dynamic" Comparator can
58  * quickly degrade. To mitigate this it is possible to turn "caching" on whereby the
59  * Comparator will "remember" the results of the functions on a per object basis and use those
60  * values instead of calling them again. This is not without it's downside however.
61  * Firstly since a reference to the object will be held it is important (if caching is used
62  * that you call: {@link #clearCache()} once the Comparator has been used to free up those
63  * references (it was considered using a {@link java.util.WeakHashMap} but that doesn't provide
64  * exactly what's needed here).<br /><br />
65  * It is recommended that caching is turned on when the Comparator is to be used in a sort
66  * operation , i.e. calling: {@link java.util.Collections#sort(List,Comparator)} or similar
67  * (however careful consideration needs to be given to the amount of memory that this
68  * may consume, i.e. 4 bytes = 1 object reference, plus 1 List, plus 4 bytes per order
69  * by "column" it soon adds up)<br /><br />
70  * If the comparator is to be used in a {@link java.util.TreeMap} or {@link java.util.TreeSet}
71  * then caching should not be used since the values may (and perhaps should) change over time
72  * but due to caching the order won't change.
73  * <p>
74  * Last Modified By: $Author: barrygently $<br />
75  * Last Modified On: $Date: 2005/01/20 15:27:25 $<br />
76  * Current Revision: $Revision: 1.1 $<br />
77  */

78 public class JoSQLComparator implements Comparator JavaDoc
79 {
80
81     private Query q = null;
82     private Exception JavaDoc exp = null;
83     private ListExpressionComparator c = null;
84
85     /**
86      * Execute the EXECUTE ON ALL expressions.
87      *
88      * @param l The list to execute the expressions on.
89      */

90     public void doExecuteOn (List JavaDoc l)
91                          throws QueryExecutionException
92     {
93
94     this.q.doExecuteOn (l,
95                 Query.ALL);
96
97     }
98
99     /**
100      * Clear the cache, it is VITAL that you call this method before you use
101      * the comparator (if it has been used before) otherwise data objects will
102      * be "left around" and preventing the GC from cleaning them up.
103      */

104     public void clearCache ()
105     {
106
107     if (this.q != null)
108     {
109
110         this.c.clearCache ();
111
112     }
113
114     }
115
116     /**
117      * Return whether this comparator uses caching to improve performance.
118      *
119      * @return <code>true</code> if caching is on.
120      * @throws IllegalStateException If the query has not yet been parsed or set.
121      */

122     public boolean isCaching ()
123                          throws IllegalStateException JavaDoc
124     {
125
126     if ((this.q == null)
127         ||
128         (!this.q.parsed ())
129        )
130     {
131
132         throw new IllegalStateException JavaDoc ("Query has not yet been parsed.");
133
134     }
135
136     return this.c.isCaching ();
137
138     }
139
140     /**
141      * Set whether the comparator should use caching to improve performance.
142      *
143      * @param b Set to <code>true</code> to turn caching on.
144      * @throws IllegalStateException If the query has not yet been parsed or set.
145      */

146     public void setCaching (boolean b)
147                         throws IllegalStateException JavaDoc
148     {
149
150     if ((this.q == null)
151         ||
152         (!this.q.parsed ())
153        )
154     {
155
156         throw new IllegalStateException JavaDoc ("Query has not yet been parsed.");
157
158     }
159
160     this.c.setCaching (b);
161
162     }
163
164     /**
165      * Init this filter with the query.
166      *
167      * @param q The query.
168      * @throws QueryParseException If there is an issue with the parsing of the query.
169      */

170     public JoSQLComparator (String JavaDoc q)
171                         throws QueryParseException
172     {
173
174     this.setQuery (q);
175
176     }
177
178     /**
179      * Compares the objects as according to the ORDER BY clause.
180      *
181      * @param o1 The first object.
182      * @param o2 The second object.
183      */

184     public int compare (Object JavaDoc o1,
185             Object JavaDoc o2)
186     {
187
188     try
189     {
190
191         return c.ci (o1,
192              o2);
193
194     } catch (Exception JavaDoc e) {
195
196         this.exp = e;
197
198         return 0;
199
200     }
201
202     }
203
204     /**
205      * Init this file filter with the query already built and parsed.
206      *
207      * @param q The query.
208      * @throws IllegalStateException If the Query object has not been parsed.
209      * @throws QueryParseException If the FROM class is not as expected.
210      */

211     public JoSQLComparator (Query q)
212                         throws IllegalStateException JavaDoc,
213                                 QueryParseException
214     {
215
216     this.setQuery (q);
217
218     }
219
220     /**
221      * The {@link Comparator#compare(Object,Object)} method does not allow for
222      * any exceptions to be thrown however since the execution of the ORDER BY clause
223      * on the objects can cause the throwing of a {@link QueryParseException} it should
224      * be captured. If the exception is thrown then this method will return it.
225      *
226      * @return The exception thrown by the execution of the ORDER BY clause in {@link #compare(Object,Object)}
227      * or by sub-class/interface specific methods, this may be null if no exception was thrown.
228      */

229     public Exception JavaDoc getException ()
230     {
231
232     return this.exp;
233
234     }
235
236     /**
237      * Set a new Query (string form) for use in this filter.
238      *
239      * @param q The Query to use.
240      * @throws QueryParseException If there is an issue with the parsing of the query,
241      * or if the FROM class is not as expected.
242      */

243     public void setQuery (String JavaDoc q)
244                       throws QueryParseException
245     {
246
247     this.q = new Query ();
248     this.q.parse (q);
249
250     this.c = (ListExpressionComparator) this.q.getOrderByComparator ();
251
252     this.exp = null;
253
254     }
255
256     /**
257      * Set a new Query object for use in this filter.
258      *
259      * @param q The Query to use.
260      * @throws IllegalStateException If the Query object has not been parsed.
261      * @throws QueryParseException If the FROM class is not as expected.
262      */

263     public void setQuery (Query q)
264                       throws IllegalStateException JavaDoc,
265                               QueryParseException
266     {
267
268     if (!q.parsed ())
269     {
270
271         throw new IllegalStateException JavaDoc ("Query has not yet been parsed.");
272
273     }
274
275     this.q = q;
276
277     this.c = (ListExpressionComparator) this.q.getOrderByComparator ();
278
279     this.exp = null;
280
281     }
282
283     /**
284      * Get the Query we are using to process objects.
285      *
286      * @return The Query.
287      */

288     public Query getQuery ()
289     {
290
291     return this.q;
292
293     }
294
295 }
296
Popular Tags