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