/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.dlect.events.collections;
import com.google.common.base.Objects;
import com.google.common.collect.ForwardingIterator;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ForwardingMapEntry;
import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class EventFiringMap<K, V> extends ForwardingMap<K, V> {
private final Map<K, V> delegate;
private final CollectionEventHelper<Map.Entry<K, V>> helper;
/**
* Creates a new map with the ability to fire events through the given helper.
*
* @param delegate The base map to delegate method calls to.
* @param helper The event helper to fire events through.
*/
public EventFiringMap(Map<K, V> delegate, CollectionEventHelper<Map.Entry<K, V>> helper) {
this.delegate = delegate;
this.helper = helper;
}
@Override
protected Map<K, V> delegate() {
return delegate;
}
protected CollectionEventHelper<Entry<K, V>> getHelper() {
return helper;
}
@Override
public void clear() {
super.standardClear();
}
@Override
public Set<Entry<K, V>> entrySet() {
return new StandardEntrySet() {
@Override
public Iterator<Entry<K, V>> iterator() {
return new EventFiringEntrySetIterator<>(delegate().entrySet().iterator(),
helper);
}
};
}
@Override
public Set<K> keySet() {
return new StandardKeySet();
}
@Override
public Collection<V> values() {
return new StandardValues();
}
@Override
public V put(K key, V value) {
boolean replace = this.containsKey(key);
V old = super.put(key, value);
if (replace && !Objects.equal(old, value)) {
// Replacing and value has changed.
helper.fireReplace(imEntry(key, old), imEntry(key, value));
} else if (!replace) {
helper.fireAdd(imEntry(key, value));
}
return old;
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
super.standardPutAll(map);
}
@Override
public V remove(Object key) {
return super.standardRemove(key);
}
private static class EventFiringEntry<K, V> extends ForwardingMapEntry<K, V> {
private final CollectionEventHelper<Entry<K, V>> helper;
private final Entry<K, V> input;
protected EventFiringEntry(Entry<K, V> input, CollectionEventHelper<Entry<K, V>> helper) {
this.input = input;
this.helper = helper;
}
@Override
protected Entry<K, V> delegate() {
return input;
}
@Override
public V setValue(V value) {
V old = super.setValue(value);
if (!Objects.equal(old, value)) { // Changed
helper.fireReplace(imEntry(getKey(), old), imEntry(getKey(), value));
}
return old;
}
}
private static class EventFiringEntrySetIterator<K, V> extends ForwardingIterator<Entry<K, V>> {
private final Iterator<Entry<K, V>> delegate;
private final CollectionEventHelper<Entry<K, V>> helper;
private Entry<K, V> current;
protected EventFiringEntrySetIterator(Iterator<Entry<K, V>> delegate, CollectionEventHelper<Entry<K, V>> helper) {
this.delegate = delegate;
this.helper = helper;
}
@Override
protected Iterator<Entry<K, V>> delegate() {
return delegate;
}
@Override
public Entry<K, V> next() {
current = super.next();
return new EventFiringEntry<>(current, helper);
}
@Override
public void remove() {
super.remove();
helper.fireRemove(imEntry(current));
}
}
protected static <K, V> Entry<K, V> imEntry(K key, V value) {
return Maps.immutableEntry(key, value);
}
protected static <K, V> Entry<K, V> imEntry(Entry<K, V> entry) {
return Maps.immutableEntry(entry.getKey(), entry.getValue());
}
}