package net.techreadiness.util.observables; import java.io.Serializable; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import com.google.common.collect.ForwardingMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multiset; /** * * A Multimap wrapper which allows observation of added and removed elements via an ElementObserver * @param <K> The type of keys for the map * @param <V> The type of values for the map * */ public class ObservableMultimap<K, V> extends ForwardingMultimap<K, V> implements Serializable { private static final long serialVersionUID = 1L; private final ElementObserver<Entry<K, V>> observer; private final Multimap<K, V> delegate; public ObservableMultimap(Multimap<K, V> delegate, ElementObserver<Entry<K, V>> observer) { this.delegate = delegate; this.observer = observer; } @Override protected Multimap<K, V> delegate() { return delegate; } class MultiMapObserver implements ElementObserver<Entry<K, Collection<V>>> { private static final long serialVersionUID = 1L; @Override public void elementAdded(Entry<K, Collection<V>> element) { for (V value : element.getValue()) { observer.elementAdded(Maps.immutableEntry(element.getKey(), value)); } } @Override public void elementRemoved(Entry<K, Collection<V>> element) { for (V value : element.getValue()) { observer.elementRemoved(Maps.immutableEntry(element.getKey(), value)); } } } class ValueObserver implements ElementObserver<V> { private static final long serialVersionUID = 1L; @Override public void elementAdded(V element) { observer.elementAdded(Maps.immutableEntry((K) null, element)); } @Override public void elementRemoved(V element) { observer.elementRemoved(Maps.immutableEntry((K) null, element)); } } class KeyObserver implements ElementObserver<K> { private static final long serialVersionUID = 1L; @Override public void elementAdded(K element) { observer.elementAdded(Maps.immutableEntry(element, (V) null)); } @Override public void elementRemoved(K element) { observer.elementRemoved(Maps.immutableEntry(element, (V) null)); } } @Override public Map<K, Collection<V>> asMap() { // TODO: modifications to this map are not supported return ImmutableMap.copyOf(super.asMap()); } @Override public void clear() { for (Entry<K, V> entry : super.entries()) { observer.elementRemoved(entry); } super.clear(); } @Override public Collection<Entry<K, V>> entries() { return new ObservableCollection<>(super.entries(), observer); } @Override public Collection<V> get(K key) { return new ObservableCollection<>(delegate.get(key), new ValueObserver()); } @Override public Multiset<K> keys() { // TODO: modifications to this structure are not supported return ImmutableMultiset.<K> builder().addAll(super.keys()).build(); } @Override public Set<K> keySet() { return new ObservableSet<>(super.keySet(), new KeyObserver()); } @Override public boolean put(K key, V value) { observer.elementAdded(Maps.immutableEntry(key, value)); return super.put(key, value); } @Override public boolean putAll(K key, Iterable<? extends V> values) { for (V value : values) { observer.elementAdded(Maps.immutableEntry(key, value)); } return super.putAll(key, values); } @Override @SuppressWarnings("unchecked") public boolean putAll(Multimap<? extends K, ? extends V> multimap) { for (Entry<?, ?> entry : multimap.entries()) { observer.elementAdded((Entry<K, V>) entry); } return super.putAll(multimap); } @Override public boolean remove(Object key, Object value) { observer.elementRemoved((Entry<K, V>) Maps.immutableEntry(key, value)); return super.remove(key, value); } @Override public Collection<V> removeAll(Object key) { Collection<V> removedValues = super.removeAll(key); for (V value : removedValues) { observer.elementRemoved((Entry<K, V>) Maps.immutableEntry(key, value)); } return removedValues; } @Override public Collection<V> replaceValues(K key, Iterable<? extends V> values) { Collection<V> replacedValues = super.replaceValues(key, values); for (V value : replacedValues) { observer.elementRemoved(Maps.immutableEntry(key, value)); } for (V value : values) { observer.elementAdded(Maps.immutableEntry(key, value)); } return replacedValues; } }