diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/dl/beans/collections/ListenableCollections.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/dl/beans/collections/ListenableCollections.java new file mode 100644 index 00000000000..81aa75ffa1b --- /dev/null +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/dl/beans/collections/ListenableCollections.java @@ -0,0 +1,360 @@ +/** + * ListenableCollections.java + * + * Created on 25.04.2010 + */ + +package org.mage.plugins.card.dl.beans.collections; + + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.AbstractMap; +import java.util.AbstractSequentialList; +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.RandomAccess; +import java.util.Set; + + +/** + * The class ListenableCollections supports applications that need to listen for modifications on different + * collections. Unlike most listener models, a listenable collection does not have public methods for adding or + * removing listeners. Instead, the wrapping methods take exactly one listener, because listening to collections is + * low-level. That single listener will usually manage multiple high-level listeners. + * + * @version V0.0 25.04.2010 + * @author Clemens Koza + */ +public final class ListenableCollections { + private ListenableCollections() {} + + public static List listenableList(List list, ListListener listener) { + if(list instanceof RandomAccess) return new ListenableList(list, listener); + else return new ListenableSequentialList(list, listener); + } + + public static Set listenableSet(Set set, SetListener listener) { + return new ListenableSet(set, listener); + } + + public static Map listenableMap(Map map, MapListener listener) { + return new ListenableMap(map, listener); + } + + public static interface ListListener extends Serializable { + /** + * Notified after a value was added to the list. + */ + public void add(int index, E newValue); + + /** + * Notified after a value in the list was changed. + */ + public void set(int index, E oldValue, E newValue); + + /** + * Notified after a value was removed from the list. + */ + public void remove(int index, E oldValue); + } + + private static class ListenableList extends AbstractList implements RandomAccess, Serializable { + private static final long serialVersionUID = 8622608480525537692L; + + private List delegate; + private ListListener listener; + + public ListenableList(List delegate, ListListener listener) { + this.delegate = delegate; + this.listener = listener; + } + + @Override + public void add(int index, E e) { + delegate.add(index, e); + listener.add(index, e); + } + + @Override + public E set(int index, E element) { + E e = delegate.set(index, element); + listener.set(index, e, element); + return e; + } + + @Override + public E remove(int index) { + E e = delegate.remove(index); + listener.remove(index, e); + return e; + } + + @Override + public E get(int index) { + return delegate.get(index); + } + + @Override + public int size() { + return delegate.size(); + } + } + + private static class ListenableSequentialList extends AbstractSequentialList implements Serializable { + private static final long serialVersionUID = 3630474556578001885L; + + private List delegate; + private ListListener listener; + + public ListenableSequentialList(List delegate, ListListener listener) { + this.delegate = delegate; + this.listener = listener; + } + + @Override + public ListIterator listIterator(final int index) { + return new ListIterator() { + private final ListIterator it = delegate.listIterator(index); + private int lastIndex; + private E lastValue; + + public boolean hasNext() { + return it.hasNext(); + } + + public boolean hasPrevious() { + return it.hasPrevious(); + } + + public E next() { + lastIndex = it.nextIndex(); + lastValue = it.next(); + return lastValue; + } + + public int nextIndex() { + return it.nextIndex(); + } + + public E previous() { + lastIndex = it.previousIndex(); + lastValue = it.previous(); + return lastValue; + } + + public int previousIndex() { + return it.previousIndex(); + } + + public void add(E o) { + it.add(o); + listener.add(previousIndex(), o); + } + + public void set(E o) { + it.set(o); + listener.set(lastIndex, lastValue, o); + } + + public void remove() { + it.remove(); + listener.remove(lastIndex, lastValue); + } + }; + } + + @Override + public int size() { + return delegate.size(); + } + } + + public static interface SetListener extends Serializable { + /** + * Notified after a value was added to the set. + */ + public void add(E newValue); + + /** + * Notified after a value was removed from the set. + */ + public void remove(E oldValue); + } + + private static class ListenableSet extends AbstractSet implements Serializable { + private static final long serialVersionUID = 7728087988927063221L; + + private Set delegate; + private SetListener listener; + + public ListenableSet(Set set, SetListener listener) { + delegate = set; + this.listener = listener; + } + + @Override + public boolean contains(Object o) { + return delegate.contains(o); + } + + @Override + public boolean add(E o) { + boolean b = delegate.add(o); + if(b) listener.add(o); + return b; + }; + + @SuppressWarnings("unchecked") + @Override + public boolean remove(Object o) { + boolean b = delegate.remove(o); + if(b) listener.remove((E) o); + return b; + } + + @Override + public Iterator iterator() { + return new Iterator() { + private final Iterator it = delegate.iterator(); + private boolean hasLast; + private E last; + + public boolean hasNext() { + return it.hasNext(); + } + + public E next() { + last = it.next(); + hasLast = true; + return last; + } + + public void remove() { + if(!hasLast) throw new IllegalStateException(); + it.remove(); + listener.remove(last); + } + }; + } + + @Override + public int size() { + return delegate.size(); + } + } + + public static interface MapListener extends Serializable { + /** + * Notified after a value was put into the map. + */ + public void put(K key, V newValue); + + /** + * Notified after a value in the map was changed. + */ + public void set(K key, V oldValue, V newValue); + + /** + * Notified after a value was removed from the map. + */ + public void remove(K key, V oldValue); + } + + private static class ListenableMap extends AbstractMap implements Serializable { + private static final long serialVersionUID = 4032087477448965103L; + + private Map delegate; + private MapListener listener; + private Set> entrySet; + + public ListenableMap(Map map, MapListener listener) { + this.listener = listener; + delegate = map; + entrySet = new EntrySet(); + } + + @Override + public V put(K key, V value) { + if(delegate.containsKey(key)) { + V old = delegate.put(key, value); + listener.set(key, old, value); + return old; + } else { + delegate.put(key, value); + listener.put(key, value); + return null; + } + + } + + @SuppressWarnings("unchecked") + @Override + public V remove(Object key) { + if(delegate.containsKey(key)) { + V old = delegate.remove(key); + listener.remove((K) key, old); + return old; + } else return null; + } + + @Override + public V get(Object key) { + return delegate.get(key); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate.containsValue(value); + } + + @Override + public Set> entrySet() { + return entrySet; + } + + private final class EntrySet extends AbstractSet> implements Serializable { + private static final long serialVersionUID = -780485106953107075L; + private final Set> delegate = ListenableMap.this.delegate.entrySet(); + + @Override + public int size() { + return delegate.size(); + } + + @Override + public Iterator> iterator() { + return new Iterator>() { + private final Iterator> it = delegate.iterator(); + private boolean hasLast; + private Entry last; + + public boolean hasNext() { + return it.hasNext(); + } + + public Entry next() { + last = it.next(); + hasLast = true; + return last; + } + + public void remove() { + if(!hasLast) throw new IllegalStateException(); + hasLast = false; + it.remove(); + listener.remove(last.getKey(), last.getValue()); + } + }; + } + } + } +}