View Javadoc

1   /**
2    * Copyright 2008 Atlassian Pty Ltd 
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0 
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package com.atlassian.util.concurrent;
18  
19  import static com.atlassian.util.concurrent.Assertions.notNull;
20  import net.jcip.annotations.ThreadSafe;
21  
22  import java.io.Serializable;
23  import java.util.Collection;
24  import java.util.Iterator;
25  import java.util.Map;
26  import java.util.Set;
27  
28  @ThreadSafe abstract class AbstractCopyOnWriteMap<K, V, M extends Map<K, V>> implements Map<K, V>, Serializable {
29      private static final long serialVersionUID = 4508989182041753878L;
30  
31      private volatile M delegate;
32      private final transient EntrySet entrySet = new EntrySet();
33      private final transient KeySet keySet = new KeySet();
34      private final transient Values values = new Values();
35  
36      /**
37       * Create a new {@link CopyOnWriteMap} with the supplied {@link Map} to initialize the values
38       * and the {@link CopyFunction} for creating our actual delegate instances.
39       * 
40       * @param map the initial map to initialize with
41       * @param factory the copy function
42       */
43      protected <N extends Map<? extends K, ? extends V>> AbstractCopyOnWriteMap(final N map) {
44          this.delegate = notNull("delegate", copy(notNull("map", map)));
45      }
46  
47      abstract <N extends Map<? extends K, ? extends V>> M copy(N map);
48  
49      //
50      // mutable operations
51      //
52  
53      public synchronized final void clear() {
54          final M map = copy();
55          map.clear();
56          set(map);
57      }
58  
59      public synchronized final V remove(final Object key) {
60          // short circuit if key doesn't exist
61          if (!delegate.containsKey(key)) {
62              return null;
63          }
64          final M map = copy();
65          final V result = map.remove(key);
66          set(map);
67          return result;
68      }
69  
70      public synchronized final V put(final K key, final V value) {
71          final M map = copy();
72          final V result = map.put(key, value);
73          set(map);
74          return result;
75      }
76  
77      public synchronized final void putAll(final Map<? extends K, ? extends V> t) {
78          final M map = copy();
79          map.putAll(t);
80          set(map);
81      }
82  
83      protected synchronized void removeAll(final Collection<K> keys) {
84          final M map = copy();
85          for (final K k : keys) {
86              map.remove(k);
87          }
88          set(map);
89      }
90  
91      protected synchronized M copy() {
92          return copy(delegate);
93      }
94  
95      protected synchronized void set(final M map) {
96          delegate = map;
97      }
98  
99      //
100     // Collection views
101     //
102 
103     public final Set<Map.Entry<K, V>> entrySet() {
104         return entrySet;
105     }
106 
107     public final Set<K> keySet() {
108         return keySet;
109     }
110 
111     public final Collection<V> values() {
112         return values;
113     }
114 
115     //
116     // delegate operations
117     //
118 
119     public final boolean containsKey(final Object key) {
120         return delegate.containsKey(key);
121     }
122 
123     public final boolean containsValue(final Object value) {
124         return delegate.containsValue(value);
125     }
126 
127     public final V get(final Object key) {
128         return delegate.get(key);
129     }
130 
131     public final boolean isEmpty() {
132         return delegate.isEmpty();
133     }
134 
135     public final int size() {
136         return delegate.size();
137     }
138 
139     @Override public final boolean equals(final Object o) {
140         return delegate.equals(o);
141     }
142 
143     @Override public final int hashCode() {
144         return delegate.hashCode();
145     }
146 
147     protected final M getDelegate() {
148         return delegate;
149     }
150 
151     @Override public String toString() {
152         return delegate.toString();
153     }
154 
155     //
156     // inner interfaces
157     //
158 
159     /**
160      * Copy the current map. Always done under a lock so we don't get multiple threads doing this
161      * concurrently.
162      */
163     protected interface CopyFunction<M extends Map<?, ?>> {
164         /**
165          * Create a new map copied from the one supplied. Implementations should not keep a
166          * reference to this map, and must not modify the map after it has been returned. This will
167          * be called under synchronization, so it should not do any IO or blocking operations.
168          * 
169          * @param map the map to copy. Will not be null.
170          * @return a new copied map. Must not be null.
171          */
172         M copy(M map);
173     }
174 
175     //
176     // inner classes
177     //
178 
179     private class KeySet extends CollectionView<K> implements Set<K> {
180 
181         @Override Collection<K> getDelegate() {
182             return delegate.keySet();
183         }
184 
185         //
186         // mutable operations
187         //
188 
189         public void clear() {
190             synchronized (AbstractCopyOnWriteMap.this) {
191                 final M map = copy();
192                 map.keySet().clear();
193                 set(map);
194             }
195         }
196 
197         public boolean remove(final Object o) {
198             return AbstractCopyOnWriteMap.this.remove(o) != null;
199         }
200 
201         public boolean removeAll(final Collection<?> c) {
202             synchronized (AbstractCopyOnWriteMap.this) {
203                 final M map = copy();
204                 final boolean result = map.keySet().removeAll(c);
205                 set(map);
206                 return result;
207             }
208         }
209 
210         public boolean retainAll(final Collection<?> c) {
211             synchronized (AbstractCopyOnWriteMap.this) {
212                 final M map = copy();
213                 final boolean result = map.keySet().retainAll(c);
214                 set(map);
215                 return result;
216             }
217         }
218     }
219 
220     private final class Values extends CollectionView<V> implements Collection<V> {
221 
222         @Override Collection<V> getDelegate() {
223             return delegate.values();
224         }
225 
226         public void clear() {
227             synchronized (AbstractCopyOnWriteMap.this) {
228                 final M map = copy();
229                 map.values().clear();
230                 set(map);
231             }
232         }
233 
234         public boolean remove(final Object o) {
235             synchronized (AbstractCopyOnWriteMap.this) {
236                 if (!contains(o)) {
237                     return false;
238                 }
239                 final M map = copy();
240                 final boolean result = map.values().remove(o);
241                 set(map);
242                 return result;
243             }
244         }
245 
246         public boolean removeAll(final Collection<?> c) {
247             synchronized (AbstractCopyOnWriteMap.this) {
248                 final M map = copy();
249                 final boolean result = map.values().removeAll(c);
250                 set(map);
251                 return result;
252             }
253         }
254 
255         public boolean retainAll(final Collection<?> c) {
256             synchronized (AbstractCopyOnWriteMap.this) {
257                 final M map = copy();
258                 final boolean result = map.values().retainAll(c);
259                 set(map);
260                 return result;
261             }
262         }
263     }
264 
265     private class EntrySet extends CollectionView<Entry<K, V>> implements Set<Map.Entry<K, V>> {
266 
267         @Override Collection<java.util.Map.Entry<K, V>> getDelegate() {
268             return delegate.entrySet();
269         }
270 
271         public void clear() {
272             synchronized (AbstractCopyOnWriteMap.this) {
273                 final M map = copy();
274                 map.entrySet().clear();
275                 set(map);
276             }
277         }
278 
279         public boolean remove(final Object o) {
280             synchronized (AbstractCopyOnWriteMap.this) {
281                 if (!contains(o)) {
282                     return false;
283                 }
284                 final M map = copy();
285                 final boolean result = map.entrySet().remove(o);
286                 set(map);
287                 return result;
288             }
289         }
290 
291         public boolean removeAll(final Collection<?> c) {
292             synchronized (AbstractCopyOnWriteMap.this) {
293                 final M map = copy();
294                 final boolean result = map.entrySet().removeAll(c);
295                 set(map);
296                 return result;
297             }
298         }
299 
300         public boolean retainAll(final Collection<?> c) {
301             synchronized (AbstractCopyOnWriteMap.this) {
302                 final M map = copy();
303                 final boolean result = map.entrySet().retainAll(c);
304                 set(map);
305                 return result;
306             }
307         }
308     }
309 
310     private static class UnmodifiableIterator<T> implements Iterator<T> {
311         private final Iterator<T> delegate;
312 
313         public UnmodifiableIterator(final Iterator<T> delegate) {
314             this.delegate = delegate;
315         }
316 
317         public boolean hasNext() {
318             return delegate.hasNext();
319         }
320 
321         public T next() {
322             return delegate.next();
323         }
324 
325         public void remove() {
326             throw new UnsupportedOperationException();
327         }
328     }
329 
330     protected static abstract class CollectionView<E> implements Collection<E> {
331 
332         abstract Collection<E> getDelegate();
333 
334         //
335         // delegate operations
336         //
337 
338         public final boolean contains(final Object o) {
339             return getDelegate().contains(o);
340         }
341 
342         public final boolean containsAll(final Collection<?> c) {
343             return getDelegate().containsAll(c);
344         }
345 
346         public final Iterator<E> iterator() {
347             return new UnmodifiableIterator<E>(getDelegate().iterator());
348         }
349 
350         public final boolean isEmpty() {
351             return getDelegate().isEmpty();
352         }
353 
354         public final int size() {
355             return getDelegate().size();
356         }
357 
358         public final Object[] toArray() {
359             return getDelegate().toArray();
360         }
361 
362         public final <T> T[] toArray(final T[] a) {
363             return getDelegate().toArray(a);
364         }
365 
366         @Override public int hashCode() {
367             return getDelegate().hashCode();
368         }
369 
370         @Override public boolean equals(final Object obj) {
371             return getDelegate().equals(obj);
372         }
373 
374         //
375         // unsupported operations
376         //
377 
378         public final boolean add(final E o) {
379             throw new UnsupportedOperationException();
380         }
381 
382         public final boolean addAll(final Collection<? extends E> c) {
383             throw new UnsupportedOperationException();
384         }
385     }
386 }