    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.4 2006/01/26 14:51:20 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,
    070                                                 Cloneable, PublicCloneable,
    071                                                 Serializable {
    072
    073        /** For serialization. */
    074        private static final long serialVersionUID = -2065799050738449903L;
    075
    076        /**
    077         * Storage for aggregate task keys (the task description is used as the
    078         * key).
    079         */
    080        private List keys;
    081
    082        /** Storage for the series. */
    083        private List data;
    084
    085        /**
    086         * Default constructor.
    087         */
    088        public TaskSeriesCollection() {
    089            this.keys = new java.util.ArrayList();
    090            this.data = new java.util.ArrayList();
    091        }
    092
    093        /**
    094         * Returns a series from the collection.
    095         *
    096         * @param key  the series key (<code>null</code> not permitted).
    097         *
    098         * @return The series.
    099         *
    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    }