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                    return _values;
179            }
180    
181            protected Reference<V> wrapValue(Object key, Object value) {
182                    return FinalizeManager.register(
183                            (V)value, new RemoveEntryFinalizeAction((K) key));
184            }
185    
186            private transient Set<Map.Entry<K, V>> _entrySet;
187            private final ConcurrentMap<K, Reference<V>> _map;
188            private transient Collection<V> _values;
189    
190            private class RemoveEntryFinalizeAction implements FinalizeAction {
191    
192                    public RemoveEntryFinalizeAction(K key) {
193                            _key = key;
194                    }
195    
196                    public void doFinalize() {
197                            remove(_key);
198                    }
199    
200                    private final K _key;
201    
202            }
203    
204            private class UnwrapEntry implements Map.Entry<K, V> {
205    
206                    public UnwrapEntry(Entry<K, Reference<V>> entry) {
207                            _entry = entry;
208                    }
209    
210                    public K getKey() {
211                            return _entry.getKey();
212                    }
213    
214                    public V getValue() {
215                            Reference<V> valueReference = _entry.getValue();
216    
217                            if (valueReference != null) {
218                                    return valueReference.get();
219                            }
220    
221                            return null;
222                    }
223    
224                    public V setValue(V value) {
225                            return WeakValueConcurrentHashMap.this.put(_entry.getKey(), value);
226                    }
227    
228                    private Map.Entry<K, Reference<V>> _entry;
229    
230            }
231    
232            private class UnwrapEntryIterator implements Iterator<Map.Entry<K, V>> {
233    
234                    public UnwrapEntryIterator() {
235                            _iterator = _map.entrySet().iterator();
236                    }
237    
238                    public boolean hasNext() {
239                            return _iterator.hasNext();
240                    }
241    
242                    public Entry<K, V> next() {
243                            return new UnwrapEntry(_iterator.next());
244                    }
245    
246                    public void remove() {
247                            _iterator.remove();
248                    }
249    
250                    private Iterator<Map.Entry<K, Reference<V>>> _iterator;
251    
252            }
253    
254            private class UnwrapEntrySet extends AbstractSet<Map.Entry<K, V>> {
255    
256                    @Override
257                    public void clear() {
258                            WeakValueConcurrentHashMap.this.clear();
259                    }
260    
261                    @Override
262                    public boolean contains(Object obj) {
263                            if (!(obj instanceof Map.Entry<?, ?>)) {
264                                    return false;
265                            }
266    
267                            Map.Entry<K, V> entry = (Map.Entry<K, V>)obj;
268    
269                            V value = WeakValueConcurrentHashMap.this.get(entry.getKey());
270    
271                            if ((value != null) && value.equals(entry.getValue())) {
272                                    return true;
273                            }
274                            else {
275                                    return false;
276                            }
277                    }
278    
279                    @Override
280                    public Iterator<Map.Entry<K, V>> iterator() {
281                            return new UnwrapEntryIterator();
282                    }
283    
284                    @Override
285                    public boolean remove(Object obj) {
286                            if (!(obj instanceof Map.Entry<?, ?>)) {
287                                    return false;
288                            }
289    
290                            Map.Entry<K, V> entry = (Map.Entry<K, V>)obj;
291    
292                            return WeakValueConcurrentHashMap.this.remove(
293                                    entry.getKey(), entry.getValue());
294                    }
295    
296                    @Override
297                    public int size() {
298                            return WeakValueConcurrentHashMap.this.size();
299                    }
300    
301                    @Override
302                    public Object[] toArray() {
303                            List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size());
304    
305                            Iterator<Map.Entry<K, V>> iterator = iterator();
306    
307                            while (iterator.hasNext()) {
308                                    list.add(iterator.next());
309                            }
310    
311                            return list.toArray();
312                    }
313    
314                    @Override
315                    public <T> T[] toArray(T[] array) {
316                            List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size());
317    
318                            Iterator<Map.Entry<K, V>> iterator = iterator();
319    
320                            while (iterator.hasNext()) {
321                                    list.add(iterator.next());
322                            }
323    
324                            return list.toArray(array);
325                    }
326    
327            }
328    
329            private class UnwrapValueIterator implements Iterator<V> {
330    
331                    public UnwrapValueIterator() {
332                            _iterator = _map.values().iterator();
333                    }
334    
335                    public boolean hasNext() {
336                            return _iterator.hasNext();
337                    }
338    
339                    public V next() {
340                            Reference<V> valueReference = _iterator.next();
341    
342                            if (valueReference != null) {
343                                    return valueReference.get();
344                            }
345    
346                            return null;
347                    }
348    
349                    public void remove() {
350                            _iterator.remove();
351                    }
352    
353                    private Iterator<Reference<V>> _iterator;
354    
355            }
356    
357            private class UnwrapValues extends AbstractCollection<V> {
358    
359                    @Override
360                    public void clear() {
361                            WeakValueConcurrentHashMap.this.clear();
362                    }
363    
364                    @Override
365                    public boolean contains(Object obj) {
366                            return WeakValueConcurrentHashMap.this.containsValue(obj);
367                    }
368    
369                    @Override
370                    public Iterator<V> iterator() {
371                            return new UnwrapValueIterator();
372                    }
373    
374                    @Override
375                    public int size() {
376                            return WeakValueConcurrentHashMap.this.size();
377                    }
378    
379                    @Override
380                    public Object[] toArray() {
381                            List<V> list = new ArrayList<V>();
382    
383                            Iterator<V> iterator = iterator();
384    
385                            while (iterator.hasNext()) {
386                                    list.add(iterator.next());
387                            }
388    
389                            return list.toArray();
390                    }
391    
392                    @Override
393                    public <T> T[] toArray(T[] a) {
394                            List<V> list = new ArrayList<V>();
395    
396                            Iterator<V> iterator = iterator();
397    
398                            while (iterator.hasNext()) {
399                                    list.add(iterator.next());
400                            }
401    
402                            return list.toArray(a);
403                    }
404    
405            }
406    
407    }