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.Map;
020: import java.util.HashMap;
021:
022: import org.jfree.data.xy.XYDataset;
023:
024: import org.jfree.data.DomainOrder;
025:
026: import org.jfree.data.general.DatasetGroup;
027: import org.jfree.data.general.DatasetChangeListener;
028: import org.jfree.data.general.DatasetChangeEvent;
029:
030: import org.josql.Query;
031: import org.josql.QueryResults;
032: import org.josql.QueryExecutionException;
033: import org.josql.QueryParseException;
034:
035: import org.josql.internal.Utilities;
036:
037: import org.josql.expressions.SelectItemExpression;
038:
039: public class JoSQLFreeChartXYDataset extends Query implements XYDataset {
040:
041: private List results = null;
042: private Map series = new HashMap();
043: private List listeners = new ArrayList();
044: private DatasetGroup group = null;
045:
046: public JoSQLFreeChartXYDataset() {
047:
048: }
049:
050: public void addChangeListener(DatasetChangeListener l) {
051:
052: this .listeners.add(l);
053:
054: }
055:
056: public void removeChangeListener(DatasetChangeListener l) {
057:
058: this .listeners.remove(l);
059:
060: }
061:
062: public DatasetGroup getGroup() {
063:
064: return this .group;
065:
066: }
067:
068: public void setGroup(DatasetGroup g) {
069:
070: this .group = g;
071:
072: }
073:
074: public int indexOf(Comparable c) {
075:
076: // Must be an integer.
077: return ((Integer) c).intValue();
078:
079: }
080:
081: public Comparable getSeriesKey(int series) {
082:
083: return Integer.valueOf(series);
084:
085: }
086:
087: public int getSeriesCount() {
088:
089: return this .series.size();
090:
091: }
092:
093: public void removeSeries(int series) {
094:
095: this .series.remove(Integer.valueOf(series));
096:
097: }
098:
099: public void addSeries(int series, int xCol, int yCol)
100: throws IllegalArgumentException, IllegalStateException,
101: QueryParseException {
102:
103: if (!this .parsed()) {
104:
105: throw new IllegalStateException(
106: "Cannot add a series until a query has been specified and parsed.");
107:
108: }
109:
110: if (xCol < 1) {
111:
112: throw new IllegalArgumentException(
113: "X column index must be a minimum of 1.");
114:
115: }
116:
117: if (yCol < 1) {
118:
119: throw new IllegalArgumentException(
120: "Y column index must be a minimum of 1.");
121:
122: }
123:
124: List cols = this .getColumns();
125:
126: if (xCol > cols.size()) {
127:
128: throw new IllegalArgumentException(
129: "X column index must be a minimum of "
130: + cols.size() + ".");
131:
132: }
133:
134: if (yCol > cols.size()) {
135:
136: throw new IllegalArgumentException(
137: "Y column index must be a minimum of "
138: + cols.size() + ".");
139:
140: }
141:
142: SelectItemExpression xexp = (SelectItemExpression) cols
143: .get(xCol - 1);
144:
145: Class xc = xexp.getExpectedReturnType(this );
146:
147: if (!Utilities.isNumber(xc)) {
148:
149: throw new IllegalArgumentException(
150: "X column: "
151: + xexp
152: + " will evaluate to an instance of type: "
153: + xc.getName()
154: + ", but only columns that return numbers are allowed.");
155:
156: }
157:
158: SelectItemExpression yexp = (SelectItemExpression) cols
159: .get(yCol - 1);
160:
161: Class yc = yexp.getExpectedReturnType(this );
162:
163: if (!Utilities.isNumber(yc)) {
164:
165: throw new IllegalArgumentException(
166: "Y column: "
167: + yexp
168: + " will evaluate to an instance of type: "
169: + yc.getName()
170: + ", but only columns that return numbers are allowed.");
171:
172: }
173:
174: this .series
175: .put(Integer.valueOf(series), new Series(xCol, yCol));
176:
177: }
178:
179: /**
180: * Exectute the query and return the results. A reference to the results is also held to
181: * allow them to be iterated over. If you plan on re-using this data source then
182: * you should call: {@link #clearResults()} to free up the references to the results.
183: *
184: * @param l The List of objects to execute the query on.
185: * @return The results.
186: * @throws QueryExecutionException If the query cannot be executed, or if the query
187: * is set to return objects rather than "columns".
188: */
189: public QueryResults executeQuery(List l)
190: throws QueryExecutionException {
191:
192: if (this .isWantObjects()) {
193:
194: throw new QueryExecutionException(
195: "Only SQL statements that return columns (not the objects passed in) can be used.");
196:
197: }
198:
199: QueryResults qr = super .execute(l);
200:
201: this .results = qr.getResults();
202:
203: // Notify our listeners.
204: DatasetChangeEvent dce = new DatasetChangeEvent(this , this );
205:
206: for (int i = 0; i < this .listeners.size(); i++) {
207:
208: DatasetChangeListener d = (DatasetChangeListener) this .listeners
209: .get(i);
210:
211: d.datasetChanged(dce);
212:
213: }
214:
215: return qr;
216:
217: }
218:
219: public List getResults() {
220:
221: return this .results;
222:
223: }
224:
225: public void clearResults() {
226:
227: this .results = null;
228:
229: }
230:
231: public int getItemCount(int series) {
232:
233: if (this .results == null) {
234:
235: return 0;
236:
237: }
238:
239: return this .results.size();
240:
241: }
242:
243: public double getXValue(int series, int item) {
244:
245: // Bad man!
246: return ((Double) this .getX(series, item)).doubleValue();
247:
248: }
249:
250: public double getYValue(int series, int item) {
251:
252: // Bad man!
253: return ((Double) this .getY(series, item)).doubleValue();
254:
255: }
256:
257: public Number getX(int series, int item) {
258:
259: if (this .results == null) {
260:
261: return new Double(0);
262:
263: }
264:
265: List l = (List) this .results.get(item);
266:
267: Series s = (Series) this .series.get(Integer.valueOf(series));
268:
269: if (s == null) {
270:
271: return new Double(0);
272:
273: }
274:
275: Number n = (Number) l.get(s.xCol - 1);
276:
277: if (n instanceof Double) {
278:
279: return n;
280:
281: } else {
282:
283: return new Double(n.doubleValue());
284:
285: }
286:
287: }
288:
289: public Number getY(int series, int item) {
290:
291: if (this .results == null) {
292:
293: return new Double(0);
294:
295: }
296:
297: List l = (List) this .results.get(item);
298:
299: Series s = (Series) this .series.get(Integer.valueOf(series));
300:
301: if (s == null) {
302:
303: return new Double(0);
304:
305: }
306:
307: Number n = (Number) l.get(s.yCol - 1);
308:
309: if (n instanceof Double) {
310:
311: return n;
312:
313: } else {
314:
315: return new Double(n.doubleValue());
316:
317: }
318:
319: }
320:
321: public DomainOrder getDomainOrder() {
322:
323: return DomainOrder.ASCENDING;
324:
325: }
326:
327: private class Series {
328:
329: public int xCol = 0;
330: public int yCol = 0;
331:
332: public Series(int x, int y) {
333:
334: this.xCol = x;
335: this.yCol = y;
336:
337: }
338:
339: }
340:
341: }
|