001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2006, 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-2006, 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.5 2007/03/08 13:57:08 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: * 18-Jan-2006 : Added new methods getSeries(int) and
048: * getSeries(Comparable) (DG);
049: *
050: */
051:
052: package org.jfree.data.gantt;
053:
054: import java.io.Serializable;
055: import java.util.Iterator;
056: import java.util.List;
057:
058: import org.jfree.data.general.AbstractSeriesDataset;
059: import org.jfree.data.general.SeriesChangeEvent;
060: import org.jfree.data.time.TimePeriod;
061: import org.jfree.util.ObjectUtilities;
062: import org.jfree.util.PublicCloneable;
063:
064: /**
065: * A collection of {@link TaskSeries} objects. This class provides one
066: * implementation of the {@link GanttCategoryDataset} interface.
067: */
068: public class TaskSeriesCollection extends AbstractSeriesDataset
069: implements GanttCategoryDataset, Cloneable, PublicCloneable,
070: Serializable {
071:
072: /** For serialization. */
073: private static final long serialVersionUID = -2065799050738449903L;
074:
075: /**
076: * Storage for aggregate task keys (the task description is used as the
077: * key).
078: */
079: private List keys;
080:
081: /** Storage for the series. */
082: private List data;
083:
084: /**
085: * Default constructor.
086: */
087: public TaskSeriesCollection() {
088: this .keys = new java.util.ArrayList();
089: this .data = new java.util.ArrayList();
090: }
091:
092: /**
093: * Returns a series from the collection.
094: *
095: * @param key the series key (<code>null</code> not permitted).
096: *
097: * @return The series.
098: *
099: * @since 1.0.1
100: */
101: public TaskSeries getSeries(Comparable key) {
102: if (key == null) {
103: throw new NullPointerException("Null 'key' argument.");
104: }
105: TaskSeries result = null;
106: int index = getRowIndex(key);
107: if (index >= 0) {
108: result = getSeries(index);
109: }
110: return result;
111: }
112:
113: /**
114: * Returns a series from the collection.
115: *
116: * @param series the series index (zero-based).
117: *
118: * @return The series.
119: *
120: * @since 1.0.1
121: */
122: public TaskSeries getSeries(int series) {
123: if ((series < 0) || (series >= getSeriesCount())) {
124: throw new IllegalArgumentException(
125: "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(
251: "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(
279: "Null 'series' argument.");
280: }
281: if (this .data.contains(series)) {
282: series.removeChangeListener(this );
283: this .data.remove(series);
284: fireDatasetChanged();
285: }
286: }
287:
288: /**
289: * Removes a series from the collection and sends
290: * a {@link org.jfree.data.general.DatasetChangeEvent}
291: * to all registered listeners.
292: *
293: * @param series the series (zero based index).
294: */
295: public void remove(int series) {
296: if ((series < 0) || (series >= getSeriesCount())) {
297: throw new IllegalArgumentException(
298: "TaskSeriesCollection.remove(): index outside valid range.");
299: }
300:
301: // fetch the series, remove the change listener, then remove the series.
302: TaskSeries ts = (TaskSeries) this .data.get(series);
303: ts.removeChangeListener(this );
304: this .data.remove(series);
305: fireDatasetChanged();
306:
307: }
308:
309: /**
310: * Removes all the series from the collection and sends
311: * a {@link org.jfree.data.general.DatasetChangeEvent}
312: * to all registered listeners.
313: */
314: public void removeAll() {
315:
316: // deregister the collection as a change listener to each series in
317: // the collection.
318: Iterator iterator = this .data.iterator();
319: while (iterator.hasNext()) {
320: TaskSeries series = (TaskSeries) iterator.next();
321: series.removeChangeListener(this );
322: }
323:
324: // remove all the series from the collection and notify listeners.
325: this .data.clear();
326: fireDatasetChanged();
327:
328: }
329:
330: /**
331: * Returns the value for an item.
332: *
333: * @param rowKey the row key.
334: * @param columnKey the column key.
335: *
336: * @return The item value.
337: */
338: public Number getValue(Comparable rowKey, Comparable columnKey) {
339: return getStartValue(rowKey, columnKey);
340: }
341:
342: /**
343: * Returns the value for a task.
344: *
345: * @param row the row index (zero-based).
346: * @param column the column index (zero-based).
347: *
348: * @return The start value.
349: */
350: public Number getValue(int row, int column) {
351: return getStartValue(row, column);
352: }
353:
354: /**
355: * Returns the start value for a task. This is a date/time value, measured
356: * in milliseconds since 1-Jan-1970.
357: *
358: * @param rowKey the series.
359: * @param columnKey the category.
360: *
361: * @return The start value (possibly <code>null</code>).
362: */
363: public Number getStartValue(Comparable rowKey, Comparable columnKey) {
364: Number result = null;
365: int row = getRowIndex(rowKey);
366: TaskSeries series = (TaskSeries) this .data.get(row);
367: Task task = series.get(columnKey.toString());
368: if (task != null) {
369: TimePeriod duration = task.getDuration();
370: if (duration != null) {
371: result = new Long(duration.getStart().getTime());
372: }
373: }
374: return result;
375: }
376:
377: /**
378: * Returns the start value for a task.
379: *
380: * @param row the row index (zero-based).
381: * @param column the column index (zero-based).
382: *
383: * @return The start value.
384: */
385: public Number getStartValue(int row, int column) {
386: Comparable rowKey = getRowKey(row);
387: Comparable columnKey = getColumnKey(column);
388: return getStartValue(rowKey, columnKey);
389: }
390:
391: /**
392: * Returns the end value for a task. This is a date/time value, measured
393: * in milliseconds since 1-Jan-1970.
394: *
395: * @param rowKey the series.
396: * @param columnKey the category.
397: *
398: * @return The end value (possibly <code>null</code>).
399: */
400: public Number getEndValue(Comparable rowKey, Comparable columnKey) {
401: Number result = null;
402: int row = getRowIndex(rowKey);
403: TaskSeries series = (TaskSeries) this .data.get(row);
404: Task task = series.get(columnKey.toString());
405: if (task != null) {
406: TimePeriod duration = task.getDuration();
407: if (duration != null) {
408: result = new Long(duration.getEnd().getTime());
409: }
410: }
411: return result;
412: }
413:
414: /**
415: * Returns the end value for a task.
416: *
417: * @param row the row index (zero-based).
418: * @param column the column index (zero-based).
419: *
420: * @return The end value.
421: */
422: public Number getEndValue(int row, int column) {
423: Comparable rowKey = getRowKey(row);
424: Comparable columnKey = getColumnKey(column);
425: return getEndValue(rowKey, columnKey);
426: }
427:
428: /**
429: * Returns the percent complete for a given item.
430: *
431: * @param row the row index (zero-based).
432: * @param column the column index (zero-based).
433: *
434: * @return The percent complete (possibly <code>null</code>).
435: */
436: public Number getPercentComplete(int row, int column) {
437: Comparable rowKey = getRowKey(row);
438: Comparable columnKey = getColumnKey(column);
439: return getPercentComplete(rowKey, columnKey);
440: }
441:
442: /**
443: * Returns the percent complete for a given item.
444: *
445: * @param rowKey the row key.
446: * @param columnKey the column key.
447: *
448: * @return The percent complete.
449: */
450: public Number getPercentComplete(Comparable rowKey,
451: Comparable columnKey) {
452: Number result = null;
453: int row = getRowIndex(rowKey);
454: TaskSeries series = (TaskSeries) this .data.get(row);
455: Task task = series.get(columnKey.toString());
456: if (task != null) {
457: result = task.getPercentComplete();
458: }
459: return result;
460: }
461:
462: /**
463: * Returns the number of sub-intervals for a given item.
464: *
465: * @param row the row index (zero-based).
466: * @param column the column index (zero-based).
467: *
468: * @return The sub-interval count.
469: */
470: public int getSubIntervalCount(int row, int column) {
471: Comparable rowKey = getRowKey(row);
472: Comparable columnKey = getColumnKey(column);
473: return getSubIntervalCount(rowKey, columnKey);
474: }
475:
476: /**
477: * Returns the number of sub-intervals for a given item.
478: *
479: * @param rowKey the row key.
480: * @param columnKey the column key.
481: *
482: * @return The sub-interval count.
483: */
484: public int getSubIntervalCount(Comparable rowKey,
485: Comparable columnKey) {
486: int result = 0;
487: int row = getRowIndex(rowKey);
488: TaskSeries series = (TaskSeries) this .data.get(row);
489: Task task = series.get(columnKey.toString());
490: if (task != null) {
491: result = task.getSubtaskCount();
492: }
493: return result;
494: }
495:
496: /**
497: * Returns the start value of a sub-interval for a given item.
498: *
499: * @param row the row index (zero-based).
500: * @param column the column index (zero-based).
501: * @param subinterval the sub-interval index (zero-based).
502: *
503: * @return The start value (possibly <code>null</code>).
504: */
505: public Number getStartValue(int row, int column, int subinterval) {
506: Comparable rowKey = getRowKey(row);
507: Comparable columnKey = getColumnKey(column);
508: return getStartValue(rowKey, columnKey, subinterval);
509: }
510:
511: /**
512: * Returns the start value of a sub-interval for a given item.
513: *
514: * @param rowKey the row key.
515: * @param columnKey the column key.
516: * @param subinterval the subinterval.
517: *
518: * @return The start value (possibly <code>null</code>).
519: */
520: public Number getStartValue(Comparable rowKey,
521: Comparable columnKey, int subinterval) {
522: Number result = null;
523: int row = getRowIndex(rowKey);
524: TaskSeries series = (TaskSeries) this .data.get(row);
525: Task task = series.get(columnKey.toString());
526: if (task != null) {
527: Task sub = task.getSubtask(subinterval);
528: if (sub != null) {
529: TimePeriod duration = sub.getDuration();
530: result = new Long(duration.getStart().getTime());
531: }
532: }
533: return result;
534: }
535:
536: /**
537: * Returns the end value of a sub-interval for a given item.
538: *
539: * @param row the row index (zero-based).
540: * @param column the column index (zero-based).
541: * @param subinterval the subinterval.
542: *
543: * @return The end value (possibly <code>null</code>).
544: */
545: public Number getEndValue(int row, int column, int subinterval) {
546: Comparable rowKey = getRowKey(row);
547: Comparable columnKey = getColumnKey(column);
548: return getEndValue(rowKey, columnKey, subinterval);
549: }
550:
551: /**
552: * Returns the end value of a sub-interval for a given item.
553: *
554: * @param rowKey the row key.
555: * @param columnKey the column key.
556: * @param subinterval the subinterval.
557: *
558: * @return The end value (possibly <code>null</code>).
559: */
560: public Number getEndValue(Comparable rowKey, Comparable columnKey,
561: int subinterval) {
562: Number result = null;
563: int row = getRowIndex(rowKey);
564: TaskSeries series = (TaskSeries) this .data.get(row);
565: Task task = series.get(columnKey.toString());
566: if (task != null) {
567: Task sub = task.getSubtask(subinterval);
568: if (sub != null) {
569: TimePeriod duration = sub.getDuration();
570: result = new Long(duration.getEnd().getTime());
571: }
572: }
573: return result;
574: }
575:
576: /**
577: * Returns the percentage complete value of a sub-interval for a given item.
578: *
579: * @param row the row index (zero-based).
580: * @param column the column index (zero-based).
581: * @param subinterval the sub-interval.
582: *
583: * @return The percent complete value (possibly <code>null</code>).
584: */
585: public Number getPercentComplete(int row, int column,
586: int subinterval) {
587: Comparable rowKey = getRowKey(row);
588: Comparable columnKey = getColumnKey(column);
589: return getPercentComplete(rowKey, columnKey, subinterval);
590: }
591:
592: /**
593: * Returns the percentage complete value of a sub-interval for a given item.
594: *
595: * @param rowKey the row key.
596: * @param columnKey the column key.
597: * @param subinterval the sub-interval.
598: *
599: * @return The precent complete value (possibly <code>null</code>).
600: */
601: public Number getPercentComplete(Comparable rowKey,
602: Comparable columnKey, int subinterval) {
603: Number result = null;
604: int row = getRowIndex(rowKey);
605: TaskSeries series = (TaskSeries) this .data.get(row);
606: Task task = series.get(columnKey.toString());
607: if (task != null) {
608: Task sub = task.getSubtask(subinterval);
609: if (sub != null) {
610: result = sub.getPercentComplete();
611: }
612: }
613: return result;
614: }
615:
616: /**
617: * Called when a series belonging to the dataset changes.
618: *
619: * @param event information about the change.
620: */
621: public void seriesChanged(SeriesChangeEvent event) {
622: refreshKeys();
623: fireDatasetChanged();
624: }
625:
626: /**
627: * Refreshes the keys.
628: */
629: private void refreshKeys() {
630:
631: this .keys.clear();
632: for (int i = 0; i < getSeriesCount(); i++) {
633: TaskSeries series = (TaskSeries) this .data.get(i);
634: // look for any keys that we don't already know about...
635: Iterator iterator = series.getTasks().iterator();
636: while (iterator.hasNext()) {
637: Task task = (Task) iterator.next();
638: String key = task.getDescription();
639: int index = this .keys.indexOf(key);
640: if (index < 0) {
641: this .keys.add(key);
642: }
643: }
644: }
645:
646: }
647:
648: /**
649: * Tests this instance for equality with an arbitrary object.
650: *
651: * @param obj the object (<code>null</code> permitted).
652: *
653: * @return A boolean.
654: */
655: public boolean equals(Object obj) {
656: if (obj == this ) {
657: return true;
658: }
659: if (!(obj instanceof TaskSeriesCollection)) {
660: return false;
661: }
662: TaskSeriesCollection that = (TaskSeriesCollection) obj;
663: if (!ObjectUtilities.equal(this .data, that.data)) {
664: return false;
665: }
666: return true;
667: }
668:
669: }
|