KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jimm > datavision > source > Query


1 package jimm.datavision.source;
2 import jimm.datavision.*;
3 import jimm.datavision.field.*;
4 import jimm.util.XMLWriter;
5 import java.util.*;
6
7 /**
8  * A data source query.
9  *
10  * @author Jim Menard, <a HREF="mailto:jimm@io.com">jimm@io.com</a>
11  */

12 public class Query implements Writeable {
13
14 public static final int SORT_UNDEFINED = -1;
15 public static final int SORT_DESCENDING = 0;
16 public static final int SORT_ASCENDING = 1;
17
18 protected Report report;
19 protected ArrayList joins;
20 protected String JavaDoc whereClause;
21 protected ArrayList sortSelectables;
22 protected ArrayList sortOrders;
23 protected ArrayList selectables; // Can't be a Set; we need selectable indices
24

25 /**
26  * Constructor.
27  *
28  * @param r the report for which this query will retrieve data
29  */

30 public Query(Report r) {
31     report = r;
32     joins = new ArrayList();
33     whereClause = null;
34     sortSelectables = new ArrayList();
35     sortOrders = new ArrayList();
36     selectables = new ArrayList();
37 }
38
39 /**
40  * Returns <code>true</code> if the specified parameter exists within this
41  * query's where clause.
42  *
43  * @param p a parameter
44  * @return <code>true</code> if the specified parameter exists within
45  * the where clause
46  */

47 public boolean containsReferenceTo(Parameter p) {
48     if (whereClause == null || whereClause.indexOf("{") == -1)
49     return false;
50
51     int pos, endPos;
52     for (pos = 0, endPos = -1;
53      (pos = whereClause.indexOf("{", endPos + 1)) >= 0;
54      pos = endPos + 1)
55     {
56     endPos = whereClause.indexOf("}", pos);
57     if (endPos == -1)
58         return false;
59
60     switch (whereClause.charAt(pos + 1)) {
61     case '@': // Formula
62
String JavaDoc idAsString = whereClause.substring(pos + 2, endPos);
63         Formula f = report.findFormula(idAsString);
64         if (f.refersTo(p))
65         return true;
66         break;
67     case '?': // Parameter
68
idAsString = whereClause.substring(pos + 2, endPos);
69         if (p.getId().toString().equals(idAsString))
70         return true;
71         break;
72     }
73     pos = endPos + 1;
74     }
75
76     return false;
77 }
78
79 /**
80  * Adds a join to the list of joins used by this query.
81  *
82  * @param join a join
83  */

84 public void addJoin(Join join) {
85     joins.add(join);
86 }
87
88 /**
89  * Adds all joins in the collection to our list.
90  *
91  * @param coll a collection of joins
92  */

93 public void addAllJoins(Collection coll) {
94     joins.addAll(coll);
95 }
96
97 /**
98  * Removes a join from the list of joins used by this query.
99  *
100  * @param join a join
101  */

102 public void removeJoin(Join join) {
103     joins.remove(join);
104 }
105
106 /**
107  * Removes all joins in the collection from our list.
108  */

109 public void clearJoins() {
110     joins.clear();
111 }
112
113 /**
114  * Returns an iterator over all the joins used by this query.
115  *
116  * @return an iterator over all the joins
117  */

118 public Iterator joins() { return joins.iterator(); }
119
120 /**
121  * Returns the where clause fit for human consumption. This mainly means
122  * that we substitute formula, parameter, and user column id numbers with
123  * names. Called from a where clause editor. This code assumes that curly
124  * braces are never nested.
125  *
126  * @return the eval string with formula, parameter, and user column id
127  * numbers replaced with names
128  * @see jimm.datavision.gui.sql.WhereClauseWin
129  */

130 public String JavaDoc getEditableWhereClause() {
131     return Expression.expressionToDisplay(report, whereClause);
132 }
133
134 /**
135  * Returns the raw where clause string; may be <code>null</code>.
136  *
137  * @return the where clause string; may be <code>null</code>
138  */

139 public String JavaDoc getWhereClause() {
140     return whereClause;
141 }
142
143 /**
144  * Sets the where clause (may be <code>null</code>). The string passed
145  * to us contains parameter and formula display strings, not their
146  * "real" <code>formulaString</code> representations. Translate the latter
147  * into the former before saving this string.
148  *
149  * @param newWhereClause a where clause string; may be <code>null</code>
150  */

151 public void setEditableWhereClause(String JavaDoc newWhereClause) {
152     setWhereClause(Expression.displayToExpression(report, newWhereClause));
153 }
154
155 /**
156  * Sets the where clause (may be <code>null</code>).
157  *
158  * @param newWhereClause a where clause string; may be <code>null</code>
159  */

160 public void setWhereClause(String JavaDoc newWhereClause) {
161     whereClause = newWhereClause;
162 }
163
164 /**
165  * Adds a sort order for the specified selectable. The first character
166  * of the <i>order</i> string is inspected. If it is a 'd' or 'D',
167  * then it's taken to mean "descending". Anything else will result
168  * in an "ascending" sort order.
169  *
170  * @param sel a selectable
171  * @param order either <code>SORT_DESCENDING</code> or
172  * <code>SORT_ASCENDING</code>
173  */

174 public void addSort(Selectable sel, int order) {
175     sortSelectables.add(sel);
176     sortOrders.add(new Integer JavaDoc(order));
177 }
178
179 /**
180  * Removes a sorting from the list.
181  *
182  * @param sel a selectable
183  */

184 public void removeSort(Selectable sel) {
185     // I used to use a map, but then sorts would not keep their order,
186
// which is important. That's why this code isn't just a map lookup
187
// any more.
188
for (int i = 0; i < sortSelectables.size(); ++i) {
189     if (sortSelectables.get(i) == sel) {
190         sortSelectables.remove(i);
191         sortOrders.remove(i);
192         return;
193     }
194     }
195 }
196
197 /**
198  * Removes all sorts from our list.
199  */

200 public void clearSorts() {
201     sortSelectables = new ArrayList();
202     sortOrders = new ArrayList();
203 }
204
205 /**
206  * Returns an iterator over all selectables.
207  *
208  * @return an iterator over all selectables
209  */

210 public Iterator selectables() { return selectables.iterator(); }
211
212 /**
213  * Returns an iterator over all the sorted selectables used by this query.
214  *
215  * @return an iterator
216  */

217 public Iterator sortedSelectables() { return sortSelectables.iterator(); }
218
219 /**
220  * Returns the sort order (<code>SORT_DESCENDING</code>,
221  * <code>SORT_ASCENDING</code>, or <code>SORT_UNDEFINED</code>) of the
222  * specified selectable.
223  *
224  * @param sel a database selectable
225  * @return the sort order (<code>SORT_DESCENDING</code>,
226  * <code>SORT_ASCENDING</code>, or <code>SORT_UNDEFINED</code>) of the
227  * specified selectable.
228  */

229 public int sortOrderOf(Selectable sel) {
230     // I used to use a map, but then sorts would not keep their order,
231
// which is important. That's why this code isn't just a map lookup
232
// any more.
233
for (int i = 0; i < sortSelectables.size(); ++i) {
234     if (sortSelectables.get(i) == sel)
235         return ((Integer JavaDoc)sortOrders.get(i)).intValue();
236     }
237     return SORT_UNDEFINED;
238 }
239
240 /**
241  * Returns the index of the specified selectable.
242  *
243  * @param selectable a database selectable
244  */

245 public int indexOfSelectable(Selectable selectable) {
246     return selectables.indexOf(selectable);
247 }
248
249 /**
250  * Builds collections of the selectables actually used in the report.
251  */

252 public void findSelectablesUsed() {
253     // It would be nice if the selectables collection was a set, so we
254
// could avoid the contains() calls below. However, we need it to be
255
// indexable.
256

257     selectables.clear();
258     report.withFieldsDo(new FieldWalker() {
259     public void step(Field f) {
260         if (f instanceof ColumnField) {
261         Column col = ((ColumnField)f).getColumn();
262         if (!selectables.contains(col)) selectables.add(col);
263         }
264         else if (f instanceof FormulaField) {
265         FormulaField ff = (FormulaField)f;
266         for (Iterator iter = ff.columnsUsed().iterator();
267              iter.hasNext(); )
268         {
269             Column col = (Column)iter.next();
270             if (!selectables.contains(col)) selectables.add(col);
271         }
272         for (Iterator iter = ff.userColumnsUsed().iterator();
273              iter.hasNext(); )
274         {
275             UserColumn uc = (UserColumn)iter.next();
276             if (!selectables.contains(uc)) selectables.add(uc);
277         }
278         }
279         else if (f instanceof UserColumnField) {
280         UserColumn uc = ((UserColumnField)f).getUserColumn();
281         if (!selectables.contains(uc)) selectables.add(uc);
282         }
283     }
284     });
285
286     // Add groups' selectables, which may or may not be used in any
287
// report field
288
for (Iterator iter = report.groups(); iter.hasNext(); ) {
289     Group g = (Group)iter.next();
290     Selectable s = g.getSelectable();
291     if (!selectables.contains(s)) selectables.add(s);
292     }
293
294     // Add all selectables used in sorts.
295
for (Iterator iter = sortedSelectables(); iter.hasNext(); ) {
296     Selectable s = (Selectable)iter.next();
297     if (!selectables.contains(s)) selectables.add(s);
298     }
299
300     // Add all columns used by subreports' joins. Though only a report
301
// that uses a SQL data source can have subreports right now, that may
302
// not be true in the future. There is no harm in implementing this
303
// here (rather than in SQLQuery).
304
for (Iterator iter = report.subreports(); iter.hasNext(); ) {
305     Subreport sub = (Subreport)iter.next();
306     for (Iterator subIter = sub.parentColumns(); subIter.hasNext(); ) {
307         Column col = (Column)subIter.next();
308         if (!selectables.contains(col)) selectables.add(col);
309     }
310     }
311 }
312
313 /**
314  * Returns the number of selectables in the query. Does not recalculate the
315  * selectables used; we assume this is being called after the qurey has been
316  * run or {@link #findSelectablesUsed} has been called.
317  *
318  * @return the number of selectables (database and user columns) in the query
319  */

320 public int getNumSelectables() { return selectables.size(); }
321
322 /**
323  * Called from <code>DataSource.reloadColumns</code>, this method gives the
324  * query source a chance to tell its ancillary objects (such as joins and
325  * the sort) to reload selectable objects.
326  * <p>
327  * This is necessary, for example, after a SQL database data source has
328  * reloaded all of its table and column information. The old column
329  * objects no longer exist. New ones (with the same ids, we assume) have
330  * taken their place.
331  *
332  * @param dataSource the data source
333  */

334 public void reloadColumns(DataSource dataSource)
335 {
336     // Joins
337
for (Iterator iter = joins(); iter.hasNext(); ) {
338     Join j = (Join)iter.next();
339     j.setFrom(dataSource.findColumn(j.getFrom().getId()));
340     j.setTo(dataSource.findColumn(j.getTo().getId()));
341     }
342
343     // Selectables
344
ArrayList newSelectables = new ArrayList();
345     for (Iterator iter = selectables.iterator(); iter.hasNext(); ) {
346     Selectable g = (Selectable)iter.next();
347     newSelectables.add(g.reloadInstance(dataSource));
348     }
349     selectables = newSelectables;
350
351     // Sort selectables
352
ArrayList newSortCols = new ArrayList();
353     for (Iterator iter = sortSelectables.iterator(); iter.hasNext(); ) {
354     Selectable s = (Selectable)iter.next();
355     newSortCols.add(s.reloadInstance(dataSource));
356     }
357     sortSelectables = newSortCols;
358 }
359
360 /**
361  * Writes this query as an XML tag. Writes all joins, where clauses,
362  * and sorts as well.
363  *
364  * @param out a writer that knows how to write XML
365  */

366 public void writeXML(XMLWriter out) {
367     out.startElement("query");
368
369     ListWriter.writeList(out, joins);
370
371     if (whereClause != null && whereClause.length() > 0)
372     out.cdataElement("where", whereClause);
373
374     for (int i = 0; i < sortSelectables.size(); ++i) {
375     int sortOrder = ((Integer JavaDoc)sortOrders.get(i)).intValue();
376     Selectable selectable = (Selectable)sortSelectables.get(i);
377     out.startElement("sort");
378     out.attr("order", sortOrder == Query.SORT_DESCENDING ? "desc" : "asc");
379     out.attr("groupable-id", selectable.getId());
380     out.attr("groupable-type", selectable.fieldTypeString());
381     out.endElement();
382     }
383     writeExtras(out);
384
385     out.endElement();
386 }
387
388 /** This method exists so subclasses can write out extra information. */
389 protected void writeExtras(XMLWriter out) { }
390
391 }
392
Popular Tags