/** * */ package cz.cuni.mff.peckam.java.origamist.utils; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListenerProxy; import java.beans.PropertyChangeSupport; import java.util.LinkedList; import java.util.List; /** * A property change support allowing to remove a given listener instance completely from this support. * * @author Martin Pecka */ public class CustomPropertyChangeSupport extends PropertyChangeSupport { /** */ private static final long serialVersionUID = 1L; /** * Constructs a <code>CustomPropertyChangeSupport</code> object. * * @param sourceBean The bean to be given as the source for any events. */ public CustomPropertyChangeSupport(Object sourceBean) { super(sourceBean); } /** * Removes all occurences of the given instance of listener from this support. * * @param listener The listener instance to remove. * * @return The list of properties this listener was removed from (<code>null</code> signalizes all properties). */ public List<String> removeAllListeners(PropertyChangeListener listener) { List<String> result = new LinkedList<String>(); PropertyChangeListener[] listeners = getPropertyChangeListeners(); for (int i = 0; i < listeners.length; i++) { if (listeners[i] instanceof PropertyChangeListenerProxy) { PropertyChangeListenerProxy proxy = (PropertyChangeListenerProxy) listeners[i]; if (proxy.getListener() == listener) { removePropertyChangeListener(proxy.getPropertyName(), (PropertyChangeListener) proxy.getListener()); result.add(proxy.getPropertyName()); } } else { if (listeners[i] == listener) { removePropertyChangeListener(listener); result.add(null); } } } return result; } /** * Add a PropertyChangeListener for a specific hierarchicak property. The listener will be invoked only when a call * on firePropertyChange names that specific property. * The same listener object may be added more than once. For each property, the listener will be invoked the number * of times it was added for that property. * If <code>propertyName</code> or <code>listener</code> is null, no exception is thrown and no action is taken. * If <code>propertyName</code> is empty, {@link #addPropertyChangeListener(PropertyChangeListener)} is called. * * @param listener The PropertyChangeListener to be added * @param propertyName The components of hierarchical name of the property to listen on. */ public synchronized void addPropertyChangeListener(PropertyChangeListener listener, String... propertyName) { String name = getHierarchicalName(propertyName); addPropertyChangeListener("".equals(name) ? null : name, listener); } /** * Remove a PropertyChangeListener for a specific hierarchical property. * If <code>listener</code> was added more than once to the same event source for the specified property, it will be * notified one less time after being removed. * If <code>propertyName</code> is null, no exception is thrown and no action is taken. * If <code>propertyName</code> is empty, {@link #removePropertyChangeListener(PropertyChangeListener)} is called. * If <code>listener</code> is null, or was never added for the specified property, no exception is thrown and no * action is taken. * * @param listener The PropertyChangeListener to be removed * @param propertyName The components of hierarchical name of the property that was listened on. */ public synchronized void removePropertyChangeListener(PropertyChangeListener listener, String... propertyName) { String name = getHierarchicalName(propertyName); removePropertyChangeListener("".equals(name) ? null : name, listener); } /** * Add a listener that will receive all events with property names beginning with the specified prefix. * <p> * Remove this listener by {@link #removePrefixedPropertyChangeListener(PropertyChangeListener)}. * * @param listener The listener to be triggered. * @param prefix The prefix with which this listener will be triggered. If more strings are provided, they are * composed together to form a hierarchical property name. */ public void addPrefixedPropertyChangeListener(PropertyChangeListener listener, String... prefix) { addPropertyChangeListener(new PrefixedPropertyChangeListener(getHierarchicalName(prefix), listener)); } /** * Remove all occurences of a prefixed listener added by * {@link #addPrefixedPropertyChangeListener(String, PropertyChangeListener)}. * * @param listener The listener to be removed. */ public void removePrefixedPropertyChangeListener(PropertyChangeListener listener) { List<PropertyChangeListener> toRemove = new LinkedList<PropertyChangeListener>(); for (PropertyChangeListener l : getPropertyChangeListeners()) { if (l instanceof PrefixedPropertyChangeListener) { if (((PrefixedPropertyChangeListener) l).listener == listener) toRemove.add(l); } } for (PropertyChangeListener l : toRemove) { removePropertyChangeListener(l); } } /** * Build the hierarchical property name out of the given list of property names. * * @param names The list of property names to build the hierarchical name of. * @return The hierarchical property name. */ protected String getHierarchicalName(String... names) { if (names == null || names.length == 0) return ""; StringBuilder sb = new StringBuilder(names[0]); for (int i = 1; i < names.length; i++) sb.append("@").append(names[i]); return sb.toString(); } /** * A listener that is triggered on events with specific prefix. * * @author Martin Pecka */ protected class PrefixedPropertyChangeListener implements PropertyChangeListener { /** The prefix with which this listener will be triggered. */ protected String prefix; /** The listener to be triggered. */ protected PropertyChangeListener listener; /** * @param prefix The prefix with which this listener will be triggered. * @param listener The listener to be triggered. */ public PrefixedPropertyChangeListener(String prefix, PropertyChangeListener listener) { this.prefix = prefix; this.listener = listener; } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().startsWith(prefix)) listener.propertyChange(evt); } } }