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