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  
21  import java.io.Serializable;
22  import java.util.Collection;
23  import java.util.Iterator;
24  import java.util.Map;
25  import java.util.Set;
26  import java.util.concurrent.locks.Lock;
27  import java.util.concurrent.locks.ReentrantLock;
28  
29  import net.jcip.annotations.GuardedBy;
30  import net.jcip.annotations.ThreadSafe;
31  
32  /**
33   * Abstract base class for COW {@link Map} implementations that delegate to an
34   * internal map.
35   * 
36   * @param <K> The key type
37   * @param <V> The value type
38   * @param <M> the internal {@link Map} or extension for things like sorted and
39   * navigable maps.
40   */
41  @ThreadSafe
42  abstract class AbstractCopyOnWriteMap<K, V, M extends Map<K, V>> implements Map<K, V>, Serializable {
43      private static final long serialVersionUID = 4508989182041753878L;
44  
45      private volatile M delegate;
46      private final transient EntrySet entrySet = new EntrySet();
47      private final transient KeySet keySet = new KeySet();
48      private final transient Values values = new Values();
49      private final transient Lock lock = new ReentrantLock();
50  
51      /**
52       * Create a new {@link CopyOnWriteMap} with the supplied {@link Map} to
53       * initialize the values and the {@link CopyFunction} for creating our
54       * actual delegate instances.
55       * 
56       * @param map the initial map to initialize with
57       * @param factory the copy function
58       */
59      protected <N extends Map<? extends K, ? extends V>> AbstractCopyOnWriteMap(final N map) {
60          this.delegate = notNull("delegate", copy(notNull("map", map)));
61      }
62  
63      abstract <N extends Map<? extends K, ? extends V>> M copy(N map);
64  
65      //
66      // mutable operations
67      //
68  
69      public final void clear() {
70          lock.lock();
71          try {
72              final M map = copy();
73              map.clear();
74              set(map);
75          } finally {
76              lock.unlock();
77          }
78      }
79  
80      public final V remove(final Object key) {
81          // short circuit if key doesn't exist
82          if (!delegate.containsKey(key)) {
83              return null;
84          }
85          lock.lock();
86          try {
87              final M map = copy();
88              final V result = map.remove(key);
89              set(map);
90              return result;
91          } finally {
92              lock.unlock();
93          }
94      }
95  
96      public final V put(final K key, final V value) {
97          lock.lock();
98          try {
99              final M map = copy();
100             final V result = map.put(key, value);
101             set(map);
102             return result;
103         } finally {
104             lock.unlock();
105         }
106     }
107 
108     public final void putAll(final Map<? extends K, ? extends V> t) {
109         lock.lock();
110         try {
111             final M map = copy();
112             map.putAll(t);
113             set(map);
114         } finally {
115             lock.unlock();
116         }
117     }
118 
119     protected M copy() {
120         lock.lock();
121         try {
122             return copy(delegate);
123         } finally {
124             lock.unlock();
125         }
126     }
127 
128     @GuardedBy("lock")
129     protected void set(final M map) {
130         delegate = map;
131     }
132 
133     //
134     // Collection views
135     //
136 
137     public final Set<Map.Entry<K, V>> entrySet() {
138         return entrySet;
139     }
140 
141     public final Set<K> keySet() {
142         return keySet;
143     }
144 
145     public final Collection<V> values() {
146         return values;
147     }
148 
149     //
150     // delegate operations
151     //
152 
153     public final boolean containsKey(final Object key) {
154         return delegate.containsKey(key);
155     }
156 
157     public final boolean containsValue(final Object value) {
158         return delegate.containsValue(value);
159     }
160 
161     public final V get(final Object key) {
162         return delegate.get(key);
163     }
164 
165     public final boolean isEmpty() {
166         return delegate.isEmpty();
167     }
168 
169     public final int size() {
170         return delegate.size();
171     }
172 
173     @Override
174     public final boolean equals(final Object o) {
175         return delegate.equals(o);
176     }
177 
178     @Override
179     public final int hashCode() {
180         return delegate.hashCode();
181     }
182 
183     protected final M getDelegate() {
184         return delegate;
185     }
186 
187     @Override
188     public String toString() {
189         return delegate.toString();
190     }
191 
192     //
193     // inner classes
194     //
195 
196     private class KeySet extends CollectionView<K> implements Set<K> {
197 
198         @Override
199         Collection<K> getDelegate() {
200             return delegate.keySet();
201         }
202 
203         //
204         // mutable operations
205         //
206 
207         public void clear() {
208             lock.lock();
209             try {
210                 final M map = copy();
211                 map.keySet().clear();
212                 set(map);
213             } finally {
214                 lock.unlock();
215             }
216         }
217 
218         public boolean remove(final Object o) {
219             return AbstractCopyOnWriteMap.this.remove(o) != null;
220         }
221 
222         public boolean removeAll(final Collection<?> c) {
223             lock.lock();
224             try {
225                 final M map = copy();
226                 final boolean result = map.keySet().removeAll(c);
227                 set(map);
228                 return result;
229             } finally {
230                 lock.unlock();
231             }
232         }
233 
234         public boolean retainAll(final Collection<?> c) {
235             lock.lock();
236             try {
237                 final M map = copy();
238                 final boolean result = map.keySet().retainAll(c);
239                 set(map);
240                 return result;
241             } finally {
242                 lock.unlock();
243             }
244         }
245     }
246 
247     private final class Values extends CollectionView<V> implements Collection<V> {
248 
249         @Override
250         Collection<V> getDelegate() {
251             return delegate.values();
252         }
253 
254         public void clear() {
255             lock.lock();
256             try {
257                 final M map = copy();
258                 map.values().clear();
259                 set(map);
260             } finally {
261                 lock.unlock();
262             }
263         }
264 
265         public boolean remove(final Object o) {
266             lock.lock();
267             try {
268                 if (!contains(o)) {
269                     return false;
270                 }
271                 final M map = copy();
272                 final boolean result = map.values().remove(o);
273                 set(map);
274                 return result;
275             } finally {
276                 lock.unlock();
277             }
278         }
279 
280         public boolean removeAll(final Collection<?> c) {
281             lock.lock();
282             try {
283                 final M map = copy();
284                 final boolean result = map.values().removeAll(c);
285                 set(map);
286                 return result;
287             } finally {
288                 lock.unlock();
289             }
290         }
291 
292         public boolean retainAll(final Collection<?> c) {
293             lock.lock();
294             try {
295                 final M map = copy();
296                 final boolean result = map.values().retainAll(c);
297                 set(map);
298                 return result;
299             } finally {
300                 lock.unlock();
301             }
302         }
303     }
304 
305     private class EntrySet extends CollectionView<Entry<K, V>> implements Set<Map.Entry<K, V>> {
306 
307         @Override
308         Collection<java.util.Map.Entry<K, V>> getDelegate() {
309             return delegate.entrySet();
310         }
311 
312         public void clear() {
313             lock.lock();
314             try {
315                 final M map = copy();
316                 map.entrySet().clear();
317                 set(map);
318             } finally {
319                 lock.unlock();
320             }
321         }
322 
323         public boolean remove(final Object o) {
324             lock.lock();
325             try {
326                 if (!contains(o)) {
327                     return false;
328                 }
329                 final M map = copy();
330                 final boolean result = map.entrySet().remove(o);
331                 set(map);
332                 return result;
333             } finally {
334                 lock.unlock();
335             }
336         }
337 
338         public boolean removeAll(final Collection<?> c) {
339             lock.lock();
340             try {
341                 final M map = copy();
342                 final boolean result = map.entrySet().removeAll(c);
343                 set(map);
344                 return result;
345             } finally {
346                 lock.unlock();
347             }
348         }
349 
350         public boolean retainAll(final Collection<?> c) {
351             lock.lock();
352             try {
353                 final M map = copy();
354                 final boolean result = map.entrySet().retainAll(c);
355                 set(map);
356                 return result;
357             } finally {
358                 lock.unlock();
359             }
360         }
361     }
362 
363     private static class UnmodifiableIterator<T> implements Iterator<T> {
364         private final Iterator<T> delegate;
365 
366         public UnmodifiableIterator(final Iterator<T> delegate) {
367             this.delegate = delegate;
368         }
369 
370         public boolean hasNext() {
371             return delegate.hasNext();
372         }
373 
374         public T next() {
375             return delegate.next();
376         }
377 
378         public void remove() {
379             throw new UnsupportedOperationException();
380         }
381     }
382 
383     protected static abstract class CollectionView<E> implements Collection<E> {
384 
385         abstract Collection<E> getDelegate();
386 
387         //
388         // delegate operations
389         //
390 
391         public final boolean contains(final Object o) {
392             return getDelegate().contains(o);
393         }
394 
395         public final boolean containsAll(final Collection<?> c) {
396             return getDelegate().containsAll(c);
397         }
398 
399         public final Iterator<E> iterator() {
400             return new UnmodifiableIterator<E>(getDelegate().iterator());
401         }
402 
403         public final boolean isEmpty() {
404             return getDelegate().isEmpty();
405         }
406 
407         public final int size() {
408             return getDelegate().size();
409         }
410 
411         public final Object[] toArray() {
412             return getDelegate().toArray();
413         }
414 
415         public final <T> T[] toArray(final T[] a) {
416             return getDelegate().toArray(a);
417         }
418 
419         @Override
420         public int hashCode() {
421             return getDelegate().hashCode();
422         }
423 
424         @Override
425         public boolean equals(final Object obj) {
426             return getDelegate().equals(obj);
427         }
428 
429         @Override
430         public String toString() {
431             return getDelegate().toString();
432         }
433 
434         //
435         // unsupported operations
436         //
437 
438         public final boolean add(final E o) {
439             throw new UnsupportedOperationException();
440         }
441 
442         public final boolean addAll(final Collection<? extends E> c) {
443             throw new UnsupportedOperationException();
444         }
445     }
446 }