001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.tools;
003
004import java.util.AbstractCollection;
005import java.util.Iterator;
006import java.util.List;
007
008/**
009 * This is a proxy of a collection that notifies a listener on every collection change
010 * @author Michael Zangl
011 *
012 * @param <T> The entry type
013 * @since 12267 (extracted from GpxData)
014 * @since 12156
015 */
016public class ListeningCollection<T> extends AbstractCollection<T> {
017    private final List<T> base;
018    private final Runnable runOnModification;
019
020    /**
021     * Constructs a new {@code ListeningCollection}.
022     * @param base base collection
023     * @param runOnModification runnable run at each modification
024     * @since 12269
025     */
026    public ListeningCollection(List<T> base, Runnable runOnModification) {
027        this.base = base;
028        this.runOnModification = runOnModification;
029    }
030
031    @Override
032    public final Iterator<T> iterator() {
033        Iterator<T> it = base.iterator();
034        return new Iterator<T>() {
035            private T object;
036
037            @Override
038            public boolean hasNext() {
039                return it.hasNext();
040            }
041
042            @Override
043            public T next() {
044                object = it.next();
045                return object;
046            }
047
048            @Override
049            public void remove() {
050                if (object != null) {
051                    removed(object);
052                    object = null;
053                }
054                it.remove();
055            }
056        };
057    }
058
059    @Override
060    public final int size() {
061        return base.size();
062    }
063
064    @Override
065    @SuppressWarnings("unchecked")
066    public final boolean remove(Object o) {
067        boolean remove = base.remove(o);
068        if (remove) {
069            removed((T) o);
070        }
071        return remove;
072    }
073
074    @Override
075    public final boolean add(T e) {
076        boolean add = base.add(e);
077        added(e);
078        return add;
079    }
080
081    /**
082     * Called when an object is removed.
083     * @param object the removed object
084     */
085    protected void removed(T object) {
086        runOnModification.run();
087    }
088
089    /**
090     * Called when an object is added.
091     * @param object the added object
092     */
093    protected void added(T object) {
094        runOnModification.run();
095    }
096}