001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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 * 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 }