001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2020, 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-2020, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 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 * @since 1.0.7 213 */ 214 public void insertValue(int position, Comparable key, Object value) { 215 if (position < 0 || position > this.data.size()) { 216 throw new IllegalArgumentException("'position' out of bounds."); 217 } 218 Args.nullNotPermitted(key, "key"); 219 int pos = getIndex(key); 220 if (pos >= 0) { 221 this.data.remove(pos); 222 } 223 KeyedObject item = new KeyedObject(key, value); 224 if (position <= this.data.size()) { 225 this.data.add(position, item); 226 } 227 else { 228 this.data.add(item); 229 } 230 } 231 232 /** 233 * Removes a value from the collection. 234 * 235 * @param index the index of the item to remove. 236 * 237 * @see #removeValue(Comparable) 238 */ 239 public void removeValue(int index) { 240 this.data.remove(index); 241 } 242 243 /** 244 * Removes a value from the collection. 245 * 246 * @param key the key ({@code null} not permitted). 247 * 248 * @see #removeValue(int) 249 * 250 * @throws UnknownKeyException if the key is not recognised. 251 */ 252 public void removeValue(Comparable key) { 253 // defer argument checking 254 int index = getIndex(key); 255 if (index < 0) { 256 throw new UnknownKeyException("The key (" + key.toString() 257 + ") is not recognised."); 258 } 259 removeValue(index); 260 } 261 262 /** 263 * Clears all values from the collection. 264 * 265 * @since 1.0.7 266 */ 267 public void clear() { 268 this.data.clear(); 269 } 270 271 /** 272 * Returns a clone of this object. Keys in the list should be immutable 273 * and are not cloned. Objects in the list are cloned only if they 274 * implement {@link PublicCloneable}. 275 * 276 * @return A clone. 277 * 278 * @throws CloneNotSupportedException if there is a problem cloning. 279 */ 280 @Override 281 public Object clone() throws CloneNotSupportedException { 282 KeyedObjects clone = (KeyedObjects) super.clone(); 283 clone.data = new java.util.ArrayList(); 284 Iterator iterator = this.data.iterator(); 285 while (iterator.hasNext()) { 286 KeyedObject ko = (KeyedObject) iterator.next(); 287 clone.data.add(ko.clone()); 288 } 289 return clone; 290 } 291 292 /** 293 * Tests this object for equality with an arbitrary object. 294 * 295 * @param obj the object ({@code null} permitted). 296 * 297 * @return A boolean. 298 */ 299 @Override 300 public boolean equals(Object obj) { 301 302 if (obj == this) { 303 return true; 304 } 305 if (!(obj instanceof KeyedObjects)) { 306 return false; 307 } 308 KeyedObjects that = (KeyedObjects) obj; 309 int count = getItemCount(); 310 if (count != that.getItemCount()) { 311 return false; 312 } 313 314 for (int i = 0; i < count; i++) { 315 Comparable k1 = getKey(i); 316 Comparable k2 = that.getKey(i); 317 if (!k1.equals(k2)) { 318 return false; 319 } 320 Object o1 = getObject(i); 321 Object o2 = that.getObject(i); 322 if (o1 == null) { 323 if (o2 != null) { 324 return false; 325 } 326 } 327 else { 328 if (!o1.equals(o2)) { 329 return false; 330 } 331 } 332 } 333 return true; 334 335 } 336 337 /** 338 * Returns a hash code. 339 * 340 * @return A hash code. 341 */ 342 @Override 343 public int hashCode() { 344 return (this.data != null ? this.data.hashCode() : 0); 345 } 346 347}