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