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