    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
    044package org.jfree.data;
    045
    046import java.io.Serializable;
    047import java.util.Iterator;
    048import java.util.List;
    049
    050import org.jfree.util.PublicCloneable;
    051
    052/**
    053 * A collection of (key, object) pairs.
    054 */
    055public 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}