/* * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. * * Distributable under LGPL license. * See terms of license at gnu.org. */ package net.java.sip.communicator.impl.configuration; import java.util.*; import net.java.sip.communicator.service.configuration.*; import net.java.sip.communicator.service.configuration.event.*; import net.java.sip.communicator.util.*; /** * This is a utility class that can be used by objects that support constrained * properties. You can use an instance of this class as a member field and * delegate various work to it. * * @author Emil Ivov */ public class ChangeEventDispatcher { /** * All property change listeners registered so far. */ private List<PropertyChangeListener> propertyChangeListeners; /** * All listeners registered for vetoable change events. */ private List<VetoableChangeListener> vetoableChangeListeners; /** * Hashtable for managing property change listeners registered for specific * properties. Maps property names to PropertyChangeSupport objects. */ private Map<String, ChangeEventDispatcher> propertyChangeChildren; /** * Hashtable for managing vetoable change listeners registered for specific * properties. Maps property names to PropertyChangeSupport objects. */ private Map<String, ChangeEventDispatcher> vetoableChangeChildren; /** * The object to be provided as the "source" for any generated events. */ private Object source; /** * Constructs a <tt>VetoableChangeSupport</tt> object. * * @param sourceObject The object to be given as the source for any events. */ public ChangeEventDispatcher(Object sourceObject) { if (sourceObject == null) { throw new NullPointerException(); } source = sourceObject; } /** * Add a PropertyChangeListener to the listener list. * The listener is registered for all properties. * * @param listener The PropertyChangeChangeListener to be added */ public synchronized void addPropertyChangeListener( PropertyChangeListener listener) { if (propertyChangeListeners == null) { propertyChangeListeners = new Vector<PropertyChangeListener>(); } propertyChangeListeners.add(listener); } /** * Add a PropertyChangeListener for a specific property. The listener * will be invoked only when a call on firePropertyChange names that * specific property. * * @param propertyName The name of the property to listen on. * @param listener The ConfigurationChangeListener to be added */ public synchronized void addPropertyChangeListener( String propertyName, PropertyChangeListener listener) { if (propertyChangeChildren == null) { propertyChangeChildren = new Hashtable<String, ChangeEventDispatcher>(); } ChangeEventDispatcher child = (ChangeEventDispatcher) propertyChangeChildren.get( propertyName); if (child == null) { child = new ChangeEventDispatcher(source); propertyChangeChildren.put(propertyName, child); } child.addPropertyChangeListener(listener); } /** * Remove a PropertyChangeListener from the listener list. * This removes a ConfigurationChangeListener that was registered * for all properties. * * @param listener The PropertyChangeListener to be removed */ public synchronized void removePropertyChangeListener( PropertyChangeListener listener) { if (propertyChangeListeners != null) { propertyChangeListeners.remove(listener); } } /** * Remove a PropertyChangeListener for a specific property. * * @param propertyName The name of the property that was listened on. * @param listener The VetoableChangeListener to be removed */ public synchronized void removePropertyChangeListener( String propertyName, PropertyChangeListener listener) { if (propertyChangeChildren == null) { return; } ChangeEventDispatcher child = propertyChangeChildren.get(propertyName); if (child == null) { return; } child.removePropertyChangeListener(listener); } /** * Add a VetoableChangeListener to the listener list. * The listener is registered for all properties. * * @param listener The VetoableChangeListener to be added */ public synchronized void addVetoableChangeListener( VetoableChangeListener listener) { if (vetoableChangeListeners == null) { vetoableChangeListeners = new Vector<VetoableChangeListener>(); } vetoableChangeListeners.add(listener); } /** * Remove a VetoableChangeListener from the listener list. * This removes a VetoableChangeListener that was registered * for all properties. * * @param listener The VetoableChangeListener to be removed */ public synchronized void removeVetoableChangeListener( VetoableChangeListener listener) { if (vetoableChangeListeners != null) { vetoableChangeListeners.remove(listener); } } /** * Add a VetoableChangeListener for a specific property. The listener * will be invoked only when a call on fireVetoableChange names that * specific property. * * @param propertyName The name of the property to listen on. * @param listener The ConfigurationChangeListener to be added */ public synchronized void addVetoableChangeListener( String propertyName, VetoableChangeListener listener) { if (vetoableChangeChildren == null) { vetoableChangeChildren = new Hashtable<String, ChangeEventDispatcher>(); } ChangeEventDispatcher child = vetoableChangeChildren.get(propertyName); if (child == null) { child = new ChangeEventDispatcher(source); vetoableChangeChildren.put(propertyName, child); } child.addVetoableChangeListener(listener); } /** * Remove a VetoableChangeListener for a specific property. * * @param propertyName The name of the property that was listened on. * @param listener The VetoableChangeListener to be removed */ public synchronized void removeVetoableChangeListener( String propertyName, VetoableChangeListener listener) { if (vetoableChangeChildren == null) { return; } ChangeEventDispatcher child = vetoableChangeChildren.get( propertyName ); if (child == null) { return; } child.removeVetoableChangeListener(listener); } /** * Report a vetoable property update to any registered listeners. If * no one vetos the change, then fire a new ConfigurationChangeEvent * indicating that the change has been accepted. In the case of a * PropertyVetoException, end eventdispatch and rethrow the eception * <p> * No event is fired if old and new are equal and non-null. * * @param propertyName The programmatic name of the property * that is about to change.. * @param oldValue The old value of the property. * @param newValue The new value of the property. * @exception PropertyVetoException if the recipient wishes the property * change to be rolled back. */ public void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws PropertyVetoException { if (vetoableChangeListeners == null && vetoableChangeChildren == null) { return; } PropertyChangeEvent evt = new PropertyChangeEvent(source, propertyName, oldValue, newValue); fireVetoableChange(evt); } /** * Fire a vetoable property update to any registered listeners. If * anyone vetos the change, then the excption will be rethrown by this * method. * <p> * No event is fired if old and new are equal and non-null. * * @param evt The PropertyChangeEvent to be fired. * @exception PropertyVetoException if at least one of the recipients has * vetoed the change. */ public void fireVetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { Object oldValue = evt.getOldValue(); Object newValue = evt.getNewValue(); String propertyName = evt.getPropertyName(); if (oldValue != null && newValue != null && oldValue.equals(newValue)) { return; } VetoableChangeListener[] targets = null; ChangeEventDispatcher child = null; synchronized (this) { if (vetoableChangeListeners != null) { targets = vetoableChangeListeners .toArray(new VetoableChangeListener[vetoableChangeListeners .size()]); } if (vetoableChangeChildren != null && propertyName != null) { child = vetoableChangeChildren.get(propertyName); } } if (vetoableChangeListeners != null) { for (int i = 0; i < targets.length; i++) { VetoableChangeListener target = targets[i]; // don't catch the exception - let it bounce to the caller. target.vetoableChange(evt); } } if (child != null) { child.fireVetoableChange(evt); } } /** * Report a bound property update to any registered listeners. * No event is fired if old and new are equal and non-null. * * @param propertyName The programmatic name of the property * that was changed. * @param oldValue The old value of the property. * @param newValue The new value of the property. */ public void firePropertyChange(String propertyName, Object oldValue, Object newValue) { if (oldValue != null && newValue != null && oldValue.equals(newValue)) { return; } firePropertyChange(new PropertyChangeEvent(source, propertyName, oldValue, newValue)); } /** * Fire an existing PropertyChangeEvent to any registered listeners. * No event is fired if the given event's old and new values are * equal and non-null. * @param evt The PropertyChangeEvent object. */ public void firePropertyChange(PropertyChangeEvent evt) { Object oldValue = evt.getOldValue(); Object newValue = evt.getNewValue(); String propertyName = evt.getPropertyName(); if (oldValue != null && newValue != null && oldValue.equals(newValue)) { return; } if (propertyChangeListeners != null) { for (PropertyChangeListener target : propertyChangeListeners) { target.propertyChange(evt); } } if (propertyChangeChildren != null && propertyName != null) { ChangeEventDispatcher child = propertyChangeChildren.get(propertyName); if (child != null) { child.firePropertyChange(evt); } } } /** * Check if there are any listeners for a specific property. (Generic * listeners count as well) * * @param propertyName the property name. * @return true if there are one or more listeners for the given property */ public synchronized boolean hasPropertyChangeListeners(String propertyName) { if(propertyChangeListeners != null && !propertyChangeListeners.isEmpty()) { // there is a generic listener return true; } if (propertyChangeChildren != null) { ChangeEventDispatcher child = propertyChangeChildren.get(propertyName); if (child != null && child.propertyChangeListeners != null) { return!child.propertyChangeListeners.isEmpty(); } } return false; } /** * Check if there are any vetoable change listeners for a specific property. * (Generic vetoable change listeners count as well) * * @param propertyName the property name. * @return true if there are one or more listeners for the given property */ public synchronized boolean hasVetoableChangeListeners(String propertyName) { if(vetoableChangeListeners != null && !vetoableChangeListeners.isEmpty()) { // there is a generic listener return true; } if (vetoableChangeChildren != null) { ChangeEventDispatcher child = vetoableChangeChildren.get(propertyName); if (child != null && child.vetoableChangeListeners != null) { return!child.vetoableChangeListeners.isEmpty(); } } return false; } }