    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2008, 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     * DefaultIntervalCategoryDataset.java
    029     * -----------------------------------
    030     * (C) Copyright 2002-2008, by Jeremy Bowman and Contributors.
    031     *
    032     * Original Author:  Jeremy Bowman;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *
    035     * Changes
    036     * -------
    037     * 29-Apr-2002 : Version 1, contributed by Jeremy Bowman (DG);
    038     * 24-Oct-2002 : Amendments for changes made to the dataset interface (DG);
    039     * ------------- JFREECHART 1.0.x ---------------------------------------------
    040     * 08-Mar-2007 : Added equals() and clone() overrides (DG);
    041     * 25-Feb-2008 : Fix for the special case where the dataset is empty, see bug
    042     *               1897580 (DG)
    043     *
    044     */
    045
    046    package org.jfree.data.category;
    047
    048    import java.util.ArrayList;
    049    import java.util.Arrays;
    050    import java.util.Collections;
    051    import java.util.List;
    052    import java.util.ResourceBundle;
    053
    054    import org.jfree.data.DataUtilities;
    055    import org.jfree.data.UnknownKeyException;
    056    import org.jfree.data.general.AbstractSeriesDataset;
    057
    058    /**
    059     * A convenience class that provides a default implementation of the
    060     * {@link IntervalCategoryDataset} interface.
    061     * <p>
    062     * The standard constructor accepts data in a two dimensional array where the
    063     * first dimension is the series, and the second dimension is the category.
    064     */
    065    public class DefaultIntervalCategoryDataset extends AbstractSeriesDataset
    066            implements IntervalCategoryDataset {
    067
    068        /** The series keys. */
    069        private Comparable[] seriesKeys;
    070
    071        /** The category keys. */
    072        private Comparable[] categoryKeys;
    073
    074        /** Storage for the start value data. */
    075        private Number[][] startData;
    076
    077        /** Storage for the end value data. */
    078        private Number[][] endData;
    079
    080        /**
    081         * Creates a new dataset using the specified data values and automatically
    082         * generated series and category keys.
    083         *
    084         * @param starts  the starting values for the intervals (<code>null</code>
    085         *                not permitted).
    086         * @param ends  the ending values for the intervals (<code>null</code> not
    087         *                permitted).
    088         */
    089        public DefaultIntervalCategoryDataset(double[][] starts, double[][] ends) {
    090            this(DataUtilities.createNumberArray2D(starts),
    091                    DataUtilities.createNumberArray2D(ends));
    092        }
    093
    094        /**
    095         * Constructs a dataset and populates it with data from the array.
    096         * <p>
    097         * The arrays are indexed as data[series][category].  Series and category
    098         * names are automatically generated - you can change them using the
    099         * {@link #setSeriesKeys(Comparable[])} and
    100         * {@link #setCategoryKeys(Comparable[])} methods.
    101         *
    102         * @param starts  the start values data.
    103         * @param ends  the end values data.
    104         */
    105        public DefaultIntervalCategoryDataset(Number[][] starts, Number[][] ends) {
    106            this(null, null, starts, ends);
    107        }
    108
    109        /**
    110         * Constructs a DefaultIntervalCategoryDataset, populates it with data
    111         * from the arrays, and uses the supplied names for the series.
    112         * <p>
    113         * Category names are generated automatically ("Category 1", "Category 2",
    114         * etc).
    115         *
    116         * @param seriesNames  the series names (if <code>null</code>, series names
    117         *         will be generated automatically).
    118         * @param starts  the start values data, indexed as data[series][category].
    119         * @param ends  the end values data, indexed as data[series][category].
    120         */
    121        public DefaultIntervalCategoryDataset(String[] seriesNames,
    122                                              Number[][] starts,
    123                                              Number[][] ends) {
    124
    125            this(seriesNames, null, starts, ends);
    126
    127        }
    128
    129        /**
    130         * Constructs a DefaultIntervalCategoryDataset, populates it with data
    131         * from the arrays, and uses the supplied names for the series and the
    132         * supplied objects for the categories.
    133         *
    134         * @param seriesKeys  the series keys (if <code>null</code>, series keys
    135         *         will be generated automatically).
    136         * @param categoryKeys  the category keys (if <code>null</code>, category
    137         *         keys will be generated automatically).
    138         * @param starts  the start values data, indexed as data[series][category].
    139         * @param ends  the end values data, indexed as data[series][category].
    140         */
    141        public DefaultIntervalCategoryDataset(Comparable[] seriesKeys,
    142                                              Comparable[] categoryKeys,
    143                                              Number[][] starts,
    144                                              Number[][] ends) {
    145
    146            this.startData = starts;
    147            this.endData = ends;
    148
    149            if (starts != null && ends != null) {
    150
    151                String baseName = "org.jfree.data.resources.DataPackageResources";
    152                ResourceBundle resources = ResourceBundle.getBundle(baseName);
    153
    154                int seriesCount = starts.length;
    155                if (seriesCount != ends.length) {
    156                    String errMsg = "DefaultIntervalCategoryDataset: the number "
    157                        + "of series in the start value dataset does "
    158                        + "not match the number of series in the end "
    159                        + "value dataset.";
    160                    throw new IllegalArgumentException(errMsg);
    161                }
    162                if (seriesCount > 0) {
    163
    164                    // set up the series names...
    165                    if (seriesKeys != null) {
    166
    167                        if (seriesKeys.length != seriesCount) {
    168                            throw new IllegalArgumentException(
    169                                    "The number of series keys does not "
    170                                    + "match the number of series in the data.");
    171                        }
    172
    173                        this.seriesKeys = seriesKeys;
    174                    }
    175                    else {
    176                        String prefix = resources.getString(
    177                                "series.default-prefix") + " ";
    178                        this.seriesKeys = generateKeys(seriesCount, prefix);
    179                    }
    180
    181                    // set up the category names...
    182                    int categoryCount = starts[0].length;
    183                    if (categoryCount != ends[0].length) {
    184                        String errMsg = "DefaultIntervalCategoryDataset: the "
    185                                    + "number of categories in the start value "
    186                                    + "dataset does not match the number of "
    187                                    + "categories in the end value dataset.";
    188                        throw new IllegalArgumentException(errMsg);
    189                    }
    190                    if (categoryKeys != null) {
    191                        if (categoryKeys.length != categoryCount) {
    192                            throw new IllegalArgumentException(
    193                                    "The number of category keys does not match "
    194                                    + "the number of categories in the data.");
    195                        }
    196                        this.categoryKeys = categoryKeys;
    197                    }
    198                    else {
    199                        String prefix = resources.getString(
    200                                "categories.default-prefix") + " ";
    201                        this.categoryKeys = generateKeys(categoryCount, prefix);
    202                    }
    203
    204                }
    205                else {
    206                    this.seriesKeys = new Comparable[0];
    207                    this.categoryKeys = new Comparable[0];
    208                }
    209            }
    210
    211        }
    212
    213        /**
    214         * Returns the number of series in the dataset (possibly zero).
    215         *
    216         * @return The number of series in the dataset.
    217         *
    218         * @see #getRowCount()
    219         * @see #getCategoryCount()
    220         */
    221        public int getSeriesCount() {
    222            int result = 0;
    223            if (this.startData != null) {
    224                result = this.startData.length;
    225            }
    226            return result;
    227        }
    228
    229        /**
    230         * Returns a series index.
    231         *
    232         * @param seriesKey  the series key.
    233         *
    234         * @return The series index.
    235         *
    236         * @see #getRowIndex(Comparable)
    237         * @see #getSeriesKey(int)
    238         */
    239        public int getSeriesIndex(Comparable seriesKey) {
    240            int result = -1;
    241            for (int i = 0; i < this.seriesKeys.length; i++) {
    242                if (seriesKey.equals(this.seriesKeys[i])) {
    243                    result = i;
    244                    break;
    245                }
    246            }
    247            return result;
    248        }
    249
    250        /**
    251         * Returns the name of the specified series.
    252         *
    253         * @param series  the index of the required series (zero-based).
    254         *
    255         * @return The name of the specified series.
    256         *
    257         * @see #getSeriesIndex(Comparable)
    258         */
    259        public Comparable getSeriesKey(int series) {
    260            if ((series >= getSeriesCount()) || (series < 0)) {
    261                throw new IllegalArgumentException("No such series : " + series);
    262            }
    263            return this.seriesKeys[series];
    264        }
    265
    266        /**
    267         * Sets the names of the series in the dataset.
    268         *
    269         * @param seriesKeys  the new keys (<code>null</code> not permitted, the
    270         *         length of the array must match the number of series in the
    271         *         dataset).
    272         *
    273         * @see #setCategoryKeys(Comparable[])
    274         */
    275        public void setSeriesKeys(Comparable[] seriesKeys) {
    276            if (seriesKeys == null) {
    277                throw new IllegalArgumentException("Null 'seriesKeys' argument.");
    278            }
    279            if (seriesKeys.length != getSeriesCount()) {
    280                throw new IllegalArgumentException(
    281                        "The number of series keys does not match the data.");
    282            }
    283            this.seriesKeys = seriesKeys;
    284            fireDatasetChanged();
    285        }
    286
    287        /**
    288         * Returns the number of categories in the dataset.
    289         *
    290         * @return The number of categories in the dataset.
    291         *
    292         * @see #getColumnCount()
    293         */
    294        public int getCategoryCount() {
    295            int result = 0;
    296            if (this.startData != null) {
    297                if (getSeriesCount() > 0) {
    298                    result = this.startData[0].length;
    299                }
    300            }
    301            return result;
    302        }
    303
    304        /**
    305         * Returns a list of the categories in the dataset.  This method supports
    306         * the {@link CategoryDataset} interface.
    307         *
    308         * @return A list of the categories in the dataset.
    309         *
    310         * @see #getRowKeys()
    311         */
    312        public List getColumnKeys() {
    313            // the CategoryDataset interface expects a list of categories, but
    314            // we've stored them in an array...
    315            if (this.categoryKeys == null) {
    316                return new ArrayList();
    317            }
    318            else {
    319                return Collections.unmodifiableList(Arrays.asList(
    320                        this.categoryKeys));
    321            }
    322        }
    323
    324        /**
    325         * Sets the categories for the dataset.
    326         *
    327         * @param categoryKeys  an array of objects representing the categories in
    328         *                      the dataset.
    329         *
    330         * @see #getRowKeys()
    331         * @see #setSeriesKeys(Comparable[])
    332         */
    333        public void setCategoryKeys(Comparable[] categoryKeys) {
    334            if (categoryKeys == null) {
    335                throw new IllegalArgumentException("Null 'categoryKeys' argument.");
    336            }
    337            if (categoryKeys.length != getCategoryCount()) {
    338                throw new IllegalArgumentException(
    339                        "The number of categories does not match the data.");
    340            }
    341            for (int i = 0; i < categoryKeys.length; i++) {
    342                if (categoryKeys[i] == null) {
    343                    throw new IllegalArgumentException(
    344                        "DefaultIntervalCategoryDataset.setCategoryKeys(): "
    345                        + "null category not permitted.");
    346                }
    347            }
    348            this.categoryKeys = categoryKeys;
    349            fireDatasetChanged();
    350        }
    351
    352        /**
    353         * Returns the data value for one category in a series.
    354         * <P>
    355         * This method is part of the CategoryDataset interface.  Not particularly
    356         * meaningful for this class...returns the end value.
    357         *
    358         * @param series    The required series (zero based index).
    359         * @param category  The required category.
    360         *
    361         * @return The data value for one category in a series (null possible).
    362         *
    363         * @see #getEndValue(Comparable, Comparable)
    364         */
    365        public Number getValue(Comparable series, Comparable category) {
    366            int seriesIndex = getSeriesIndex(series);
    367            if (seriesIndex < 0) {
    368                throw new UnknownKeyException("Unknown 'series' key.");
    369            }
    370            int itemIndex = getColumnIndex(category);
    371            if (itemIndex < 0) {
    372                throw new UnknownKeyException("Unknown 'category' key.");
    373            }
    374            return getValue(seriesIndex, itemIndex);
    375        }
    376
    377        /**
    378         * Returns the data value for one category in a series.
    379         * <P>
    380         * This method is part of the CategoryDataset interface.  Not particularly
    381         * meaningful for this class...returns the end value.
    382         *
    383         * @param series  the required series (zero based index).
    384         * @param category  the required category.
    385         *
    386         * @return The data value for one category in a series (null possible).
    387         *
    388         * @see #getEndValue(int, int)
    389         */
    390        public Number getValue(int series, int category) {
    391            return getEndValue(series, category);
    392        }
    393
    394        /**
    395         * Returns the start data value for one category in a series.
    396         *
    397         * @param series  the required series.
    398         * @param category  the required category.
    399         *
    400         * @return The start data value for one category in a series
    401         *         (possibly <code>null</code>).
    402         *
    403         * @see #getStartValue(int, int)
    404         */
    405        public Number getStartValue(Comparable series, Comparable category) {
    406            int seriesIndex = getSeriesIndex(series);
    407            if (seriesIndex < 0) {
    408                throw new UnknownKeyException("Unknown 'series' key.");
    409            }
    410            int itemIndex = getColumnIndex(category);
    411            if (itemIndex < 0) {
    412                throw new UnknownKeyException("Unknown 'category' key.");
    413            }
    414            return getStartValue(seriesIndex, itemIndex);
    415        }
    416
    417        /**
    418         * Returns the start data value for one category in a series.
    419         *
    420         * @param series  the required series (zero based index).
    421         * @param category  the required category.
    422         *
    423         * @return The start data value for one category in a series
    424         *         (possibly <code>null</code>).
    425         *
    426         * @see #getStartValue(Comparable, Comparable)
    427         */
    428        public Number getStartValue(int series, int category) {
    429
    430            // check arguments...
    431            if ((series < 0) || (series >= getSeriesCount())) {
    432                throw new IllegalArgumentException(
    433                    "DefaultIntervalCategoryDataset.getValue(): "
    434                    + "series index out of range.");
    435            }
    436
    437            if ((category < 0) || (category >= getCategoryCount())) {
    438                throw new IllegalArgumentException(
    439                    "DefaultIntervalCategoryDataset.getValue(): "
    440                    + "category index out of range.");
    441            }
    442
    443            // fetch the value...
    444            return this.startData[series][category];
    445
    446        }
    447
    448        /**
    449         * Returns the end data value for one category in a series.
    450         *
    451         * @param series  the required series.
    452         * @param category  the required category.
    453         *
    454         * @return The end data value for one category in a series (null possible).
    455         *
    456         * @see #getEndValue(int, int)
    457         */
    458        public Number getEndValue(Comparable series, Comparable category) {
    459            int seriesIndex = getSeriesIndex(series);
    460            if (seriesIndex < 0) {
    461                throw new UnknownKeyException("Unknown 'series' key.");
    462            }
    463            int itemIndex = getColumnIndex(category);
    464            if (itemIndex < 0) {
    465                throw new UnknownKeyException("Unknown 'category' key.");
    466            }
    467            return getEndValue(seriesIndex, itemIndex);
    468        }
    469
    470        /**
    471         * Returns the end data value for one category in a series.
    472         *
    473         * @param series  the required series (zero based index).
    474         * @param category  the required category.
    475         *
    476         * @return The end data value for one category in a series (null possible).
    477         *
    478         * @see #getEndValue(Comparable, Comparable)
    479         */
    480        public Number getEndValue(int series, int category) {
    481            if ((series < 0) || (series >= getSeriesCount())) {
    482                throw new IllegalArgumentException(
    483                    "DefaultIntervalCategoryDataset.getValue(): "
    484                    + "series index out of range.");
    485            }
    486
    487            if ((category < 0) || (category >= getCategoryCount())) {
    488                throw new IllegalArgumentException(
    489                    "DefaultIntervalCategoryDataset.getValue(): "
    490                    + "category index out of range.");
    491            }
    492
    493            return this.endData[series][category];
    494        }
    495
    496        /**
    497         * Sets the start data value for one category in a series.
    498         *
    499         * @param series  the series (zero-based index).
    500         * @param category  the category.
    501         *
    502         * @param value The value.
    503         *
    504         * @see #setEndValue(int, Comparable, Number)
    505         */
    506        public void setStartValue(int series, Comparable category, Number value) {
    507
    508            // does the series exist?
    509            if ((series < 0) || (series > getSeriesCount() - 1)) {
    510                throw new IllegalArgumentException(
    511                    "DefaultIntervalCategoryDataset.setValue: "
    512                    + "series outside valid range.");
    513            }
    514
    515            // is the category valid?
    516            int categoryIndex = getCategoryIndex(category);
    517            if (categoryIndex < 0) {
    518                throw new IllegalArgumentException(
    519                    "DefaultIntervalCategoryDataset.setValue: "
    520                    + "unrecognised category.");
    521            }
    522
    523            // update the data...
    524            this.startData[series][categoryIndex] = value;
    525            fireDatasetChanged();
    526
    527        }
    528
    529        /**
    530         * Sets the end data value for one category in a series.
    531         *
    532         * @param series  the series (zero-based index).
    533         * @param category  the category.
    534         *
    535         * @param value the value.
    536         *
    537         * @see #setStartValue(int, Comparable, Number)
    538         */
    539        public void setEndValue(int series, Comparable category, Number value) {
    540
    541            // does the series exist?
    542            if ((series < 0) || (series > getSeriesCount() - 1)) {
    543                throw new IllegalArgumentException(
    544                    "DefaultIntervalCategoryDataset.setValue: "
    545                    + "series outside valid range.");
    546            }
    547
    548            // is the category valid?
    549            int categoryIndex = getCategoryIndex(category);
    550            if (categoryIndex < 0) {
    551                throw new IllegalArgumentException(
    552                    "DefaultIntervalCategoryDataset.setValue: "
    553                    + "unrecognised category.");
    554            }
    555
    556            // update the data...
    557            this.endData[series][categoryIndex] = value;
    558            fireDatasetChanged();
    559
    560        }
    561
    562        /**
    563         * Returns the index for the given category.
    564         *
    565         * @param category  the category (<code>null</code> not permitted).
    566         *
    567         * @return The index.
    568         *
    569         * @see #getColumnIndex(Comparable)
    570         */
    571        public int getCategoryIndex(Comparable category) {
    572            int result = -1;
    573            for (int i = 0; i < this.categoryKeys.length; i++) {
    574                if (category.equals(this.categoryKeys[i])) {
    575                    result = i;
    576                    break;
    577                }
    578            }
    579            return result;
    580        }
    581
    582        /**
    583         * Generates an array of keys, by appending a space plus an integer
    584         * (starting with 1) to the supplied prefix string.
    585         *
    586         * @param count  the number of keys required.
    587         * @param prefix  the name prefix.
    588         *
    589         * @return An array of <i>prefixN</i> with N = { 1 .. count}.
    590         */
    591        private Comparable[] generateKeys(int count, String prefix) {
    592            Comparable[] result = new Comparable[count];
    593            String name;
    594            for (int i = 0; i < count; i++) {
    595                name = prefix + (i + 1);
    596                result[i] = name;
    597            }
    598            return result;
    599        }
    600
    601        /**
    602         * Returns a column key.
    603         *
    604         * @param column  the column index.
    605         *
    606         * @return The column key.
    607         *
    608         * @see #getRowKey(int)
    609         */
    610        public Comparable getColumnKey(int column) {
    611            return this.categoryKeys[column];
    612        }
    613
    614        /**
    615         * Returns a column index.
    616         *
    617         * @param columnKey  the column key (<code>null</code> not permitted).
    618         *
    619         * @return The column index.
    620         *
    621         * @see #getCategoryIndex(Comparable)
    622         */
    623        public int getColumnIndex(Comparable columnKey) {
    624            if (columnKey == null) {
    625                throw new IllegalArgumentException("Null 'columnKey' argument.");
    626            }
    627            return getCategoryIndex(columnKey);
    628        }
    629
    630        /**
    631         * Returns a row index.
    632         *
    633         * @param rowKey  the row key.
    634         *
    635         * @return The row index.
    636         *
    637         * @see #getSeriesIndex(Comparable)
    638         */
    639        public int getRowIndex(Comparable rowKey) {
    640            return getSeriesIndex(rowKey);
    641        }
    642
    643        /**
    644         * Returns a list of the series in the dataset.  This method supports the
    645         * {@link CategoryDataset} interface.
    646         *
    647         * @return A list of the series in the dataset.
    648         *
    649         * @see #getColumnKeys()
    650         */
    651        public List getRowKeys() {
    652            // the CategoryDataset interface expects a list of series, but
    653            // we've stored them in an array...
    654            if (this.seriesKeys == null) {
    655                return new java.util.ArrayList();
    656            }
    657            else {
    658                return Collections.unmodifiableList(Arrays.asList(this.seriesKeys));
    659            }
    660        }
    661
    662        /**
    663         * Returns the name of the specified series.
    664         *
    665         * @param row  the index of the required row/series (zero-based).
    666         *
    667         * @return The name of the specified series.
    668         *
    669         * @see #getColumnKey(int)
    670         */
    671        public Comparable getRowKey(int row) {
    672            if ((row >= getRowCount()) || (row < 0)) {
    673                throw new IllegalArgumentException(
    674                        "The 'row' argument is out of bounds.");
    675            }
    676            return this.seriesKeys[row];
    677        }
    678
    679        /**
    680         * Returns the number of categories in the dataset.  This method is part of
    681         * the {@link CategoryDataset} interface.
    682         *
    683         * @return The number of categories in the dataset.
    684         *
    685         * @see #getCategoryCount()
    686         * @see #getRowCount()
    687         */
    688        public int getColumnCount() {
    689            return this.categoryKeys.length;
    690        }
    691
    692        /**
    693         * Returns the number of series in the dataset (possibly zero).
    694         *
    695         * @return The number of series in the dataset.
    696         *
    697         * @see #getSeriesCount()
    698         * @see #getColumnCount()
    699         */
    700        public int getRowCount() {
    701            return this.seriesKeys.length;
    702        }
    703
    704        /**
    705         * Tests this dataset for equality with an arbitrary object.
    706         *
    707         * @param obj  the object (<code>null</code> permitted).
    708         *
    709         * @return A boolean.
    710         */
    711        public boolean equals(Object obj) {
    712            if (obj == this) {
    713                return true;
    714            }
    715            if (!(obj instanceof DefaultIntervalCategoryDataset)) {
    716                return false;
    717            }
    718            DefaultIntervalCategoryDataset that
    719                    = (DefaultIntervalCategoryDataset) obj;
    720            if (!Arrays.equals(this.seriesKeys, that.seriesKeys)) {
    721                return false;
    722            }
    723            if (!Arrays.equals(this.categoryKeys, that.categoryKeys)) {
    724                return false;
    725            }
    726            if (!equal(this.startData, that.startData)) {
    727                return false;
    728            }
    729            if (!equal(this.endData, that.endData)) {
    730                return false;
    731            }
    732            // seem to be the same...
    733            return true;
    734        }
    735
    736        /**
    737         * Returns a clone of this dataset.
    738         *
    739         * @return A clone.
    740         *
    741         * @throws CloneNotSupportedException if there is a problem cloning the
    742         *         dataset.
    743         */
    744        public Object clone() throws CloneNotSupportedException {
    745            DefaultIntervalCategoryDataset clone
    746                    = (DefaultIntervalCategoryDataset) super.clone();
    747            clone.categoryKeys = (Comparable[]) this.categoryKeys.clone();
    748            clone.seriesKeys = (Comparable[]) this.seriesKeys.clone();
    749            clone.startData = clone(this.startData);
    750            clone.endData = clone(this.endData);
    751            return clone;
    752        }
    753
    754        /**
    755         * Tests two double[][] arrays for equality.
    756         *
    757         * @param array1  the first array (<code>null</code> permitted).
    758         * @param array2  the second arrray (<code>null</code> permitted).
    759         *
    760         * @return A boolean.
    761         */
    762        private static boolean equal(Number[][] array1, Number[][] array2) {
    763            if (array1 == null) {
    764                return (array2 == null);
    765            }
    766            if (array2 == null) {
    767                return false;
    768            }
    769            if (array1.length != array2.length) {
    770                return false;
    771            }
    772            for (int i = 0; i < array1.length; i++) {
    773                if (!Arrays.equals(array1[i], array2[i])) {
    774                    return false;
    775                }
    776            }
    777            return true;
    778        }
    779
    780        /**
    781         * Clones a two dimensional array of <code>Number</code> objects.
    782         *
    783         * @param array  the array (<code>null</code> not permitted).
    784         *
    785         * @return A clone of the array.
    786         */
    787        private static Number[][] clone(Number[][] array) {
    788            if (array == null) {
    789                throw new IllegalArgumentException("Null 'array' argument.");
    790            }
    791            Number[][] result = new Number[array.length][];
    792            for (int i = 0; i < array.length; i++) {
    793                Number[] child = array[i];
    794                Number[] copychild = new Number[child.length];
    795                System.arraycopy(child, 0, copychild, 0, child.length);
    796                result[i] = copychild;
    797            }
    798            return result;
    799        }
    800
    801        /**
    802         * Returns a list of the series in the dataset.
    803         *
    804         * @return A list of the series in the dataset.
    805         *
    806         * @deprecated Use {@link #getRowKeys()} instead.
    807         */
    808        public List getSeries() {
    809            if (this.seriesKeys == null) {
    810                return new java.util.ArrayList();
    811            }
    812            else {
    813                return Collections.unmodifiableList(Arrays.asList(this.seriesKeys));
    814            }
    815        }
    816
    817        /**
    818         * Returns a list of the categories in the dataset.
    819         *
    820         * @return A list of the categories in the dataset.
    821         *
    822         * @deprecated Use {@link #getColumnKeys()} instead.
    823         */
    824        public List getCategories() {
    825            return getColumnKeys();
    826        }
    827
    828        /**
    829         * Returns the item count.
    830         *
    831         * @return The item count.
    832         *
    833         * @deprecated Use {@link #getCategoryCount()} instead.
    834         */
    835        public int getItemCount() {
    836            return this.categoryKeys.length;
    837        }
    838
    839    }