001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import com.liferay.portal.kernel.memory.EqualityWeakReference;
018    import com.liferay.portal.kernel.memory.FinalizeAction;
019    import com.liferay.portal.kernel.memory.FinalizeManager;
020    
021    import java.io.Serializable;
022    
023    import java.lang.ref.Reference;
024    
025    import java.util.AbstractCollection;
026    import java.util.AbstractSet;
027    import java.util.ArrayList;
028    import java.util.Collection;
029    import java.util.Iterator;
030    import java.util.List;
031    import java.util.Map;
032    import java.util.Set;
033    import java.util.concurrent.ConcurrentHashMap;
034    import java.util.concurrent.ConcurrentMap;
035    
036    /**
037     * @author Shuyang Zhou
038     */
039    public class WeakValueConcurrentHashMap<K, V>
040            implements ConcurrentMap<K, V>, Serializable {
041    
042            public WeakValueConcurrentHashMap() {
043                    _map = new ConcurrentHashMap<K, Reference<V>>();
044            }
045    
046            public WeakValueConcurrentHashMap(int initialCapacity) {
047                    _map = new ConcurrentHashMap<K, Reference<V>>(initialCapacity);
048            }
049    
050            public WeakValueConcurrentHashMap(
051                    int initialCapacity, float loadFactor, int concurrencyLevel) {
052                    _map = new ConcurrentHashMap<K, Reference<V>>(
053                            initialCapacity, loadFactor, concurrencyLevel);
054            }
055    
056            public WeakValueConcurrentHashMap(Map<? extends K, ? extends V> map) {
057                    _map = new ConcurrentHashMap<K, Reference<V>>();
058    
059                    putAll(map);
060            }
061    
062            public void clear() {
063                    _map.clear();
064            }
065    
066            public boolean containsKey(Object key) {
067                    return _map.containsKey(key);
068            }
069    
070            public boolean containsValue(Object value) {
071                    return _map.containsValue(new EqualityWeakReference<V>((V)value));
072            }
073    
074            public Set<Entry<K, V>> entrySet() {
075                    if (_entrySet == null) {
076                            _entrySet = new UnwrapEntrySet();
077                    }
078    
079                    return _entrySet;
080            }
081    
082            public V get(Object key) {
083                    Reference<V> valueReference = _map.get(key);
084    
085                    if (valueReference != null) {
086                            return valueReference.get();
087                    }
088    
089                    return null;
090            }
091    
092            public boolean isEmpty() {
093                    return _map.isEmpty();
094            }
095    
096            public Set<K> keySet() {
097                    return _map.keySet();
098            }
099    
100            public V put(K key, V value) {
101                    Reference<V> valueReference = wrapValue(key, value);
102    
103                    valueReference = _map.putIfAbsent(key, valueReference);
104    
105                    if (valueReference != null) {
106                            return valueReference.get();
107                    }
108    
109                    return null;
110            }
111    
112            public final void putAll(Map<? extends K, ? extends V> map) {
113                    for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
114                            K key = entry.getKey();
115                            V value = entry.getValue();
116    
117                            Reference<V> valueReference = wrapValue(key, value);
118    
119                            _map.put(key, valueReference);
120                    }
121            }
122    
123            public V putIfAbsent(K key, V value) {
124                    Reference<V> valueReference = wrapValue(key, value);
125    
126                    valueReference = _map.putIfAbsent(key, valueReference);
127    
128                    if (valueReference != null) {
129                            return valueReference.get();
130                    }
131    
132                    return null;
133            }
134    
135            public V remove(Object key) {
136                    Reference<V> valueReference = _map.remove(key);
137    
138                    if (valueReference != null) {
139                            return valueReference.get();
140                    }
141    
142                    return null;
143            }
144    
145            public boolean remove(Object key, Object value) {
146                    Reference<V> valueReference = wrapValue(key, value);
147    
148                    return _map.remove(key, valueReference);
149            }
150    
151            public V replace(K key, V value) {
152                    Reference<V> valueReference = wrapValue(key, value);
153    
154                    valueReference = _map.replace(key, valueReference);
155    
156                    if (valueReference != null) {
157                            return valueReference.get();
158                    }
159    
160                    return null;
161            }
162    
163            public boolean replace(K key, V oldValue, V newValue) {
164                    Reference<V> oldValueReference = wrapValue(key, oldValue);
165                    Reference<V> newValueReference = wrapValue(key, newValue);
166    
167                    return _map.replace(key, oldValueReference, newValueReference);
168            }
169    
170            public int size() {
171                    return _map.size();
172            }
173    
174            public Collection<V> values() {
175                    if (_values == null) {
176                            _values = new UnwrapValues();
177                    }
178    
179                    return _values;
180            }
181    
182            protected Reference<V> wrapValue(Object key, Object value) {
183                    return FinalizeManager.register(
184                            (V)value, new RemoveEntryFinalizeAction((K) key));
185            }
186    
187            private transient Set<Map.Entry<K, V>> _entrySet;
188            private final ConcurrentMap<K, Reference<V>> _map;
189            private transient Collection<V> _values;
190    
191            private class RemoveEntryFinalizeAction implements FinalizeAction {
192    
193                    public RemoveEntryFinalizeAction(K key) {
194                            _key = key;
195                    }
196    
197                    public void doFinalize() {
198                            remove(_key);
199                    }
200    
201                    private final K _key;
202    
203            }
204    
205            private class UnwrapEntry implements Map.Entry<K, V> {
206    
207                    public UnwrapEntry(Entry<K, Reference<V>> entry) {
208                            _entry = entry;
209                    }
210    
211                    public K getKey() {
212                            return _entry.getKey();
213                    }
214    
215                    public V getValue() {
216                            Reference<V> valueReference = _entry.getValue();
217    
218                            if (valueReference != null) {
219                                    return valueReference.get();
220                            }
221    
222                            return null;
223                    }
224    
225                    public V setValue(V value) {
226                            return WeakValueConcurrentHashMap.this.put(_entry.getKey(), value);
227                    }
228    
229                    private Map.Entry<K, Reference<V>> _entry;
230    
231            }
232    
233            private class UnwrapEntryIterator implements Iterator<Map.Entry<K, V>> {
234    
235                    public UnwrapEntryIterator() {
236                            _iterator = _map.entrySet().iterator();
237                    }
238    
239                    public boolean hasNext() {
240                            return _iterator.hasNext();
241                    }
242    
243                    public Entry<K, V> next() {
244                            return new UnwrapEntry(_iterator.next());
245                    }
246    
247                    public void remove() {
248                            _iterator.remove();
249                    }
250    
251                    private Iterator<Map.Entry<K, Reference<V>>> _iterator;
252    
253            }
254    
255            private class UnwrapEntrySet extends AbstractSet<Map.Entry<K, V>> {
256    
257                    @Override
258                    public void clear() {
259                            WeakValueConcurrentHashMap.this.clear();
260                    }
261    
262                    @Override
263                    public boolean contains(Object obj) {
264                            if (!(obj instanceof Map.Entry<?, ?>)) {
265                                    return false;
266                            }
267    
268                            Map.Entry<K, V> entry = (Map.Entry<K, V>)obj;
269    
270                            V value = WeakValueConcurrentHashMap.this.get(entry.getKey());
271    
272                            if ((value != null) && value.equals(entry.getValue())) {
273                                    return true;
274                            }
275                            else {
276                                    return false;
277                            }
278                    }
279    
280                    @Override
281                    public Iterator<Map.Entry<K, V>> iterator() {
282                            return new UnwrapEntryIterator();
283                    }
284    
285                    @Override
286                    public boolean remove(Object obj) {
287                            if (!(obj instanceof Map.Entry<?, ?>)) {
288                                    return false;
289                            }
290    
291                            Map.Entry<K, V> entry = (Map.Entry<K, V>)obj;
292    
293                            return WeakValueConcurrentHashMap.this.remove(
294                                    entry.getKey(), entry.getValue());
295                    }
296    
297                    @Override
298                    public int size() {
299                            return WeakValueConcurrentHashMap.this.size();
300                    }
301    
302                    @Override
303                    public Object[] toArray() {
304                            List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size());
305    
306                            Iterator<Map.Entry<K, V>> iterator = iterator();
307    
308                            while (iterator.hasNext()) {
309                                    list.add(iterator.next());
310                            }
311    
312                            return list.toArray();
313                    }
314    
315                    @Override
316                    public <T> T[] toArray(T[] array) {
317                            List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size());
318    
319                            Iterator<Map.Entry<K, V>> iterator = iterator();
320    
321                            while (iterator.hasNext()) {
322                                    list.add(iterator.next());
323                            }
324    
325                            return list.toArray(array);
326                    }
327    
328            }
329    
330            private class UnwrapValueIterator implements Iterator<V> {
331    
332                    public UnwrapValueIterator() {
333                            _iterator = _map.values().iterator();
334                    }
335    
336                    public boolean hasNext() {
337                            return _iterator.hasNext();
338                    }
339    
340                    public V next() {
341                            Reference<V> valueReference = _iterator.next();
342    
343                            if (valueReference != null) {
344                                    return valueReference.get();
345                            }
346    
347                            return null;
348                    }
349    
350                    public void remove() {
351                            _iterator.remove();
352                    }
353    
354                    private Iterator<Reference<V>> _iterator;
355    
356            }
357    
358            private class UnwrapValues extends AbstractCollection<V> {
359    
360                    @Override
361                    public void clear() {
362                            WeakValueConcurrentHashMap.this.clear();
363                    }
364    
365                    @Override
366                    public boolean contains(Object obj) {
367                            return WeakValueConcurrentHashMap.this.containsValue(obj);
368                    }
369    
370                    @Override
371                    public Iterator<V> iterator() {
372                            return new UnwrapValueIterator();
373                    }
374    
375                    @Override
376                    public int size() {
377                            return WeakValueConcurrentHashMap.this.size();
378                    }
379    
380                    @Override
381                    public Object[] toArray() {
382                            List<V> list = new ArrayList<V>();
383    
384                            Iterator<V> iterator = iterator();
385    
386                            while (iterator.hasNext()) {
387                                    list.add(iterator.next());
388                            }
389    
390                            return list.toArray();
391                    }
392    
393                    @Override
394                    public <T> T[] toArray(T[] a) {
395                            List<V> list = new ArrayList<V>();
396    
397                            Iterator<V> iterator = iterator();
398    
399                            while (iterator.hasNext()) {
400                                    list.add(iterator.next());
401                            }
402    
403                            return list.toArray(a);
404                    }
405    
406            }
407    
408    }