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