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