All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jgoodies.binding.beans.IndirectPropertyChangeSupport Maven / Gradle / Ivy

Go to download

The JGoodies Binding library connects object properties to Swing user interface components. And it helps you represent the state and behavior of a presentation independently of the GUI components used in the interface.

There is a newer version: 2.13.0
Show newest version
/*
 * Copyright (c) 2002-2013 JGoodies Software GmbH. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  o Neither the name of JGoodies Software GmbH nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.jgoodies.binding.beans;

import static com.jgoodies.common.base.Preconditions.checkNotNull;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.jgoodies.binding.value.ValueHolder;
import com.jgoodies.binding.value.ValueModel;


/**
 * A helper class for observing changes in bound bean properties
 * where the target bean changes.
 *
 * Provides two access styles to the target bean that holds the observed
 * property: you can specify a bean directly,
 * or you can use a bean channel to access the bean indirectly.
 * In the latter case you specify a {@code ValueModel}
 * that holds the bean that in turn holds the observed properties.

* * If the target bean is {@code null}, it won't report any changes.

* * It is recommended to remove all listener by invoking {@code #removeAll} * if the observed bean lives much longer than this change support instance. * As an alternative you may use event listener lists that are based * on {@code WeakReference}s.

* * Constraints: All target bean classes must support * bound properties, i. e. must provide the following pair of methods * for registration of multicast property change event listeners: *

 * public void addPropertyChangeListener(PropertyChangeListener x);
 * public void removePropertyChangeListener(PropertyChangeListener x);
 * 
* and the following methods for listening on named properties: *
 * public void addPropertyChangeListener(String, PropertyChangeListener x);
 * public void removePropertyChangeListener(String, PropertyChangeListener x);
 * 
* * @author Karsten Lentzsch * @version $Revision: 1.13 $ * * @see PropertyChangeEvent * @see PropertyChangeListener * @see PropertyChangeSupport * @see com.jgoodies.binding.beans.BeanAdapter */ public final class IndirectPropertyChangeSupport { /** * Holds a {@code ValueModel} that holds the bean, that in turn * holds the adapted property. * * @see #getBean() * @see #setBean(Object) */ private final ValueModel beanChannel; /** * Holds the PropertyChangeListeners that are registered for all bound * properties of the target bean. If the target bean changes, * these listeners are removed from the old bean and added to the new bean. * * @see #addPropertyChangeListener(PropertyChangeListener) * @see #removePropertyChangeListener(PropertyChangeListener) * @see #getPropertyChangeListeners() */ private final List listenerList; /** * Maps property names to the list of PropertyChangeListeners that are * registered for the associated bound property of the target bean. * If the target bean changes, these listeners are removed from * the old bean and added to the new bean. * * @see #addPropertyChangeListener(String, PropertyChangeListener) * @see #removePropertyChangeListener(String, PropertyChangeListener) * @see #getPropertyChangeListeners(String) */ private final Map> namedListeners; // Instance creation **************************************************** /** * Constructs an IndirectPropertyChangeSupport that has no bean set. */ public IndirectPropertyChangeSupport() { this(new ValueHolder(null, true)); } /** * Constructs an IndirectPropertyChangeSupport with the given initial bean. * * @param bean the initial bean */ public IndirectPropertyChangeSupport(Object bean) { this(new ValueHolder(bean, true)); } /** * Constructs an IndirectPropertyChangeSupport using the given bean channel. * * @param beanChannel the ValueModel that holds the bean */ public IndirectPropertyChangeSupport(ValueModel beanChannel) { this.beanChannel = checkNotNull(beanChannel, "The bean channel must not be null."); listenerList = new ArrayList(); namedListeners = new HashMap>(); beanChannel.addValueChangeListener(new BeanChangeHandler()); } // Accessors ************************************************************ /** * Returns the Java Bean that holds the observed properties. * * @return the Bean that holds the observed properties * * @see #setBean(Object) */ public Object getBean() { return beanChannel.getValue(); } /** * Sets a new Java Bean as holder of the observed properties. * Removes all registered listeners from the old bean and * adds them to the new bean. * * @param newBean the new holder of the observed properties * * @see #getBean() */ public void setBean(Object newBean) { beanChannel.setValue(newBean); } // Managing Property Change Listeners ************************************* /** * Adds a PropertyChangeListener to the list of bean listeners. * The listener is registered for all bound properties of the target bean. *

* * If listener is {@code null}, no exception is thrown and no action is performed. * * @param listener the PropertyChangeListener to be added * * @see #removePropertyChangeListener(PropertyChangeListener) * @see #removePropertyChangeListener(String, PropertyChangeListener) * @see #addPropertyChangeListener(String, PropertyChangeListener) * @see #getPropertyChangeListeners() */ public synchronized void addPropertyChangeListener( PropertyChangeListener listener) { if (listener == null) { return; } listenerList.add(listener); Object bean = getBean(); if (bean != null) { BeanUtils.addPropertyChangeListener(bean, listener); } } /** * Removes a PropertyChangeListener from the list of bean listeners. * This method should be used to remove PropertyChangeListeners that * were registered for all bound properties of the target bean.

* * If listener is {@code null}, no exception is thrown and no action is performed. * * @param listener the PropertyChangeListener to be removed * * @see #addPropertyChangeListener(PropertyChangeListener) * @see #addPropertyChangeListener(String, PropertyChangeListener) * @see #removePropertyChangeListener(String, PropertyChangeListener) * @see #getPropertyChangeListeners() */ public synchronized void removePropertyChangeListener( PropertyChangeListener listener) { if (listener == null) { return; } listenerList.remove(listener); Object bean = getBean(); if (bean != null) { BeanUtils.removePropertyChangeListener(bean, listener); } } /** * Adds a PropertyChangeListener to the list of bean listeners for a * specific property. The specified property may be user-defined.

* * Note that if the bean is inheriting a bound property, then no event * will be fired in response to a change in the inherited property.

* * If listener is {@code null}, no exception is thrown and no action is performed. * * @param propertyName one of the property names listed above * @param listener the PropertyChangeListener to be added * * @see #removePropertyChangeListener(String, PropertyChangeListener) * @see #addPropertyChangeListener(String, PropertyChangeListener) * @see #getPropertyChangeListeners(String) */ public synchronized void addPropertyChangeListener( String propertyName, PropertyChangeListener listener) { if (listener == null) { return; } List namedListenerList = namedListeners.get(propertyName); if (namedListenerList == null) { namedListenerList = new ArrayList(); namedListeners.put(propertyName, namedListenerList); } namedListenerList.add(listener); Object bean = getBean(); if (bean != null) { BeanUtils.addPropertyChangeListener(bean, propertyName, listener); } } /** * Removes a PropertyChangeListener from the listener list for a specific * property. This method should be used to remove PropertyChangeListeners * that were registered for a specific bound property.

* * If listener is {@code null}, no exception is thrown and no action is performed. * * @param propertyName a valid property name * @param listener the PropertyChangeListener to be removed * * @see #addPropertyChangeListener(String, PropertyChangeListener) * @see #removePropertyChangeListener(PropertyChangeListener) * @see #getPropertyChangeListeners(String) */ public synchronized void removePropertyChangeListener( String propertyName, PropertyChangeListener listener) { if (listener == null) { return; } List namedListenerList = namedListeners.get(propertyName); if (namedListenerList == null) { return; } namedListenerList.remove(listener); Object bean = getBean(); if (bean != null) { BeanUtils.removePropertyChangeListener(bean, propertyName, listener); } } // Requesting Listener Sets *********************************************** /** * Returns an array of all the property change listeners * registered on this component. * * @return all of this component's {@code PropertyChangeListener}s * or an empty array if no property change * listeners are currently registered * * @see #addPropertyChangeListener(PropertyChangeListener) * @see #removePropertyChangeListener(PropertyChangeListener) * @see #getPropertyChangeListeners(String) * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners() */ public synchronized PropertyChangeListener[] getPropertyChangeListeners() { if (listenerList.isEmpty()) { return new PropertyChangeListener[0]; } return listenerList.toArray(new PropertyChangeListener[listenerList.size()]); } /** * Returns an array of all the listeners which have been associated * with the named property. * * @param propertyName the name of the property to lookup listeners * @return all of the {@code PropertyChangeListeners} associated with * the named property or an empty array if no listeners have * been added * * @see #addPropertyChangeListener(String, PropertyChangeListener) * @see #removePropertyChangeListener(String, PropertyChangeListener) * @see #getPropertyChangeListeners() */ public synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { List namedListenerList = namedListeners.get(propertyName); if (namedListenerList == null || namedListenerList.isEmpty()) { return new PropertyChangeListener[0]; } return namedListenerList.toArray(new PropertyChangeListener[namedListenerList.size()]); } // Releasing PropertyChangeListeners ************************************** /** * Removes all registered PropertyChangeListeners from * the current target bean - if any. */ public void removeAll() { removeAllListenersFrom(getBean()); } // Changing the Bean & Adding and Removing the PropertyChangeHandlers ***** private void setBean0(Object oldBean, Object newBean) { removeAllListenersFrom(oldBean); addAllListenersTo(newBean); } /** * Adds all registered PropertyChangeListeners from the given bean. * If the bean is null no exception is thrown and no action is taken. * * @param bean the bean to add a the property change listeners to */ private void addAllListenersTo(Object bean) { if (bean == null) { return; } for (PropertyChangeListener listener : listenerList) { BeanUtils.addPropertyChangeListener(bean, listener); } for (Entry> entry : namedListeners.entrySet()) { String propertyName = entry.getKey(); for (PropertyChangeListener listener : entry.getValue()) { BeanUtils.addPropertyChangeListener(bean, propertyName, listener); } } } /** * Removes all registered PropertyChangeListeners from the given bean. * If the bean is null no exception is thrown and no action is taken. * * @param bean the bean to remove the property change handler from. * @throws PropertyUnboundException * if the bean does not support bound properties * @throws PropertyNotBindableException * if the property change handler cannot be removed successfully */ private void removeAllListenersFrom(Object bean) { if (bean == null) { return; } for (PropertyChangeListener listener : listenerList) { BeanUtils.removePropertyChangeListener(bean, listener); } for (Entry> entry : namedListeners.entrySet()) { String propertyName = entry.getKey(); for (PropertyChangeListener listener : entry.getValue()) { BeanUtils.removePropertyChangeListener(bean, propertyName, listener); } } } // Helper Classes ********************************************************* /** * Listens to changes of the bean. */ private final class BeanChangeHandler implements PropertyChangeListener { /** * The bean channel's value has been changed. Set the new bean, * remove all listeners from the old bean and add them to the new bean. * * @param evt the property change event to be handled */ @Override public void propertyChange(PropertyChangeEvent evt) { setBean0(evt.getOldValue(), evt.getNewValue()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy