/* * org.openmicroscopy.shoola.util.ui.component.AbstractComponent * *------------------------------------------------------------------------------ * Copyright (C) 2006 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.util.ui.component; //Java imports import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; //Third-party libraries //Application-internal dependencies /** * Convenience class from which components can inherit to gain support for * change propagation. * This class implements the {@link ObservableComponent} and is an abstract * publisher that maintains a registry of currently-subscribed observers. * A subclass automatically becomes a <i>source bean</i> and uses the * {@link #firePropertyChange(String, Object, Object) firePropertyChange} to * trigger notification of <i>bound property</i> changes. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author <br>Andrea Falconi      * <a href="mailto:a.falconi@dundee.ac.uk"> * a.falconi@dundee.ac.uk</a> * @version 2.2 * <small> * (<b>Internal version:</b> $Revision$ $Date$) * </small> * @since OME2.2 */ public abstract class AbstractComponent implements ObservableComponent { /** Change notification registry for change observers. */ private Set<ChangeListener> changeRegistry; /** Used for all change notifications coming from this publisher. */ private ChangeEvent changeEvent; /** Change notification registry for bound properties observers. */ private Map<Object, Set> propsRegistry; /** * Key used to look up observers in the {@link #propsRegistry} that * registered for all bound properties. */ private Object allPropsKey; /** * Initializes the notification registries. */ protected AbstractComponent() { changeRegistry = new HashSet<ChangeListener>(); changeEvent = new ChangeEvent(this); propsRegistry = new HashMap<Object, Set>(); allPropsKey = new Object(); } /** * Supports reporting any state change. * All change listeners will be notified. */ protected void fireStateChange() { ChangeListener observer; Iterator<ChangeListener> i = changeRegistry.iterator(); while (i.hasNext()) { observer = i.next(); observer.stateChanged(changeEvent); } } /** * Supports reporting bound property changes. * Listeners will be notified only if <code>newValue</code> is not the * same as <code>oldValue</code>. In this case all state change listeners * will be notified too, as state has changed. * * @param propertyName The property that has been set. * @param oldValue The previous value of the property. * @param newValue The value that has just been set. */ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { //Make sure we've got an actual state change. if (oldValue == null && newValue == null) return; if (oldValue != null && newValue != null && oldValue.equals(newValue)) return; //Build the union of the all-properties observers and propertyName //observers sets. This way we avoid repeated notification. Set allPropsListeners = propsRegistry.get(allPropsKey), propListeners = propsRegistry.get(propertyName); Set<Set> notificationSet = new HashSet<Set>(); if (allPropsListeners != null) notificationSet.addAll(allPropsListeners); if (propListeners != null) notificationSet.addAll(propListeners); //Do the stateless notification, then just return if there are no //observers for the stateful notification. //fireStateChange(); 05/03 test if (notificationSet.size() == 0) return; //Ok then, we've got observers. Let's create the event and then //dispatch it. PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); PropertyChangeListener observer; Iterator i = notificationSet.iterator(); while (i.hasNext()) { observer = (PropertyChangeListener) i.next(); observer.propertyChange(event); } } /** * Implemented as specified by the {@link ObservableComponent} interface. * @see ObservableComponent#addChangeListener(ChangeListener) */ public void addChangeListener(ChangeListener observer) { if (observer == null) throw new NullPointerException("No observer."); changeRegistry.add(observer); } /** * Implemented as specified by the {@link ObservableComponent} interface. * @see ObservableComponent#removeChangeListener(ChangeListener) */ public void removeChangeListener(ChangeListener observer) { if (observer == null) throw new NullPointerException("No observer."); changeRegistry.remove(observer); } /** * Implemented as specified by the {@link ObservableComponent} interface. * @see ObservableComponent#addPropertyChangeListener( * PropertyChangeListener) */ public void addPropertyChangeListener(PropertyChangeListener observer) { if (observer == null) throw new NullPointerException("No observer."); Set allPropsListeners = propsRegistry.get(allPropsKey); if (allPropsListeners == null) { allPropsListeners = new HashSet(); propsRegistry.put(allPropsKey, allPropsListeners); } allPropsListeners.add(observer); } /** * Implemented as specified by the {@link ObservableComponent} interface. * @see ObservableComponent#removePropertyChangeListener( * PropertyChangeListener) */ public void removePropertyChangeListener(PropertyChangeListener observer) { if (observer == null) throw new NullPointerException("No observer."); Set allPropsListeners = propsRegistry.get(allPropsKey); if (allPropsListeners != null) allPropsListeners.remove(observer); } /** * Implemented as specified by the {@link ObservableComponent} interface. * @see ObservableComponent#addPropertyChangeListener(String, * PropertyChangeListener) */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener observer) { if (propertyName == null) throw new NullPointerException("No property name."); if (observer == null) throw new NullPointerException("No observer."); Set propListeners = propsRegistry.get(propertyName); if (propListeners == null) { propListeners = new HashSet(); propsRegistry.put(propertyName, propListeners); } propListeners.add(observer); } /** * Implemented as specified by the {@link ObservableComponent} interface. * @see ObservableComponent#removePropertyChangeListener(String, * PropertyChangeListener) */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener observer) { if (propertyName == null) throw new NullPointerException("No property name."); if (observer == null) throw new NullPointerException("No observer."); Set propListeners = propsRegistry.get(propertyName); if (propListeners != null) propListeners.remove(observer); } }