1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 package nl.openedge.baritus.util;
59
60 import java.io.IOException;
61 import java.io.ObjectInputStream;
62 import java.util.AbstractCollection;
63 import java.util.ArrayList;
64 import java.util.Collection;
65 import java.util.HashMap;
66 import java.util.Iterator;
67 import java.util.Map;
68 import java.util.NoSuchElementException;
69 import java.util.Set;
70
71 /***
72 * A <code>MultiMap</code> is a Map with slightly different semantics.
73 * Putting a value into the map will add the value to a Collection at that
74 * key. Getting a value will always return a Collection, holding all the
75 * values put to that key. This implementation uses an ArrayList as the
76 * collection.
77 * <p>
78 * For example:
79 * <pre>
80 * MultiMap mhm = new MultiHashMap();
81 * mhm.put(key, "A");
82 * mhm.put(key, "B");
83 * mhm.put(key, "C");
84 * Collection coll = mhm.get(key);</pre>
85 * <p>
86 * <code>coll</code> will be a list containing "A", "B", "C".
87 *
88 * NOTE: copied this from Commons Collections, as this is the only class
89 * used from the Collections package, and it's for internal use.
90 *
91 * Commons Collections 2.0
92 * Revision: 1.11 $ $Date: 2003/05/16 14:40:56
93 *
94 * @author Christopher Berry
95 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
96 * @author Steve Downey
97 * @author Stephen Colebourne
98 * @author <a href="mailto:jburet@yahoo.com">Julien Buret</a>
99 * @author Serhiy Yevtushenko
100 */
101 public class MultiHashMap extends HashMap implements Map
102 {
103
104
105 private transient Collection values = null;
106
107 /***
108 * Constructor.
109 */
110 public MultiHashMap()
111 {
112 super();
113 }
114
115 /***
116 * Constructor.
117 *
118 * @param initialCapacity the initial map capacity
119 */
120 public MultiHashMap(int initialCapacity)
121 {
122 super(initialCapacity);
123 }
124
125 /***
126 * Constructor.
127 *
128 * @param initialCapacity the initial map capacity
129 * @param loadFactor the amount 0.0-1.0 at which to resize the map
130 */
131 public MultiHashMap(int initialCapacity, float loadFactor)
132 {
133 super(initialCapacity, loadFactor);
134 }
135
136 /***
137 * Constructor.
138 *
139 * @param mapToCopy a Map to copy
140 */
141 public MultiHashMap(Map mapToCopy)
142 {
143 super(mapToCopy);
144 }
145
146 /***
147 * Read the object during deserialization.
148 */
149 private void readObject(ObjectInputStream s)
150 throws IOException, ClassNotFoundException
151 {
152
153
154
155
156 s.defaultReadObject();
157
158
159 String version = "1.2";
160 try
161 {
162 version = System.getProperty("java.version");
163 }
164 catch (SecurityException ex)
165 {
166
167 }
168
169 if (version.startsWith("1.2") || version.startsWith("1.3"))
170 {
171 for (Iterator iterator = entrySet().iterator();
172 iterator.hasNext();
173 )
174 {
175 Map.Entry entry = (Map.Entry)iterator.next();
176
177 super.put(
178 entry.getKey(),
179 ((Collection)entry.getValue()).iterator().next());
180 }
181 }
182 }
183
184 /***
185 * Put a key and value into the map.
186 * <p>
187 * The value is added to a collection mapped to the key instead of
188 * replacing the previous value.
189 *
190 * @param key the key to set
191 * @param value the value to set the key to
192 * @return the value added if the add is successful, <code>null</code> otherwise
193 */
194 public Object put(Object key, Object value)
195 {
196
197
198 Collection coll = (Collection)super.get(key);
199 if (coll == null)
200 {
201 coll = createCollection(null);
202 super.put(key, coll);
203 }
204 boolean results = coll.add(value);
205
206 return (results ? value : null);
207 }
208
209 /***
210 * Does the map contain a specific value.
211 * <p>
212 * This searches the collection mapped to each key, and thus could be slow.
213 *
214 * @param value the value to search for
215 * @return true if the list contains the value
216 */
217 public boolean containsValue(Object value)
218 {
219 Set pairs = super.entrySet();
220
221 if (pairs == null)
222 {
223 return false;
224 }
225 Iterator pairsIterator = pairs.iterator();
226 while (pairsIterator.hasNext())
227 {
228 Map.Entry keyValuePair = (Map.Entry)pairsIterator.next();
229 Collection coll = (Collection)keyValuePair.getValue();
230 if (coll.contains(value))
231 {
232 return true;
233 }
234 }
235 return false;
236 }
237
238 /***
239 * Removes a specific value from map.
240 * <p>
241 * The item is removed from the collection mapped to the specified key.
242 *
243 * @param key the key to remove from
244 * @param value the value to remove
245 * @return the value removed (which was passed in)
246 */
247 public Object remove(Object key, Object value)
248 {
249 Collection valuesForKey = (Collection)super.get(key);
250 if (valuesForKey == null)
251 {
252 return null;
253 }
254 valuesForKey.remove(value);
255
256
257
258 if (valuesForKey.isEmpty())
259 {
260 remove(key);
261 }
262 return value;
263 }
264
265 /***
266 * Clear the map.
267 * <p>
268 * This clears each collection in the map, and so may be slow.
269 */
270 public void clear()
271 {
272
273 Set pairs = super.entrySet();
274 Iterator pairsIterator = pairs.iterator();
275 while (pairsIterator.hasNext())
276 {
277 Map.Entry keyValuePair = (Map.Entry)pairsIterator.next();
278 Collection coll = (Collection)keyValuePair.getValue();
279 coll.clear();
280 }
281 super.clear();
282 }
283
284 /***
285 * Gets a view over all the values in the map.
286 * <p>
287 * The values view includes all the entries in the collections at each map key.
288 *
289 * @return the collection view of all the values in the map
290 */
291 public Collection values()
292 {
293 Collection vs = values;
294 return (vs != null ? vs : (values = new Values()));
295 }
296
297 /***
298 * Inner class to view the elements.
299 */
300 private class Values extends AbstractCollection
301 {
302
303 public Iterator iterator()
304 {
305 return new ValueIterator();
306 }
307
308 public int size()
309 {
310 int compt = 0;
311 Iterator it = iterator();
312 while (it.hasNext())
313 {
314 it.next();
315 compt++;
316 }
317 return compt;
318 }
319
320 public void clear()
321 {
322 MultiHashMap.this.clear();
323 }
324
325 }
326
327 /***
328 * Inner iterator to view the elements.
329 */
330 private class ValueIterator implements Iterator
331 {
332 private Iterator backedIterator;
333 private Iterator tempIterator;
334
335 private ValueIterator()
336 {
337 backedIterator = MultiHashMap.super.values().iterator();
338 }
339
340 private boolean searchNextIterator()
341 {
342 while (tempIterator == null || tempIterator.hasNext() == false)
343 {
344 if (backedIterator.hasNext() == false)
345 {
346 return false;
347 }
348 tempIterator = ((Collection)backedIterator.next()).iterator();
349 }
350 return true;
351 }
352
353 public boolean hasNext()
354 {
355 return searchNextIterator();
356 }
357
358 public Object next()
359 {
360 if (searchNextIterator() == false)
361 {
362 throw new NoSuchElementException();
363 }
364 return tempIterator.next();
365 }
366
367 public void remove()
368 {
369 if (tempIterator == null)
370 {
371 throw new IllegalStateException();
372 }
373 tempIterator.remove();
374 }
375
376 }
377
378 /***
379 * Clone the map.
380 * <p>
381 * The clone will shallow clone the collections as well as the map.
382 *
383 * @return the cloned map
384 */
385 public Object clone()
386 {
387 MultiHashMap obj = (MultiHashMap)super.clone();
388
389
390 for (Iterator it = entrySet().iterator(); it.hasNext();)
391 {
392 Map.Entry entry = (Map.Entry)it.next();
393 Collection coll = (Collection)entry.getValue();
394 Collection newColl = createCollection(coll);
395 entry.setValue(newColl);
396 }
397 return obj;
398 }
399
400 /***
401 * Creates a new instance of the map value Collection container.
402 * <p>
403 * This method can be overridden to use your own collection type.
404 *
405 * @param coll the collection to copy, may be null
406 * @return the new collection
407 */
408 protected Collection createCollection(Collection coll)
409 {
410 if (coll == null)
411 {
412 return new ArrayList();
413 }
414 else
415 {
416 return new ArrayList(coll);
417 }
418 }
419
420 }