Frames | No Frames |
1: /* ======================================================================== 2: * JCommon : a free general purpose class library for the Java(tm) platform 3: * ======================================================================== 4: * 5: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jcommon/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ------------- 28: * HashNMap.java 29: * ------------- 30: * (C)opyright 2002-2005, by Thomas Morgner and Contributors. 31: * 32: * Original Author: Thomas Morgner; 33: * Contributor(s): David Gilbert (for Object Refinery Limited); 34: * 35: * $Id: HashNMap.java,v 1.7 2005/10/18 13:24:19 mungady Exp $ 36: * 37: * Changes 38: * ------- 39: * 20-May-2002 : Initial version 40: * 10-Dec-2002 : Minor Javadoc updates (DG); 41: * 29-Jul-2004 : Replaced 'enum' variable name (reserved word in JDK 1.5) (DG); 42: * 12-Mar-2005 : Some performance improvements, this implementation is no 43: * longer forced to use ArrayLists, add/put behaviour changed to 44: * fit the common behaviour of collections. 45: * 46: */ 47: 48: package org.jfree.util; 49: 50: import java.io.Serializable; 51: import java.util.ArrayList; 52: import java.util.HashMap; 53: import java.util.Iterator; 54: import java.util.List; 55: import java.util.NoSuchElementException; 56: import java.util.Set; 57: 58: /** 59: * The HashNMap can be used to store multiple values by a single key value. The 60: * values stored can be retrieved using a direct query or by creating an 61: * enumeration over the stored elements. 62: * 63: * @author Thomas Morgner 64: */ 65: public class HashNMap implements Serializable, Cloneable { 66: 67: /** Serialization support. */ 68: private static final long serialVersionUID = -670924844536074826L; 69: 70: /** 71: * An helper class to implement an empty iterator. This iterator will always 72: * return false when <code>hasNext</code> is called. 73: */ 74: private static final class EmptyIterator implements Iterator { 75: 76: /** 77: * DefaultConstructor. 78: */ 79: private EmptyIterator() { 80: super(); 81: } 82: 83: /** 84: * Returns <tt>true</tt> if the iteration has more elements. (In other 85: * words, returns <tt>true</tt> if <tt>next</tt> would return an element 86: * rather than throwing an exception.) 87: * 88: * @return <tt>true</tt> if the iterator has more elements. 89: */ 90: public boolean hasNext() { 91: return false; 92: } 93: 94: /** 95: * Returns the next element in the iteration. 96: * 97: * @return the next element in the iteration. 98: * @throws NoSuchElementException iteration has no more elements. 99: */ 100: public Object next() { 101: throw new NoSuchElementException("This iterator is empty."); 102: } 103: 104: /** 105: * Removes from the underlying collection the last element returned by the 106: * iterator (optional operation). This method can be called only once per 107: * call to <tt>next</tt>. The behavior of an iterator is unspecified if 108: * the underlying collection is modified while the iteration is in 109: * progress in any way other than by calling this method. 110: * 111: * @throws UnsupportedOperationException if the <tt>remove</tt> 112: * operation is not supported by this Iterator. 113: * @throws IllegalStateException if the <tt>next</tt> method has not 114: * yet been called, or the <tt>remove</tt> method has already 115: * been called after the last call to the <tt>next</tt> 116: * method. 117: */ 118: public void remove() { 119: throw new UnsupportedOperationException("This iterator is empty, no remove supported."); 120: } 121: } 122: 123: /** 124: * A singleton instance of the empty iterator. This object can be safely 125: * shared. 126: */ 127: private static final Iterator EMPTY_ITERATOR = new EmptyIterator(); 128: 129: /** 130: * The underlying storage. 131: */ 132: private HashMap table; 133: 134: /** 135: * An empty array. 136: */ 137: private static final Object[] EMPTY_ARRAY = new Object[0]; 138: 139: /** 140: * Default constructor. 141: */ 142: public HashNMap() { 143: this.table = new HashMap(); 144: } 145: 146: /** 147: * Returns a new empty list. 148: * 149: * @return A new empty list. 150: */ 151: protected List createList() { 152: return new ArrayList(); 153: } 154: 155: /** 156: * Inserts a new key/value pair into the map. If such a pair already 157: * exists, it gets replaced with the given values. 158: * 159: * @param key the key. 160: * @param val the value. 161: * @return A boolean. 162: */ 163: public boolean put(final Object key, final Object val) { 164: final List v = (List) this.table.get(key); 165: if (v == null) { 166: final List newList = createList(); 167: newList.add(val); 168: this.table.put(key, newList); 169: return true; 170: } 171: else { 172: v.clear(); 173: return v.add(val); 174: } 175: } 176: 177: /** 178: * Adds a new key/value pair into this map. If the key is not yet in the 179: * map, it gets added to the map and the call is equal to 180: * put(Object,Object). 181: * 182: * @param key the key. 183: * @param val the value. 184: * @return true, if the value has been added, false otherwise 185: */ 186: public boolean add(final Object key, final Object val) { 187: final List v = (List) this.table.get(key); 188: if (v == null) { 189: put(key, val); 190: return true; 191: } 192: else { 193: return v.add(val); 194: } 195: } 196: 197: /** 198: * Retrieves the first value registered for an key or null if there was no 199: * such key in the list. 200: * 201: * @param key the key. 202: * @return the value. 203: */ 204: public Object getFirst(final Object key) { 205: return get(key, 0); 206: } 207: 208: /** 209: * Retrieves the n-th value registered for an key or null if there was no 210: * such key in the list. An index out of bounds exception is thrown if 211: * there are less than n elements registered to this key. 212: * 213: * @param key the key. 214: * @param n the index. 215: * @return the object. 216: */ 217: public Object get(final Object key, final int n) { 218: final List v = (List) this.table.get(key); 219: if (v == null) { 220: return null; 221: } 222: return v.get(n); 223: } 224: 225: /** 226: * Returns an iterator over all elements registered to the given key. 227: * 228: * @param key the key. 229: * @return an iterator. 230: */ 231: public Iterator getAll(final Object key) { 232: final List v = (List) this.table.get(key); 233: if (v == null) { 234: return EMPTY_ITERATOR; 235: } 236: return v.iterator(); 237: } 238: 239: /** 240: * Returns all registered keys as an enumeration. 241: * 242: * @return an enumeration of the keys. 243: */ 244: public Iterator keys() { 245: return this.table.keySet().iterator(); 246: } 247: 248: /** 249: * Returns all registered keys as set. 250: * 251: * @return a set of keys. 252: */ 253: public Set keySet() { 254: return this.table.keySet(); 255: } 256: 257: /** 258: * Removes the key/value pair from the map. If the removed entry was the 259: * last entry for this key, the key gets also removed. 260: * 261: * @param key the key. 262: * @param value the value. 263: * @return true, if removing the element was successfull, false otherwise. 264: */ 265: public boolean remove(final Object key, final Object value) { 266: final List v = (List) this.table.get(key); 267: if (v == null) { 268: return false; 269: } 270: 271: if (!v.remove(value)) { 272: return false; 273: } 274: if (v.size() == 0) { 275: this.table.remove(key); 276: } 277: return true; 278: } 279: 280: /** 281: * Removes all elements for the given key. 282: * 283: * @param key the key. 284: */ 285: public void removeAll(final Object key) { 286: this.table.remove(key); 287: } 288: 289: /** 290: * Clears all keys and values of this map. 291: */ 292: public void clear() { 293: this.table.clear(); 294: } 295: 296: /** 297: * Tests whether this map contains the given key. 298: * 299: * @param key the key. 300: * @return true if the key is contained in the map 301: */ 302: public boolean containsKey(final Object key) { 303: return this.table.containsKey(key); 304: } 305: 306: /** 307: * Tests whether this map contains the given value. 308: * 309: * @param value the value. 310: * @return true if the value is registered in the map for an key. 311: */ 312: public boolean containsValue(final Object value) { 313: final Iterator e = this.table.values().iterator(); 314: boolean found = false; 315: while (e.hasNext() && !found) { 316: final List v = (List) e.next(); 317: found = v.contains(value); 318: } 319: return found; 320: } 321: 322: /** 323: * Tests whether this map contains the given value. 324: * 325: * @param value the value. 326: * @param key the key under which to find the value 327: * @return true if the value is registered in the map for an key. 328: */ 329: public boolean containsValue(final Object key, final Object value) { 330: final List v = (List) this.table.get(key); 331: if (v == null) { 332: return false; 333: } 334: return v.contains(value); 335: } 336: 337: /** 338: * Tests whether this map contains the given key or value. 339: * 340: * @param value the value. 341: * @return true if the key or value is contained in the map 342: */ 343: public boolean contains(final Object value) { 344: if (containsKey(value)) { 345: return true; 346: } 347: return containsValue(value); 348: } 349: 350: /** 351: * Creates a deep copy of this HashNMap. 352: * 353: * @return a clone. 354: * @throws CloneNotSupportedException this should never happen. 355: */ 356: public Object clone() throws CloneNotSupportedException { 357: final HashNMap map = (HashNMap) super.clone(); 358: map.table = new HashMap(); 359: final Iterator iterator = keys(); 360: while (iterator.hasNext()) { 361: final Object key = iterator.next(); 362: final List list = (List) map.table.get(key); 363: if (list != null) { 364: map.table.put(key, ObjectUtilities.clone(list)); 365: } 366: } 367: return map; 368: } 369: 370: /** 371: * Returns the contents for the given key as object array. If there were 372: * no objects registered with that key, an empty object array is returned. 373: * 374: * @param key the key. 375: * @param data the object array to receive the contents. 376: * @return the contents. 377: */ 378: public Object[] toArray(final Object key, final Object[] data) { 379: if (key == null) { 380: throw new NullPointerException("Key must not be null."); 381: } 382: final List list = (List) this.table.get(key); 383: if (list != null) { 384: return list.toArray(data); 385: } 386: if (data.length > 0) { 387: data[0] = null; 388: } 389: return data; 390: } 391: 392: /** 393: * Returns the contents for the given key as object array. If there were 394: * no objects registered with that key, an empty object array is returned. 395: * 396: * @param key the key. 397: * @return the contents. 398: */ 399: public Object[] toArray(final Object key) { 400: if (key == null) { 401: throw new NullPointerException("Key must not be null."); 402: } 403: final List list = (List) this.table.get(key); 404: if (list != null) { 405: return list.toArray(); 406: } 407: return EMPTY_ARRAY; 408: } 409: 410: /** 411: * Returns the number of elements registered with the given key. 412: * 413: * @param key the key. 414: * @return the number of element for this key, or 0 if there are no elements 415: * registered. 416: */ 417: public int getValueCount(final Object key) { 418: if (key == null) { 419: throw new NullPointerException("Key must not be null."); 420: } 421: final List list = (List) this.table.get(key); 422: if (list != null) { 423: return list.size(); 424: } 425: return 0; 426: } 427: }