/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.collections.wrap; import com.jcwhatever.nucleus.utils.PreCon; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; /** * An abstract implementation of a synchronized {@link Map} wrapper. The wrapper is * optionally synchronized via a sync object or {@link ReadWriteLock} passed into the * constructor using a {@link SyncStrategy}. * * <p>If the map is synchronized, the sync object must be externally locked while * any associated iterator is in use. Otherwise, a {@link java.lang.IllegalStateException} will * be thrown.</p> * * <p>The actual map is provided to the abstract implementation by * overriding and returning it from the {@link #map} method.</p> * * <p>In order to make using the wrapper as an extension of a map easier, * several protected methods are provided for optional override. See {@link #onPut}, * {@link #onRemove}, {@link #onClear}.</p> */ public abstract class MapWrapper<K, V> implements Map<K, V> { private final ValuesWrapper _valuesWrapper; private final KeySetWrapper _keySetWrapper; private final EntrySetWrapper _entrySetWrapper; protected final Object _sync; protected final ReadWriteLock _lock; protected final SyncStrategy _strategy; /** * Constructor. * * <p>No synchronization.</p> */ public MapWrapper() { this(SyncStrategy.NONE); } /** * Constructor. * * @param strategy The synchronization strategy to use. */ public MapWrapper(SyncStrategy strategy) { PreCon.notNull(strategy); _sync = strategy.getSync(this); _strategy = new SyncStrategy(_sync); _lock = _sync instanceof ReadWriteLock ? (ReadWriteLock)_sync : null; _valuesWrapper = new ValuesWrapper(); _keySetWrapper = new KeySetWrapper(); _entrySetWrapper = new EntrySetWrapper(); } /** * Invoked after an entry is put into the map. * * <p>Not guaranteed to be called from a synchronized block.</p> * * <p>Intended to be optionally overridden by implementation.</p> * * @param key The key. * @param value The value. */ protected void onPut(K key, V value) {} /** * Invoked after an entry is removed from the map except * when the map is cleared. * * <p>Not guaranteed to be called from a synchronized block.</p> * * <p>Intended to be optionally overridden by implementation.</p> * * @param key The key. * @param removed The value. */ protected void onRemove(Object key, V removed) {} /** * Invoked after the map is cleared. * * <p>Not guaranteed to be called from a synchronized block.</p> * * <p>Intended to be optionally overridden by implementation.</p> * * @param entries The cleared entries. */ protected void onClear(Collection<Entry<K, V>> entries) {} /** * Invoked from a synchronized block to get the encapsulated {@link Map}. */ protected abstract Map<K, V> map(); @Override public int size() { if (_lock != null) { _lock.readLock().lock(); try { return map().size(); } finally { _lock.readLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { return map().size(); } } else { return map().size(); } } @Override public boolean isEmpty() { if (_lock != null) { _lock.readLock().lock(); try { return map().isEmpty(); } finally { _lock.readLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { return map().isEmpty(); } } else { return map().isEmpty(); } } @Override public boolean containsKey(Object key) { if (_lock != null) { _lock.readLock().lock(); try { return map().containsKey(key); } finally { _lock.readLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { return map().containsKey(key); } } else { return map().containsKey(key); } } @Override public boolean containsValue(Object value) { if (_lock != null) { _lock.readLock().lock(); try { return map().containsValue(value); } finally { _lock.readLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { return map().containsValue(value); } } else { return map().containsValue(value); } } @Override public V get(Object key) { if (_lock != null) { _lock.readLock().lock(); try { return map().get(key); } finally { _lock.readLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { return map().get(key); } } else { return map().get(key); } } @Override public V put(K key, V value) { PreCon.notNull(key); V previous; if (_lock != null) { _lock.writeLock().lock(); try { previous = map().put(key, value); } finally { _lock.writeLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { previous = map().put(key, value); } } else { previous = map().put(key, value); } if (previous == null || !previous.equals(value)) { onPut(key, value); } return previous; } @Override public V remove(Object key) { V removed; if (_lock != null) { _lock.writeLock().lock(); try { removed = map().remove(key); } finally { _lock.writeLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { removed = map().remove(key); } } else { removed = map().remove(key); } if (removed != null) onRemove(key, removed); return removed; } @Override public void putAll(Map<? extends K, ? extends V> m) { List<Entry<? extends K, ? extends V>> entrySet; if (_lock != null) { _lock.readLock().lock(); try { entrySet = new ArrayList<Entry<? extends K, ? extends V>>(m.entrySet()); } finally { _lock.readLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { entrySet = new ArrayList<Entry<? extends K, ? extends V>>(m.entrySet()); } } else { entrySet = new ArrayList<Entry<? extends K, ? extends V>>(m.entrySet()); } for (Entry<? extends K, ? extends V> entry : entrySet) { put(entry.getKey(), entry.getValue()); } } @Override public void clear() { if (_lock != null) { _lock.writeLock().lock(); try { clearSource(); } finally { _lock.writeLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { clearSource(); } } else { clearSource(); } } private void clearSource() { Set<Entry<K, V>> entries = new HashSet<>(map().entrySet()); map().clear(); onClear(entries); } @Override public Set<K> keySet() { return _keySetWrapper; } @Override public Collection<V> values() { return _valuesWrapper; } @Override public Set<Entry<K, V>> entrySet() { return _entrySetWrapper; } private class ValuesWrapper extends CollectionWrapper<V> { ValuesWrapper() { super(MapWrapper.this._strategy); } @Override protected boolean onPreAdd(V v) { throw new UnsupportedOperationException(); } @Override protected boolean onPreRemove(Object o) { throw new UnsupportedOperationException(); } @Override protected void onPreClear() { throw new UnsupportedOperationException(); } @Override protected Collection<V> collection() { return map().values(); } } private class KeySetWrapper extends SetWrapper<K> { KeySetWrapper() { super(MapWrapper.this._strategy); } V removed; Set<Entry<K, V>> cleared; @Override protected Set<K> set() { return map().keySet(); } @Override protected boolean onPreAdd(K k) { throw new UnsupportedOperationException(); } @Override protected boolean onPreRemove(Object o) { if (_lock != null) { _lock.readLock().lock(); try { //noinspection SuspiciousMethodCalls removed = map().get(o); } finally { _lock.readLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { //noinspection SuspiciousMethodCalls removed = map().get(o); } } else { //noinspection SuspiciousMethodCalls removed = map().get(o); } return true; } @Override protected void onRemoved(Object key) { MapWrapper.this.onRemove(key, removed); removed = null; } @Override protected void onPreClear() { if (_lock != null) { _lock.readLock().lock(); try { cleared = new HashSet<Entry<K, V>>(map().entrySet()); } finally { _lock.readLock().unlock(); } } else if (_sync != null) { synchronized (_sync) { cleared = new HashSet<Entry<K, V>>(map().entrySet()); } } else { cleared = new HashSet<Entry<K, V>>(map().entrySet()); } } @Override protected void onClear(Collection<K> values) { MapWrapper.this.onClear(cleared); cleared = null; } } private class EntrySetWrapper extends SetWrapper<Entry<K, V>> { EntrySetWrapper() { super(MapWrapper.this._strategy); } @Override protected Set<Entry<K, V>> set() { return map().entrySet(); } @Override protected boolean onPreAdd(Entry<K, V> kvEntry) { return true; } @Override protected void onAdded(Entry<K, V> kvEntry) { MapWrapper.this.onPut(kvEntry.getKey(), kvEntry.getValue()); } @Override protected boolean onPreRemove(Object o) { return o instanceof Entry; } @Override protected void onRemoved(Object o) { if (o instanceof Entry) { @SuppressWarnings("unchecked") Entry<K, V> entry = (Entry<K, V>)o; MapWrapper.this.onRemove(entry.getKey(), entry.getValue()); } } @Override protected void onClear(Collection<Entry<K, V>> values) { MapWrapper.this.onClear(values); } } }