    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    }