/* * Beanfabrics Framework Copyright (C) by Michael Karneim, beanfabrics.org * Use is subject to license terms. See license.txt. */ package org.beanfabrics.event; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.EventObject; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.WeakHashMap; import org.beanfabrics.model.IListPM; import org.beanfabrics.model.PresentationModel; import org.beanfabrics.util.Interval; /** * The {@link ListSupport} is a utility class for handling {@link ListListener} * objects. * * @author Michael Karneim */ public class ListSupport { private final IListPM<?> source; private List<ListListener> listeners = new LinkedList<ListListener>(); private final WeakHashMap<ListListener, WeakWrapper> weakWrapperMap = new WeakHashMap<ListListener, WeakWrapper>(); /** * Constructs a {@link ListSupport} for the given source. * * @param source */ public ListSupport(IListPM<?> source) { this.source = source; } /** * Adds the given listener. * * @param listener */ public synchronized void addListListener(ListListener listener) { if (listener == null) { throw new IllegalArgumentException("l==null"); } if (listener instanceof WeakListener) { listener = this.getOrCreateWeakWrapper(listener); } LinkedList<ListListener> newList = new LinkedList<ListListener>(this.listeners); removeUnusedWeakWrappers(newList.iterator()); newList.add(listener); this.listeners = newList; } /** * Removes the given listener. * * @param listener */ public synchronized void removeListListener(ListListener listener) { LinkedList<ListListener> newList = new LinkedList<ListListener>(listeners); if (listener instanceof WeakListener) { listener = this.getOrCreateWeakWrapper(listener); this.removeWeakWrapper((WeakWrapper)listener); } newList.remove(listener); this.listeners = newList; } /** * Fires the given {@link ElementsAddedEvent} to each registered listener. * * @param evt */ public void fireElementsAdded(ElementsAddedEvent evt) { for (ListListener l : this.listeners) { l.elementsAdded(evt); } } /** * Fires a {@link ElementsAddedEvent} with the given attributes to each * registered listener. * * @param beginIndex * @param length */ public void fireElementsAdded(int beginIndex, int length) { this.fireElementsAdded(new ElementsAddedEvent(this.source, beginIndex, length)); } /** * Fires the given {@link ElementsRemovedEvent} to each registered listener. * * @param evt */ public void fireElementsRemoved(ElementsRemovedEvent evt) { for (ListListener l : this.listeners) { l.elementsRemoved(evt); } } /** * Fires a {@link ElementsRemovedEvent} with the given attributes to each * registered listener. * * @param beginIndex * @param length * @param removed */ public void fireElementsRemoved(int beginIndex, int length, Collection<PresentationModel> removed) { this.fireElementsRemoved(new ElementsRemovedEvent(this.source, beginIndex, length, removed)); } /** * Fires a {@link ElementsRemovedEvent} with the given attributes to each * registered listener. * * @param index * @param removed */ public void fireElementsRemoved(int index, PresentationModel removed) { ArrayList<PresentationModel> list = new ArrayList<PresentationModel>(1); list.add(removed); this.fireElementsRemoved(new ElementsRemovedEvent(this.source, index, 1, list)); } /** * Fires the given {@link ElementsReplacedEvent} to each registered * listener. * * @param evt */ public void fireElementsReplaced(ElementsReplacedEvent evt) { for (ListListener l : this.listeners) { l.elementsReplaced(evt); } } /** * Fires a {@link ElementsReplacedEvent} with the given attributes to each * registered listener. * * @param beginIndex * @param length * @param old */ public void fireElementsReplaced(int beginIndex, int length, Collection<PresentationModel> old) { this.fireElementsReplaced(new ElementsReplacedEvent(this.source, beginIndex, length, old)); } /** * Fires a {@link ElementsReplacedEvent} with the given attributes to each * registered listener. * * @param index * @param old */ public void fireElementsReplaced(int index, PresentationModel old) { ArrayList<PresentationModel> oldList = new ArrayList<PresentationModel>(1); oldList.add(old); this.fireElementsReplaced(new ElementsReplacedEvent(this.source, index, 1, oldList)); } /** * Fires the given {@link ElementChangedEvent} to each registered listener. * * @param evt */ public void fireElementChanged(ElementChangedEvent evt) { for (ListListener l : this.listeners) { l.elementChanged(evt); } } /** * Fires a {@link ElementChangedEvent} with the given index to each * registered listener. * * @param index */ public void fireElementChanged(int index) { this.fireElementChanged(new ElementChangedEvent(this.source, index)); } /** * Fires a {@link ElementChangedEvent} with the given attributes to each * registered listener. * * @param index * @param cause */ public void fireElementChanged(int index, EventObject cause) { this.fireElementChanged(new ElementChangedEvent(this.source, index, cause)); } /** * Fires the given {@link ElementsSelectedEvent} to each registered * listener. * * @param evt */ public void fireElementsSelected(ElementsSelectedEvent evt) { for (ListListener l : this.listeners) { l.elementsSelected(evt); } } /** * Fires a {@link ElementsSelectedEvent} with the given attributes to each * registered listener. * * @param beginIndex * @param length */ public void fireElementsSelected(int beginIndex, int length) { this.fireElementsSelected(new ElementsSelectedEvent(this.source, beginIndex, length)); } /** * Fires a {@link ElementsSelectedEvent} with the given indices to each * registered listener. * * @param indices */ public void fireElementsSelected(int[] indices) { Interval[] intervals = Interval.createIntervals(indices); for (Interval i : intervals) { fireElementsSelected(i.startIndex, 1 + i.endIndex - i.startIndex); } } /** * Fires a {@link ElementsSelectedEvent} with the given indices to each * registered listener. * * @param indices */ public void fireElementsSelected(Collection<Integer> indices) { Interval[] intervals = Interval.createIntervals(indices); for (Interval i : intervals) { fireElementsSelected(i.startIndex, 1 + i.endIndex - i.startIndex); } } /** * Fires the given {@link ElementsDeselectedEvent} to each registered * listener. * * @param evt */ public void fireElementsDeselected(ElementsDeselectedEvent evt) { for (ListListener l : this.listeners) { l.elementsDeselected(evt); } } /** * Fires a {@link ElementsDeselectedEvent} with the given attributes to each * registered listener. * * @param beginIndex * @param length */ public void fireElementsDeselected(int beginIndex, int length) { this.fireElementsDeselected(new ElementsDeselectedEvent(this.source, beginIndex, length)); } /** * Fires a {@link ElementsDeselectedEvent} with the given indices to each * registered listener. * * @param indices */ public void fireElementsDeselected(int[] indices) { Interval[] intervals = Interval.createIntervals(indices); for (Interval i : intervals) { fireElementsDeselected(i.startIndex, 1 + i.endIndex - i.startIndex); } } /** * Fires a {@link ElementsDeselectedEvent} with the given indices to each * registered listener. * * @param indices */ public void fireElementsDeselected(Collection<Integer> indices) { Interval[] intervals = Interval.createIntervals(indices); for (Interval i : intervals) { fireElementsDeselected(i.startIndex, 1 + i.endIndex - i.startIndex); } } /** * Returns the {@link WeakWrapper} for the given listener. * * @param listener * @return the {@link WeakWrapper} for the given listener */ private WeakWrapper getOrCreateWeakWrapper(ListListener listener) { WeakWrapper result = this.weakWrapperMap.get(listener); if (result == null) { result = new WeakWrapper(listener); this.weakWrapperMap.put(listener, result); } return result; } /** * Removes the given {@link WeakWrapper} from the weakWrapper cache. * * @param wrapper */ private void removeWeakWrapper(WeakWrapper wrapper) { this.weakWrapperMap.remove(wrapper); } /** * Removes all unused WeakWrapper instances from the given iterator. * * @param it */ private static void removeUnusedWeakWrappers(Iterator<ListListener> it) { while (it.hasNext()) { ListListener l = it.next(); if (l instanceof WeakWrapper && ((WeakWrapper)l).isCleared()) { it.remove(); } } } /** * The {@link WeakWrapper} is a delegator that forwards {@link ListEvent}s * to a weakly referenced delegate object as long as that reference has not * been cleared. */ private static class WeakWrapper implements ListListener { private WeakReference<ListListener> ref; /** * Constructs a {@link WeakWrapper} for the given delegate. * * @param aDelegate */ WeakWrapper(ListListener aDelegate) { this.setDelegate(aDelegate); } /** * Returns <code>true</code>, if the delegate reference has been * cleared. * * @return <code>true</code>, if the delegate reference has been cleared */ boolean isCleared() { return this.ref == null || this.ref.get() == null; } /** * Returns the delegate. * * @return the delegate */ ListListener getDelegate() { if (this.ref == null) { return null; } return this.ref.get(); } /** * Sets the delegate. * * @param aDelegate */ void setDelegate(ListListener aDelegate) { if (aDelegate == null) { this.ref = null; } else { this.ref = new WeakReference<ListListener>(aDelegate); } } /** {@inheritDoc} */ public void elementChanged(ElementChangedEvent evt) { if (!isCleared()) { this.getDelegate().elementChanged(evt); } } /** {@inheritDoc} */ public void elementsAdded(ElementsAddedEvent evt) { if (!isCleared()) { this.getDelegate().elementsAdded(evt); } } /** {@inheritDoc} */ public void elementsDeselected(ElementsDeselectedEvent evt) { if (!isCleared()) { this.getDelegate().elementsDeselected(evt); } } /** {@inheritDoc} */ public void elementsRemoved(ElementsRemovedEvent evt) { if (!isCleared()) { this.getDelegate().elementsRemoved(evt); } } /** {@inheritDoc} */ public void elementsReplaced(ElementsReplacedEvent evt) { if (!isCleared()) { this.getDelegate().elementsReplaced(evt); } } /** {@inheritDoc} */ public void elementsSelected(ElementsSelectedEvent evt) { if (!isCleared()) { this.getDelegate().elementsSelected(evt); } } } }