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