001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
006 *
007 * Project Info: http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022 * USA.
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * ------------------
028 * XYTaskDataset.java
029 * ------------------
030 * (C) Copyright 2008, by Object Refinery Limited.
031 *
032 * Original Author: David Gilbert (for Object Refinery Limited);
033 * Contributor(s): -;
034 *
035 * Changes
036 * -------
037 * 17-Sep-2008 : Version 1 (DG);
038 *
039 */
040
041 package org.jfree.data.gantt;
042
043 import java.util.Date;
044
045 import org.jfree.chart.axis.SymbolAxis;
046 import org.jfree.chart.renderer.xy.XYBarRenderer;
047 import org.jfree.data.general.DatasetChangeEvent;
048 import org.jfree.data.general.DatasetChangeListener;
049 import org.jfree.data.time.TimePeriod;
050 import org.jfree.data.xy.AbstractXYDataset;
051 import org.jfree.data.xy.IntervalXYDataset;
052
053 /**
054 * A dataset implementation that wraps a {@link TaskSeriesCollection} and
055 * presents it as an {@link IntervalXYDataset}, allowing a set of tasks to
056 * be displayed using an {@link XYBarRenderer} (and usually a
057 * {@link SymbolAxis}). This is a very specialised dataset implementation
058 * ---before using it, you should take some time to understand the use-cases
059 * that it is designed for.
060 *
061 * @since 1.0.11
062 */
063 public class XYTaskDataset extends AbstractXYDataset
064 implements IntervalXYDataset, DatasetChangeListener {
065
066 /** The underlying tasks. */
067 private TaskSeriesCollection underlying;
068
069 /** The series interval width (typically 0.0 < w <= 1.0). */
070 private double seriesWidth;
071
072 /** A flag that controls whether or not the data values are transposed. */
073 private boolean transposed;
074
075 /**
076 * Creates a new dataset based on the supplied collection of tasks.
077 *
078 * @param tasks the underlying dataset (<code>null</code> not permitted).
079 */
080 public XYTaskDataset(TaskSeriesCollection tasks) {
081 if (tasks == null) {
082 throw new IllegalArgumentException("Null 'tasks' argument.");
083 }
084 this.underlying = tasks;
085 this.seriesWidth = 0.8;
086 this.underlying.addChangeListener(this);
087 }
088
089 /**
090 * Returns the underlying task series collection that was supplied to the
091 * constructor.
092 *
093 * @return The underlying collection (never <code>null</code>).
094 */
095 public TaskSeriesCollection getTasks() {
096 return this.underlying;
097 }
098
099 /**
100 * Returns the width of the interval for each series this dataset.
101 *
102 * @return The width of the series interval.
103 *
104 * @see #setSeriesWidth(double)
105 */
106 public double getSeriesWidth() {
107 return this.seriesWidth;
108 }
109
110 /**
111 * Sets the series interval width and sends a {@link DatasetChangeEvent} to
112 * all registered listeners.
113 *
114 * @param w the width.
115 *
116 * @see #getSeriesWidth()
117 */
118 public void setSeriesWidth(double w) {
119 if (w <= 0.0) {
120 throw new IllegalArgumentException("Requires 'w' > 0.0.");
121 }
122 this.seriesWidth = w;
123 fireDatasetChanged();
124 }
125
126 /**
127 * Returns a flag that indicates whether or not the dataset is transposed.
128 * The default is <code>false</code> which means the x-values are integers
129 * corresponding to the series indices, and the y-values are millisecond
130 * values corresponding to the task date/time intervals. If the flag
131 * is set to <code>true</code>, the x and y-values are reversed.
132 *
133 * @return The flag.
134 *
135 * @see #setTransposed(boolean)
136 */
137 public boolean isTransposed() {
138 return this.transposed;
139 }
140
141 /**
142 * Sets the flag that controls whether or not the dataset is transposed
143 * and sends a {@link DatasetChangeEvent} to all registered listeners.
144 *
145 * @param transposed the new flag value.
146 *
147 * @see #isTransposed()
148 */
149 public void setTransposed(boolean transposed) {
150 this.transposed = transposed;
151 fireDatasetChanged();
152 }
153
154 /**
155 * Returns the number of series in the dataset.
156 *
157 * @return The series count.
158 */
159 public int getSeriesCount() {
160 return this.underlying.getSeriesCount();
161 }
162
163 /**
164 * Returns the name of a series.
165 *
166 * @param series the series index (zero-based).
167 *
168 * @return The name of a series.
169 */
170 public Comparable getSeriesKey(int series) {
171 return this.underlying.getSeriesKey(series);
172 }
173
174 /**
175 * Returns the number of items (tasks) in the specified series.
176 *
177 * @param series the series index (zero-based).
178 *
179 * @return The item count.
180 */
181 public int getItemCount(int series) {
182 return this.underlying.getSeries(series).getItemCount();
183 }
184
185 /**
186 * Returns the x-value (as a double primitive) for an item within a series.
187 *
188 * @param series the series index (zero-based).
189 * @param item the item index (zero-based).
190 *
191 * @return The value.
192 */
193 public double getXValue(int series, int item) {
194 if (!this.transposed) {
195 return getSeriesValue(series);
196 }
197 else {
198 return getItemValue(series, item);
199 }
200 }
201
202 /**
203 * Returns the starting date/time for the specified item (task) in the
204 * given series, measured in milliseconds since 1-Jan-1970 (as in
205 * java.util.Date).
206 *
207 * @param series the series index.
208 * @param item the item (or task) index.
209 *
210 * @return The start date/time.
211 */
212 public double getStartXValue(int series, int item) {
213 if (!this.transposed) {
214 return getSeriesStartValue(series);
215 }
216 else {
217 return getItemStartValue(series, item);
218 }
219 }
220
221 /**
222 * Returns the ending date/time for the specified item (task) in the
223 * given series, measured in milliseconds since 1-Jan-1970 (as in
224 * java.util.Date).
225 *
226 * @param series the series index.
227 * @param item the item (or task) index.
228 *
229 * @return The end date/time.
230 */
231 public double getEndXValue(int series, int item) {
232 if (!this.transposed) {
233 return getSeriesEndValue(series);
234 }
235 else {
236 return getItemEndValue(series, item);
237 }
238 }
239
240 /**
241 * Returns the x-value for the specified series.
242 *
243 * @param series the series index.
244 * @param item the item index.
245 *
246 * @return The x-value (in milliseconds).
247 */
248 public Number getX(int series, int item) {
249 return new Double(getXValue(series, item));
250 }
251
252 /**
253 * Returns the starting date/time for the specified item (task) in the
254 * given series, measured in milliseconds since 1-Jan-1970 (as in
255 * java.util.Date).
256 *
257 * @param series the series index.
258 * @param item the item (or task) index.
259 *
260 * @return The start date/time.
261 */
262 public Number getStartX(int series, int item) {
263 return new Double(getStartXValue(series, item));
264 }
265
266 /**
267 * Returns the ending date/time for the specified item (task) in the
268 * given series, measured in milliseconds since 1-Jan-1970 (as in
269 * java.util.Date).
270 *
271 * @param series the series index.
272 * @param item the item (or task) index.
273 *
274 * @return The end date/time.
275 */
276 public Number getEndX(int series, int item) {
277 return new Double(getEndXValue(series, item));
278 }
279
280 /**
281 * Returns the y-value (as a double primitive) for an item within a series.
282 *
283 * @param series the series index (zero-based).
284 * @param item the item index (zero-based).
285 *
286 * @return The value.
287 */
288 public double getYValue(int series, int item) {
289 if (!this.transposed) {
290 return getItemValue(series, item);
291 }
292 else {
293 return getSeriesValue(series);
294 }
295 }
296
297 /**
298 * Returns the starting value of the y-interval for an item in the
299 * given series.
300 *
301 * @param series the series index.
302 * @param item the item (or task) index.
303 *
304 * @return The y-interval start.
305 */
306 public double getStartYValue(int series, int item) {
307 if (!this.transposed) {
308 return getItemStartValue(series, item);
309 }
310 else {
311 return getSeriesStartValue(series);
312 }
313 }
314
315 /**
316 * Returns the ending value of the y-interval for an item in the
317 * given series.
318 *
319 * @param series the series index.
320 * @param item the item (or task) index.
321 *
322 * @return The y-interval end.
323 */
324 public double getEndYValue(int series, int item) {
325 if (!this.transposed) {
326 return getItemEndValue(series, item);
327 }
328 else {
329 return getSeriesEndValue(series);
330 }
331 }
332
333 /**
334 * Returns the y-value for the specified series/item. In this
335 * implementation, we return the series index as the y-value (this means
336 * that every item in the series has a constant integer value).
337 *
338 * @param series the series index.
339 * @param item the item index.
340 *
341 * @return The y-value.
342 */
343 public Number getY(int series, int item) {
344 return new Double(getYValue(series, item));
345 }
346
347 /**
348 * Returns the starting value of the y-interval for an item in the
349 * given series.
350 *
351 * @param series the series index.
352 * @param item the item (or task) index.
353 *
354 * @return The y-interval start.
355 */
356 public Number getStartY(int series, int item) {
357 return new Double(getStartYValue(series, item));
358 }
359
360 /**
361 * Returns the ending value of the y-interval for an item in the
362 * given series.
363 *
364 * @param series the series index.
365 * @param item the item (or task) index.
366 *
367 * @return The y-interval end.
368 */
369 public Number getEndY(int series, int item) {
370 return new Double(getEndYValue(series, item));
371 }
372
373 private double getSeriesValue(int series) {
374 return series;
375 }
376
377 private double getSeriesStartValue(int series) {
378 return series - this.seriesWidth / 2.0;
379 }
380
381 private double getSeriesEndValue(int series) {
382 return series + this.seriesWidth / 2.0;
383 }
384
385 private double getItemValue(int series, int item) {
386 TaskSeries s = this.underlying.getSeries(series);
387 Task t = s.get(item);
388 TimePeriod duration = t.getDuration();
389 Date start = duration.getStart();
390 Date end = duration.getEnd();
391 return (start.getTime() + end.getTime()) / 2.0;
392 }
393
394 private double getItemStartValue(int series, int item) {
395 TaskSeries s = this.underlying.getSeries(series);
396 Task t = s.get(item);
397 TimePeriod duration = t.getDuration();
398 Date start = duration.getStart();
399 return start.getTime();
400 }
401
402 private double getItemEndValue(int series, int item) {
403 TaskSeries s = this.underlying.getSeries(series);
404 Task t = s.get(item);
405 TimePeriod duration = t.getDuration();
406 Date end = duration.getEnd();
407 return end.getTime();
408 }
409
410
411 /**
412 * Receives a change event from the underlying dataset and responds by
413 * firing a change event for this dataset.
414 *
415 * @param event the event.
416 */
417 public void datasetChanged(DatasetChangeEvent event) {
418 fireDatasetChanged();
419 }
420
421 /**
422 * Tests this dataset for equality with an arbitrary object.
423 *
424 * @param obj the object (<code>null</code> permitted).
425 *
426 * @return A boolean.
427 */
428 public boolean equals(Object obj) {
429 if (obj == this) {
430 return true;
431 }
432 if (!(obj instanceof XYTaskDataset)) {
433 return false;
434 }
435 XYTaskDataset that = (XYTaskDataset) obj;
436 if (this.seriesWidth != that.seriesWidth) {
437 return false;
438 }
439 if (this.transposed != that.transposed) {
440 return false;
441 }
442 if (!this.underlying.equals(that.underlying)) {
443 return false;
444 }
445 return true;
446 }
447
448 /**
449 * Returns a clone of this dataset.
450 *
451 * @return A clone of this dataset.
452 *
453 * @throws CloneNotSupportedException if there is a problem cloning.
454 */
455 public Object clone() throws CloneNotSupportedException {
456 XYTaskDataset clone = (XYTaskDataset) super.clone();
457 clone.underlying = (TaskSeriesCollection) this.underlying.clone();
458 return clone;
459 }
460
461 }