001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2013, 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-2013, 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 * 03-Jul-2013 : Use ParamChecks (DG); 042 * 043 */ 044 045package org.jfree.data; 046 047import java.io.Serializable; 048import java.util.Iterator; 049import java.util.List; 050import org.jfree.chart.util.ParamChecks; 051 052import org.jfree.util.PublicCloneable; 053 054/** 055 * A collection of (key, object) pairs. 056 */ 057public class KeyedObjects implements Cloneable, PublicCloneable, Serializable { 058 059 /** For serialization. */ 060 private static final long serialVersionUID = 1321582394193530984L; 061 062 /** Storage for the data. */ 063 private List data; 064 065 /** 066 * Creates a new collection (initially empty). 067 */ 068 public KeyedObjects() { 069 this.data = new java.util.ArrayList(); 070 } 071 072 /** 073 * Returns the number of items (values) in the collection. 074 * 075 * @return The item count. 076 */ 077 public int getItemCount() { 078 return this.data.size(); 079 } 080 081 /** 082 * Returns an object from the list. 083 * 084 * @param item the item index (zero-based). 085 * 086 * @return The object (possibly <code>null</code>). 087 * 088 * @throws IndexOutOfBoundsException if <code>item</code> is out of bounds. 089 */ 090 public Object getObject(int item) { 091 Object result = null; 092 KeyedObject kobj = (KeyedObject) this.data.get(item); 093 if (kobj != null) { 094 result = kobj.getObject(); 095 } 096 return result; 097 } 098 099 /** 100 * Returns the key at the specified position in the list. 101 * 102 * @param index the item index (zero-based). 103 * 104 * @return The row key. 105 * 106 * @throws IndexOutOfBoundsException if <code>item</code> is out of bounds. 107 * 108 * @see #getIndex(Comparable) 109 */ 110 public Comparable getKey(int index) { 111 Comparable result = null; 112 KeyedObject item = (KeyedObject) this.data.get(index); 113 if (item != null) { 114 result = item.getKey(); 115 } 116 return result; 117 } 118 119 /** 120 * Returns the index for a given key, or <code>-1</code>. 121 * 122 * @param key the key (<code>null</code> not permitted). 123 * 124 * @return The index, or <code>-1</code> if the key is unrecognised. 125 * 126 * @see #getKey(int) 127 */ 128 public int getIndex(Comparable key) { 129 ParamChecks.nullNotPermitted(key, "key"); 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 ParamChecks.nullNotPermitted(key, "key"); 228 int pos = getIndex(key); 229 if (pos >= 0) { 230 this.data.remove(pos); 231 } 232 KeyedObject item = new KeyedObject(key, value); 233 if (position <= this.data.size()) { 234 this.data.add(position, item); 235 } 236 else { 237 this.data.add(item); 238 } 239 } 240 241 /** 242 * Removes a value from the collection. 243 * 244 * @param index the index of the item to remove. 245 * 246 * @see #removeValue(Comparable) 247 */ 248 public void removeValue(int index) { 249 this.data.remove(index); 250 } 251 252 /** 253 * Removes a value from the collection. 254 * 255 * @param key the key (<code>null</code> not permitted). 256 * 257 * @see #removeValue(int) 258 * 259 * @throws UnknownKeyException if the key is not recognised. 260 */ 261 public void removeValue(Comparable key) { 262 // defer argument checking 263 int index = getIndex(key); 264 if (index < 0) { 265 throw new UnknownKeyException("The key (" + key.toString() 266 + ") is not recognised."); 267 } 268 removeValue(index); 269 } 270 271 /** 272 * Clears all values from the collection. 273 * 274 * @since 1.0.7 275 */ 276 public void clear() { 277 this.data.clear(); 278 } 279 280 /** 281 * Returns a clone of this object. Keys in the list should be immutable 282 * and are not cloned. Objects in the list are cloned only if they 283 * implement {@link PublicCloneable}. 284 * 285 * @return A clone. 286 * 287 * @throws CloneNotSupportedException if there is a problem cloning. 288 */ 289 @Override 290 public Object clone() throws CloneNotSupportedException { 291 KeyedObjects clone = (KeyedObjects) super.clone(); 292 clone.data = new java.util.ArrayList(); 293 Iterator iterator = this.data.iterator(); 294 while (iterator.hasNext()) { 295 KeyedObject ko = (KeyedObject) iterator.next(); 296 clone.data.add(ko.clone()); 297 } 298 return clone; 299 } 300 301 /** 302 * Tests this object for equality with an arbitrary object. 303 * 304 * @param obj the object (<code>null</code> permitted). 305 * 306 * @return A boolean. 307 */ 308 @Override 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 @Override 352 public int hashCode() { 353 return (this.data != null ? this.data.hashCode() : 0); 354 } 355 356}