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 }