001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2014, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ------------------ 028 * XYTaskDataset.java 029 * ------------------ 030 * (C) Copyright 2008-2014, 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 * 03-Jul-2013 : Use ParamChecks (DG); 039 * 040 */ 041 042package org.jfree.data.gantt; 043 044import java.util.Date; 045 046import org.jfree.chart.axis.SymbolAxis; 047import org.jfree.chart.renderer.xy.XYBarRenderer; 048import org.jfree.chart.util.ParamChecks; 049import org.jfree.data.general.DatasetChangeEvent; 050import org.jfree.data.general.DatasetChangeListener; 051import org.jfree.data.time.TimePeriod; 052import org.jfree.data.xy.AbstractXYDataset; 053import org.jfree.data.xy.IntervalXYDataset; 054 055/** 056 * A dataset implementation that wraps a {@link TaskSeriesCollection} and 057 * presents it as an {@link IntervalXYDataset}, allowing a set of tasks to 058 * be displayed using an {@link XYBarRenderer} (and usually a 059 * {@link SymbolAxis}). This is a very specialised dataset implementation 060 * ---before using it, you should take some time to understand the use-cases 061 * that it is designed for. 062 * 063 * @since 1.0.11 064 */ 065public class XYTaskDataset extends AbstractXYDataset 066 implements IntervalXYDataset, DatasetChangeListener { 067 068 /** The underlying tasks. */ 069 private TaskSeriesCollection underlying; 070 071 /** The series interval width (typically 0.0 < w <= 1.0). */ 072 private double seriesWidth; 073 074 /** A flag that controls whether or not the data values are transposed. */ 075 private boolean transposed; 076 077 /** 078 * Creates a new dataset based on the supplied collection of tasks. 079 * 080 * @param tasks the underlying dataset (<code>null</code> not permitted). 081 */ 082 public XYTaskDataset(TaskSeriesCollection tasks) { 083 ParamChecks.nullNotPermitted(tasks, "tasks"); 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 @Override 160 public int getSeriesCount() { 161 return this.underlying.getSeriesCount(); 162 } 163 164 /** 165 * Returns the name of a series. 166 * 167 * @param series the series index (zero-based). 168 * 169 * @return The name of a series. 170 */ 171 @Override 172 public Comparable getSeriesKey(int series) { 173 return this.underlying.getSeriesKey(series); 174 } 175 176 /** 177 * Returns the number of items (tasks) in the specified series. 178 * 179 * @param series the series index (zero-based). 180 * 181 * @return The item count. 182 */ 183 @Override 184 public int getItemCount(int series) { 185 return this.underlying.getSeries(series).getItemCount(); 186 } 187 188 /** 189 * Returns the x-value (as a double primitive) for an item within a series. 190 * 191 * @param series the series index (zero-based). 192 * @param item the item index (zero-based). 193 * 194 * @return The value. 195 */ 196 @Override 197 public double getXValue(int series, int item) { 198 if (!this.transposed) { 199 return getSeriesValue(series); 200 } 201 else { 202 return getItemValue(series, item); 203 } 204 } 205 206 /** 207 * Returns the starting date/time for the specified item (task) in the 208 * given series, measured in milliseconds since 1-Jan-1970 (as in 209 * java.util.Date). 210 * 211 * @param series the series index. 212 * @param item the item (or task) index. 213 * 214 * @return The start date/time. 215 */ 216 @Override 217 public double getStartXValue(int series, int item) { 218 if (!this.transposed) { 219 return getSeriesStartValue(series); 220 } 221 else { 222 return getItemStartValue(series, item); 223 } 224 } 225 226 /** 227 * Returns the ending date/time for the specified item (task) in the 228 * given series, measured in milliseconds since 1-Jan-1970 (as in 229 * java.util.Date). 230 * 231 * @param series the series index. 232 * @param item the item (or task) index. 233 * 234 * @return The end date/time. 235 */ 236 @Override 237 public double getEndXValue(int series, int item) { 238 if (!this.transposed) { 239 return getSeriesEndValue(series); 240 } 241 else { 242 return getItemEndValue(series, item); 243 } 244 } 245 246 /** 247 * Returns the x-value for the specified series. 248 * 249 * @param series the series index. 250 * @param item the item index. 251 * 252 * @return The x-value (in milliseconds). 253 */ 254 @Override 255 public Number getX(int series, int item) { 256 return new Double(getXValue(series, item)); 257 } 258 259 /** 260 * Returns the starting date/time for the specified item (task) in the 261 * given series, measured in milliseconds since 1-Jan-1970 (as in 262 * java.util.Date). 263 * 264 * @param series the series index. 265 * @param item the item (or task) index. 266 * 267 * @return The start date/time. 268 */ 269 @Override 270 public Number getStartX(int series, int item) { 271 return new Double(getStartXValue(series, item)); 272 } 273 274 /** 275 * Returns the ending date/time for the specified item (task) in the 276 * given series, measured in milliseconds since 1-Jan-1970 (as in 277 * java.util.Date). 278 * 279 * @param series the series index. 280 * @param item the item (or task) index. 281 * 282 * @return The end date/time. 283 */ 284 @Override 285 public Number getEndX(int series, int item) { 286 return new Double(getEndXValue(series, item)); 287 } 288 289 /** 290 * Returns the y-value (as a double primitive) for an item within a series. 291 * 292 * @param series the series index (zero-based). 293 * @param item the item index (zero-based). 294 * 295 * @return The value. 296 */ 297 @Override 298 public double getYValue(int series, int item) { 299 if (!this.transposed) { 300 return getItemValue(series, item); 301 } 302 else { 303 return getSeriesValue(series); 304 } 305 } 306 307 /** 308 * Returns the starting value of the y-interval for an item in the 309 * given series. 310 * 311 * @param series the series index. 312 * @param item the item (or task) index. 313 * 314 * @return The y-interval start. 315 */ 316 @Override 317 public double getStartYValue(int series, int item) { 318 if (!this.transposed) { 319 return getItemStartValue(series, item); 320 } 321 else { 322 return getSeriesStartValue(series); 323 } 324 } 325 326 /** 327 * Returns the ending value of the y-interval for an item in the 328 * given series. 329 * 330 * @param series the series index. 331 * @param item the item (or task) index. 332 * 333 * @return The y-interval end. 334 */ 335 @Override 336 public double getEndYValue(int series, int item) { 337 if (!this.transposed) { 338 return getItemEndValue(series, item); 339 } 340 else { 341 return getSeriesEndValue(series); 342 } 343 } 344 345 /** 346 * Returns the y-value for the specified series/item. In this 347 * implementation, we return the series index as the y-value (this means 348 * that every item in the series has a constant integer value). 349 * 350 * @param series the series index. 351 * @param item the item index. 352 * 353 * @return The y-value. 354 */ 355 @Override 356 public Number getY(int series, int item) { 357 return new Double(getYValue(series, item)); 358 } 359 360 /** 361 * Returns the starting 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 start. 368 */ 369 @Override 370 public Number getStartY(int series, int item) { 371 return new Double(getStartYValue(series, item)); 372 } 373 374 /** 375 * Returns the ending value of the y-interval for an item in the 376 * given series. 377 * 378 * @param series the series index. 379 * @param item the item (or task) index. 380 * 381 * @return The y-interval end. 382 */ 383 @Override 384 public Number getEndY(int series, int item) { 385 return new Double(getEndYValue(series, item)); 386 } 387 388 private double getSeriesValue(int series) { 389 return series; 390 } 391 392 private double getSeriesStartValue(int series) { 393 return series - this.seriesWidth / 2.0; 394 } 395 396 private double getSeriesEndValue(int series) { 397 return series + this.seriesWidth / 2.0; 398 } 399 400 private double getItemValue(int series, int item) { 401 TaskSeries s = this.underlying.getSeries(series); 402 Task t = s.get(item); 403 TimePeriod duration = t.getDuration(); 404 Date start = duration.getStart(); 405 Date end = duration.getEnd(); 406 return (start.getTime() + end.getTime()) / 2.0; 407 } 408 409 private double getItemStartValue(int series, int item) { 410 TaskSeries s = this.underlying.getSeries(series); 411 Task t = s.get(item); 412 TimePeriod duration = t.getDuration(); 413 Date start = duration.getStart(); 414 return start.getTime(); 415 } 416 417 private double getItemEndValue(int series, int item) { 418 TaskSeries s = this.underlying.getSeries(series); 419 Task t = s.get(item); 420 TimePeriod duration = t.getDuration(); 421 Date end = duration.getEnd(); 422 return end.getTime(); 423 } 424 425 426 /** 427 * Receives a change event from the underlying dataset and responds by 428 * firing a change event for this dataset. 429 * 430 * @param event the event. 431 */ 432 @Override 433 public void datasetChanged(DatasetChangeEvent event) { 434 fireDatasetChanged(); 435 } 436 437 /** 438 * Tests this dataset for equality with an arbitrary object. 439 * 440 * @param obj the object (<code>null</code> permitted). 441 * 442 * @return A boolean. 443 */ 444 @Override 445 public boolean equals(Object obj) { 446 if (obj == this) { 447 return true; 448 } 449 if (!(obj instanceof XYTaskDataset)) { 450 return false; 451 } 452 XYTaskDataset that = (XYTaskDataset) obj; 453 if (this.seriesWidth != that.seriesWidth) { 454 return false; 455 } 456 if (this.transposed != that.transposed) { 457 return false; 458 } 459 if (!this.underlying.equals(that.underlying)) { 460 return false; 461 } 462 return true; 463 } 464 465 /** 466 * Returns a clone of this dataset. 467 * 468 * @return A clone of this dataset. 469 * 470 * @throws CloneNotSupportedException if there is a problem cloning. 471 */ 472 @Override 473 public Object clone() throws CloneNotSupportedException { 474 XYTaskDataset clone = (XYTaskDataset) super.clone(); 475 clone.underlying = (TaskSeriesCollection) this.underlying.clone(); 476 return clone; 477 } 478 479}