001    /**
002     * Copyright (c) 2000-present 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.concurrent;
016    
017    import java.io.Serializable;
018    
019    import java.util.AbstractCollection;
020    import java.util.AbstractMap;
021    import java.util.AbstractSet;
022    import java.util.Collection;
023    import java.util.Iterator;
024    import java.util.Map;
025    import java.util.Objects;
026    import java.util.Set;
027    import java.util.concurrent.ConcurrentMap;
028    
029    /**
030     * @author Shuyang Zhou
031     */
032    public abstract class ConcurrentMapperHashMap<K, IK, V, IV>
033            extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable {
034    
035            @Override
036            public void clear() {
037                    innerConcurrentMap.clear();
038            }
039    
040            @Override
041            public boolean containsKey(Object key) {
042                    if (key == null) {
043                            throw new NullPointerException("Key is null");
044                    }
045    
046                    return innerConcurrentMap.containsKey(mapKeyForQuery((K)key));
047            }
048    
049            @Override
050            public boolean containsValue(Object value) {
051                    if (value == null) {
052                            throw new NullPointerException("Value is null");
053                    }
054    
055                    return innerConcurrentMap.containsValue(mapValueForQuery((V)value));
056            }
057    
058            @Override
059            public Set<Entry<K, V>> entrySet() {
060                    if (entrySet == null) {
061                            entrySet = new UnwrapEntrySet();
062                    }
063    
064                    return entrySet;
065            }
066    
067            @Override
068            public V get(Object key) {
069                    if (key == null) {
070                            throw new NullPointerException("Key is null");
071                    }
072    
073                    IV innerValue = innerConcurrentMap.get(mapKeyForQuery((K)key));
074    
075                    if (innerValue == null) {
076                            return null;
077                    }
078    
079                    return unmapValueForQuery(innerValue);
080            }
081    
082            @Override
083            public boolean isEmpty() {
084                    return innerConcurrentMap.isEmpty();
085            }
086    
087            @Override
088            public Set<K> keySet() {
089                    if (keySet == null) {
090                            keySet = new UnwrapKeySet();
091                    }
092    
093                    return keySet;
094            }
095    
096            @Override
097            public V put(K key, V value) {
098                    if (key == null) {
099                            throw new NullPointerException("Key is null");
100                    }
101    
102                    if (value == null) {
103                            throw new NullPointerException("Value is null");
104                    }
105    
106                    IK innerKey = mapKey(key);
107    
108                    IV oldInnerValue = innerConcurrentMap.put(
109                            innerKey, mapValue(key, value));
110    
111                    if (oldInnerValue == null) {
112                            return null;
113                    }
114    
115                    unmapKey(innerKey);
116    
117                    return unmapValue(oldInnerValue);
118            }
119    
120            @Override
121            public void putAll(Map<? extends K, ? extends V> map) {
122                    for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
123                            put(entry.getKey(), entry.getValue());
124                    }
125            }
126    
127            @Override
128            public V putIfAbsent(K key, V value) {
129                    if (key == null) {
130                            throw new NullPointerException("Key is null");
131                    }
132    
133                    if (value == null) {
134                            throw new NullPointerException("Value is null");
135                    }
136    
137                    IK innerKey = mapKey(key);
138                    IV innerValue = mapValue(key, value);
139    
140                    IV previousInnerValue = innerConcurrentMap.putIfAbsent(
141                            innerKey, innerValue);
142    
143                    if (previousInnerValue == null) {
144                            return null;
145                    }
146    
147                    unmapKey(innerKey);
148                    unmapValue(innerValue);
149    
150                    return unmapValueForQuery(previousInnerValue);
151            }
152    
153            @Override
154            public V remove(Object key) {
155                    if (key == null) {
156                            throw new NullPointerException("Key is null");
157                    }
158    
159                    IK innerKey = mapKeyForQuery((K)key);
160    
161                    IV innerValue = innerConcurrentMap.remove(innerKey);
162    
163                    if (innerValue == null) {
164                            return null;
165                    }
166    
167                    return unmapValue(innerValue);
168            }
169    
170            @Override
171            public boolean remove(Object key, Object value) {
172                    if (key == null) {
173                            throw new NullPointerException("Key is null");
174                    }
175    
176                    if (value == null) {
177                            throw new NullPointerException("Value is null");
178                    }
179    
180                    IK innerKey = mapKeyForQuery((K)key);
181                    IV innerValue = mapValueForQuery((V)value);
182    
183                    IV previousInnerValue = innerConcurrentMap.get(innerKey);
184    
185                    if (!innerValue.equals(previousInnerValue) ||
186                            !innerConcurrentMap.remove(innerKey, previousInnerValue)) {
187    
188                            return false;
189                    }
190    
191                    unmapValue(previousInnerValue);
192    
193                    return true;
194            }
195    
196            @Override
197            public V replace(K key, V value) {
198                    if (key == null) {
199                            throw new NullPointerException("Key is null");
200                    }
201    
202                    if (value == null) {
203                            throw new NullPointerException("Value is null");
204                    }
205    
206                    IV newInnerValue = mapValue(key, value);
207    
208                    IV oldInnerValue = innerConcurrentMap.replace(
209                            mapKeyForQuery(key), newInnerValue);
210    
211                    if (oldInnerValue == null) {
212                            unmapValue(newInnerValue);
213    
214                            return null;
215                    }
216    
217                    return unmapValue(oldInnerValue);
218            }
219    
220            @Override
221            public boolean replace(K key, V oldValue, V newValue) {
222                    if (key == null) {
223                            throw new NullPointerException("Key is null");
224                    }
225    
226                    if (oldValue == null) {
227                            throw new NullPointerException("Old value is null");
228                    }
229    
230                    if (newValue == null) {
231                            throw new NullPointerException("New value is null");
232                    }
233    
234                    IK innerKey = mapKeyForQuery(key);
235    
236                    IV newInnerValue = mapValue(key, newValue);
237    
238                    IV oldInnerValue = innerConcurrentMap.get(innerKey);
239    
240                    if ((oldInnerValue == null) ||
241                            !oldValue.equals(unmapValueForQuery(oldInnerValue))) {
242    
243                            unmapValue(newInnerValue);
244    
245                            return false;
246                    }
247    
248                    if (innerConcurrentMap.replace(
249                                    innerKey, oldInnerValue, newInnerValue)) {
250    
251                            unmapValue(oldInnerValue);
252    
253                            return true;
254                    }
255    
256                    unmapValue(newInnerValue);
257    
258                    return false;
259            }
260    
261            @Override
262            public int size() {
263                    return innerConcurrentMap.size();
264            }
265    
266            @Override
267            public Collection<V> values() {
268                    if (values == null) {
269                            values = new UnwrapValues();
270                    }
271    
272                    return values;
273            }
274    
275            protected ConcurrentMapperHashMap(
276                    ConcurrentMap<IK, IV> innerConcurrentMap) {
277    
278                    this.innerConcurrentMap = innerConcurrentMap;
279            }
280    
281            protected abstract IK mapKey(K key);
282    
283            protected abstract IK mapKeyForQuery(K key);
284    
285            protected abstract IV mapValue(K key, V value);
286    
287            protected abstract IV mapValueForQuery(V value);
288    
289            protected abstract K unmapKey(IK key);
290    
291            protected abstract K unmapKeyForQuery(IK key);
292    
293            protected abstract V unmapValue(IV value);
294    
295            protected abstract V unmapValueForQuery(IV value);
296    
297            protected transient Set<Map.Entry<K, V>> entrySet;
298            protected final ConcurrentMap<IK, IV> innerConcurrentMap;
299            protected transient Set<K> keySet;
300            protected transient Collection<V> values;
301    
302            private static final long serialVersionUID = 1L;
303    
304            private class UnwrapEntry implements Map.Entry<K, V> {
305    
306                    public UnwrapEntry(Entry<IK, IV> innerEntry) {
307                            _innerEntry = innerEntry;
308                    }
309    
310                    @Override
311                    public boolean equals(Object obj) {
312                            if (this == obj) {
313                                    return true;
314                            }
315    
316                            if (!(obj instanceof Map.Entry)) {
317                                    return false;
318                            }
319    
320                            Map.Entry<K, V> entry = (Map.Entry<K, V>)obj;
321    
322                            if (Objects.equals(getKey(), entry.getKey()) &&
323                                    Objects.equals(getValue(), entry.getValue())) {
324    
325                                    return true;
326                            }
327    
328                            return false;
329                    }
330    
331                    @Override
332                    public K getKey() {
333                            return unmapKeyForQuery(_innerEntry.getKey());
334                    }
335    
336                    @Override
337                    public V getValue() {
338                            return unmapValueForQuery(_innerEntry.getValue());
339                    }
340    
341                    @Override
342                    public int hashCode() {
343                            return _innerEntry.hashCode();
344                    }
345    
346                    @Override
347                    public V setValue(V value) {
348                            K key = getKey();
349    
350                            V v = unmapValueForQuery(
351                                    _innerEntry.setValue(mapValueForQuery(value)));
352    
353                            ConcurrentMapperHashMap.this.put(key, value);
354    
355                            return v;
356                    }
357    
358                    private final Entry<IK, IV> _innerEntry;
359    
360            }
361    
362            private class UnwrapEntryIterator implements Iterator<Map.Entry<K, V>> {
363    
364                    public UnwrapEntryIterator() {
365                            Set<Entry<IK, IV>> entrySet = innerConcurrentMap.entrySet();
366    
367                            _iterator = entrySet.iterator();
368                    }
369    
370                    @Override
371                    public boolean hasNext() {
372                            return _iterator.hasNext();
373                    }
374    
375                    @Override
376                    public Entry<K, V> next() {
377                            return new UnwrapEntry(_iterator.next());
378                    }
379    
380                    @Override
381                    public void remove() {
382                            _iterator.remove();
383                    }
384    
385                    private final Iterator<Map.Entry<IK, IV>> _iterator;
386    
387            }
388    
389            private class UnwrapEntrySet extends AbstractSet<Map.Entry<K, V>> {
390    
391                    @Override
392                    public void clear() {
393                            ConcurrentMapperHashMap.this.clear();
394                    }
395    
396                    @Override
397                    public boolean contains(Object obj) {
398                            if (!(obj instanceof Map.Entry<?, ?>)) {
399                                    return false;
400                            }
401    
402                            Map.Entry<K, V> entry = (Map.Entry<K, V>)obj;
403    
404                            V value = ConcurrentMapperHashMap.this.get(entry.getKey());
405    
406                            if ((value != null) && value.equals(entry.getValue())) {
407                                    return true;
408                            }
409                            else {
410                                    return false;
411                            }
412                    }
413    
414                    @Override
415                    public boolean isEmpty() {
416                            return ConcurrentMapperHashMap.this.isEmpty();
417                    }
418    
419                    @Override
420                    public Iterator<Map.Entry<K, V>> iterator() {
421                            return new UnwrapEntryIterator();
422                    }
423    
424                    @Override
425                    public boolean remove(Object obj) {
426                            if (!(obj instanceof Map.Entry<?, ?>)) {
427                                    return false;
428                            }
429    
430                            Map.Entry<K, V> entry = (Map.Entry<K, V>)obj;
431    
432                            return ConcurrentMapperHashMap.this.remove(
433                                    entry.getKey(), entry.getValue());
434                    }
435    
436                    @Override
437                    public int size() {
438                            return ConcurrentMapperHashMap.this.size();
439                    }
440    
441            }
442    
443            private class UnwrapKeyIterator implements Iterator<K> {
444    
445                    public UnwrapKeyIterator() {
446                            Set<IK> keySet = innerConcurrentMap.keySet();
447    
448                            _iterator = keySet.iterator();
449                    }
450    
451                    @Override
452                    public boolean hasNext() {
453                            return _iterator.hasNext();
454                    }
455    
456                    @Override
457                    public K next() {
458                            return unmapKeyForQuery(_iterator.next());
459                    }
460    
461                    @Override
462                    public void remove() {
463                            _iterator.remove();
464                    }
465    
466                    private final Iterator<IK> _iterator;
467    
468            }
469    
470            private class UnwrapKeySet extends AbstractSet<K> {
471    
472                    @Override
473                    public void clear() {
474                            ConcurrentMapperHashMap.this.clear();
475                    }
476    
477                    @Override
478                    public boolean contains(Object o) {
479                            return ConcurrentMapperHashMap.this.containsKey(o);
480                    }
481    
482                    @Override
483                    public boolean isEmpty() {
484                            return ConcurrentMapperHashMap.this.isEmpty();
485                    }
486    
487                    @Override
488                    public Iterator<K> iterator() {
489                            return new UnwrapKeyIterator();
490                    }
491    
492                    @Override
493                    public boolean remove(Object o) {
494                            if (ConcurrentMapperHashMap.this.remove(o) != null) {
495                                    return true;
496                            }
497    
498                            return false;
499                    }
500    
501                    @Override
502                    public int size() {
503                            return ConcurrentMapperHashMap.this.size();
504                    }
505    
506            }
507    
508            private class UnwrapValueIterator implements Iterator<V> {
509    
510                    public UnwrapValueIterator() {
511                            Collection<IV> values = innerConcurrentMap.values();
512    
513                            _iterator = values.iterator();
514                    }
515    
516                    @Override
517                    public boolean hasNext() {
518                            return _iterator.hasNext();
519                    }
520    
521                    @Override
522                    public V next() {
523                            return unmapValueForQuery(_iterator.next());
524                    }
525    
526                    @Override
527                    public void remove() {
528                            _iterator.remove();
529                    }
530    
531                    private final Iterator<IV> _iterator;
532    
533            }
534    
535            private class UnwrapValues extends AbstractCollection<V> {
536    
537                    @Override
538                    public void clear() {
539                            ConcurrentMapperHashMap.this.clear();
540                    }
541    
542                    @Override
543                    public boolean contains(Object obj) {
544                            return ConcurrentMapperHashMap.this.containsValue(obj);
545                    }
546    
547                    @Override
548                    public boolean isEmpty() {
549                            return ConcurrentMapperHashMap.this.isEmpty();
550                    }
551    
552                    @Override
553                    public Iterator<V> iterator() {
554                            return new UnwrapValueIterator();
555                    }
556    
557                    @Override
558                    public int size() {
559                            return ConcurrentMapperHashMap.this.size();
560                    }
561    
562            }
563    
564    }