    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jfreechart/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * --------------------------------------
    028     * DefaultStatisticalCategoryDataset.java
    029     * --------------------------------------
    030     * (C) Copyright 2002-2008, by Pascal Collet and Contributors.
    031     *
    032     * Original Author:  Pascal Collet;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *
    035     * Changes
    036     * -------
    037     * 21-Aug-2002 : Version 1, contributed by Pascal Collet (DG);
    038     * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    039     * 05-Feb-2003 : Revised implementation to use KeyedObjects2D (DG);
    040     * 28-Aug-2003 : Moved from org.jfree.data --> org.jfree.data.statistics (DG);
    041     * 06-Oct-2003 : Removed incorrect Javadoc text (DG);
    042     * 18-Nov-2004 : Updated for changes in RangeInfo interface (DG);
    043     * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0
    044     *               release (DG);
    045     * 01-Feb-2005 : Changed minimumRangeValue and maximumRangeValue from Double
    046     *               to double (DG);
    047     * 05-Feb-2005 : Implemented equals() method (DG);
    048     * ------------- JFREECHART 1.0.x ---------------------------------------------
    049     * 08-Aug-2006 : Reworked implementation of RangeInfo methods (DG);
    050     * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG);
    051     * 28-Sep-2007 : Fixed cloning bug (DG);
    052     * 02-Oct-2007 : Fixed bug updating cached range values (DG);
    053     *
    054     */
    055
    056    package org.jfree.data.statistics;
    057
    058    import java.util.List;
    059
    060    import org.jfree.data.KeyedObjects2D;
    061    import org.jfree.data.Range;
    062    import org.jfree.data.RangeInfo;
    063    import org.jfree.data.general.AbstractDataset;
    064    import org.jfree.data.general.DatasetChangeEvent;
    065    import org.jfree.util.PublicCloneable;
    066
    067    /**
    068     * A convenience class that provides a default implementation of the
    069     * {@link StatisticalCategoryDataset} interface.
    070     */
    071    public class DefaultStatisticalCategoryDataset extends AbstractDataset
    072            implements StatisticalCategoryDataset, RangeInfo, PublicCloneable {
    073
    074        /** Storage for the data. */
    075        private KeyedObjects2D data;
    076
    077        /** The minimum range value. */
    078        private double minimumRangeValue;
    079
    080        /** The row index for the minimum range value. */
    081        private int minimumRangeValueRow;
    082
    083        /** The column index for the minimum range value. */
    084        private int minimumRangeValueColumn;
    085
    086        /** The minimum range value including the standard deviation. */
    087        private double minimumRangeValueIncStdDev;
    088
    089        /**
    090         * The row index for the minimum range value (including the standard
    091         * deviation).
    092         */
    093        private int minimumRangeValueIncStdDevRow;
    094
    095        /**
    096         * The column index for the minimum range value (including the standard
    097         * deviation).
    098         */
    099        private int minimumRangeValueIncStdDevColumn;
    100
    101        /** The maximum range value. */
    102        private double maximumRangeValue;
    103
    104        /** The row index for the maximum range value. */
    105        private int maximumRangeValueRow;
    106
    107        /** The column index for the maximum range value. */
    108        private int maximumRangeValueColumn;
    109
    110        /** The maximum range value including the standard deviation. */
    111        private double maximumRangeValueIncStdDev;
    112
    113        /**
    114         * The row index for the maximum range value (including the standard
    115         * deviation).
    116         */
    117        private int maximumRangeValueIncStdDevRow;
    118
    119        /**
    120         * The column index for the maximum range value (including the standard
    121         * deviation).
    122         */
    123        private int maximumRangeValueIncStdDevColumn;
    124
    125        /**
    126         * Creates a new dataset.
    127         */
    128        public DefaultStatisticalCategoryDataset() {
    129            this.data = new KeyedObjects2D();
    130            this.minimumRangeValue = Double.NaN;
    131            this.minimumRangeValueRow = -1;
    132            this.minimumRangeValueColumn = -1;
    133            this.maximumRangeValue = Double.NaN;
    134            this.maximumRangeValueRow = -1;
    135            this.maximumRangeValueColumn = -1;
    136            this.minimumRangeValueIncStdDev = Double.NaN;
    137            this.minimumRangeValueIncStdDevRow = -1;
    138            this.minimumRangeValueIncStdDevColumn = -1;
    139            this.maximumRangeValueIncStdDev = Double.NaN;
    140            this.maximumRangeValueIncStdDevRow = -1;
    141            this.maximumRangeValueIncStdDevColumn = -1;
    142        }
    143
    144        /**
    145         * Returns the mean value for an item.
    146         *
    147         * @param row  the row index (zero-based).
    148         * @param column  the column index (zero-based).
    149         *
    150         * @return The mean value (possibly <code>null</code>).
    151         */
    152        public Number getMeanValue(int row, int column) {
    153            Number result = null;
    154            MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
    155                    this.data.getObject(row, column);
    156            if (masd != null) {
    157                result = masd.getMean();
    158            }
    159            return result;
    160        }
    161
    162        /**
    163         * Returns the value for an item (for this dataset, the mean value is
    164         * returned).
    165         *
    166         * @param row  the row index.
    167         * @param column  the column index.
    168         *
    169         * @return The value (possibly <code>null</code>).
    170         */
    171        public Number getValue(int row, int column) {
    172            return getMeanValue(row, column);
    173        }
    174
    175        /**
    176         * Returns the value for an item (for this dataset, the mean value is
    177         * returned).
    178         *
    179         * @param rowKey  the row key.
    180         * @param columnKey  the columnKey.
    181         *
    182         * @return The value (possibly <code>null</code>).
    183         */
    184        public Number getValue(Comparable rowKey, Comparable columnKey) {
    185            return getMeanValue(rowKey, columnKey);
    186        }
    187
    188        /**
    189         * Returns the mean value for an item.
    190         *
    191         * @param rowKey  the row key.
    192         * @param columnKey  the columnKey.
    193         *
    194         * @return The mean value (possibly <code>null</code>).
    195         */
    196        public Number getMeanValue(Comparable rowKey, Comparable columnKey) {
    197            Number result = null;
    198            MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
    199                    this.data.getObject(rowKey, columnKey);
    200            if (masd != null) {
    201                result = masd.getMean();
    202            }
    203            return result;
    204        }
    205
    206        /**
    207         * Returns the standard deviation value for an item.
    208         *
    209         * @param row  the row index (zero-based).
    210         * @param column  the column index (zero-based).
    211         *
    212         * @return The standard deviation (possibly <code>null</code>).
    213         */
    214        public Number getStdDevValue(int row, int column) {
    215            Number result = null;
    216            MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
    217                    this.data.getObject(row, column);
    218            if (masd != null) {
    219                result = masd.getStandardDeviation();
    220            }
    221            return result;
    222        }
    223
    224        /**
    225         * Returns the standard deviation value for an item.
    226         *
    227         * @param rowKey  the row key.
    228         * @param columnKey  the columnKey.
    229         *
    230         * @return The standard deviation (possibly <code>null</code>).
    231         */
    232        public Number getStdDevValue(Comparable rowKey, Comparable columnKey) {
    233            Number result = null;
    234            MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
    235                    this.data.getObject(rowKey, columnKey);
    236            if (masd != null) {
    237                result = masd.getStandardDeviation();
    238            }
    239            return result;
    240        }
    241
    242        /**
    243         * Returns the column index for a given key.
    244         *
    245         * @param key  the column key (<code>null</code> not permitted).
    246         *
    247         * @return The column index.
    248         */
    249        public int getColumnIndex(Comparable key) {
    250            // defer null argument check
    251            return this.data.getColumnIndex(key);
    252        }
    253
    254        /**
    255         * Returns a column key.
    256         *
    257         * @param column  the column index (zero-based).
    258         *
    259         * @return The column key.
    260         */
    261        public Comparable getColumnKey(int column) {
    262            return this.data.getColumnKey(column);
    263        }
    264
    265        /**
    266         * Returns the column keys.
    267         *
    268         * @return The keys.
    269         */
    270        public List getColumnKeys() {
    271            return this.data.getColumnKeys();
    272        }
    273
    274        /**
    275         * Returns the row index for a given key.
    276         *
    277         * @param key  the row key (<code>null</code> not permitted).
    278         *
    279         * @return The row index.
    280         */
    281        public int getRowIndex(Comparable key) {
    282            // defer null argument check
    283            return this.data.getRowIndex(key);
    284        }
    285
    286        /**
    287         * Returns a row key.
    288         *
    289         * @param row  the row index (zero-based).
    290         *
    291         * @return The row key.
    292         */
    293        public Comparable getRowKey(int row) {
    294            return this.data.getRowKey(row);
    295        }
    296
    297        /**
    298         * Returns the row keys.
    299         *
    300         * @return The keys.
    301         */
    302        public List getRowKeys() {
    303            return this.data.getRowKeys();
    304        }
    305
    306        /**
    307         * Returns the number of rows in the table.
    308         *
    309         * @return The row count.
    310         *
    311         * @see #getColumnCount()
    312         */
    313        public int getRowCount() {
    314            return this.data.getRowCount();
    315        }
    316
    317        /**
    318         * Returns the number of columns in the table.
    319         *
    320         * @return The column count.
    321         *
    322         * @see #getRowCount()
    323         */
    324        public int getColumnCount() {
    325            return this.data.getColumnCount();
    326        }
    327
    328        /**
    329         * Adds a mean and standard deviation to the table.
    330         *
    331         * @param mean  the mean.
    332         * @param standardDeviation  the standard deviation.
    333         * @param rowKey  the row key.
    334         * @param columnKey  the column key.
    335         */
    336        public void add(double mean, double standardDeviation,
    337                        Comparable rowKey, Comparable columnKey) {
    338            add(new Double(mean), new Double(standardDeviation), rowKey, columnKey);
    339        }
    340
    341        /**
    342         * Adds a mean and standard deviation to the table.
    343         *
    344         * @param mean  the mean.
    345         * @param standardDeviation  the standard deviation.
    346         * @param rowKey  the row key.
    347         * @param columnKey  the column key.
    348         */
    349        public void add(Number mean, Number standardDeviation,
    350                        Comparable rowKey, Comparable columnKey) {
    351            MeanAndStandardDeviation item = new MeanAndStandardDeviation(
    352                    mean, standardDeviation);
    353            this.data.addObject(item, rowKey, columnKey);
    354
    355            double m = Double.NaN;
    356            double sd = Double.NaN;
    357            if (mean != null) {
    358                m = mean.doubleValue();
    359            }
    360            if (standardDeviation != null) {
    361                sd = standardDeviation.doubleValue();
    362            }
    363
    364            // update cached range values
    365            int r = this.data.getColumnIndex(columnKey);
    366            int c = this.data.getRowIndex(rowKey);
    367            if ((r == this.maximumRangeValueRow && c
    368                    == this.maximumRangeValueColumn) || (r
    369                    == this.maximumRangeValueIncStdDevRow && c
    370                    == this.maximumRangeValueIncStdDevColumn) || (r
    371                    == this.minimumRangeValueRow && c
    372                    == this.minimumRangeValueColumn) || (r
    373                    == this.minimumRangeValueIncStdDevRow && c
    374                    == this.minimumRangeValueIncStdDevColumn)) {
    375
    376                // iterate over all data items and update mins and maxes
    377                updateBounds();
    378            }
    379            else {
    380                if (!Double.isNaN(m)) {
    381                    if (Double.isNaN(this.maximumRangeValue)
    382                            || m > this.maximumRangeValue) {
    383                        this.maximumRangeValue = m;
    384                        this.maximumRangeValueRow = r;
    385                        this.maximumRangeValueColumn = c;
    386                    }
    387                }
    388
    389                if (!Double.isNaN(m + sd)) {
    390                    if (Double.isNaN(this.maximumRangeValueIncStdDev)
    391                            || (m + sd) > this.maximumRangeValueIncStdDev) {
    392                        this.maximumRangeValueIncStdDev = m + sd;
    393                        this.maximumRangeValueIncStdDevRow = r;
    394                        this.maximumRangeValueIncStdDevColumn = c;
    395                    }
    396                }
    397
    398                if (!Double.isNaN(m)) {
    399                    if (Double.isNaN(this.minimumRangeValue)
    400                            || m < this.minimumRangeValue) {
    401                        this.minimumRangeValue = m;
    402                        this.minimumRangeValueRow = r;
    403                        this.minimumRangeValueColumn = c;
    404                    }
    405                }
    406
    407                if (!Double.isNaN(m - sd)) {
    408                    if (Double.isNaN(this.minimumRangeValueIncStdDev)
    409                            || (m - sd) < this.minimumRangeValueIncStdDev) {
    410                        this.minimumRangeValueIncStdDev = m - sd;
    411                        this.minimumRangeValueIncStdDevRow = r;
    412                        this.minimumRangeValueIncStdDevColumn = c;
    413                    }
    414                }
    415            }
    416            fireDatasetChanged();
    417        }
    418
    419        /**
    420         * Removes an item from the dataset and sends a {@link DatasetChangeEvent}
    421         * to all registered listeners.
    422         *
    423         * @param rowKey  the row key (<code>null</code> not permitted).
    424         * @param columnKey  the column key (<code>null</code> not permitted).
    425         *
    426         * @see #add(double, double, Comparable, Comparable)
    427         *
    428         * @since 1.0.7
    429         */
    430        public void remove(Comparable rowKey, Comparable columnKey) {
    431            // defer null argument checks
    432            int r = getRowIndex(rowKey);
    433            int c = getColumnIndex(columnKey);
    434            this.data.removeObject(rowKey, columnKey);
    435
    436            // if this cell held a maximum and/or minimum value, we'll need to
    437            // update the cached bounds...
    438            if ((r == this.maximumRangeValueRow && c
    439                    == this.maximumRangeValueColumn) || (r
    440                    == this.maximumRangeValueIncStdDevRow && c
    441                    == this.maximumRangeValueIncStdDevColumn) || (r
    442                    == this.minimumRangeValueRow && c
    443                    == this.minimumRangeValueColumn) || (r
    444                    == this.minimumRangeValueIncStdDevRow && c
    445                    == this.minimumRangeValueIncStdDevColumn)) {
    446
    447                // iterate over all data items and update mins and maxes
    448                updateBounds();
    449            }
    450
    451            fireDatasetChanged();
    452        }
    453
    454
    455        /**
    456         * Removes a row from the dataset and sends a {@link DatasetChangeEvent}
    457         * to all registered listeners.
    458         *
    459         * @param rowIndex  the row index.
    460         *
    461         * @see #removeColumn(int)
    462         *
    463         * @since 1.0.7
    464         */
    465        public void removeRow(int rowIndex) {
    466            this.data.removeRow(rowIndex);
    467            updateBounds();
    468            fireDatasetChanged();
    469        }
    470
    471        /**
    472         * Removes a row from the dataset and sends a {@link DatasetChangeEvent}
    473         * to all registered listeners.
    474         *
    475         * @param rowKey  the row key (<code>null</code> not permitted).
    476         *
    477         * @see #removeColumn(Comparable)
    478         *
    479         * @since 1.0.7
    480         */
    481        public void removeRow(Comparable rowKey) {
    482            this.data.removeRow(rowKey);
    483            updateBounds();
    484            fireDatasetChanged();
    485        }
    486
    487        /**
    488         * Removes a column from the dataset and sends a {@link DatasetChangeEvent}
    489         * to all registered listeners.
    490         *
    491         * @param columnIndex  the column index.
    492         *
    493         * @see #removeRow(int)
    494         *
    495         * @since 1.0.7
    496         */
    497        public void removeColumn(int columnIndex) {
    498            this.data.removeColumn(columnIndex);
    499            updateBounds();
    500            fireDatasetChanged();
    501        }
    502
    503        /**
    504         * Removes a column from the dataset and sends a {@link DatasetChangeEvent}
    505         * to all registered listeners.
    506         *
    507         * @param columnKey  the column key (<code>null</code> not permitted).
    508         *
    509         * @see #removeRow(Comparable)
    510         *
    511         * @since 1.0.7
    512         */
    513        public void removeColumn(Comparable columnKey) {
    514            this.data.removeColumn(columnKey);
    515            updateBounds();
    516            fireDatasetChanged();
    517        }
    518
    519        /**
    520         * Clears all data from the dataset and sends a {@link DatasetChangeEvent}
    521         * to all registered listeners.
    522         *
    523         * @since 1.0.7
    524         */
    525        public void clear() {
    526            this.data.clear();
    527            updateBounds();
    528            fireDatasetChanged();
    529        }
    530
    531        /**
    532         * Iterate over all the data items and update the cached bound values.
    533         */
    534        private void updateBounds() {
    535            this.maximumRangeValue = Double.NaN;
    536            this.maximumRangeValueRow = -1;
    537            this.maximumRangeValueColumn = -1;
    538            this.minimumRangeValue = Double.NaN;
    539            this.minimumRangeValueRow = -1;
    540            this.minimumRangeValueColumn = -1;
    541            this.maximumRangeValueIncStdDev = Double.NaN;
    542            this.maximumRangeValueIncStdDevRow = -1;
    543            this.maximumRangeValueIncStdDevColumn = -1;
    544            this.minimumRangeValueIncStdDev = Double.NaN;
    545            this.minimumRangeValueIncStdDevRow = -1;
    546            this.minimumRangeValueIncStdDevColumn = -1;
    547
    548            int rowCount = this.data.getRowCount();
    549            int columnCount = this.data.getColumnCount();
    550            for (int r = 0; r < rowCount; r++) {
    551                for (int c = 0; c < columnCount; c++) {
    552                    double m = Double.NaN;
    553                    double sd = Double.NaN;
    554                    MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
    555                            this.data.getObject(r, c);
    556                    if (masd == null) {
    557                        continue;
    558                    }
    559                    m = masd.getMeanValue();
    560                    sd = masd.getStandardDeviationValue();
    561
    562                    if (!Double.isNaN(m)) {
    563
    564                        // update the max value
    565                        if (Double.isNaN(this.maximumRangeValue)) {
    566                            this.maximumRangeValue = m;
    567                            this.maximumRangeValueRow = r;
    568                            this.maximumRangeValueColumn = c;
    569                        }
    570                        else {
    571                            if (m > this.maximumRangeValue) {
    572                                this.maximumRangeValue = m;
    573                                this.maximumRangeValueRow = r;
    574                                this.maximumRangeValueColumn = c;
    575                            }
    576                        }
    577
    578                        // update the min value
    579                        if (Double.isNaN(this.minimumRangeValue)) {
    580                            this.minimumRangeValue = m;
    581                            this.minimumRangeValueRow = r;
    582                            this.minimumRangeValueColumn = c;
    583                        }
    584                        else {
    585                            if (m < this.minimumRangeValue) {
    586                                this.minimumRangeValue = m;
    587                                this.minimumRangeValueRow = r;
    588                                this.minimumRangeValueColumn = c;
    589                            }
    590                        }
    591
    592                        if (!Double.isNaN(sd)) {
    593                            // update the max value
    594                            if (Double.isNaN(this.maximumRangeValueIncStdDev)) {
    595                                this.maximumRangeValueIncStdDev = m + sd;
    596                                this.maximumRangeValueIncStdDevRow = r;
    597                                this.maximumRangeValueIncStdDevColumn = c;
    598                            }
    599                            else {
    600                                if (m + sd > this.maximumRangeValueIncStdDev) {
    601                                    this.maximumRangeValueIncStdDev = m + sd;
    602                                    this.maximumRangeValueIncStdDevRow = r;
    603                                    this.maximumRangeValueIncStdDevColumn = c;
    604                                }
    605                            }
    606
    607                            // update the min value
    608                            if (Double.isNaN(this.minimumRangeValueIncStdDev)) {
    609                                this.minimumRangeValueIncStdDev = m - sd;
    610                                this.minimumRangeValueIncStdDevRow = r;
    611                                this.minimumRangeValueIncStdDevColumn = c;
    612                            }
    613                            else {
    614                                if (m - sd < this.minimumRangeValueIncStdDev) {
    615                                    this.minimumRangeValueIncStdDev = m - sd;
    616                                    this.minimumRangeValueIncStdDevRow = r;
    617                                    this.minimumRangeValueIncStdDevColumn = c;
    618                                }
    619                            }
    620                        }
    621                    }
    622                }
    623            }
    624        }
    625
    626        /**
    627         * Returns the minimum y-value in the dataset.
    628         *
    629         * @param includeInterval  a flag that determines whether or not the
    630         *                         y-interval is taken into account.
    631         *
    632         * @return The minimum value.
    633         *
    634         * @see #getRangeUpperBound(boolean)
    635         */
    636        public double getRangeLowerBound(boolean includeInterval) {
    637            if (includeInterval) {
    638                return this.minimumRangeValueIncStdDev;
    639            }
    640            else {
    641                return this.minimumRangeValue;
    642            }
    643        }
    644
    645        /**
    646         * Returns the maximum y-value in the dataset.
    647         *
    648         * @param includeInterval  a flag that determines whether or not the
    649         *                         y-interval is taken into account.
    650         *
    651         * @return The maximum value.
    652         *
    653         * @see #getRangeLowerBound(boolean)
    654         */
    655        public double getRangeUpperBound(boolean includeInterval) {
    656            if (includeInterval) {
    657                return this.maximumRangeValueIncStdDev;
    658            }
    659            else {
    660                return this.maximumRangeValue;
    661            }
    662        }
    663
    664        /**
    665         * Returns the range of the values in this dataset's range.
    666         *
    667         * @param includeInterval  a flag that determines whether or not the
    668         *                         y-interval is taken into account.
    669         *
    670         * @return The range.
    671         */
    672        public Range getRangeBounds(boolean includeInterval) {
    673            Range result = null;
    674            if (includeInterval) {
    675                if (!Double.isNaN(this.minimumRangeValueIncStdDev)
    676                        && !Double.isNaN(this.maximumRangeValueIncStdDev)) {
    677                    result = new Range(this.minimumRangeValueIncStdDev,
    678                            this.maximumRangeValueIncStdDev);
    679                }
    680            }
    681            else {
    682                if (!Double.isNaN(this.minimumRangeValue)
    683                        && !Double.isNaN(this.maximumRangeValue)) {
    684                    result = new Range(this.minimumRangeValue,
    685                            this.maximumRangeValue);
    686                }
    687            }
    688            return result;
    689        }
    690
    691        /**
    692         * Tests this instance for equality with an arbitrary object.
    693         *
    694         * @param obj  the object (<code>null</code> permitted).
    695         *
    696         * @return A boolean.
    697         */
    698        public boolean equals(Object obj) {
    699            if (obj == this) {
    700                return true;
    701            }
    702            if (!(obj instanceof DefaultStatisticalCategoryDataset)) {
    703                return false;
    704            }
    705            DefaultStatisticalCategoryDataset that
    706                    = (DefaultStatisticalCategoryDataset) obj;
    707            if (!this.data.equals(that.data)) {
    708                return false;
    709            }
    710            return true;
    711        }
    712
    713        /**
    714         * Returns a clone of this dataset.
    715         *
    716         * @return A clone of this dataset.
    717         *
    718         * @throws CloneNotSupportedException if cloning cannot be completed.
    719         */
    720        public Object clone() throws CloneNotSupportedException {
    721            DefaultStatisticalCategoryDataset clone
    722                    = (DefaultStatisticalCategoryDataset) super.clone();
    723            clone.data = (KeyedObjects2D) this.data.clone();
    724            return clone;
    725        }
    726    }