    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2006, 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     * DefaultKeyedValues.java
    029     * -----------------------
    030     * (C) Copyright 2002-2006, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: DefaultKeyedValues.java,v 1.8.2.5 2006/11/16 14:41:25 mungady Exp $
    036     *
    037     * Changes:
    038     * --------
    039     * 31-Oct-2002 : Version 1 (DG);
    040     * 11-Feb-2003 : Fixed bug in getValue(key) method for unrecognised key (DG);
    041     * 05-Mar-2003 : Added methods to sort stored data 'by key' or 'by value' (DG);
    042     * 13-Mar-2003 : Implemented Serializable (DG);
    043     * 08-Apr-2003 : Modified removeValue(Comparable) method to fix bug 717049 (DG);
    044     * 18-Aug-2003 : Implemented Cloneable (DG);
    045     * 27-Aug-2003 : Moved SortOrder from org.jfree.data --> org.jfree.util (DG);
    046     * 09-Feb-2004 : Modified getIndex() method - see bug report 893256 (DG);
    047     * 15-Sep-2004 : Updated clone() method and added PublicCloneable
    048     *               interface (DG);
    049     * 25-Nov-2004 : Small update to the clone() implementation (DG);
    050     * 24-Feb-2005 : Added methods addValue(Comparable, double) and
    051     *               setValue(Comparable, double) for convenience (DG);
    052     * ------------- JFREECHART 1.0.0 -----------------------------------------------
    053     * 31-Jul-2006 : Added a clear() method (DG);
    054     * 01-Aug-2006 : Added argument check to getIndex() method (DG);
    055     *
    056     */
    057
    058    package org.jfree.data;
    059
    060    import java.io.Serializable;
    061    import java.util.Collections;
    062    import java.util.Comparator;
    063    import java.util.Iterator;
    064    import java.util.List;
    065
    066    import org.jfree.util.ObjectUtilities;
    067    import org.jfree.util.PublicCloneable;
    068    import org.jfree.util.SortOrder;
    069
    070    /**
    071     * An ordered list of (key, value) items.  This class provides a default
    072     * implementation of the {@link KeyedValues} interface.
    073     */
    074    public class DefaultKeyedValues implements KeyedValues,
    075                                               Cloneable, PublicCloneable,
    076                                               Serializable {
    077
    078        /** For serialization. */
    079        private static final long serialVersionUID = 8468154364608194797L;
    080
    081        /** Storage for the data. */
    082        private List data;
    083
    084        /**
    085         * Creates a new collection (initially empty).
    086         */
    087        public DefaultKeyedValues() {
    088            this.data = new java.util.ArrayList();
    089        }
    090
    091        /**
    092         * Returns the number of items (values) in the collection.
    093         *
    094         * @return The item count.
    095         */
    096        public int getItemCount() {
    097            return this.data.size();
    098        }
    099
    100        /**
    101         * Returns a value.
    102         *
    103         * @param item  the item of interest (zero-based index).
    104         *
    105         * @return The value.
    106         *
    107         * @throws IndexOutOfBoundsException if <code>item</code> is out of bounds.
    108         */
    109        public Number getValue(int item) {
    110            Number result = null;
    111            KeyedValue kval = (KeyedValue) this.data.get(item);
    112            if (kval != null) {
    113                result = kval.getValue();
    114            }
    115            return result;
    116        }
    117
    118        /**
    119         * Returns a key.
    120         *
    121         * @param index  the item index (zero-based).
    122         *
    123         * @return The row key.
    124         *
    125         * @throws IndexOutOfBoundsException if <code>item</code> is out of bounds.
    126         */
    127        public Comparable getKey(int index) {
    128            Comparable result = null;
    129            KeyedValue item = (KeyedValue) this.data.get(index);
    130            if (item != null) {
    131                result = item.getKey();
    132            }
    133            return result;
    134        }
    135
    136        /**
    137         * Returns the index for a given key.
    138         *
    139         * @param key  the key (<code>null</code> not permitted).
    140         *
    141         * @return The index, or <code>-1</code> if the key is not recognised.
    142         *
    143         * @throws IllegalArgumentException if <code>key</code> is
    144         *     <code>null</code>.
    145         */
    146        public int getIndex(Comparable key) {
    147            if (key == null) {
    148                throw new IllegalArgumentException("Null 'key' argument.");
    149            }
    150            int i = 0;
    151            Iterator iterator = this.data.iterator();
    152            while (iterator.hasNext()) {
    153                KeyedValue kv = (KeyedValue) iterator.next();
    154                if (kv.getKey().equals(key)) {
    155                    return i;
    156                }
    157                i++;
    158            }
    159            return -1;  // key not found
    160        }
    161
    162        /**
    163         * Returns the keys for the values in the collection.
    164         *
    165         * @return The keys (never <code>null</code>).
    166         */
    167        public List getKeys() {
    168            List result = new java.util.ArrayList();
    169            Iterator iterator = this.data.iterator();
    170            while (iterator.hasNext()) {
    171                KeyedValue kv = (KeyedValue) iterator.next();
    172                result.add(kv.getKey());
    173            }
    174            return result;
    175        }
    176
    177        /**
    178         * Returns the value for a given key.
    179         *
    180         * @param key  the key.
    181         *
    182         * @return The value (possibly <code>null</code>).
    183         *
    184         * @throws UnknownKeyException if the key is not recognised.
    185         */
    186        public Number getValue(Comparable key) {
    187            int index = getIndex(key);
    188            if (index < 0) {
    189                throw new UnknownKeyException("Key not found: " + key);
    190            }
    191            return getValue(index);
    192        }
    193
    194        /**
    195         * Updates an existing value, or adds a new value to the collection.
    196         *
    197         * @param key  the key (<code>null</code> not permitted).
    198         * @param value  the value.
    199         */
    200        public void addValue(Comparable key, double value) {
    201            addValue(key, new Double(value));
    202        }
    203
    204        /**
    205         * Adds a new value to the collection, or updates an existing value.
    206         * This method passes control directly to the
    207         * {@link #setValue(Comparable, Number)} method.
    208         *
    209         * @param key  the key (<code>null</code> not permitted).
    210         * @param value  the value (<code>null</code> permitted).
    211         */
    212        public void addValue(Comparable key, Number value) {
    213            setValue(key, value);
    214        }
    215
    216        /**
    217         * Updates an existing value, or adds a new value to the collection.
    218         *
    219         * @param key  the key (<code>null</code> not permitted).
    220         * @param value  the value.
    221         */
    222        public void setValue(Comparable key, double value) {
    223            setValue(key, new Double(value));
    224        }
    225
    226        /**
    227         * Updates an existing value, or adds a new value to the collection.
    228         *
    229         * @param key  the key (<code>null</code> not permitted).
    230         * @param value  the value (<code>null</code> permitted).
    231         */
    232        public void setValue(Comparable key, Number value) {
    233            if (key == null) {
    234                throw new IllegalArgumentException("Null 'key' argument.");
    235            }
    236            int keyIndex = getIndex(key);
    237            if (keyIndex >= 0) {
    238                DefaultKeyedValue kv = (DefaultKeyedValue) this.data.get(keyIndex);
    239                kv.setValue(value);
    240            }
    241            else {
    242                KeyedValue kv = new DefaultKeyedValue(key, value);
    243                this.data.add(kv);
    244            }
    245        }
    246
    247        /**
    248         * Removes a value from the collection.
    249         *
    250         * @param index  the index of the item to remove (in the range
    251         *     <code>0</code> to <code>getItemCount() - 1</code>).
    252         *
    253         * @throws IndexOutOfBoundsException if <code>index</code> is not within
    254         *     the specified range.
    255         */
    256        public void removeValue(int index) {
    257            this.data.remove(index);
    258        }
    259
    260        /**
    261         * Removes a value from the collection.  If there is no item with the
    262         * specified key, this method does nothing.
    263         *
    264         * @param key  the item key (<code>null</code> not permitted).
    265         *
    266         * @throws IllegalArgumentException if <code>key</code> is
    267         *     <code>null</code>.
    268         */
    269        public void removeValue(Comparable key) {
    270            int index = getIndex(key);
    271            if (index >= 0) {
    272                removeValue(index);
    273            }
    274        }
    275
    276        /**
    277         * Clears all values from the collection.
    278         *
    279         * @since 1.0.2
    280         */
    281        public void clear() {
    282            this.data.clear();
    283        }
    284
    285        /**
    286         * Sorts the items in the list by key.
    287         *
    288         * @param order  the sort order (<code>null</code> not permitted).
    289         */
    290        public void sortByKeys(SortOrder order) {
    291            Comparator comparator = new KeyedValueComparator(
    292                    KeyedValueComparatorType.BY_KEY, order);
    293            Collections.sort(this.data, comparator);
    294        }
    295
    296        /**
    297         * Sorts the items in the list by value.  If the list contains
    298         * <code>null</code> values, they will sort to the end of the list,
    299         * irrespective of the sort order.
    300         *
    301         * @param order  the sort order (<code>null</code> not permitted).
    302         */
    303        public void sortByValues(SortOrder order) {
    304            Comparator comparator = new KeyedValueComparator(
    305                    KeyedValueComparatorType.BY_VALUE, order);
    306            Collections.sort(this.data, comparator);
    307        }
    308
    309        /**
    310         * Tests if this object is equal to another.
    311         *
    312         * @param obj  the object (<code>null</code> permitted).
    313         *
    314         * @return A boolean.
    315         */
    316        public boolean equals(Object obj) {
    317            if (obj == this) {
    318                return true;
    319            }
    320
    321            if (!(obj instanceof KeyedValues)) {
    322                return false;
    323            }
    324
    325            KeyedValues that = (KeyedValues) obj;
    326            int count = getItemCount();
    327            if (count != that.getItemCount()) {
    328                return false;
    329            }
    330
    331            for (int i = 0; i < count; i++) {
    332                Comparable k1 = getKey(i);
    333                Comparable k2 = that.getKey(i);
    334                if (!k1.equals(k2)) {
    335                    return false;
    336                }
    337                Number v1 = getValue(i);
    338                Number v2 = that.getValue(i);
    339                if (v1 == null) {
    340                    if (v2 != null) {
    341                        return false;
    342                    }
    343                }
    344                else {
    345                    if (!v1.equals(v2)) {
    346                        return false;
    347                    }
    348                }
    349            }
    350            return true;
    351        }
    352
    353        /**
    354         * Returns a hash code.
    355         *
    356         * @return A hash code.
    357         */
    358        public int hashCode() {
    359            return (this.data != null ? this.data.hashCode() : 0);
    360        }
    361
    362        /**
    363         * Returns a clone.
    364         *
    365         * @return A clone.
    366         *
    367         * @throws CloneNotSupportedException  this class will not throw this
    368         *         exception, but subclasses might.
    369         */
    370        public Object clone() throws CloneNotSupportedException {
    371            DefaultKeyedValues clone = (DefaultKeyedValues) super.clone();
    372            clone.data = (List) ObjectUtilities.deepClone(this.data);
    373            return clone;
    374        }
    375
    376    }