001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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 * TaskSeriesCollection.java 029 * ------------------------- 030 * (C) Copyright 2002-2005, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Thomas Schuster; 034 * 035 * $Id: TaskSeriesCollection.java,v 1.9.2.2 2005/10/25 21:31:44 mungady Exp $ 036 * 037 * Changes 038 * ------- 039 * 06-Jun-2002 : Version 1 (DG); 040 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG); 041 * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 042 * CategoryToolTipGenerator interface (DG); 043 * 10-Jan-2003 : Renamed GanttSeriesCollection --> TaskSeriesCollection (DG); 044 * 04-Sep-2003 : Fixed bug 800324 (DG); 045 * 16-Sep-2003 : Implemented GanttCategoryDataset (DG); 046 * 12-Jan-2005 : Fixed bug 1099331 (DG); 047 * 048 */ 049 050 package org.jfree.data.gantt; 051 052 import java.io.Serializable; 053 import java.util.Iterator; 054 import java.util.List; 055 056 import org.jfree.data.general.AbstractSeriesDataset; 057 import org.jfree.data.general.SeriesChangeEvent; 058 import org.jfree.data.time.TimePeriod; 059 import org.jfree.util.ObjectUtilities; 060 import org.jfree.util.PublicCloneable; 061 062 /** 063 * A collection of {@link TaskSeries} objects. This class provides one 064 * implementation of the {@link GanttCategoryDataset} interface. 065 */ 066 public class TaskSeriesCollection extends AbstractSeriesDataset 067 implements GanttCategoryDataset, 068 Cloneable, PublicCloneable, 069 Serializable { 070 071 /** For serialization. */ 072 private static final long serialVersionUID = -2065799050738449903L; 073 074 /** 075 * Storage for aggregate task keys (the task description is used as the 076 * key). 077 */ 078 private List keys; 079 080 /** Storage for the series. */ 081 private List data; 082 083 /** 084 * Default constructor. 085 */ 086 public TaskSeriesCollection() { 087 this.keys = new java.util.ArrayList(); 088 this.data = new java.util.ArrayList(); 089 } 090 091 /** 092 * Returns the number of series in the collection. 093 * 094 * @return The series count. 095 */ 096 public int getSeriesCount() { 097 return getRowCount(); 098 } 099 100 /** 101 * Returns the name of a series. 102 * 103 * @param series the series index (zero-based). 104 * 105 * @return The name of a series. 106 */ 107 public Comparable getSeriesKey(int series) { 108 TaskSeries ts = (TaskSeries) this.data.get(series); 109 return ts.getKey(); 110 } 111 112 /** 113 * Returns the number of rows (series) in the collection. 114 * 115 * @return The series count. 116 */ 117 public int getRowCount() { 118 return this.data.size(); 119 } 120 121 /** 122 * Returns the row keys. In this case, each series is a key. 123 * 124 * @return The row keys. 125 */ 126 public List getRowKeys() { 127 return this.data; 128 } 129 130 /** 131 * Returns the number of column in the dataset. 132 * 133 * @return The column count. 134 */ 135 public int getColumnCount() { 136 return this.keys.size(); 137 } 138 139 /** 140 * Returns a list of the column keys in the dataset. 141 * 142 * @return The category list. 143 */ 144 public List getColumnKeys() { 145 return this.keys; 146 } 147 148 /** 149 * Returns a column key. 150 * 151 * @param index the column index. 152 * 153 * @return The column key. 154 */ 155 public Comparable getColumnKey(int index) { 156 return (Comparable) this.keys.get(index); 157 } 158 159 /** 160 * Returns the column index for a column key. 161 * 162 * @param columnKey the columnKey. 163 * 164 * @return The column index. 165 */ 166 public int getColumnIndex(Comparable columnKey) { 167 return this.keys.indexOf(columnKey); 168 } 169 170 /** 171 * Returns the row index for the given row key. 172 * 173 * @param rowKey the row key. 174 * 175 * @return The index. 176 */ 177 public int getRowIndex(Comparable rowKey) { 178 int result = -1; 179 int count = this.data.size(); 180 for (int i = 0; i < count; i++) { 181 TaskSeries s = (TaskSeries) this.data.get(i); 182 if (s.getKey().equals(rowKey)) { 183 result = i; 184 break; 185 } 186 } 187 return result; 188 } 189 190 /** 191 * Returns the key for a row. 192 * 193 * @param index the row index (zero-based). 194 * 195 * @return The key. 196 */ 197 public Comparable getRowKey(int index) { 198 TaskSeries series = (TaskSeries) this.data.get(index); 199 return series.getKey(); 200 } 201 202 /** 203 * Adds a series to the dataset and sends a 204 * {@link org.jfree.data.general.DatasetChangeEvent} to all registered 205 * listeners. 206 * 207 * @param series the series (<code>null</code> not permitted). 208 */ 209 public void add(TaskSeries series) { 210 if (series == null) { 211 throw new IllegalArgumentException("Null 'series' argument."); 212 } 213 this.data.add(series); 214 series.addChangeListener(this); 215 216 // look for any keys that we don't already know about... 217 Iterator iterator = series.getTasks().iterator(); 218 while (iterator.hasNext()) { 219 Task task = (Task) iterator.next(); 220 String key = task.getDescription(); 221 int index = this.keys.indexOf(key); 222 if (index < 0) { 223 this.keys.add(key); 224 } 225 } 226 fireDatasetChanged(); 227 } 228 229 /** 230 * Removes a series from the collection and sends 231 * a {@link org.jfree.data.general.DatasetChangeEvent} 232 * to all registered listeners. 233 * 234 * @param series the series. 235 */ 236 public void remove(TaskSeries series) { 237 if (series == null) { 238 throw new IllegalArgumentException("Null 'series' argument."); 239 } 240 if (this.data.contains(series)) { 241 series.removeChangeListener(this); 242 this.data.remove(series); 243 fireDatasetChanged(); 244 } 245 } 246 247 /** 248 * Removes a series from the collection and sends 249 * a {@link org.jfree.data.general.DatasetChangeEvent} 250 * to all registered listeners. 251 * 252 * @param series the series (zero based index). 253 */ 254 public void remove(int series) { 255 if ((series < 0) || (series > getSeriesCount())) { 256 throw new IllegalArgumentException( 257 "TaskSeriesCollection.remove(): index outside valid range."); 258 } 259 260 // fetch the series, remove the change listener, then remove the series. 261 TaskSeries ts = (TaskSeries) this.data.get(series); 262 ts.removeChangeListener(this); 263 this.data.remove(series); 264 fireDatasetChanged(); 265 266 } 267 268 /** 269 * Removes all the series from the collection and sends 270 * a {@link org.jfree.data.general.DatasetChangeEvent} 271 * to all registered listeners. 272 */ 273 public void removeAll() { 274 275 // deregister the collection as a change listener to each series in 276 // the collection. 277 Iterator iterator = this.data.iterator(); 278 while (iterator.hasNext()) { 279 TaskSeries series = (TaskSeries) iterator.next(); 280 series.removeChangeListener(this); 281 } 282 283 // remove all the series from the collection and notify listeners. 284 this.data.clear(); 285 fireDatasetChanged(); 286 287 } 288 289 /** 290 * Returns the value for an item. 291 * 292 * @param rowKey the row key. 293 * @param columnKey the column key. 294 * 295 * @return The item value. 296 */ 297 public Number getValue(Comparable rowKey, Comparable columnKey) { 298 return getStartValue(rowKey, columnKey); 299 } 300 301 /** 302 * Returns the value for a task. 303 * 304 * @param row the row index (zero-based). 305 * @param column the column index (zero-based). 306 * 307 * @return The start value. 308 */ 309 public Number getValue(int row, int column) { 310 return getStartValue(row, column); 311 } 312 313 /** 314 * Returns the start value for a task. This is a date/time value, measured 315 * in milliseconds since 1-Jan-1970. 316 * 317 * @param rowKey the series. 318 * @param columnKey the category. 319 * 320 * @return The start value (possibly <code>null</code>. 321 */ 322 public Number getStartValue(Comparable rowKey, Comparable columnKey) { 323 Number result = null; 324 int row = getRowIndex(rowKey); 325 TaskSeries series = (TaskSeries) this.data.get(row); 326 Task task = series.get(columnKey.toString()); 327 if (task != null) { 328 TimePeriod duration = task.getDuration(); 329 if (duration != null) { 330 result = new Long(duration.getStart().getTime()); 331 } 332 } 333 return result; 334 } 335 336 /** 337 * Returns the start value for a task. 338 * 339 * @param row the row index (zero-based). 340 * @param column the column index (zero-based). 341 * 342 * @return The start value. 343 */ 344 public Number getStartValue(int row, int column) { 345 Comparable rowKey = getRowKey(row); 346 Comparable columnKey = getColumnKey(column); 347 return getStartValue(rowKey, columnKey); 348 } 349 350 /** 351 * Returns the end value for a task. This is a date/time value, measured 352 * in milliseconds since 1-Jan-1970. 353 * 354 * @param rowKey the series. 355 * @param columnKey the category. 356 * 357 * @return The end value (possibly <code>null</code>). 358 */ 359 public Number getEndValue(Comparable rowKey, Comparable columnKey) { 360 Number result = null; 361 int row = getRowIndex(rowKey); 362 TaskSeries series = (TaskSeries) this.data.get(row); 363 Task task = series.get(columnKey.toString()); 364 if (task != null) { 365 TimePeriod duration = task.getDuration(); 366 if (duration != null) { 367 result = new Long(duration.getEnd().getTime()); 368 } 369 } 370 return result; 371 } 372 373 /** 374 * Returns the end value for a task. 375 * 376 * @param row the row index (zero-based). 377 * @param column the column index (zero-based). 378 * 379 * @return The end value. 380 */ 381 public Number getEndValue(int row, int column) { 382 Comparable rowKey = getRowKey(row); 383 Comparable columnKey = getColumnKey(column); 384 return getEndValue(rowKey, columnKey); 385 } 386 387 /** 388 * Returns the percent complete for a given item. 389 * 390 * @param row the row index (zero-based). 391 * @param column the column index (zero-based). 392 * 393 * @return The percent complete (possibly <code>null</code>). 394 */ 395 public Number getPercentComplete(int row, int column) { 396 Comparable rowKey = getRowKey(row); 397 Comparable columnKey = getColumnKey(column); 398 return getPercentComplete(rowKey, columnKey); 399 } 400 401 /** 402 * Returns the percent complete for a given item. 403 * 404 * @param rowKey the row key. 405 * @param columnKey the column key. 406 * 407 * @return The percent complete. 408 */ 409 public Number getPercentComplete(Comparable rowKey, Comparable columnKey) { 410 Number result = null; 411 int row = getRowIndex(rowKey); 412 TaskSeries series = (TaskSeries) this.data.get(row); 413 Task task = series.get(columnKey.toString()); 414 if (task != null) { 415 result = task.getPercentComplete(); 416 } 417 return result; 418 } 419 420 /** 421 * Returns the number of sub-intervals for a given item. 422 * 423 * @param row the row index (zero-based). 424 * @param column the column index (zero-based). 425 * 426 * @return The sub-interval count. 427 */ 428 public int getSubIntervalCount(int row, int column) { 429 Comparable rowKey = getRowKey(row); 430 Comparable columnKey = getColumnKey(column); 431 return getSubIntervalCount(rowKey, columnKey); 432 } 433 434 /** 435 * Returns the number of sub-intervals for a given item. 436 * 437 * @param rowKey the row key. 438 * @param columnKey the column key. 439 * 440 * @return The sub-interval count. 441 */ 442 public int getSubIntervalCount(Comparable rowKey, Comparable columnKey) { 443 int result = 0; 444 int row = getRowIndex(rowKey); 445 TaskSeries series = (TaskSeries) this.data.get(row); 446 Task task = series.get(columnKey.toString()); 447 if (task != null) { 448 result = task.getSubtaskCount(); 449 } 450 return result; 451 } 452 453 /** 454 * Returns the start value of a sub-interval for a given item. 455 * 456 * @param row the row index (zero-based). 457 * @param column the column index (zero-based). 458 * @param subinterval the sub-interval index (zero-based). 459 * 460 * @return The start value (possibly <code>null</code>). 461 */ 462 public Number getStartValue(int row, int column, int subinterval) { 463 Comparable rowKey = getRowKey(row); 464 Comparable columnKey = getColumnKey(column); 465 return getStartValue(rowKey, columnKey, subinterval); 466 } 467 468 /** 469 * Returns the start value of a sub-interval for a given item. 470 * 471 * @param rowKey the row key. 472 * @param columnKey the column key. 473 * @param subinterval the subinterval. 474 * 475 * @return The start value (possibly <code>null</code>). 476 */ 477 public Number getStartValue(Comparable rowKey, Comparable columnKey, 478 int subinterval) { 479 Number result = null; 480 int row = getRowIndex(rowKey); 481 TaskSeries series = (TaskSeries) this.data.get(row); 482 Task task = series.get(columnKey.toString()); 483 if (task != null) { 484 Task sub = task.getSubtask(subinterval); 485 if (sub != null) { 486 TimePeriod duration = sub.getDuration(); 487 result = new Long(duration.getStart().getTime()); 488 } 489 } 490 return result; 491 } 492 493 /** 494 * Returns the end value of a sub-interval for a given item. 495 * 496 * @param row the row index (zero-based). 497 * @param column the column index (zero-based). 498 * @param subinterval the subinterval. 499 * 500 * @return The end value (possibly <code>null</code>). 501 */ 502 public Number getEndValue(int row, int column, int subinterval) { 503 Comparable rowKey = getRowKey(row); 504 Comparable columnKey = getColumnKey(column); 505 return getEndValue(rowKey, columnKey, subinterval); 506 } 507 508 /** 509 * Returns the end value of a sub-interval for a given item. 510 * 511 * @param rowKey the row key. 512 * @param columnKey the column key. 513 * @param subinterval the subinterval. 514 * 515 * @return The end value (possibly <code>null</code>). 516 */ 517 public Number getEndValue(Comparable rowKey, Comparable columnKey, 518 int subinterval) { 519 Number result = null; 520 int row = getRowIndex(rowKey); 521 TaskSeries series = (TaskSeries) this.data.get(row); 522 Task task = series.get(columnKey.toString()); 523 if (task != null) { 524 Task sub = task.getSubtask(subinterval); 525 if (sub != null) { 526 TimePeriod duration = sub.getDuration(); 527 result = new Long(duration.getEnd().getTime()); 528 } 529 } 530 return result; 531 } 532 533 /** 534 * Returns the percentage complete value of a sub-interval for a given item. 535 * 536 * @param row the row index (zero-based). 537 * @param column the column index (zero-based). 538 * @param subinterval the sub-interval. 539 * 540 * @return The percent complete value (possibly <code>null</code>). 541 */ 542 public Number getPercentComplete(int row, int column, int subinterval) { 543 Comparable rowKey = getRowKey(row); 544 Comparable columnKey = getColumnKey(column); 545 return getPercentComplete(rowKey, columnKey, subinterval); 546 } 547 548 /** 549 * Returns the percentage complete value of a sub-interval for a given item. 550 * 551 * @param rowKey the row key. 552 * @param columnKey the column key. 553 * @param subinterval the sub-interval. 554 * 555 * @return The precent complete value (possibly <code>null</code>). 556 */ 557 public Number getPercentComplete(Comparable rowKey, Comparable columnKey, 558 int subinterval) { 559 Number result = null; 560 int row = getRowIndex(rowKey); 561 TaskSeries series = (TaskSeries) this.data.get(row); 562 Task task = series.get(columnKey.toString()); 563 if (task != null) { 564 Task sub = task.getSubtask(subinterval); 565 if (sub != null) { 566 result = sub.getPercentComplete(); 567 } 568 } 569 return result; 570 } 571 572 /** 573 * Called when a series belonging to the dataset changes. 574 * 575 * @param event information about the change. 576 */ 577 public void seriesChanged(SeriesChangeEvent event) { 578 refreshKeys(); 579 fireDatasetChanged(); 580 } 581 582 /** 583 * Refreshes the keys. 584 */ 585 private void refreshKeys() { 586 587 this.keys.clear(); 588 for (int i = 0; i < getSeriesCount(); i++) { 589 TaskSeries series = (TaskSeries) this.data.get(i); 590 // look for any keys that we don't already know about... 591 Iterator iterator = series.getTasks().iterator(); 592 while (iterator.hasNext()) { 593 Task task = (Task) iterator.next(); 594 String key = task.getDescription(); 595 int index = this.keys.indexOf(key); 596 if (index < 0) { 597 this.keys.add(key); 598 } 599 } 600 } 601 602 } 603 604 /** 605 * Tests this instance for equality with an arbitrary object. 606 * 607 * @param obj the object (<code>null</code> permitted). 608 * 609 * @return A boolean. 610 */ 611 public boolean equals(Object obj) { 612 if (obj == this) { 613 return true; 614 } 615 if (!(obj instanceof TaskSeriesCollection)) { 616 return false; 617 } 618 TaskSeriesCollection that = (TaskSeriesCollection) obj; 619 if (!ObjectUtilities.equal(this.data, that.data)) { 620 return false; 621 } 622 return true; 623 } 624 625 }