001: /* ========================================================================
002: * JCommon : a free general purpose class library for the Java(tm) platform
003: * ========================================================================
004: *
005: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jcommon/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: * HashNMap.java
029: * -------------
030: * (C)opyright 2002-2005, by Thomas Morgner and Contributors.
031: *
032: * Original Author: Thomas Morgner;
033: * Contributor(s): David Gilbert (for Object Refinery Limited);
034: *
035: * $Id: HashNMap.java,v 1.7 2005/10/18 13:24:19 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 20-May-2002 : Initial version
040: * 10-Dec-2002 : Minor Javadoc updates (DG);
041: * 29-Jul-2004 : Replaced 'enum' variable name (reserved word in JDK 1.5) (DG);
042: * 12-Mar-2005 : Some performance improvements, this implementation is no
043: * longer forced to use ArrayLists, add/put behaviour changed to
044: * fit the common behaviour of collections.
045: *
046: */
047:
048: package org.jfree.util;
049:
050: import java.io.Serializable;
051: import java.util.ArrayList;
052: import java.util.HashMap;
053: import java.util.Iterator;
054: import java.util.List;
055: import java.util.NoSuchElementException;
056: import java.util.Set;
057:
058: /**
059: * The HashNMap can be used to store multiple values by a single key value. The
060: * values stored can be retrieved using a direct query or by creating an
061: * enumeration over the stored elements.
062: *
063: * @author Thomas Morgner
064: */
065: public class HashNMap implements Serializable, Cloneable {
066:
067: /** Serialization support. */
068: private static final long serialVersionUID = -670924844536074826L;
069:
070: /**
071: * An helper class to implement an empty iterator. This iterator will always
072: * return false when <code>hasNext</code> is called.
073: */
074: private static final class EmptyIterator implements Iterator {
075:
076: /**
077: * DefaultConstructor.
078: */
079: private EmptyIterator() {
080: super ();
081: }
082:
083: /**
084: * Returns <tt>true</tt> if the iteration has more elements. (In other
085: * words, returns <tt>true</tt> if <tt>next</tt> would return an element
086: * rather than throwing an exception.)
087: *
088: * @return <tt>true</tt> if the iterator has more elements.
089: */
090: public boolean hasNext() {
091: return false;
092: }
093:
094: /**
095: * Returns the next element in the iteration.
096: *
097: * @return the next element in the iteration.
098: * @throws NoSuchElementException iteration has no more elements.
099: */
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(
120: "This iterator is empty, no remove supported.");
121: }
122: }
123:
124: /**
125: * A singleton instance of the empty iterator. This object can be safely
126: * shared.
127: */
128: private static final Iterator EMPTY_ITERATOR = new EmptyIterator();
129:
130: /**
131: * The underlying storage.
132: */
133: private HashMap table;
134:
135: /**
136: * An empty array.
137: */
138: private static final Object[] EMPTY_ARRAY = new Object[0];
139:
140: /**
141: * Default constructor.
142: */
143: public HashNMap() {
144: this .table = new HashMap();
145: }
146:
147: /**
148: * Returns a new empty list.
149: *
150: * @return A new empty list.
151: */
152: protected List createList() {
153: return new ArrayList();
154: }
155:
156: /**
157: * Inserts a new key/value pair into the map. If such a pair already
158: * exists, it gets replaced with the given values.
159: *
160: * @param key the key.
161: * @param val the value.
162: * @return A boolean.
163: */
164: public boolean put(final Object key, final Object val) {
165: final List v = (List) this .table.get(key);
166: if (v == null) {
167: final List newList = createList();
168: newList.add(val);
169: this .table.put(key, newList);
170: return true;
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: } else {
192: return v.add(val);
193: }
194: }
195:
196: /**
197: * Retrieves the first value registered for an key or null if there was no
198: * such key in the list.
199: *
200: * @param key the key.
201: * @return the value.
202: */
203: public Object getFirst(final Object key) {
204: return get(key, 0);
205: }
206:
207: /**
208: * Retrieves the n-th value registered for an key or null if there was no
209: * such key in the list. An index out of bounds exception is thrown if
210: * there are less than n elements registered to this key.
211: *
212: * @param key the key.
213: * @param n the index.
214: * @return the object.
215: */
216: public Object get(final Object key, final int n) {
217: final List v = (List) this .table.get(key);
218: if (v == null) {
219: return null;
220: }
221: return v.get(n);
222: }
223:
224: /**
225: * Returns an iterator over all elements registered to the given key.
226: *
227: * @param key the key.
228: * @return an iterator.
229: */
230: public Iterator getAll(final Object key) {
231: final List v = (List) this .table.get(key);
232: if (v == null) {
233: return EMPTY_ITERATOR;
234: }
235: return v.iterator();
236: }
237:
238: /**
239: * Returns all registered keys as an enumeration.
240: *
241: * @return an enumeration of the keys.
242: */
243: public Iterator keys() {
244: return this .table.keySet().iterator();
245: }
246:
247: /**
248: * Returns all registered keys as set.
249: *
250: * @return a set of keys.
251: */
252: public Set keySet() {
253: return this .table.keySet();
254: }
255:
256: /**
257: * Removes the key/value pair from the map. If the removed entry was the
258: * last entry for this key, the key gets also removed.
259: *
260: * @param key the key.
261: * @param value the value.
262: * @return true, if removing the element was successfull, false otherwise.
263: */
264: public boolean remove(final Object key, final Object value) {
265: final List v = (List) this .table.get(key);
266: if (v == null) {
267: return false;
268: }
269:
270: if (!v.remove(value)) {
271: return false;
272: }
273: if (v.size() == 0) {
274: this .table.remove(key);
275: }
276: return true;
277: }
278:
279: /**
280: * Removes all elements for the given key.
281: *
282: * @param key the key.
283: */
284: public void removeAll(final Object key) {
285: this .table.remove(key);
286: }
287:
288: /**
289: * Clears all keys and values of this map.
290: */
291: public void clear() {
292: this .table.clear();
293: }
294:
295: /**
296: * Tests whether this map contains the given key.
297: *
298: * @param key the key.
299: * @return true if the key is contained in the map
300: */
301: public boolean containsKey(final Object key) {
302: return this .table.containsKey(key);
303: }
304:
305: /**
306: * Tests whether this map contains the given value.
307: *
308: * @param value the value.
309: * @return true if the value is registered in the map for an key.
310: */
311: public boolean containsValue(final Object value) {
312: final Iterator e = this .table.values().iterator();
313: boolean found = false;
314: while (e.hasNext() && !found) {
315: final List v = (List) e.next();
316: found = v.contains(value);
317: }
318: return found;
319: }
320:
321: /**
322: * Tests whether this map contains the given value.
323: *
324: * @param value the value.
325: * @param key the key under which to find the value
326: * @return true if the value is registered in the map for an key.
327: */
328: public boolean containsValue(final Object key, final Object value) {
329: final List v = (List) this .table.get(key);
330: if (v == null) {
331: return false;
332: }
333: return v.contains(value);
334: }
335:
336: /**
337: * Tests whether this map contains the given key or value.
338: *
339: * @param value the value.
340: * @return true if the key or value is contained in the map
341: */
342: public boolean contains(final Object value) {
343: if (containsKey(value)) {
344: return true;
345: }
346: return containsValue(value);
347: }
348:
349: /**
350: * Creates a deep copy of this HashNMap.
351: *
352: * @return a clone.
353: * @throws CloneNotSupportedException this should never happen.
354: */
355: public Object clone() throws CloneNotSupportedException {
356: final HashNMap map = (HashNMap) super .clone();
357: map.table = new HashMap();
358: final Iterator iterator = keys();
359: while (iterator.hasNext()) {
360: final Object key = iterator.next();
361: final List list = (List) map.table.get(key);
362: if (list != null) {
363: map.table.put(key, ObjectUtilities.clone(list));
364: }
365: }
366: return map;
367: }
368:
369: /**
370: * Returns the contents for the given key as object array. If there were
371: * no objects registered with that key, an empty object array is returned.
372: *
373: * @param key the key.
374: * @param data the object array to receive the contents.
375: * @return the contents.
376: */
377: public Object[] toArray(final Object key, final Object[] data) {
378: if (key == null) {
379: throw new NullPointerException("Key must not be null.");
380: }
381: final List list = (List) this .table.get(key);
382: if (list != null) {
383: return list.toArray(data);
384: }
385: if (data.length > 0) {
386: data[0] = null;
387: }
388: return data;
389: }
390:
391: /**
392: * Returns the contents for the given key as object array. If there were
393: * no objects registered with that key, an empty object array is returned.
394: *
395: * @param key the key.
396: * @return the contents.
397: */
398: public Object[] toArray(final Object key) {
399: if (key == null) {
400: throw new NullPointerException("Key must not be null.");
401: }
402: final List list = (List) this .table.get(key);
403: if (list != null) {
404: return list.toArray();
405: }
406: return EMPTY_ARRAY;
407: }
408:
409: /**
410: * Returns the number of elements registered with the given key.
411: *
412: * @param key the key.
413: * @return the number of element for this key, or 0 if there are no elements
414: * registered.
415: */
416: public int getValueCount(final Object key) {
417: if (key == null) {
418: throw new NullPointerException("Key must not be null.");
419: }
420: final List list = (List) this .table.get(key);
421: if (list != null) {
422: return list.size();
423: }
424: return 0;
425: }
426: }
|