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 }