Frames | No Frames |
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: }