/******************************************************************************* * 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.q; import java.util.AbstractCollection; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.andork.model.Model; import org.andork.util.Java7; public abstract class QMap<K, V, C extends Map<K, V>> extends QElement implements Map<K, V>, Model { 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<K, V> e = (Map.Entry<K, V>) o; V 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(); } } 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; @Override public void clear() { Map<K, V> clone = new LinkedHashMap<K, V>(map); map.clear(); for (Map.Entry<K, V> entry : clone.entrySet()) { if (entry.getValue() != null) { changeSupport.firePropertyChange(this, entry.getKey(), entry.getValue(), null); } } clearChildren(); } @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()); } @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) { if (prev != null) { removeChild(prev); } if (value != null) { addChild(value); } changeSupport.firePropertyChange(this, key, prev, value); } return prev; } @Override public void putAll(Map<? extends K, ? extends V> m) { List<Object> added = new ArrayList<Object>(); List<Object> removed = new ArrayList<Object>(); for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) { K key = entry.getKey(); V value = entry.getValue(); V prev = map.put(key, value); if (prev != null) { removed.add(prev); } if (value != null) { added.add(prev); } if (prev != value) { changeSupport.firePropertyChange(this, key, prev, value); } } removeChildren(removed); addChildren(added); } @Override public V remove(Object key) { V prev = map.remove(key); if (prev != null) { removeChild(prev); changeSupport.firePropertyChange(this, key, prev, null); } return prev; } @Override public void set(Object key, Object newValue) { put((K) key, (V) newValue); } @Override public int size() { return map.size(); } @Override public Collection<V> values() { Collection<V> vs = values; return vs != null ? vs : (values = new Values()); } }