001: /*
002: * Copyright 2004-2007 Gary Bentley
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may
005: * not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015: package org.josql.contrib;
016:
017: import java.util.List;
018: import java.util.ArrayList;
019: import java.util.Arrays;
020:
021: import org.jfree.data.category.CategoryDataset;
022:
023: import org.jfree.data.DomainOrder;
024:
025: import org.jfree.data.general.DatasetGroup;
026: import org.jfree.data.general.DatasetChangeListener;
027: import org.jfree.data.general.DatasetChangeEvent;
028:
029: import org.josql.Query;
030: import org.josql.QueryResults;
031: import org.josql.QueryExecutionException;
032: import org.josql.QueryParseException;
033:
034: import org.josql.internal.Utilities;
035:
036: import org.josql.expressions.SelectItemExpression;
037:
038: public class JoSQLFreeChartCategoryDataset extends Query implements
039: CategoryDataset {
040:
041: private QueryResults results = null;
042: private int xCol = 0;
043: private List yCols = null;
044: private List listeners = new ArrayList();
045: private DatasetGroup group = null;
046:
047: public JoSQLFreeChartCategoryDataset() {
048:
049: }
050:
051: public void addChangeListener(DatasetChangeListener l) {
052:
053: this .listeners.add(l);
054:
055: }
056:
057: public void removeChangeListener(DatasetChangeListener l) {
058:
059: this .listeners.remove(l);
060:
061: }
062:
063: public DatasetGroup getGroup() {
064:
065: return this .group;
066:
067: }
068:
069: /**
070: * Get any results, will be null unless {@link #execute(List)} has been called.
071: *
072: * @return The results.
073: */
074: public QueryResults getResults() {
075:
076: return this .results;
077:
078: }
079:
080: /**
081: * Clear any results.
082: */
083: public void clearResults() {
084:
085: this .results = null;
086:
087: }
088:
089: public void setGroup(DatasetGroup g) {
090:
091: this .group = g;
092:
093: }
094:
095: public void define(int xCol, Object[] yCols)
096: throws IllegalArgumentException, IllegalStateException,
097: QueryParseException {
098:
099: this .define(xCol, Arrays.asList(yCols));
100:
101: }
102:
103: public void define(int xCol, int[] yCols)
104: throws IllegalArgumentException, IllegalStateException,
105: QueryParseException {
106:
107: List l = new ArrayList(yCols.length);
108:
109: for (int i = 0; i < yCols.length; i++) {
110:
111: l.add(Integer.valueOf(yCols[i]));
112:
113: }
114:
115: this .define(xCol, l);
116:
117: }
118:
119: public void define(int xCol, List yCols)
120: throws IllegalArgumentException, IllegalStateException,
121: QueryParseException {
122:
123: if (!this .parsed()) {
124:
125: throw new IllegalStateException(
126: "Cannot add a series until a query has been specified and parsed.");
127:
128: }
129:
130: if (xCol < 1) {
131:
132: throw new IllegalArgumentException(
133: "X column index must be a minimum of 1.");
134:
135: }
136:
137: for (int i = 0; i < yCols.size(); i++) {
138:
139: Object o = yCols.get(i);
140:
141: if (o instanceof String) {
142:
143: try {
144:
145: yCols.set(i, Integer.valueOf((String) o));
146:
147: continue;
148:
149: } catch (Exception e) {
150:
151: throw new IllegalArgumentException(
152: "Unable to convert y column indicator: "
153: + o + " to an integer.");
154:
155: }
156:
157: }
158:
159: if (o instanceof Number) {
160:
161: yCols.set(i, Integer.valueOf(((Number) o).intValue()));
162:
163: continue;
164:
165: }
166:
167: if (!(o instanceof Integer)) {
168:
169: throw new IllegalArgumentException(
170: "Expected y column indicator: "
171: + o
172: + " to be either a number or a string representing a number.");
173:
174: }
175:
176: }
177:
178: List cols = this .getColumns();
179:
180: for (int i = 0; i < yCols.size(); i++) {
181:
182: Integer in = (Integer) yCols.get(i);
183:
184: if (in.intValue() < 1) {
185:
186: throw new IllegalArgumentException(
187: "Y column index must be a minimum of 1.");
188:
189: }
190:
191: if (in.intValue() > cols.size()) {
192:
193: throw new IllegalArgumentException(
194: "Y column index must be a maximum of "
195: + cols.size() + ".");
196:
197: }
198:
199: SelectItemExpression yexp = (SelectItemExpression) cols
200: .get(in.intValue() - 1);
201:
202: if (yexp.getAlias() == null) {
203:
204: throw new IllegalArgumentException("Y column: " + yexp
205: + " must have an alias.");
206:
207: }
208:
209: Class yc = yexp.getExpectedReturnType(this );
210:
211: if (!Utilities.isNumber(yc)) {
212:
213: throw new IllegalArgumentException(
214: "Y column: "
215: + yexp
216: + " will evaluate to an instance of type: "
217: + yc.getName()
218: + ", but only columns that return numbers are allowed.");
219:
220: }
221:
222: }
223:
224: if (xCol > cols.size()) {
225:
226: throw new IllegalArgumentException(
227: "X column index must be a maximum of "
228: + cols.size() + ".");
229:
230: }
231:
232: SelectItemExpression xexp = (SelectItemExpression) cols
233: .get(xCol - 1);
234:
235: Class xc = xexp.getExpectedReturnType(this );
236:
237: xc = Utilities.getObjectClass(xc);
238:
239: if (!(Comparable.class.isAssignableFrom(xc))) {
240:
241: throw new IllegalArgumentException("X column: " + xexp
242: + " will evaluate to an instance of type: "
243: + xc.getName()
244: + ", but only columns that implement: "
245: + Comparable.class.getName() + " can be used.");
246:
247: }
248:
249: this .yCols = yCols;
250:
251: }
252:
253: /**
254: * Exectute the query and return the results. A reference to the results is also held to
255: * allow them to be iterated over. If you plan on re-using this data source then
256: * you should call: {@link #clearResults()} to free up the references to the results.
257: *
258: * @param l The List of objects to execute the query on.
259: * @return The results.
260: * @throws QueryExecutionException If the query cannot be executed, or if the query
261: * is set to return objects rather than "columns".
262: */
263: public QueryResults executeQuery(List l)
264: throws QueryExecutionException {
265:
266: if (this .isWantObjects()) {
267:
268: throw new QueryExecutionException(
269: "Only SQL statements that return columns (not the objects passed in) can be used.");
270:
271: }
272:
273: this .results = super .execute(l);
274:
275: // Notify our listeners.
276: DatasetChangeEvent dce = new DatasetChangeEvent(this , this );
277:
278: for (int i = 0; i < this .listeners.size(); i++) {
279:
280: DatasetChangeListener d = (DatasetChangeListener) this .listeners
281: .get(i);
282:
283: d.datasetChanged(dce);
284:
285: }
286:
287: return this .results;
288:
289: }
290:
291: public DomainOrder getDomainOrder() {
292:
293: return DomainOrder.ASCENDING;
294:
295: }
296:
297: public int getRowCount() {
298:
299: return this .results.getResults().size();
300:
301: }
302:
303: public int getColumnCount() {
304:
305: return this .yCols.size();
306:
307: }
308:
309: public Number getValue(int row, int col) {
310:
311: List l = (List) this .results.getResults().get(row);
312:
313: return (Number) l.get(col);
314:
315: }
316:
317: public List getRowKeys() {
318:
319: List ks = new ArrayList();
320:
321: for (int i = 0; i < this .results.getResults().size(); i++) {
322:
323: List l = (List) this .results.getResults().get(i);
324:
325: ks.add(l.get(this .xCol));
326:
327: }
328:
329: return ks;
330:
331: }
332:
333: public Number getValue(Comparable row, Comparable col) {
334:
335: int rk = this .getRowIndex(row);
336: int ck = this .getColumnIndex(col);
337:
338: List l = (List) this .results.getResults().get(rk);
339:
340: return (Number) l.get(ck);
341:
342: }
343:
344: public Comparable getColumnKey(int c) {
345:
346: return (Comparable) this .getColumnKeys().get(c);
347:
348: }
349:
350: public List getColumnKeys() {
351:
352: List ks = new ArrayList();
353:
354: for (int i = 0; i < this .yCols.size(); i++) {
355:
356: Integer in = (Integer) this .yCols.get(i);
357:
358: SelectItemExpression sei = (SelectItemExpression) this
359: .getColumns().get(in.intValue() - 1);
360:
361: ks.add(sei.getAlias());
362:
363: }
364:
365: return ks;
366:
367: }
368:
369: public int getColumnIndex(Comparable c) {
370:
371: List ck = this .getColumnKeys();
372:
373: for (int i = 0; i < ck.size(); i++) {
374:
375: if (((Comparable) ck.get(i)).compareTo(c) == 0) {
376:
377: return i;
378:
379: }
380:
381: }
382:
383: return -1;
384:
385: }
386:
387: public int getRowIndex(Comparable c) {
388:
389: List rk = this .getRowKeys();
390:
391: for (int i = 0; i < rk.size(); i++) {
392:
393: if (((Comparable) rk.get(i)).compareTo(c) == 0) {
394:
395: return i;
396:
397: }
398:
399: }
400:
401: return -1;
402:
403: }
404:
405: public Comparable getRowKey(int k) {
406:
407: return (Comparable) this.getRowKeys().get(k);
408:
409: }
410:
411: }
|