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