    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2011, 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     * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025     * Other names may be trademarks of their respective owners.]
    026     *
    027     * -----------------
    028     * KeyedObjects.java
    029     * -----------------
    030     * (C) Copyright 2003-2008, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * Changes:
    036     * --------
    037     * 31-Oct-2002 : Version 1 (DG);
    038     * 11-Jan-2005 : Minor tidy up (DG);
    039     * 28-Sep-2007 : Clean up equals() method (DG);
    040     * 03-Oct-2007 : Make method behaviour consistent with DefaultKeyedValues (DG);
    041     *
    042     */
    043
    044    package org.jfree.data;
    045
    046    import java.io.Serializable;
    047    import java.util.Iterator;
    048    import java.util.List;
    049
    050    import org.jfree.util.PublicCloneable;
    051
    052    /**
    053     * A collection of (key, object) pairs.
    054     */
    055    public class KeyedObjects implements Cloneable, PublicCloneable, Serializable {
    056
    057        /** For serialization. */
    058        private static final long serialVersionUID = 1321582394193530984L;
    059
    060        /** Storage for the data. */
    061        private List data;
    062
    063        /**
    064         * Creates a new collection (initially empty).
    065         */
    066        public KeyedObjects() {
    067            this.data = new java.util.ArrayList();
    068        }
    069
    070        /**
    071         * Returns the number of items (values) in the collection.
    072         *
    073         * @return The item count.
    074         */
    075        public int getItemCount() {
    076            return this.data.size();
    077        }
    078
    079        /**
    080         * Returns an object from the list.
    081         *
    082         * @param item  the item index (zero-based).
    083         *
    084         * @return The object (possibly <code>null</code>).
    085         *
    086         * @throws IndexOutOfBoundsException if <code>item</code> is out of bounds.
    087         */
    088        public Object getObject(int item) {
    089            Object result = null;
    090            KeyedObject kobj = (KeyedObject) this.data.get(item);
    091            if (kobj != null) {
    092                result = kobj.getObject();
    093            }
    094            return result;
    095        }
    096
    097        /**
    098         * Returns the key at the specified position in the list.
    099         *
    100         * @param index  the item index (zero-based).
    101         *
    102         * @return The row key.
    103         *
    104         * @throws IndexOutOfBoundsException if <code>item</code> is out of bounds.
    105         *
    106         * @see #getIndex(Comparable)
    107         */
    108        public Comparable getKey(int index) {
    109            Comparable result = null;
    110            KeyedObject item = (KeyedObject) this.data.get(index);
    111            if (item != null) {
    112                result = item.getKey();
    113            }
    114            return result;
    115        }
    116
    117        /**
    118         * Returns the index for a given key, or <code>-1</code>.
    119         *
    120         * @param key  the key (<code>null</code> not permitted).
    121         *
    122         * @return The index, or <code>-1</code> if the key is unrecognised.
    123         *
    124         * @see #getKey(int)
    125         */
    126        public int getIndex(Comparable key) {
    127            if (key == null) {
    128                throw new IllegalArgumentException("Null 'key' argument.");
    129            }
    130            int i = 0;
    131            Iterator iterator = this.data.iterator();
    132            while (iterator.hasNext()) {
    133                KeyedObject ko = (KeyedObject) iterator.next();
    134                if (ko.getKey().equals(key)) {
    135                    return i;
    136                }
    137                i++;
    138            }
    139            return -1;
    140        }
    141
    142        /**
    143         * Returns a list containing all the keys in the list.
    144         *
    145         * @return The keys (never <code>null</code>).
    146         */
    147        public List getKeys() {
    148            List result = new java.util.ArrayList();
    149            Iterator iterator = this.data.iterator();
    150            while (iterator.hasNext()) {
    151                KeyedObject ko = (KeyedObject) iterator.next();
    152                result.add(ko.getKey());
    153            }
    154            return result;
    155        }
    156
    157        /**
    158         * Returns the object for a given key. If the key is not recognised, the
    159         * method should return <code>null</code>.
    160         *
    161         * @param key  the key.
    162         *
    163         * @return The object (possibly <code>null</code>).
    164         *
    165         * @see #addObject(Comparable, Object)
    166         */
    167        public Object getObject(Comparable key) {
    168            int index = getIndex(key);
    169            if (index < 0) {
    170                throw new UnknownKeyException("The key (" + key
    171                        + ") is not recognised.");
    172            }
    173            return getObject(index);
    174        }
    175
    176        /**
    177         * Adds a new object to the collection, or overwrites an existing object.
    178         * This is the same as the {@link #setObject(Comparable, Object)} method.
    179         *
    180         * @param key  the key.
    181         * @param object  the object.
    182         *
    183         * @see #getObject(Comparable)
    184         */
    185        public void addObject(Comparable key, Object object) {
    186            setObject(key, object);
    187        }
    188
    189        /**
    190         * Replaces an existing object, or adds a new object to the collection.
    191         * This is the same as the {@link #addObject(Comparable, Object)}
    192         * method.
    193         *
    194         * @param key  the key (<code>null</code> not permitted).
    195         * @param object  the object.
    196         *
    197         * @see #getObject(Comparable)
    198         */
    199        public void setObject(Comparable key, Object object) {
    200            int keyIndex = getIndex(key);
    201            if (keyIndex >= 0) {
    202                KeyedObject ko = (KeyedObject) this.data.get(keyIndex);
    203                ko.setObject(object);
    204            }
    205            else {
    206                KeyedObject ko = new KeyedObject(key, object);
    207                this.data.add(ko);
    208            }
    209        }
    210
    211        /**
    212         * Inserts a new value at the specified position in the dataset or, if
    213         * there is an existing item with the specified key, updates the value
    214         * for that item and moves it to the specified position.
    215         *
    216         * @param position  the position (in the range <code>0</code> to
    217         *                  <code>getItemCount()</code>).
    218         * @param key  the key (<code>null</code> not permitted).
    219         * @param value  the value (<code>null</code> permitted).
    220         *
    221         * @since 1.0.7
    222         */
    223        public void insertValue(int position, Comparable key, Object value) {
    224            if (position < 0 || position > this.data.size()) {
    225                throw new IllegalArgumentException("'position' out of bounds.");
    226            }
    227            if (key == null) {
    228                throw new IllegalArgumentException("Null 'key' argument.");
    229            }
    230            int pos = getIndex(key);
    231            if (pos >= 0) {
    232                this.data.remove(pos);
    233            }
    234            KeyedObject item = new KeyedObject(key, value);
    235            if (position <= this.data.size()) {
    236                this.data.add(position, item);
    237            }
    238            else {
    239                this.data.add(item);
    240            }
    241        }
    242
    243        /**
    244         * Removes a value from the collection.
    245         *
    246         * @param index  the index of the item to remove.
    247         *
    248         * @see #removeValue(Comparable)
    249         */
    250        public void removeValue(int index) {
    251            this.data.remove(index);
    252        }
    253
    254        /**
    255         * Removes a value from the collection.
    256         *
    257         * @param key  the key (<code>null</code> not permitted).
    258         *
    259         * @see #removeValue(int)
    260         *
    261         * @throws UnknownKeyException if the key is not recognised.
    262         */
    263        public void removeValue(Comparable key) {
    264            // defer argument checking
    265            int index = getIndex(key);
    266            if (index < 0) {
    267                throw new UnknownKeyException("The key (" + key.toString()
    268                        + ") is not recognised.");
    269            }
    270            removeValue(index);
    271        }
    272
    273        /**
    274         * Clears all values from the collection.
    275         *
    276         * @since 1.0.7
    277         */
    278        public void clear() {
    279            this.data.clear();
    280        }
    281
    282        /**
    283         * Returns a clone of this object.  Keys in the list should be immutable
    284         * and are not cloned.  Objects in the list are cloned only if they
    285         * implement {@link PublicCloneable}.
    286         *
    287         * @return A clone.
    288         *
    289         * @throws CloneNotSupportedException if there is a problem cloning.
    290         */
    291        public Object clone() throws CloneNotSupportedException {
    292            KeyedObjects clone = (KeyedObjects) super.clone();
    293            clone.data = new java.util.ArrayList();
    294            Iterator iterator = this.data.iterator();
    295            while (iterator.hasNext()) {
    296                KeyedObject ko = (KeyedObject) iterator.next();
    297                clone.data.add(ko.clone());
    298            }
    299            return clone;
    300        }
    301
    302        /**
    303         * Tests this object for equality with an arbitrary object.
    304         *
    305         * @param obj  the object (<code>null</code> permitted).
    306         *
    307         * @return A boolean.
    308         */
    309        public boolean equals(Object obj) {
    310
    311            if (obj == this) {
    312                return true;
    313            }
    314            if (!(obj instanceof KeyedObjects)) {
    315                return false;
    316            }
    317            KeyedObjects that = (KeyedObjects) obj;
    318            int count = getItemCount();
    319            if (count != that.getItemCount()) {
    320                return false;
    321            }
    322
    323            for (int i = 0; i < count; i++) {
    324                Comparable k1 = getKey(i);
    325                Comparable k2 = that.getKey(i);
    326                if (!k1.equals(k2)) {
    327                    return false;
    328                }
    329                Object o1 = getObject(i);
    330                Object o2 = that.getObject(i);
    331                if (o1 == null) {
    332                    if (o2 != null) {
    333                        return false;
    334                    }
    335                }
    336                else {
    337                    if (!o1.equals(o2)) {
    338                        return false;
    339                    }
    340                }
    341            }
    342            return true;
    343
    344        }
    345
    346        /**
    347         * Returns a hash code.
    348         *
    349         * @return A hash code.
    350         */
    351        public int hashCode() {
    352            return (this.data != null ? this.data.hashCode() : 0);
    353        }
    354
    355    }