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 }