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     * 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    
048    package org.jfree.data.category;
049    
050    import java.util.ArrayList;
051    import java.util.Arrays;
052    import java.util.Collections;
053    import java.util.List;
054    import java.util.ResourceBundle;
055    
056    import org.jfree.chart.util.ResourceBundleWrapper;
057    import org.jfree.data.DataUtilities;
058    import org.jfree.data.UnknownKeyException;
059    import 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     */
068    public 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    }