    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     * DefaultStatisticalCategoryDataset.java
    029     * --------------------------------------
    030     * (C) Copyright 2002-2011, 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     * 19-May-2009 : Fixed FindBugs warnings, patch by Michal Wozniak (DG);
    054     * 20-Oct-2011 : Fixed getRangeBounds() bug 3072674 (DG);
    055     *
    056     */
    057
    058    package org.jfree.data.statistics;
    059
    060    import java.util.List;
    061
    062    import org.jfree.data.KeyedObjects2D;
    063    import org.jfree.data.Range;
    064    import org.jfree.data.RangeInfo;
    065    import org.jfree.data.general.AbstractDataset;
    066    import org.jfree.data.general.DatasetChangeEvent;
    067    import org.jfree.util.PublicCloneable;
    068
    069    /**
    070     * A convenience class that provides a default implementation of the
    071     * {@link StatisticalCategoryDataset} interface.
    072     */
    073    public class DefaultStatisticalCategoryDataset extends AbstractDataset
    074            implements StatisticalCategoryDataset, RangeInfo, PublicCloneable {
    075
    076        /** Storage for the data. */
    077        private KeyedObjects2D data;
    078
    079        /** The minimum range value. */
    080        private double minimumRangeValue;
    081
    082        /** The row index for the minimum range value. */
    083        private int minimumRangeValueRow;
    084
    085        /** The column index for the minimum range value. */
    086        private int minimumRangeValueColumn;
    087
    088        /** The minimum range value including the standard deviation. */
    089        private double minimumRangeValueIncStdDev;
    090
    091        /**
    092         * The row index for the minimum range value (including the standard
    093         * deviation).
    094         */
    095        private int minimumRangeValueIncStdDevRow;
    096
    097        /**
    098         * The column index for the minimum range value (including the standard
    099         * deviation).
    100         */
    101        private int minimumRangeValueIncStdDevColumn;
    102
    103        /** The maximum range value. */
    104        private double maximumRangeValue;
    105
    106        /** The row index for the maximum range value. */
    107        private int maximumRangeValueRow;
    108
    109        /** The column index for the maximum range value. */
    110        private int maximumRangeValueColumn;
    111
    112        /** The maximum range value including the standard deviation. */
    113        private double maximumRangeValueIncStdDev;
    114
    115        /**
    116         * The row index for the maximum range value (including the standard
    117         * deviation).
    118         */
    119        private int maximumRangeValueIncStdDevRow;
    120
    121        /**
    122         * The column index for the maximum range value (including the standard
    123         * deviation).
    124         */
    125        private int maximumRangeValueIncStdDevColumn;
    126
    127        /**
    128         * Creates a new dataset.
    129         */
    130        public DefaultStatisticalCategoryDataset() {
    131            this.data = new KeyedObjects2D();
    132            this.minimumRangeValue = Double.NaN;
    133            this.minimumRangeValueRow = -1;
    134            this.minimumRangeValueColumn = -1;
    135            this.maximumRangeValue = Double.NaN;
    136            this.maximumRangeValueRow = -1;
    137            this.maximumRangeValueColumn = -1;
    138            this.minimumRangeValueIncStdDev = Double.NaN;
    139            this.minimumRangeValueIncStdDevRow = -1;
    140            this.minimumRangeValueIncStdDevColumn = -1;
    141            this.maximumRangeValueIncStdDev = Double.NaN;
    142            this.maximumRangeValueIncStdDevRow = -1;
    143            this.maximumRangeValueIncStdDevColumn = -1;
    144        }
    145
    146        /**
    147         * Returns the mean value for an item.
    148         *
    149         * @param row  the row index (zero-based).
    150         * @param column  the column index (zero-based).
    151         *
    152         * @return The mean value (possibly <code>null</code>).
    153         */
    154        public Number getMeanValue(int row, int column) {
    155            Number result = null;
    156            MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
    157                    this.data.getObject(row, column);
    158            if (masd != null) {
    159                result = masd.getMean();
    160            }
    161            return result;
    162        }
    163
    164        /**
    165         * Returns the value for an item (for this dataset, the mean value is
    166         * returned).
    167         *
    168         * @param row  the row index.
    169         * @param column  the column index.
    170         *
    171         * @return The value (possibly <code>null</code>).
    172         */
    173        public Number getValue(int row, int column) {
    174            return getMeanValue(row, column);
    175        }
    176
    177        /**
    178         * Returns the value for an item (for this dataset, the mean value is
    179         * returned).
    180         *
    181         * @param rowKey  the row key.
    182         * @param columnKey  the columnKey.
    183         *
    184         * @return The value (possibly <code>null</code>).
    185         */
    186        public Number getValue(Comparable rowKey, Comparable columnKey) {
    187            return getMeanValue(rowKey, columnKey);
    188        }
    189
    190        /**
    191         * Returns the mean value for an item.
    192         *
    193         * @param rowKey  the row key.
    194         * @param columnKey  the columnKey.
    195         *
    196         * @return The mean value (possibly <code>null</code>).
    197         */
    198        public Number getMeanValue(Comparable rowKey, Comparable columnKey) {
    199            Number result = null;
    200            MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
    201                    this.data.getObject(rowKey, columnKey);
    202            if (masd != null) {
    203                result = masd.getMean();
    204            }
    205            return result;
    206        }
    207
    208        /**
    209         * Returns the standard deviation value for an item.
    210         *
    211         * @param row  the row index (zero-based).
    212         * @param column  the column index (zero-based).
    213         *
    214         * @return The standard deviation (possibly <code>null</code>).
    215         */
    216        public Number getStdDevValue(int row, int column) {
    217            Number result = null;
    218            MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
    219                    this.data.getObject(row, column);
    220            if (masd != null) {
    221                result = masd.getStandardDeviation();
    222            }
    223            return result;
    224        }
    225
    226        /**
    227         * Returns the standard deviation value for an item.
    228         *
    229         * @param rowKey  the row key.
    230         * @param columnKey  the columnKey.
    231         *
    232         * @return The standard deviation (possibly <code>null</code>).
    233         */
    234        public Number getStdDevValue(Comparable rowKey, Comparable columnKey) {
    235            Number result = null;
    236            MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
    237                    this.data.getObject(rowKey, columnKey);
    238            if (masd != null) {
    239                result = masd.getStandardDeviation();
    240            }
    241            return result;
    242        }
    243
    244        /**
    245         * Returns the column index for a given key.
    246         *
    247         * @param key  the column key (<code>null</code> not permitted).
    248         *
    249         * @return The column index.
    250         */
    251        public int getColumnIndex(Comparable key) {
    252            // defer null argument check
    253            return this.data.getColumnIndex(key);
    254        }
    255
    256        /**
    257         * Returns a column key.
    258         *
    259         * @param column  the column index (zero-based).
    260         *
    261         * @return The column key.
    262         */
    263        public Comparable getColumnKey(int column) {
    264            return this.data.getColumnKey(column);
    265        }
    266
    267        /**
    268         * Returns the column keys.
    269         *
    270         * @return The keys.
    271         */
    272        public List getColumnKeys() {
    273            return this.data.getColumnKeys();
    274        }
    275
    276        /**
    277         * Returns the row index for a given key.
    278         *
    279         * @param key  the row key (<code>null</code> not permitted).
    280         *
    281         * @return The row index.
    282         */
    283        public int getRowIndex(Comparable key) {
    284            // defer null argument check
    285            return this.data.getRowIndex(key);
    286        }
    287
    288        /**
    289         * Returns a row key.
    290         *
    291         * @param row  the row index (zero-based).
    292         *
    293         * @return The row key.
    294         */
    295        public Comparable getRowKey(int row) {
    296            return this.data.getRowKey(row);
    297        }
    298
    299        /**
    300         * Returns the row keys.
    301         *
    302         * @return The keys.
    303         */
    304        public List getRowKeys() {
    305            return this.data.getRowKeys();
    306        }
    307
    308        /**
    309         * Returns the number of rows in the table.
    310         *
    311         * @return The row count.
    312         *
    313         * @see #getColumnCount()
    314         */
    315        public int getRowCount() {
    316            return this.data.getRowCount();
    317        }
    318
    319        /**
    320         * Returns the number of columns in the table.
    321         *
    322         * @return The column count.
    323         *
    324         * @see #getRowCount()
    325         */
    326        public int getColumnCount() {
    327            return this.data.getColumnCount();
    328        }
    329
    330        /**
    331         * Adds a mean and standard deviation to the table.
    332         *
    333         * @param mean  the mean.
    334         * @param standardDeviation  the standard deviation.
    335         * @param rowKey  the row key.
    336         * @param columnKey  the column key.
    337         */
    338        public void add(double mean, double standardDeviation,
    339                        Comparable rowKey, Comparable columnKey) {
    340            add(new Double(mean), new Double(standardDeviation), rowKey, columnKey);
    341        }
    342
    343        /**
    344         * Adds a mean and standard deviation to the table.
    345         *
    346         * @param mean  the mean.
    347         * @param standardDeviation  the standard deviation.
    348         * @param rowKey  the row key.
    349         * @param columnKey  the column key.
    350         */
    351        public void add(Number mean, Number standardDeviation,
    352                        Comparable rowKey, Comparable columnKey) {
    353            MeanAndStandardDeviation item = new MeanAndStandardDeviation(
    354                    mean, standardDeviation);
    355            this.data.addObject(item, rowKey, columnKey);
    356
    357            double m = Double.NaN;
    358            double sd = Double.NaN;
    359            if (mean != null) {
    360                m = mean.doubleValue();
    361            }
    362            if (standardDeviation != null) {
    363                sd = standardDeviation.doubleValue();
    364            }
    365
    366            // update cached range values
    367            int r = this.data.getColumnIndex(columnKey);
    368            int c = this.data.getRowIndex(rowKey);
    369            if ((r == this.maximumRangeValueRow && c
    370                    == this.maximumRangeValueColumn) || (r
    371                    == this.maximumRangeValueIncStdDevRow && c
    372                    == this.maximumRangeValueIncStdDevColumn) || (r
    373                    == this.minimumRangeValueRow && c
    374                    == this.minimumRangeValueColumn) || (r
    375                    == this.minimumRangeValueIncStdDevRow && c
    376                    == this.minimumRangeValueIncStdDevColumn)) {
    377
    378                // iterate over all data items and update mins and maxes
    379                updateBounds();
    380            }
    381            else {
    382                if (!Double.isNaN(m)) {
    383                    if (Double.isNaN(this.maximumRangeValue)
    384                            || m > this.maximumRangeValue) {
    385                        this.maximumRangeValue = m;
    386                        this.maximumRangeValueRow = r;
    387                        this.maximumRangeValueColumn = c;
    388                    }
    389                }
    390
    391                if (!Double.isNaN(m + sd)) {
    392                    if (Double.isNaN(this.maximumRangeValueIncStdDev)
    393                            || (m + sd) > this.maximumRangeValueIncStdDev) {
    394                        this.maximumRangeValueIncStdDev = m + sd;
    395                        this.maximumRangeValueIncStdDevRow = r;
    396                        this.maximumRangeValueIncStdDevColumn = c;
    397                    }
    398                }
    399
    400                if (!Double.isNaN(m)) {
    401                    if (Double.isNaN(this.minimumRangeValue)
    402                            || m < this.minimumRangeValue) {
    403                        this.minimumRangeValue = m;
    404                        this.minimumRangeValueRow = r;
    405                        this.minimumRangeValueColumn = c;
    406                    }
    407                }
    408
    409                if (!Double.isNaN(m - sd)) {
    410                    if (Double.isNaN(this.minimumRangeValueIncStdDev)
    411                            || (m - sd) < this.minimumRangeValueIncStdDev) {
    412                        this.minimumRangeValueIncStdDev = m - sd;
    413                        this.minimumRangeValueIncStdDevRow = r;
    414                        this.minimumRangeValueIncStdDevColumn = c;
    415                    }
    416                }
    417            }
    418            fireDatasetChanged();
    419        }
    420
    421        /**
    422         * Removes an item from the dataset and sends a {@link DatasetChangeEvent}
    423         * to all registered listeners.
    424         *
    425         * @param rowKey  the row key (<code>null</code> not permitted).
    426         * @param columnKey  the column key (<code>null</code> not permitted).
    427         *
    428         * @see #add(double, double, Comparable, Comparable)
    429         *
    430         * @since 1.0.7
    431         */
    432        public void remove(Comparable rowKey, Comparable columnKey) {
    433            // defer null argument checks
    434            int r = getRowIndex(rowKey);
    435            int c = getColumnIndex(columnKey);
    436            this.data.removeObject(rowKey, columnKey);
    437
    438            // if this cell held a maximum and/or minimum value, we'll need to
    439            // update the cached bounds...
    440            if ((r == this.maximumRangeValueRow && c
    441                    == this.maximumRangeValueColumn) || (r
    442                    == this.maximumRangeValueIncStdDevRow && c
    443                    == this.maximumRangeValueIncStdDevColumn) || (r
    444                    == this.minimumRangeValueRow && c
    445                    == this.minimumRangeValueColumn) || (r
    446                    == this.minimumRangeValueIncStdDevRow && c
    447                    == this.minimumRangeValueIncStdDevColumn)) {
    448
    449                // iterate over all data items and update mins and maxes
    450                updateBounds();
    451            }
    452
    453            fireDatasetChanged();
    454        }
    455
    456
    457        /**
    458         * Removes a row from the dataset and sends a {@link DatasetChangeEvent}
    459         * to all registered listeners.
    460         *
    461         * @param rowIndex  the row index.
    462         *
    463         * @see #removeColumn(int)
    464         *
    465         * @since 1.0.7
    466         */
    467        public void removeRow(int rowIndex) {
    468            this.data.removeRow(rowIndex);
    469            updateBounds();
    470            fireDatasetChanged();
    471        }
    472
    473        /**
    474         * Removes a row from the dataset and sends a {@link DatasetChangeEvent}
    475         * to all registered listeners.
    476         *
    477         * @param rowKey  the row key (<code>null</code> not permitted).
    478         *
    479         * @see #removeColumn(Comparable)
    480         *
    481         * @since 1.0.7
    482         */
    483        public void removeRow(Comparable rowKey) {
    484            this.data.removeRow(rowKey);
    485            updateBounds();
    486            fireDatasetChanged();
    487        }
    488
    489        /**
    490         * Removes a column from the dataset and sends a {@link DatasetChangeEvent}
    491         * to all registered listeners.
    492         *
    493         * @param columnIndex  the column index.
    494         *
    495         * @see #removeRow(int)
    496         *
    497         * @since 1.0.7
    498         */
    499        public void removeColumn(int columnIndex) {
    500            this.data.removeColumn(columnIndex);
    501            updateBounds();
    502            fireDatasetChanged();
    503        }
    504
    505        /**
    506         * Removes a column from the dataset and sends a {@link DatasetChangeEvent}
    507         * to all registered listeners.
    508         *
    509         * @param columnKey  the column key (<code>null</code> not permitted).
    510         *
    511         * @see #removeRow(Comparable)
    512         *
    513         * @since 1.0.7
    514         */
    515        public void removeColumn(Comparable columnKey) {
    516            this.data.removeColumn(columnKey);
    517            updateBounds();
    518            fireDatasetChanged();
    519        }
    520
    521        /**
    522         * Clears all data from the dataset and sends a {@link DatasetChangeEvent}
    523         * to all registered listeners.
    524         *
    525         * @since 1.0.7
    526         */
    527        public void clear() {
    528            this.data.clear();
    529            updateBounds();
    530            fireDatasetChanged();
    531        }
    532
    533        /**
    534         * Iterate over all the data items and update the cached bound values.
    535         */
    536        private void updateBounds() {
    537            this.maximumRangeValue = Double.NaN;
    538            this.maximumRangeValueRow = -1;
    539            this.maximumRangeValueColumn = -1;
    540            this.minimumRangeValue = Double.NaN;
    541            this.minimumRangeValueRow = -1;
    542            this.minimumRangeValueColumn = -1;
    543            this.maximumRangeValueIncStdDev = Double.NaN;
    544            this.maximumRangeValueIncStdDevRow = -1;
    545            this.maximumRangeValueIncStdDevColumn = -1;
    546            this.minimumRangeValueIncStdDev = Double.NaN;
    547            this.minimumRangeValueIncStdDevRow = -1;
    548            this.minimumRangeValueIncStdDevColumn = -1;
    549
    550            int rowCount = this.data.getRowCount();
    551            int columnCount = this.data.getColumnCount();
    552            for (int r = 0; r < rowCount; r++) {
    553                for (int c = 0; c < columnCount; c++) {
    554                    MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
    555                            this.data.getObject(r, c);
    556                    if (masd == null) {
    557                        continue;
    558                    }
    559                    double m = masd.getMeanValue();
    560                    double 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 && !Double.isNaN(this.minimumRangeValueIncStdDev)) {
    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 && !Double.isNaN(this.maximumRangeValueIncStdDev)) {
    657                return this.maximumRangeValueIncStdDev;
    658            }
    659            else {
    660                return this.maximumRangeValue;
    661            }
    662        }
    663
    664        /**
    665         * Returns the bounds of the values in this dataset's y-values.
    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            double lower = getRangeLowerBound(includeInterval);
    674            double upper = getRangeUpperBound(includeInterval);
    675            if (Double.isNaN(lower) && Double.isNaN(upper)) {
    676                return null;
    677            }
    678            return new Range(lower, upper);
    679        }
    680
    681        /**
    682         * Tests this instance for equality with an arbitrary object.
    683         *
    684         * @param obj  the object (<code>null</code> permitted).
    685         *
    686         * @return A boolean.
    687         */
    688        public boolean equals(Object obj) {
    689            if (obj == this) {
    690                return true;
    691            }
    692            if (!(obj instanceof DefaultStatisticalCategoryDataset)) {
    693                return false;
    694            }
    695            DefaultStatisticalCategoryDataset that
    696                    = (DefaultStatisticalCategoryDataset) obj;
    697            if (!this.data.equals(that.data)) {
    698                return false;
    699            }
    700            return true;
    701        }
    702
    703        /**
    704         * Returns a clone of this dataset.
    705         *
    706         * @return A clone of this dataset.
    707         *
    708         * @throws CloneNotSupportedException if cloning cannot be completed.
    709         */
    710        public Object clone() throws CloneNotSupportedException {
    711            DefaultStatisticalCategoryDataset clone
    712                    = (DefaultStatisticalCategoryDataset) super.clone();
    713            clone.data = (KeyedObjects2D) this.data.clone();
    714            return clone;
    715        }
    716    }