/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.q2;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.andork.util.Java7;
public abstract class QMap<K, V, C extends Map<K, V>> extends QElement implements Map<K, V> {
private class EntryIterator extends HashIterator<Map.Entry<K, V>> {
@Override
public Map.Entry<K, V> next() {
return nextEntry();
}
}
private final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
@Override
public void clear() {
QMap.this.clear();
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Map.Entry)) {
return false;
}
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
Object value = QMap.this.get(e.getKey());
return Java7.Objects.equals(value, e.getValue());
}
@Override
public Iterator<Map.Entry<K, V>> iterator() {
return new EntryIterator();
}
@Override
public boolean remove(Object o) {
if (o instanceof Map.Entry) {
if (contains(o)) {
QMap.this.remove(((Map.Entry<?, ?>) o).getKey());
return true;
}
}
return false;
}
@Override
public int size() {
return QMap.this.size();
}
}
private abstract class HashIterator<E> implements Iterator<E> {
Iterator<Map.Entry<K, V>> entryIter = map.entrySet().iterator();
Map.Entry<K, V> last;
@Override
public boolean hasNext() {
return entryIter.hasNext();
}
public Map.Entry<K, V> nextEntry() {
return last = entryIter.next();
}
@Override
public void remove() {
QMap.this.remove(last.getKey());
}
}
private class KeyIterator extends HashIterator<K> {
@Override
public K next() {
return nextEntry().getKey();
}
}
private final class KeySet extends AbstractSet<K> {
@Override
public void clear() {
QMap.this.clear();
}
@Override
public boolean contains(Object o) {
return containsKey(o);
}
@Override
public Iterator<K> iterator() {
return new KeyIterator();
}
@Override
public boolean remove(Object o) {
boolean removed = containsKey(o);
QMap.this.remove(o);
return removed;
}
@Override
public int size() {
return QMap.this.size();
}
}
private class ValueIterator extends HashIterator<V> {
@Override
public V next() {
return nextEntry().getValue();
}
}
private final class Values extends AbstractCollection<V> {
@Override
public void clear() {
QMap.this.clear();
}
@Override
public boolean contains(Object o) {
return containsValue(o);
}
@Override
public Iterator<V> iterator() {
return new ValueIterator();
}
@Override
public int size() {
return QMap.this.size();
}
}
private static <T> List<T> multiply(int times, T elem) {
List<T> result = new ArrayList<T>(times);
for (int i = 0; i < times; i++) {
result.add(elem);
}
return result;
}
protected final C map = createMap();
transient volatile Set<K> keySet = null;
transient volatile Collection<V> values = null;
transient volatile Set<Map.Entry<K, V>> entrySet = null;
public void addListener(QMapListener<? super K, ? super V> listener) {
super.addListener(listener);
}
@Override
public void clear() {
List<K> oldKeys = new ArrayList<>(keySet());
List<V> oldValues = new ArrayList<>(values());
List<Boolean> removed = multiply(size(), true);
List<V> newValues = multiply(size(), null);
map.clear();
fireMapChanged(oldKeys, oldValues, removed, newValues);
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
protected abstract C createMap();
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
Set<java.util.Map.Entry<K, V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
@SuppressWarnings("unchecked")
protected void fireMapChanged(K key, V oldValue, boolean removed, V newValue) {
forEachListener(QMapListener.class, l -> l.mapChanged(this, key, oldValue, removed, newValue));
}
@SuppressWarnings("unchecked")
protected void fireMapChanged(List<K> keys, List<V> oldValues, List<Boolean> removed,
List<V> newValues) {
forEachListener(QMapListener.class, l -> l.mapChanged(this, keys, oldValues, removed, newValues));
}
@Override
public V get(Object key) {
return map.get(key);
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public Set<K> keySet() {
Set<K> ks = keySet;
return ks != null ? ks : (keySet = new KeySet());
}
@Override
public V put(K key, V value) {
V prev = map.put(key, value);
if (prev != value) {
fireMapChanged(key, prev, false, value);
}
return prev;
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
List<K> keys = new ArrayList<>();
List<V> oldValues = new ArrayList<>();
List<Boolean> removed = new ArrayList<Boolean>();
List<V> newValues = new ArrayList<>();
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
K key = entry.getKey();
V newValue = entry.getValue();
V oldValue = map.put(key, newValue);
if (oldValue != newValue) {
keys.add(key);
oldValues.add(oldValue);
removed.add(false);
newValues.add(newValue);
}
}
fireMapChanged(keys, oldValues, removed, newValues);
}
@SuppressWarnings("unchecked")
@Override
public V remove(Object key) {
V prev = map.remove(key);
if (prev != null) {
fireMapChanged((K) key, prev, true, null);
}
return prev;
}
public void removeListener(QMapListener<? super K, ? super V> listener) {
super.removeListener(listener);
}
@Override
public int size() {
return map.size();
}
@Override
public Collection<V> values() {
Collection<V> vs = values;
return vs != null ? vs : (values = new Values());
}
}