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