001    /**
002     * Copyright (c) 2000-2013 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    
053                    _map = new ConcurrentHashMap<K, Reference<V>>(
054                            initialCapacity, loadFactor, concurrencyLevel);
055            }
056    
057            public WeakValueConcurrentHashMap(Map<? extends K, ? extends V> map) {
058                    _map = new ConcurrentHashMap<K, Reference<V>>();
059    
060                    putAll(map);
061            }
062    
063            @Override
064            public void clear() {
065                    _map.clear();
066            }
067    
068            @Override
069            public boolean containsKey(Object key) {
070                    return _map.containsKey(key);
071            }
072    
073            @Override
074            public boolean containsValue(Object value) {
075                    return _map.containsValue(new EqualityWeakReference<V>((V)value));
076            }
077    
078            @Override
079            public Set<Entry<K, V>> entrySet() {
080                    if (_entrySet == null) {
081                            _entrySet = new UnwrapEntrySet();
082                    }
083    
084                    return _entrySet;
085            }
086    
087            @Override
088            public V get(Object key) {
089                    Reference<V> valueReference = _map.get(key);
090    
091                    if (valueReference != null) {
092                            return valueReference.get();
093                    }
094    
095                    return null;
096            }
097    
098            @Override
099            public boolean isEmpty() {
100                    return _map.isEmpty();
101            }
102    
103            @Override
104            public Set<K> keySet() {
105                    return _map.keySet();
106            }
107    
108            @Override
109            public V put(K key, V value) {
110                    Reference<V> valueReference = wrapValue(key, value);
111    
112                    valueReference = _map.putIfAbsent(key, valueReference);
113    
114                    if (valueReference != null) {
115                            return valueReference.get();
116                    }
117    
118                    return null;
119            }
120    
121            @Override
122            public final void putAll(Map<? extends K, ? extends V> map) {
123                    for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
124                            K key = entry.getKey();
125                            V value = entry.getValue();
126    
127                            Reference<V> valueReference = wrapValue(key, value);
128    
129                            _map.put(key, valueReference);
130                    }
131            }
132    
133            @Override
134            public V putIfAbsent(K key, V value) {
135                    Reference<V> valueReference = wrapValue(key, value);
136    
137                    valueReference = _map.putIfAbsent(key, valueReference);
138    
139                    if (valueReference != null) {
140                            return valueReference.get();
141                    }
142    
143                    return null;
144            }
145    
146            @Override
147            public V remove(Object key) {
148                    Reference<V> valueReference = _map.remove(key);
149    
150                    if (valueReference != null) {
151                            return valueReference.get();
152                    }
153    
154                    return null;
155            }
156    
157            @Override
158            public boolean remove(Object key, Object value) {
159                    Reference<V> valueReference = wrapValue(key, value);
160    
161                    return _map.remove(key, valueReference);
162            }
163    
164            @Override
165            public V replace(K key, V value) {
166                    Reference<V> valueReference = wrapValue(key, value);
167    
168                    valueReference = _map.replace(key, valueReference);
169    
170                    if (valueReference != null) {
171                            return valueReference.get();
172                    }
173    
174                    return null;
175            }
176    
177            @Override
178            public boolean replace(K key, V oldValue, V newValue) {
179                    Reference<V> oldValueReference = wrapValue(key, oldValue);
180                    Reference<V> newValueReference = wrapValue(key, newValue);
181    
182                    return _map.replace(key, oldValueReference, newValueReference);
183            }
184    
185            @Override
186            public int size() {
187                    return _map.size();
188            }
189    
190            @Override
191            public Collection<V> values() {
192                    if (_values == null) {
193                            _values = new UnwrapValues();
194                    }
195    
196                    return _values;
197            }
198    
199            protected Reference<V> wrapValue(Object key, Object value) {
200                    return FinalizeManager.register(
201                            (V)value, new RemoveEntryFinalizeAction((K)key));
202            }
203    
204            private transient Set<Map.Entry<K, V>> _entrySet;
205            private final ConcurrentMap<K, Reference<V>> _map;
206            private transient Collection<V> _values;
207    
208            private class RemoveEntryFinalizeAction implements FinalizeAction {
209    
210                    public RemoveEntryFinalizeAction(K key) {
211                            _key = key;
212                    }
213    
214                    @Override
215                    public void doFinalize() {
216                            remove(_key);
217                    }
218    
219                    private final K _key;
220    
221            }
222    
223            private class UnwrapEntry implements Map.Entry<K, V> {
224    
225                    public UnwrapEntry(Entry<K, Reference<V>> entry) {
226                            _entry = entry;
227                    }
228    
229                    @Override
230                    public K getKey() {
231                            return _entry.getKey();
232                    }
233    
234                    @Override
235                    public V getValue() {
236                            Reference<V> valueReference = _entry.getValue();
237    
238                            if (valueReference != null) {
239                                    return valueReference.get();
240                            }
241    
242                            return null;
243                    }
244    
245                    @Override
246                    public V setValue(V value) {
247                            return WeakValueConcurrentHashMap.this.put(_entry.getKey(), value);
248                    }
249    
250                    private Map.Entry<K, Reference<V>> _entry;
251    
252            }
253    
254            private class UnwrapEntryIterator implements Iterator<Map.Entry<K, V>> {
255    
256                    public UnwrapEntryIterator() {
257                            _iterator = _map.entrySet().iterator();
258                    }
259    
260                    @Override
261                    public boolean hasNext() {
262                            return _iterator.hasNext();
263                    }
264    
265                    @Override
266                    public Entry<K, V> next() {
267                            return new UnwrapEntry(_iterator.next());
268                    }
269    
270                    @Override
271                    public void remove() {
272                            _iterator.remove();
273                    }
274    
275                    private Iterator<Map.Entry<K, Reference<V>>> _iterator;
276    
277            }
278    
279            private class UnwrapEntrySet extends AbstractSet<Map.Entry<K, V>> {
280    
281                    @Override
282                    public void clear() {
283                            WeakValueConcurrentHashMap.this.clear();
284                    }
285    
286                    @Override
287                    public boolean contains(Object obj) {
288                            if (!(obj instanceof Map.Entry<?, ?>)) {
289                                    return false;
290                            }
291    
292                            Map.Entry<K, V> entry = (Map.Entry<K, V>)obj;
293    
294                            V value = WeakValueConcurrentHashMap.this.get(entry.getKey());
295    
296                            if ((value != null) && value.equals(entry.getValue())) {
297                                    return true;
298                            }
299                            else {
300                                    return false;
301                            }
302                    }
303    
304                    @Override
305                    public Iterator<Map.Entry<K, V>> iterator() {
306                            return new UnwrapEntryIterator();
307                    }
308    
309                    @Override
310                    public boolean remove(Object obj) {
311                            if (!(obj instanceof Map.Entry<?, ?>)) {
312                                    return false;
313                            }
314    
315                            Map.Entry<K, V> entry = (Map.Entry<K, V>)obj;
316    
317                            return WeakValueConcurrentHashMap.this.remove(
318                                    entry.getKey(), entry.getValue());
319                    }
320    
321                    @Override
322                    public int size() {
323                            return WeakValueConcurrentHashMap.this.size();
324                    }
325    
326                    @Override
327                    public Object[] toArray() {
328                            List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size());
329    
330                            Iterator<Map.Entry<K, V>> iterator = iterator();
331    
332                            while (iterator.hasNext()) {
333                                    list.add(iterator.next());
334                            }
335    
336                            return list.toArray();
337                    }
338    
339                    @Override
340                    public <T> T[] toArray(T[] array) {
341                            List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size());
342    
343                            Iterator<Map.Entry<K, V>> iterator = iterator();
344    
345                            while (iterator.hasNext()) {
346                                    list.add(iterator.next());
347                            }
348    
349                            return list.toArray(array);
350                    }
351    
352            }
353    
354            private class UnwrapValueIterator implements Iterator<V> {
355    
356                    public UnwrapValueIterator() {
357                            _iterator = _map.values().iterator();
358                    }
359    
360                    @Override
361                    public boolean hasNext() {
362                            return _iterator.hasNext();
363                    }
364    
365                    @Override
366                    public V next() {
367                            Reference<V> valueReference = _iterator.next();
368    
369                            if (valueReference != null) {
370                                    return valueReference.get();
371                            }
372    
373                            return null;
374                    }
375    
376                    @Override
377                    public void remove() {
378                            _iterator.remove();
379                    }
380    
381                    private Iterator<Reference<V>> _iterator;
382    
383            }
384    
385            private class UnwrapValues extends AbstractCollection<V> {
386    
387                    @Override
388                    public void clear() {
389                            WeakValueConcurrentHashMap.this.clear();
390                    }
391    
392                    @Override
393                    public boolean contains(Object obj) {
394                            return WeakValueConcurrentHashMap.this.containsValue(obj);
395                    }
396    
397                    @Override
398                    public Iterator<V> iterator() {
399                            return new UnwrapValueIterator();
400                    }
401    
402                    @Override
403                    public int size() {
404                            return WeakValueConcurrentHashMap.this.size();
405                    }
406    
407                    @Override
408                    public Object[] toArray() {
409                            List<V> list = new ArrayList<V>();
410    
411                            Iterator<V> iterator = iterator();
412    
413                            while (iterator.hasNext()) {
414                                    list.add(iterator.next());
415                            }
416    
417                            return list.toArray();
418                    }
419    
420                    @Override
421                    public <T> T[] toArray(T[] a) {
422                            List<V> list = new ArrayList<V>();
423    
424                            Iterator<V> iterator = iterator();
425    
426                            while (iterator.hasNext()) {
427                                    list.add(iterator.next());
428                            }
429    
430                            return list.toArray(a);
431                    }
432    
433            }
434    
435    }