Source for org.jfree.data.gantt.TaskSeriesCollection

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * -------------------------
  28:  * TaskSeriesCollection.java
  29:  * -------------------------
  30:  * (C) Copyright 2002-2007, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   Thomas Schuster;
  34:  *
  35:  * Changes
  36:  * -------
  37:  * 06-Jun-2002 : Version 1 (DG);
  38:  * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  39:  * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 
  40:  *               CategoryToolTipGenerator interface (DG);
  41:  * 10-Jan-2003 : Renamed GanttSeriesCollection --> TaskSeriesCollection (DG);
  42:  * 04-Sep-2003 : Fixed bug 800324 (DG);
  43:  * 16-Sep-2003 : Implemented GanttCategoryDataset (DG);
  44:  * 12-Jan-2005 : Fixed bug 1099331 (DG);
  45:  * 18-Jan-2006 : Added new methods getSeries(int) and 
  46:  *               getSeries(Comparable) (DG);
  47:  *
  48:  */
  49: 
  50: package org.jfree.data.gantt;
  51: 
  52: import java.io.Serializable;
  53: import java.util.Iterator;
  54: import java.util.List;
  55: 
  56: import org.jfree.data.general.AbstractSeriesDataset;
  57: import org.jfree.data.general.SeriesChangeEvent;
  58: import org.jfree.data.time.TimePeriod;
  59: import org.jfree.util.ObjectUtilities;
  60: import org.jfree.util.PublicCloneable;
  61: 
  62: /**
  63:  * A collection of {@link TaskSeries} objects.  This class provides one 
  64:  * implementation of the {@link GanttCategoryDataset} interface.
  65:  */
  66: public class TaskSeriesCollection extends AbstractSeriesDataset
  67:                                   implements GanttCategoryDataset,
  68:                                              Cloneable, PublicCloneable, 
  69:                                              Serializable {
  70: 
  71:     /** For serialization. */
  72:     private static final long serialVersionUID = -2065799050738449903L;
  73: 
  74:     /** 
  75:      * Storage for aggregate task keys (the task description is used as the 
  76:      * key). 
  77:      */
  78:     private List keys;
  79: 
  80:     /** Storage for the series. */
  81:     private List data;
  82: 
  83:     /**
  84:      * Default constructor.
  85:      */
  86:     public TaskSeriesCollection() {
  87:         this.keys = new java.util.ArrayList();
  88:         this.data = new java.util.ArrayList();
  89:     }
  90:     
  91:     /**
  92:      * Returns a series from the collection.
  93:      *
  94:      * @param key  the series key (<code>null</code> not permitted).
  95:      *
  96:      * @return The series.
  97:      * 
  98:      * @since 1.0.1
  99:      */
 100:     public TaskSeries getSeries(Comparable key) {
 101:         if (key == null) {
 102:             throw new NullPointerException("Null 'key' argument.");
 103:         }
 104:         TaskSeries result = null;
 105:         int index = getRowIndex(key);
 106:         if (index >= 0) {
 107:             result = getSeries(index);
 108:         }
 109:         return result;
 110:     }
 111: 
 112:     /**
 113:      * Returns a series from the collection.
 114:      *
 115:      * @param series  the series index (zero-based).
 116:      *
 117:      * @return The series.
 118:      * 
 119:      * @since 1.0.1
 120:      */
 121:     public TaskSeries getSeries(int series) {
 122:         if ((series < 0) || (series >= getSeriesCount())) {
 123:             throw new IllegalArgumentException("Series index out of bounds");
 124:         }
 125:         return (TaskSeries) this.data.get(series);
 126:     }
 127:     
 128:     /**
 129:      * Returns the number of series in the collection.
 130:      *
 131:      * @return The series count.
 132:      */
 133:     public int getSeriesCount() {
 134:         return getRowCount();
 135:     }
 136: 
 137:     /**
 138:      * Returns the name of a series.
 139:      *
 140:      * @param series  the series index (zero-based).
 141:      *
 142:      * @return The name of a series.
 143:      */
 144:     public Comparable getSeriesKey(int series) {
 145:         TaskSeries ts = (TaskSeries) this.data.get(series);
 146:         return ts.getKey();
 147:     }
 148: 
 149:     /**
 150:      * Returns the number of rows (series) in the collection.
 151:      *
 152:      * @return The series count.
 153:      */
 154:     public int getRowCount() {
 155:         return this.data.size();
 156:     }
 157: 
 158:     /**
 159:      * Returns the row keys.  In this case, each series is a key.
 160:      *
 161:      * @return The row keys.
 162:      */
 163:     public List getRowKeys() {
 164:         return this.data;
 165:     }
 166: 
 167:     /**
 168:      * Returns the number of column in the dataset.
 169:      *
 170:      * @return The column count.
 171:      */
 172:     public int getColumnCount() {
 173:         return this.keys.size();
 174:     }
 175: 
 176:     /**
 177:      * Returns a list of the column keys in the dataset.
 178:      *
 179:      * @return The category list.
 180:      */
 181:     public List getColumnKeys() {
 182:         return this.keys;
 183:     }
 184: 
 185:     /**
 186:      * Returns a column key.
 187:      *
 188:      * @param index  the column index.
 189:      *
 190:      * @return The column key.
 191:      */
 192:     public Comparable getColumnKey(int index) {
 193:         return (Comparable) this.keys.get(index);
 194:     }
 195: 
 196:     /**
 197:      * Returns the column index for a column key.
 198:      *
 199:      * @param columnKey  the column key (<code>null</code> not permitted).
 200:      *
 201:      * @return The column index.
 202:      */
 203:     public int getColumnIndex(Comparable columnKey) {
 204:         if (columnKey == null) {
 205:             throw new IllegalArgumentException("Null 'columnKey' argument.");
 206:         }
 207:         return this.keys.indexOf(columnKey);
 208:     }
 209: 
 210:     /**
 211:      * Returns the row index for the given row key.
 212:      *
 213:      * @param rowKey  the row key.
 214:      *
 215:      * @return The index.
 216:      */
 217:     public int getRowIndex(Comparable rowKey) {
 218:         int result = -1;
 219:         int count = this.data.size();
 220:         for (int i = 0; i < count; i++) {
 221:             TaskSeries s = (TaskSeries) this.data.get(i);
 222:             if (s.getKey().equals(rowKey)) {
 223:                 result = i;
 224:                 break;
 225:             }
 226:         }
 227:         return result;
 228:     }
 229: 
 230:     /**
 231:      * Returns the key for a row.
 232:      *
 233:      * @param index  the row index (zero-based).
 234:      *
 235:      * @return The key.
 236:      */
 237:     public Comparable getRowKey(int index) {
 238:         TaskSeries series = (TaskSeries) this.data.get(index);
 239:         return series.getKey();
 240:     }
 241: 
 242:     /**
 243:      * Adds a series to the dataset and sends a 
 244:      * {@link org.jfree.data.general.DatasetChangeEvent} to all registered 
 245:      * listeners.
 246:      *
 247:      * @param series  the series (<code>null</code> not permitted).
 248:      */
 249:     public void add(TaskSeries series) {
 250:         if (series == null) {
 251:             throw new IllegalArgumentException("Null 'series' argument.");
 252:         }
 253:         this.data.add(series);
 254:         series.addChangeListener(this);
 255: 
 256:         // look for any keys that we don't already know about...
 257:         Iterator iterator = series.getTasks().iterator();
 258:         while (iterator.hasNext()) {
 259:             Task task = (Task) iterator.next();
 260:             String key = task.getDescription();
 261:             int index = this.keys.indexOf(key);
 262:             if (index < 0) {
 263:                 this.keys.add(key);
 264:             }
 265:         }
 266:         fireDatasetChanged();
 267:     }
 268: 
 269:     /**
 270:      * Removes a series from the collection and sends 
 271:      * a {@link org.jfree.data.general.DatasetChangeEvent}
 272:      * to all registered listeners.
 273:      *
 274:      * @param series  the series.
 275:      */
 276:     public void remove(TaskSeries series) {
 277:         if (series == null) {
 278:             throw new IllegalArgumentException("Null 'series' argument.");
 279:         }
 280:         if (this.data.contains(series)) {
 281:             series.removeChangeListener(this);
 282:             this.data.remove(series);
 283:             fireDatasetChanged();
 284:         }
 285:     }
 286: 
 287:     /**
 288:      * Removes a series from the collection and sends 
 289:      * a {@link org.jfree.data.general.DatasetChangeEvent}
 290:      * to all registered listeners.
 291:      *
 292:      * @param series  the series (zero based index).
 293:      */
 294:     public void remove(int series) {
 295:         if ((series < 0) || (series >= getSeriesCount())) {
 296:             throw new IllegalArgumentException(
 297:                 "TaskSeriesCollection.remove(): index outside valid range.");
 298:         }
 299: 
 300:         // fetch the series, remove the change listener, then remove the series.
 301:         TaskSeries ts = (TaskSeries) this.data.get(series);
 302:         ts.removeChangeListener(this);
 303:         this.data.remove(series);
 304:         fireDatasetChanged();
 305: 
 306:     }
 307: 
 308:     /**
 309:      * Removes all the series from the collection and sends 
 310:      * a {@link org.jfree.data.general.DatasetChangeEvent}
 311:      * to all registered listeners.
 312:      */
 313:     public void removeAll() {
 314: 
 315:         // deregister the collection as a change listener to each series in 
 316:         // the collection.
 317:         Iterator iterator = this.data.iterator();
 318:         while (iterator.hasNext()) {
 319:             TaskSeries series = (TaskSeries) iterator.next();
 320:             series.removeChangeListener(this);
 321:         }
 322: 
 323:         // remove all the series from the collection and notify listeners.
 324:         this.data.clear();
 325:         fireDatasetChanged();
 326: 
 327:     }
 328: 
 329:     /**
 330:      * Returns the value for an item.
 331:      *
 332:      * @param rowKey  the row key.
 333:      * @param columnKey  the column key.
 334:      *
 335:      * @return The item value.
 336:      */
 337:     public Number getValue(Comparable rowKey, Comparable columnKey) {
 338:         return getStartValue(rowKey, columnKey);
 339:     }
 340: 
 341:     /**
 342:      * Returns the value for a task.
 343:      *
 344:      * @param row  the row index (zero-based).
 345:      * @param column  the column index (zero-based).
 346:      *
 347:      * @return The start value.
 348:      */
 349:     public Number getValue(int row, int column) {
 350:         return getStartValue(row, column);
 351:     }
 352: 
 353:     /**
 354:      * Returns the start value for a task.  This is a date/time value, measured
 355:      * in milliseconds since 1-Jan-1970.
 356:      *
 357:      * @param rowKey  the series.
 358:      * @param columnKey  the category.
 359:      *
 360:      * @return The start value (possibly <code>null</code>).
 361:      */
 362:     public Number getStartValue(Comparable rowKey, Comparable columnKey) {
 363:         Number result = null;
 364:         int row = getRowIndex(rowKey);
 365:         TaskSeries series = (TaskSeries) this.data.get(row);
 366:         Task task = series.get(columnKey.toString());
 367:         if (task != null) {
 368:             TimePeriod duration = task.getDuration();
 369:             if (duration != null) {
 370:                 result = new Long(duration.getStart().getTime());
 371:             }
 372:         }
 373:         return result;
 374:     }
 375: 
 376:     /**
 377:      * Returns the start value for a task.
 378:      *
 379:      * @param row  the row index (zero-based).
 380:      * @param column  the column index (zero-based).
 381:      *
 382:      * @return The start value.
 383:      */
 384:     public Number getStartValue(int row, int column) {
 385:         Comparable rowKey = getRowKey(row);
 386:         Comparable columnKey = getColumnKey(column);
 387:         return getStartValue(rowKey, columnKey);
 388:     }
 389: 
 390:     /**
 391:      * Returns the end value for a task.  This is a date/time value, measured
 392:      * in milliseconds since 1-Jan-1970.
 393:      *
 394:      * @param rowKey  the series.
 395:      * @param columnKey  the category.
 396:      *
 397:      * @return The end value (possibly <code>null</code>).
 398:      */
 399:     public Number getEndValue(Comparable rowKey, Comparable columnKey) {
 400:         Number result = null;
 401:         int row = getRowIndex(rowKey);
 402:         TaskSeries series = (TaskSeries) this.data.get(row);
 403:         Task task = series.get(columnKey.toString());
 404:         if (task != null) {
 405:             TimePeriod duration = task.getDuration();
 406:             if (duration != null) {
 407:                 result = new Long(duration.getEnd().getTime());
 408:             }
 409:         }
 410:         return result;
 411:     }
 412: 
 413:     /**
 414:      * Returns the end value for a task.
 415:      *
 416:      * @param row  the row index (zero-based).
 417:      * @param column  the column index (zero-based).
 418:      *
 419:      * @return The end value.
 420:      */
 421:     public Number getEndValue(int row, int column) {
 422:         Comparable rowKey = getRowKey(row);
 423:         Comparable columnKey = getColumnKey(column);
 424:         return getEndValue(rowKey, columnKey);
 425:     }
 426: 
 427:     /**
 428:      * Returns the percent complete for a given item.
 429:      *
 430:      * @param row  the row index (zero-based).
 431:      * @param column  the column index (zero-based).
 432:      *
 433:      * @return The percent complete (possibly <code>null</code>).
 434:      */
 435:     public Number getPercentComplete(int row, int column) {
 436:         Comparable rowKey = getRowKey(row);
 437:         Comparable columnKey = getColumnKey(column);
 438:         return getPercentComplete(rowKey, columnKey);
 439:     }
 440: 
 441:     /**
 442:      * Returns the percent complete for a given item.
 443:      *
 444:      * @param rowKey  the row key.
 445:      * @param columnKey  the column key.
 446:      *
 447:      * @return The percent complete.
 448:      */
 449:     public Number getPercentComplete(Comparable rowKey, Comparable columnKey) {
 450:         Number result = null;
 451:         int row = getRowIndex(rowKey);
 452:         TaskSeries series = (TaskSeries) this.data.get(row);
 453:         Task task = series.get(columnKey.toString());
 454:         if (task != null) {
 455:             result = task.getPercentComplete();
 456:         }
 457:         return result;
 458:     }
 459: 
 460:     /**
 461:      * Returns the number of sub-intervals for a given item.
 462:      *
 463:      * @param row  the row index (zero-based).
 464:      * @param column  the column index (zero-based).
 465:      *
 466:      * @return The sub-interval count.
 467:      */
 468:     public int getSubIntervalCount(int row, int column) {
 469:         Comparable rowKey = getRowKey(row);
 470:         Comparable columnKey = getColumnKey(column);
 471:         return getSubIntervalCount(rowKey, columnKey);
 472:     }
 473: 
 474:     /**
 475:      * Returns the number of sub-intervals for a given item.
 476:      *
 477:      * @param rowKey  the row key.
 478:      * @param columnKey  the column key.
 479:      *
 480:      * @return The sub-interval count.
 481:      */
 482:     public int getSubIntervalCount(Comparable rowKey, Comparable columnKey) {
 483:         int result = 0;
 484:         int row = getRowIndex(rowKey);
 485:         TaskSeries series = (TaskSeries) this.data.get(row);
 486:         Task task = series.get(columnKey.toString());
 487:         if (task != null) {
 488:             result = task.getSubtaskCount();
 489:         }
 490:         return result;
 491:     }
 492: 
 493:     /**
 494:      * Returns the start 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 sub-interval index (zero-based).
 499:      *
 500:      * @return The start value (possibly <code>null</code>).
 501:      */
 502:     public Number getStartValue(int row, int column, int subinterval) {
 503:         Comparable rowKey = getRowKey(row);
 504:         Comparable columnKey = getColumnKey(column);
 505:         return getStartValue(rowKey, columnKey, subinterval);
 506:     }
 507: 
 508:     /**
 509:      * Returns the start 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 start value (possibly <code>null</code>).
 516:      */
 517:     public Number getStartValue(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.getStart().getTime());
 528:             }
 529:         }
 530:         return result;
 531:     }
 532: 
 533:     /**
 534:      * Returns the end 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 subinterval.
 539:      *
 540:      * @return The end value (possibly <code>null</code>).
 541:      */
 542:     public Number getEndValue(int row, int column, int subinterval) {
 543:         Comparable rowKey = getRowKey(row);
 544:         Comparable columnKey = getColumnKey(column);
 545:         return getEndValue(rowKey, columnKey, subinterval);
 546:     }
 547: 
 548:     /**
 549:      * Returns the end 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 subinterval.
 554:      *
 555:      * @return The end value (possibly <code>null</code>).
 556:      */
 557:     public Number getEndValue(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:                 TimePeriod duration = sub.getDuration();
 567:                 result = new Long(duration.getEnd().getTime());
 568:             }
 569:         }
 570:         return result;
 571:     }
 572: 
 573:     /**
 574:      * Returns the percentage complete value of a sub-interval for a given item.
 575:      *
 576:      * @param row  the row index (zero-based).
 577:      * @param column  the column index (zero-based).
 578:      * @param subinterval  the sub-interval.
 579:      *
 580:      * @return The percent complete value (possibly <code>null</code>).
 581:      */
 582:     public Number getPercentComplete(int row, int column, int subinterval) {
 583:         Comparable rowKey = getRowKey(row);
 584:         Comparable columnKey = getColumnKey(column);
 585:         return getPercentComplete(rowKey, columnKey, subinterval);
 586:     }
 587: 
 588:     /**
 589:      * Returns the percentage complete value of a sub-interval for a given item.
 590:      *
 591:      * @param rowKey  the row key.
 592:      * @param columnKey  the column key.
 593:      * @param subinterval  the sub-interval.
 594:      *
 595:      * @return The precent complete value (possibly <code>null</code>).
 596:      */
 597:     public Number getPercentComplete(Comparable rowKey, Comparable columnKey, 
 598:                                      int subinterval) {
 599:         Number result = null;
 600:         int row = getRowIndex(rowKey);
 601:         TaskSeries series = (TaskSeries) this.data.get(row);
 602:         Task task = series.get(columnKey.toString());
 603:         if (task != null) {
 604:             Task sub = task.getSubtask(subinterval);
 605:             if (sub != null) {
 606:                 result = sub.getPercentComplete();
 607:             }
 608:         }
 609:         return result;
 610:     }
 611: 
 612:     /**
 613:      * Called when a series belonging to the dataset changes.
 614:      *
 615:      * @param event  information about the change.
 616:      */
 617:     public void seriesChanged(SeriesChangeEvent event) {
 618:         refreshKeys();
 619:         fireDatasetChanged();
 620:     }
 621: 
 622:     /**
 623:      * Refreshes the keys.
 624:      */
 625:     private void refreshKeys() {
 626: 
 627:         this.keys.clear();
 628:         for (int i = 0; i < getSeriesCount(); i++) {
 629:             TaskSeries series = (TaskSeries) this.data.get(i);
 630:             // look for any keys that we don't already know about...
 631:             Iterator iterator = series.getTasks().iterator();
 632:             while (iterator.hasNext()) {
 633:                 Task task = (Task) iterator.next();
 634:                 String key = task.getDescription();
 635:                 int index = this.keys.indexOf(key);
 636:                 if (index < 0) {
 637:                     this.keys.add(key);
 638:                 }
 639:             }
 640:         }
 641: 
 642:     }
 643:     
 644:     /**
 645:      * Tests this instance for equality with an arbitrary object.
 646:      * 
 647:      * @param obj  the object (<code>null</code> permitted).
 648:      * 
 649:      * @return A boolean.
 650:      */
 651:     public boolean equals(Object obj) {
 652:         if (obj == this) {
 653:             return true;
 654:         }
 655:         if (!(obj instanceof TaskSeriesCollection)) {
 656:             return false;
 657:         }
 658:         TaskSeriesCollection that = (TaskSeriesCollection) obj;
 659:         if (!ObjectUtilities.equal(this.data, that.data)) {
 660:             return false;
 661:         }
 662:         return true;
 663:     }
 664: 
 665: }