001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert 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-present, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): -; 034 * 035 */ 036 037package org.jfree.data; 038 039import java.io.Serializable; 040import java.util.Iterator; 041import java.util.List; 042import org.jfree.chart.util.Args; 043import org.jfree.chart.util.PublicCloneable; 044 045/** 046 * A collection of (key, object) pairs. 047 */ 048public class KeyedObjects implements Cloneable, PublicCloneable, Serializable { 049 050 /** For serialization. */ 051 private static final long serialVersionUID = 1321582394193530984L; 052 053 /** Storage for the data. */ 054 private List data; 055 056 /** 057 * Creates a new collection (initially empty). 058 */ 059 public KeyedObjects() { 060 this.data = new java.util.ArrayList(); 061 } 062 063 /** 064 * Returns the number of items (values) in the collection. 065 * 066 * @return The item count. 067 */ 068 public int getItemCount() { 069 return this.data.size(); 070 } 071 072 /** 073 * Returns an object from the list. 074 * 075 * @param item the item index (zero-based). 076 * 077 * @return The object (possibly {@code null}). 078 * 079 * @throws IndexOutOfBoundsException if {@code item} is out of bounds. 080 */ 081 public Object getObject(int item) { 082 Object result = null; 083 KeyedObject kobj = (KeyedObject) this.data.get(item); 084 if (kobj != null) { 085 result = kobj.getObject(); 086 } 087 return result; 088 } 089 090 /** 091 * Returns the key at the specified position in the list. 092 * 093 * @param index the item index (zero-based). 094 * 095 * @return The row key. 096 * 097 * @throws IndexOutOfBoundsException if {@code item} is out of bounds. 098 * 099 * @see #getIndex(Comparable) 100 */ 101 public Comparable getKey(int index) { 102 Comparable result = null; 103 KeyedObject item = (KeyedObject) this.data.get(index); 104 if (item != null) { 105 result = item.getKey(); 106 } 107 return result; 108 } 109 110 /** 111 * Returns the index for a given key, or {@code -1}. 112 * 113 * @param key the key ({@code null} not permitted). 114 * 115 * @return The index, or {@code -1} if the key is unrecognised. 116 * 117 * @see #getKey(int) 118 */ 119 public int getIndex(Comparable key) { 120 Args.nullNotPermitted(key, "key"); 121 int i = 0; 122 Iterator iterator = this.data.iterator(); 123 while (iterator.hasNext()) { 124 KeyedObject ko = (KeyedObject) iterator.next(); 125 if (ko.getKey().equals(key)) { 126 return i; 127 } 128 i++; 129 } 130 return -1; 131 } 132 133 /** 134 * Returns a list containing all the keys in the list. 135 * 136 * @return The keys (never {@code null}). 137 */ 138 public List getKeys() { 139 List result = new java.util.ArrayList(); 140 Iterator iterator = this.data.iterator(); 141 while (iterator.hasNext()) { 142 KeyedObject ko = (KeyedObject) iterator.next(); 143 result.add(ko.getKey()); 144 } 145 return result; 146 } 147 148 /** 149 * Returns the object for a given key. If the key is not recognised, the 150 * method should return {@code null}. 151 * 152 * @param key the key. 153 * 154 * @return The object (possibly {@code null}). 155 * 156 * @see #addObject(Comparable, Object) 157 */ 158 public Object getObject(Comparable key) { 159 int index = getIndex(key); 160 if (index < 0) { 161 throw new UnknownKeyException("The key (" + key 162 + ") is not recognised."); 163 } 164 return getObject(index); 165 } 166 167 /** 168 * Adds a new object to the collection, or overwrites an existing object. 169 * This is the same as the {@link #setObject(Comparable, Object)} method. 170 * 171 * @param key the key. 172 * @param object the object. 173 * 174 * @see #getObject(Comparable) 175 */ 176 public void addObject(Comparable key, Object object) { 177 setObject(key, object); 178 } 179 180 /** 181 * Replaces an existing object, or adds a new object to the collection. 182 * This is the same as the {@link #addObject(Comparable, Object)} 183 * method. 184 * 185 * @param key the key ({@code null} not permitted). 186 * @param object the object. 187 * 188 * @see #getObject(Comparable) 189 */ 190 public void setObject(Comparable key, Object object) { 191 int keyIndex = getIndex(key); 192 if (keyIndex >= 0) { 193 KeyedObject ko = (KeyedObject) this.data.get(keyIndex); 194 ko.setObject(object); 195 } 196 else { 197 KeyedObject ko = new KeyedObject(key, object); 198 this.data.add(ko); 199 } 200 } 201 202 /** 203 * Inserts a new value at the specified position in the dataset or, if 204 * there is an existing item with the specified key, updates the value 205 * for that item and moves it to the specified position. 206 * 207 * @param position the position (in the range {@code 0} to 208 * {@code getItemCount()}). 209 * @param key the key ({@code null} not permitted). 210 * @param value the value ({@code null} permitted). 211 */ 212 public void insertValue(int position, Comparable key, Object value) { 213 if (position < 0 || position > this.data.size()) { 214 throw new IllegalArgumentException("'position' out of bounds."); 215 } 216 Args.nullNotPermitted(key, "key"); 217 int pos = getIndex(key); 218 if (pos >= 0) { 219 this.data.remove(pos); 220 } 221 KeyedObject item = new KeyedObject(key, value); 222 if (position <= this.data.size()) { 223 this.data.add(position, item); 224 } 225 else { 226 this.data.add(item); 227 } 228 } 229 230 /** 231 * Removes a value from the collection. 232 * 233 * @param index the index of the item to remove. 234 * 235 * @see #removeValue(Comparable) 236 */ 237 public void removeValue(int index) { 238 this.data.remove(index); 239 } 240 241 /** 242 * Removes a value from the collection. 243 * 244 * @param key the key ({@code null} not permitted). 245 * 246 * @see #removeValue(int) 247 * 248 * @throws UnknownKeyException if the key is not recognised. 249 */ 250 public void removeValue(Comparable key) { 251 // defer argument checking 252 int index = getIndex(key); 253 if (index < 0) { 254 throw new UnknownKeyException("The key (" + key.toString() 255 + ") is not recognised."); 256 } 257 removeValue(index); 258 } 259 260 /** 261 * Clears all values from the collection. 262 */ 263 public void clear() { 264 this.data.clear(); 265 } 266 267 /** 268 * Returns a clone of this object. Keys in the list should be immutable 269 * and are not cloned. Objects in the list are cloned only if they 270 * implement {@link PublicCloneable}. 271 * 272 * @return A clone. 273 * 274 * @throws CloneNotSupportedException if there is a problem cloning. 275 */ 276 @Override 277 public Object clone() throws CloneNotSupportedException { 278 KeyedObjects clone = (KeyedObjects) super.clone(); 279 clone.data = new java.util.ArrayList(); 280 Iterator iterator = this.data.iterator(); 281 while (iterator.hasNext()) { 282 KeyedObject ko = (KeyedObject) iterator.next(); 283 clone.data.add(ko.clone()); 284 } 285 return clone; 286 } 287 288 /** 289 * Tests this object for equality with an arbitrary object. 290 * 291 * @param obj the object ({@code null} permitted). 292 * 293 * @return A boolean. 294 */ 295 @Override 296 public boolean equals(Object obj) { 297 298 if (obj == this) { 299 return true; 300 } 301 if (!(obj instanceof KeyedObjects)) { 302 return false; 303 } 304 KeyedObjects that = (KeyedObjects) obj; 305 int count = getItemCount(); 306 if (count != that.getItemCount()) { 307 return false; 308 } 309 310 for (int i = 0; i < count; i++) { 311 Comparable k1 = getKey(i); 312 Comparable k2 = that.getKey(i); 313 if (!k1.equals(k2)) { 314 return false; 315 } 316 Object o1 = getObject(i); 317 Object o2 = that.getObject(i); 318 if (o1 == null) { 319 if (o2 != null) { 320 return false; 321 } 322 } 323 else { 324 if (!o1.equals(o2)) { 325 return false; 326 } 327 } 328 } 329 return true; 330 331 } 332 333 /** 334 * Returns a hash code. 335 * 336 * @return A hash code. 337 */ 338 @Override 339 public int hashCode() { 340 return (this.data != null ? this.data.hashCode() : 0); 341 } 342 343}