View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  
16  package org.fourthline.cling.support.shared;
17  
18  import java.io.Serializable;
19  import java.util.*;
20  
21  /**
22   * A base class for {@code Map} implementations.
23   *
24   * <p>Subclasses that permit new mappings to be added must override {@link
25   * #put}.
26   *
27   * <p>The default implementations of many methods are inefficient for large
28   * maps. For example in the default implementation, each call to {@link #get}
29   * performs a linear iteration of the entry set. Subclasses should override such
30   * methods to improve their performance.
31   *
32   * @since 1.2
33   */
34  public abstract class AbstractMap<K, V> implements Map<K, V> {
35  
36      // Lazily initialized key set.
37      Set<K> keySet;
38  
39      Collection<V> valuesCollection;
40  
41      /**
42       * An immutable key-value mapping. Despite the name, this class is non-final
43       * and its subclasses may be mutable.
44       *
45       * @since 1.6
46       */
47      public static class SimpleImmutableEntry<K, V>
48              implements Map.Entry<K, V>, Serializable {
49          private static final long serialVersionUID = 7138329143949025153L;
50  
51          private final K key;
52          private final V value;
53  
54          public SimpleImmutableEntry(K theKey, V theValue) {
55              key = theKey;
56              value = theValue;
57          }
58  
59          /**
60           * Constructs an instance with the key and value of {@code copyFrom}.
61           */
62          public SimpleImmutableEntry(Map.Entry<? extends K, ? extends V> copyFrom) {
63              key = copyFrom.getKey();
64              value = copyFrom.getValue();
65          }
66  
67          public K getKey() {
68              return key;
69          }
70  
71          public V getValue() {
72              return value;
73          }
74  
75          /**
76           * This base implementation throws {@code UnsupportedOperationException}
77           * always.
78           */
79          public V setValue(V object) {
80              throw new UnsupportedOperationException();
81          }
82  
83          @Override public boolean equals(Object object) {
84              if (this == object) {
85                  return true;
86              }
87              if (object instanceof Map.Entry) {
88                  Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
89                  return (key == null ? entry.getKey() == null : key.equals(entry
90                          .getKey()))
91                          && (value == null ? entry.getValue() == null : value
92                                  .equals(entry.getValue()));
93              }
94              return false;
95          }
96  
97          @Override public int hashCode() {
98              return (key == null ? 0 : key.hashCode())
99                      ^ (value == null ? 0 : value.hashCode());
100         }
101 
102         @Override public String toString() {
103             return key + "=" + value;
104         }
105     }
106 
107     /**
108      * A key-value mapping with mutable values.
109      *
110      * @since 1.6
111      */
112     public static class SimpleEntry<K, V>
113             implements Map.Entry<K, V>, Serializable {
114         private static final long serialVersionUID = -8499721149061103585L;
115 
116         private final K key;
117         private V value;
118 
119         public SimpleEntry(K theKey, V theValue) {
120             key = theKey;
121             value = theValue;
122         }
123 
124         /**
125          * Constructs an instance with the key and value of {@code copyFrom}.
126          */
127         public SimpleEntry(Map.Entry<? extends K, ? extends V> copyFrom) {
128             key = copyFrom.getKey();
129             value = copyFrom.getValue();
130         }
131 
132         public K getKey() {
133             return key;
134         }
135 
136         public V getValue() {
137             return value;
138         }
139 
140         public V setValue(V object) {
141             V result = value;
142             value = object;
143             return result;
144         }
145 
146         @Override public boolean equals(Object object) {
147             if (this == object) {
148                 return true;
149             }
150             if (object instanceof Map.Entry) {
151                 Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
152                 return (key == null ? entry.getKey() == null : key.equals(entry
153                         .getKey()))
154                         && (value == null ? entry.getValue() == null : value
155                                 .equals(entry.getValue()));
156             }
157             return false;
158         }
159 
160         @Override public int hashCode() {
161             return (key == null ? 0 : key.hashCode())
162                     ^ (value == null ? 0 : value.hashCode());
163         }
164 
165         @Override public String toString() {
166             return key + "=" + value;
167         }
168     }
169 
170     protected AbstractMap() {
171         super();
172     }
173 
174     /**
175      * {@inheritDoc}
176      *
177      * <p>This implementation calls {@code entrySet().clear()}.
178      */
179     public void clear() {
180         entrySet().clear();
181     }
182 
183     /**
184      * {@inheritDoc}
185      *
186      * <p>This implementation iterates its key set, looking for a key that
187      * {@code key} equals.
188      */
189     public boolean containsKey(Object key) {
190         Iterator<Map.Entry<K, V>> it = entrySet().iterator();
191         if (key != null) {
192             while (it.hasNext()) {
193                 if (key.equals(it.next().getKey())) {
194                     return true;
195                 }
196             }
197         } else {
198             while (it.hasNext()) {
199                 if (it.next().getKey() == null) {
200                     return true;
201                 }
202             }
203         }
204         return false;
205     }
206 
207     /**
208      * {@inheritDoc}
209      *
210      * <p>This implementation iterates its entry set, looking for an entry with
211      * a value that {@code value} equals.
212      */
213     public boolean containsValue(Object value) {
214         Iterator<Map.Entry<K, V>> it = entrySet().iterator();
215         if (value != null) {
216             while (it.hasNext()) {
217                 if (value.equals(it.next().getValue())) {
218                     return true;
219                 }
220             }
221         } else {
222             while (it.hasNext()) {
223                 if (it.next().getValue() == null) {
224                     return true;
225                 }
226             }
227         }
228         return false;
229     }
230 
231     public abstract Set<Map.Entry<K, V>> entrySet();
232 
233     /**
234      * {@inheritDoc}
235      *
236      * <p>This implementation first checks the structure of {@code object}. If
237      * it is not a map or of a different size, this returns false. Otherwise it
238      * iterates its own entry set, looking up each entry's key in {@code
239      * object}. If any value does not equal the other map's value for the same
240      * key, this returns false. Otherwise it returns true.
241      */
242     @Override public boolean equals(Object object) {
243         if (this == object) {
244             return true;
245         }
246         if (object instanceof Map) {
247             Map<?, ?> map = (Map<?, ?>) object;
248             if (size() != map.size()) {
249                 return false;
250             }
251 
252             try {
253                 for (Entry<K, V> entry : entrySet()) {
254                     K key = entry.getKey();
255                     V mine = entry.getValue();
256                     Object theirs = map.get(key);
257                     if (mine == null) {
258                         if (theirs != null || !map.containsKey(key)) {
259                             return false;
260                         }
261                     } else if (!mine.equals(theirs)) {
262                         return false;
263                     }
264                 }
265             } catch (NullPointerException ignored) {
266                 return false;
267             } catch (ClassCastException ignored) {
268                 return false;
269             }
270             return true;
271         }
272         return false;
273     }
274 
275     /**
276      * {@inheritDoc}
277      *
278      * <p>This implementation iterates its entry set, looking for an entry with
279      * a key that {@code key} equals.
280      */
281     public V get(Object key) {
282         Iterator<Map.Entry<K, V>> it = entrySet().iterator();
283         if (key != null) {
284             while (it.hasNext()) {
285                 Map.Entry<K, V> entry = it.next();
286                 if (key.equals(entry.getKey())) {
287                     return entry.getValue();
288                 }
289             }
290         } else {
291             while (it.hasNext()) {
292                 Map.Entry<K, V> entry = it.next();
293                 if (entry.getKey() == null) {
294                     return entry.getValue();
295                 }
296             }
297         }
298         return null;
299     }
300 
301     /**
302      * {@inheritDoc}
303      *
304      * <p>This implementation iterates its entry set, summing the hashcodes of
305      * its entries.
306      */
307     @Override public int hashCode() {
308         int result = 0;
309         Iterator<Map.Entry<K, V>> it = entrySet().iterator();
310         while (it.hasNext()) {
311             result += it.next().hashCode();
312         }
313         return result;
314     }
315 
316     /**
317      * {@inheritDoc}
318      *
319      * <p>This implementation compares {@code size()} to 0.
320      */
321     public boolean isEmpty() {
322         return size() == 0;
323     }
324 
325     /**
326      * {@inheritDoc}
327      *
328      * <p>This implementation returns a view that calls through this to map. Its
329      * iterator transforms this map's entry set iterator to return keys.
330      */
331     public Set<K> keySet() {
332         if (keySet == null) {
333             keySet = new AbstractSet<K>() {
334                 @Override public boolean contains(Object object) {
335                     return containsKey(object);
336                 }
337 
338                 @Override public int size() {
339                     return AbstractMap.this.size();
340                 }
341 
342                 @Override public Iterator<K> iterator() {
343                     return new Iterator<K>() {
344                         Iterator<Map.Entry<K, V>> setIterator = entrySet().iterator();
345 
346                         public boolean hasNext() {
347                             return setIterator.hasNext();
348                         }
349 
350                         public K next() {
351                             return setIterator.next().getKey();
352                         }
353 
354                         public void remove() {
355                             setIterator.remove();
356                         }
357                     };
358                 }
359             };
360         }
361         return keySet;
362     }
363 
364     /**
365      * {@inheritDoc}
366      *
367      * <p>This base implementation throws {@code UnsupportedOperationException}.
368      */
369     public V put(K key, V value) {
370         throw new UnsupportedOperationException();
371     }
372 
373     /**
374      * {@inheritDoc}
375      *
376      * <p>This implementation iterates through {@code map}'s entry set, calling
377      * {@code put()} for each.
378      */
379     public void putAll(Map<? extends K, ? extends V> map) {
380         for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
381             put(entry.getKey(), entry.getValue());
382         }
383     }
384 
385     /**
386      * {@inheritDoc}
387      *
388      * <p>This implementation iterates its entry set, removing the entry with
389      * a key that {@code key} equals.
390      */
391     public V remove(Object key) {
392         Iterator<Map.Entry<K, V>> it = entrySet().iterator();
393         if (key != null) {
394             while (it.hasNext()) {
395                 Map.Entry<K, V> entry = it.next();
396                 if (key.equals(entry.getKey())) {
397                     it.remove();
398                     return entry.getValue();
399                 }
400             }
401         } else {
402             while (it.hasNext()) {
403                 Map.Entry<K, V> entry = it.next();
404                 if (entry.getKey() == null) {
405                     it.remove();
406                     return entry.getValue();
407                 }
408             }
409         }
410         return null;
411     }
412 
413     /**
414      * {@inheritDoc}
415      *
416      * <p>This implementation returns its entry set's size.
417      */
418     public int size() {
419         return entrySet().size();
420     }
421 
422     /**
423      * {@inheritDoc}
424      *
425      * <p>This implementation composes a string by iterating its entry set. If
426      * this map contains itself as a key or a value, the string "(this Map)"
427      * will appear in its place.
428      */
429     @Override public String toString() {
430         if (isEmpty()) {
431             return "{}";
432         }
433 
434         StringBuilder buffer = new StringBuilder(size() * 28);
435         buffer.append('{');
436         Iterator<Map.Entry<K, V>> it = entrySet().iterator();
437         while (it.hasNext()) {
438             Map.Entry<K, V> entry = it.next();
439             Object key = entry.getKey();
440             if (key != this) {
441                 buffer.append(key);
442             } else {
443                 buffer.append("(this Map)");
444             }
445             buffer.append('=');
446             Object value = entry.getValue();
447             if (value != this) {
448                 buffer.append(value);
449             } else {
450                 buffer.append("(this Map)");
451             }
452             if (it.hasNext()) {
453                 buffer.append(", ");
454             }
455         }
456         buffer.append('}');
457         return buffer.toString();
458     }
459 
460     /**
461      * {@inheritDoc}
462      *
463      * <p>This implementation returns a view that calls through this to map. Its
464      * iterator transforms this map's entry set iterator to return values.
465      */
466     public Collection<V> values() {
467         if (valuesCollection == null) {
468             valuesCollection = new AbstractCollection<V>() {
469                 @Override public int size() {
470                     return AbstractMap.this.size();
471                 }
472 
473                 @Override public boolean contains(Object object) {
474                     return containsValue(object);
475                 }
476 
477                 @Override public Iterator<V> iterator() {
478                     return new Iterator<V>() {
479                         Iterator<Map.Entry<K, V>> setIterator = entrySet().iterator();
480 
481                         public boolean hasNext() {
482                             return setIterator.hasNext();
483                         }
484 
485                         public V next() {
486                             return setIterator.next().getValue();
487                         }
488 
489                         public void remove() {
490                             setIterator.remove();
491                         }
492                     };
493                 }
494             };
495         }
496         return valuesCollection;
497     }
498 
499     @SuppressWarnings("unchecked")
500     @Override protected Object clone() throws CloneNotSupportedException {
501         AbstractMap<K, V> result = (AbstractMap<K, V>) super.clone();
502         result.keySet = null;
503         result.valuesCollection = null;
504         return result;
505     }
506 }