    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     * XYSeriesCollection.java
    029     * -----------------------
    030     * (C) Copyright 2001-2005, by Object Refinery Limited and Contributors.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   Aaron Metzger;
    034     *
    035     * $Id: XYSeriesCollection.java,v 1.12.2.2 2005/10/25 21:36:51 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 15-Nov-2001 : Version 1 (DG);
    040     * 03-Apr-2002 : Added change listener code (DG);
    041     * 29-Apr-2002 : Added removeSeries, removeAllSeries methods (ARM);
    042     * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    043     * 26-Mar-2003 : Implemented Serializable (DG);
    044     * 04-Aug-2003 : Added getSeries() method (DG);
    045     * 31-Mar-2004 : Modified to use an XYIntervalDelegate.
    046     * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG);
    047     * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG);
    048     * 17-Nov-2004 : Updated for changes to DomainInfo interface (DG);
    049     * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
    050     * 28-Mar-2005 : Fixed bug in getSeries(int) method (1170825) (DG);
    051     * 05-Oct-2005 : Made the interval delegate a dataset listener (DG);
    052     *
    053     */
    054
    055    package org.jfree.data.xy;
    056
    057    import java.io.Serializable;
    058    import java.util.Collections;
    059    import java.util.List;
    060
    061    import org.jfree.data.DomainInfo;
    062    import org.jfree.data.Range;
    063    import org.jfree.data.general.DatasetChangeEvent;
    064    import org.jfree.data.general.DatasetUtilities;
    065    import org.jfree.util.ObjectUtilities;
    066
    067    /**
    068     * Represents a collection of {@link XYSeries} objects that can be used as a
    069     * dataset.
    070     */
    071    public class XYSeriesCollection extends AbstractIntervalXYDataset
    072                                    implements IntervalXYDataset, DomainInfo,
    073                                               Serializable {
    074
    075        /** For serialization. */
    076        private static final long serialVersionUID = -7590013825931496766L;
    077
    078        /** The series that are included in the collection. */
    079        private List data;
    080
    081        /** The interval delegate (used to calculate the start and end x-values). */
    082        private IntervalXYDelegate intervalDelegate;
    083
    084        /**
    085         * Constructs an empty dataset.
    086         */
    087        public XYSeriesCollection() {
    088            this(null);
    089        }
    090
    091        /**
    092         * Constructs a dataset and populates it with a single series.
    093         *
    094         * @param series  the series (<code>null</code> ignored).
    095         */
    096        public XYSeriesCollection(XYSeries series) {
    097            this.data = new java.util.ArrayList();
    098            this.intervalDelegate = new IntervalXYDelegate(this, false);
    099            addChangeListener(this.intervalDelegate);
    100            if (series != null) {
    101                this.data.add(series);
    102                series.addChangeListener(this);
    103            }
    104        }
    105
    106        /**
    107         * Adds a series to the collection and sends a {@link DatasetChangeEvent}
    108         * to all registered listeners.
    109         *
    110         * @param series  the series (<code>null</code> not permitted).
    111         */
    112        public void addSeries(XYSeries series) {
    113
    114            if (series == null) {
    115                throw new IllegalArgumentException("Null 'series' argument.");
    116            }
    117            this.data.add(series);
    118            series.addChangeListener(this);
    119            fireDatasetChanged();
    120
    121        }
    122
    123        /**
    124         * Removes a series from the collection and sends a
    125         * {@link DatasetChangeEvent} to all registered listeners.
    126         *
    127         * @param series  the series index (zero-based).
    128         */
    129        public void removeSeries(int series) {
    130
    131            if ((series < 0) || (series > getSeriesCount())) {
    132                throw new IllegalArgumentException("Series index out of bounds.");
    133            }
    134
    135            // fetch the series, remove the change listener, then remove the series.
    136            XYSeries ts = (XYSeries) this.data.get(series);
    137            ts.removeChangeListener(this);
    138            this.data.remove(series);
    139            fireDatasetChanged();
    140
    141        }
    142
    143        /**
    144         * Removes a series from the collection and sends a
    145         * {@link DatasetChangeEvent} to all registered listeners.
    146         *
    147         * @param series  the series (<code>null</code> not permitted).
    148         */
    149        public void removeSeries(XYSeries series) {
    150
    151            if (series == null) {
    152                throw new IllegalArgumentException("Null 'series' argument.");
    153            }
    154            if (this.data.contains(series)) {
    155                series.removeChangeListener(this);
    156                this.data.remove(series);
    157                fireDatasetChanged();
    158            }
    159
    160        }
    161
    162        /**
    163         * Removes all the series from the collection and sends a
    164         * {@link DatasetChangeEvent} to all registered listeners.
    165         */
    166        public void removeAllSeries() {
    167            // Unregister the collection as a change listener to each series in
    168            // the collection.
    169            for (int i = 0; i < this.data.size(); i++) {
    170              XYSeries series = (XYSeries) this.data.get(i);
    171              series.removeChangeListener(this);
    172            }
    173
    174            // Remove all the series from the collection and notify listeners.
    175            this.data.clear();
    176            fireDatasetChanged();
    177        }
    178
    179        /**
    180         * Returns the number of series in the collection.
    181         *
    182         * @return The series count.
    183         */
    184        public int getSeriesCount() {
    185            return this.data.size();
    186        }
    187
    188        /**
    189         * Returns a list of all the series in the collection.
    190         *
    191         * @return The list (which is unmodifiable).
    192         */
    193        public List getSeries() {
    194            return Collections.unmodifiableList(this.data);
    195        }
    196
    197        /**
    198         * Returns a series from the collection.
    199         *
    200         * @param series  the series index (zero-based).
    201         *
    202         * @return The series.
    203         */
    204        public XYSeries getSeries(int series) {
    205            if ((series < 0) || (series >= getSeriesCount())) {
    206                throw new IllegalArgumentException("Series index out of bounds");
    207            }
    208            return (XYSeries) this.data.get(series);
    209        }
    210
    211        /**
    212         * Returns the key for a series.
    213         *
    214         * @param series  the series index (zero-based).
    215         *
    216         * @return The key for a series.
    217         */
    218        public Comparable getSeriesKey(int series) {
    219            // defer argument checking
    220            return getSeries(series).getKey();
    221        }
    222
    223        /**
    224         * Returns the number of items in the specified series.
    225         *
    226         * @param series  the series (zero-based index).
    227         *
    228         * @return The item count.
    229         */
    230        public int getItemCount(int series) {
    231            // defer argument checking
    232            return getSeries(series).getItemCount();
    233        }
    234
    235        /**
    236         * Returns the x-value for the specified series and item.
    237         *
    238         * @param series  the series (zero-based index).
    239         * @param item  the item (zero-based index).
    240         *
    241         * @return The value.
    242         */
    243        public Number getX(int series, int item) {
    244            XYSeries ts = (XYSeries) this.data.get(series);
    245            XYDataItem xyItem = ts.getDataItem(item);
    246            return xyItem.getX();
    247        }
    248
    249        /**
    250         * Returns the starting X value for the specified series and item.
    251         *
    252         * @param series  the series (zero-based index).
    253         * @param item  the item (zero-based index).
    254         *
    255         * @return The starting X value.
    256         */
    257        public Number getStartX(int series, int item) {
    258            return this.intervalDelegate.getStartX(series, item);
    259        }
    260
    261        /**
    262         * Returns the ending X value for the specified series and item.
    263         *
    264         * @param series  the series (zero-based index).
    265         * @param item  the item (zero-based index).
    266         *
    267         * @return The ending X value.
    268         */
    269        public Number getEndX(int series, int item) {
    270            return this.intervalDelegate.getEndX(series, item);
    271        }
    272
    273        /**
    274         * Returns the y-value for the specified series and item.
    275         *
    276         * @param series  the series (zero-based index).
    277         * @param index  the index of the item of interest (zero-based).
    278         *
    279         * @return The value (possibly <code>null</code>).
    280         */
    281        public Number getY(int series, int index) {
    282
    283            XYSeries ts = (XYSeries) this.data.get(series);
    284            XYDataItem xyItem = ts.getDataItem(index);
    285            return xyItem.getY();
    286
    287        }
    288
    289        /**
    290         * Returns the starting Y value for the specified series and item.
    291         *
    292         * @param series  the series (zero-based index).
    293         * @param item  the item (zero-based index).
    294         *
    295         * @return The starting Y value.
    296         */
    297        public Number getStartY(int series, int item) {
    298            return getY(series, item);
    299        }
    300
    301        /**
    302         * Returns the ending Y value for the specified series and item.
    303         *
    304         * @param series  the series (zero-based index).
    305         * @param item  the item (zero-based index).
    306         *
    307         * @return The ending Y value.
    308         */
    309        public Number getEndY(int series, int item) {
    310            return getY(series, item);
    311        }
    312
    313        /**
    314         * Tests this collection for equality with an arbitrary object.
    315         *
    316         * @param obj  the object (<code>null</code> permitted).
    317         *
    318         * @return A boolean.
    319         */
    320        public boolean equals(Object obj) {
    321            /*
    322             * XXX
    323             *
    324             * what about  the interval delegate...?
    325             * The interval width etc wasn't considered
    326             * before, hence i did not add it here (AS)
    327             *
    328             */
    329
    330            if (obj == this) {
    331                return true;
    332            }
    333            if (!(obj instanceof XYSeriesCollection)) {
    334                return false;
    335            }
    336            XYSeriesCollection that = (XYSeriesCollection) obj;
    337            return ObjectUtilities.equal(this.data, that.data);
    338        }
    339
    340        /**
    341         * Returns a hash code.
    342         *
    343         * @return A hash code.
    344         */
    345        public int hashCode() {
    346            // Same question as for equals (AS)
    347            return (this.data != null ? this.data.hashCode() : 0);
    348        }
    349
    350        /**
    351         * Returns the minimum x-value in the dataset.
    352         *
    353         * @param includeInterval  a flag that determines whether or not the
    354         *                         x-interval is taken into account.
    355         *
    356         * @return The minimum value.
    357         */
    358        public double getDomainLowerBound(boolean includeInterval) {
    359            return this.intervalDelegate.getDomainLowerBound(includeInterval);
    360        }
    361
    362        /**
    363         * Returns the maximum x-value in the dataset.
    364         *
    365         * @param includeInterval  a flag that determines whether or not the
    366         *                         x-interval is taken into account.
    367         *
    368         * @return The maximum value.
    369         */
    370        public double getDomainUpperBound(boolean includeInterval) {
    371            return this.intervalDelegate.getDomainUpperBound(includeInterval);
    372        }
    373
    374        /**
    375         * Returns the range of the values in this dataset's domain.
    376         *
    377         * @param includeInterval  a flag that determines whether or not the
    378         *                         x-interval is taken into account.
    379         *
    380         * @return The range.
    381         */
    382        public Range getDomainBounds(boolean includeInterval) {
    383            if (includeInterval) {
    384                return this.intervalDelegate.getDomainBounds(includeInterval);
    385            }
    386            else {
    387                return DatasetUtilities.iterateDomainBounds(this, includeInterval);
    388            }
    389
    390        }
    391
    392        /**
    393         * Returns the interval width. This is used to calculate the start and end
    394         * x-values, if/when the dataset is used as an {@link IntervalXYDataset}.
    395         *
    396         * @return The interval width.
    397         */
    398        public double getIntervalWidth() {
    399            return this.intervalDelegate.getIntervalWidth();
    400        }
    401
    402        /**
    403         * Sets the interval width and sends a {@link DatasetChangeEvent} to all
    404         * registered listeners.
    405         *
    406         * @param width  the width (negative values not permitted).
    407         */
    408        public void setIntervalWidth(double width) {
    409            if (width < 0.0) {
    410                throw new IllegalArgumentException("Negative 'width' argument.");
    411            }
    412            this.intervalDelegate.setFixedIntervalWidth(width);
    413            fireDatasetChanged();
    414        }
    415
    416        /**
    417         * Returns the interval position factor.
    418         *
    419         * @return The interval position factor.
    420         */
    421        public double getIntervalPositionFactor() {
    422            return this.intervalDelegate.getIntervalPositionFactor();
    423        }
    424
    425        /**
    426         * Sets the interval position factor. This controls where the x-value is in
    427         * relation to the interval surrounding the x-value (0.0 means the x-value
    428         * will be positioned at the start, 0.5 in the middle, and 1.0 at the end).
    429         *
    430         * @param factor  the factor.
    431         */
    432        public void setIntervalPositionFactor(double factor) {
    433            this.intervalDelegate.setIntervalPositionFactor(factor);
    434            fireDatasetChanged();
    435        }
    436
    437        /**
    438         * Returns whether the interval width is automatically calculated or not.
    439         *
    440         * @return Whether the width is automatically calculated or not.
    441         */
    442        public boolean isAutoWidth() {
    443            return this.intervalDelegate.isAutoWidth();
    444        }
    445
    446        /**
    447         * Sets the flag that indicates wether the interval width is automatically
    448         * calculated or not.
    449         *
    450         * @param b  a boolean.
    451         */
    452        public void setAutoWidth(boolean b) {
    453            this.intervalDelegate.setAutoWidth(b);
    454            fireDatasetChanged();
    455        }
    456
    457    }