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