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

com.jgoodies.binding.beans.ExtendedPropertyChangeSupport 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.0.6
Show newest version
/*
 * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. 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 Karsten Lentzsch 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 java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeListenerProxy;
import java.beans.PropertyChangeSupport;

/**
 * Differs from its superclass {@link PropertyChangeSupport} in that it can
 * check for changed values using #equals or ==.
 * Useful if you want to ensure that a PropertyChangeEvent is fired
 * if the old and new value are not the same but if they are equal.

* * The Java * Bean Specification recommends to not throw a * PropertyChangeEvent if the old and new value of a bound * Bean property are equal (see chapter 7.4.4). This can reduce the number * of events fired and helps avoid loops. Nevertheless a bound property * may fire an event if the old and new value are equal.

* * An example for a condition where the identity check == * is required and the #equals test fails is class * {@link com.jgoodies.binding.list.SelectionInList}. If the contained * ListModel changes its value, an internal listener is removed * from the old value and added to the new value. The listener must be * moved from the old instance to the new instance even if these are equal. * The PropertyChangeSupport doesn't fire a property change event * if such a ListModel is implemented as a {@link java.util.List}. * This is because instances of List are equal if and only if * all list members are equal and if they are in the same sequence.

* * This class provides two means to fire an event if the old and new * value are equal but not the same. First, you enable the identity check * in constructor {@link #ExtendedPropertyChangeSupport(Object, boolean)}. * By default all calls to #firePropertyChange will then * check the identity, not the equality. Second, you can invoke * {@link #firePropertyChange(PropertyChangeEvent, boolean)} or * {@link #firePropertyChange(String, Object, Object, boolean)} and * enable or disable the identity check for this call only.

* * TODO: (Issue #5) Use WeakReferences to refer to registered listeners.

* * TODO: (Issue #6) Add an optional check for valid property name * when adding a listener for a specific property.

* * TODO: (Issue #7) Add an optional strict check for existing * property names when firing a named property change.

* * TODO: (Issue #8) Add an option to ensure that update notifications * are performed in the event dispatch thread. In case a bean * is changed in a thread other than the event dispatch thread, such * a feature would help complying with Swing's single thread rule.

* * TODO: (Issue #11) Consider adding an option that saves update notifications * if 'checkIdentity' is true but the value types can be compared * safely via #equals, for example Strings, Booleans and Numbers. * * @author Mattias Neuling * @author Karsten Lentzsch * @version $Revision: 1.8 $ * * @see PropertyChangeSupport * @see PropertyChangeEvent * @see PropertyChangeListener * @see Object#equals(Object) * @see java.util.List#equals(Object) */ public final class ExtendedPropertyChangeSupport extends PropertyChangeSupport { /** * The object to be provided as the "source" for any generated events. * @serial */ private final Object source; /** * The default setting for the identity check. * Can be overridden by the #firePropertyChange methods * that accept a checkIdentity parameter. */ private final boolean checkIdentityDefault; // Instance Creation ****************************************************** /** * Constructs an ExtendedPropertyChangeSupport object. * * @param sourceBean The bean to be given as the source for any events. */ public ExtendedPropertyChangeSupport(Object sourceBean) { this(sourceBean, false); } /** * Constructs an ExtendedPropertyChangeSupport object * with the specified default test method for differences between * the old and new property values. * * @param sourceBean The object provided as the source for any generated events. * @param checkIdentityDefault true enables the identity check by default */ public ExtendedPropertyChangeSupport( Object sourceBean, boolean checkIdentityDefault) { super(sourceBean); this.source = sourceBean; this.checkIdentityDefault = checkIdentityDefault; } // Firing Events ********************************************************** /** * Fires the specified PropertyChangeEvent to any registered listeners. * Uses the default test (#equals vs. ==) * to determine whether the event's old and new values are different. * No event is fired if old and new value are the same. * * @param evt The PropertyChangeEvent object. * * @see PropertyChangeSupport#firePropertyChange(PropertyChangeEvent) */ @Override public void firePropertyChange(PropertyChangeEvent evt) { firePropertyChange(evt, checkIdentityDefault); } /** * Reports a bound property update to any registered listeners. * Uses the default test (#equals vs. ==) * to determine whether the event's old and new values are different. * No event is fired if old and new value are the same. * * @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. * * @see PropertyChangeSupport#firePropertyChange(String, Object, Object) */ @Override public void firePropertyChange( String propertyName, Object oldValue, Object newValue) { firePropertyChange(propertyName, oldValue, newValue, checkIdentityDefault); } /** * Fires an existing PropertyChangeEvent to any registered listeners. * The boolean parameter specifies whether differences between the old * and new value are tested using == or #equals. * No event is fired if old and new value are the same. * * @param evt The PropertyChangeEvent object. * @param checkIdentity true to check differences using == * false to use #equals. */ public void firePropertyChange(PropertyChangeEvent evt, boolean checkIdentity) { Object oldValue = evt.getOldValue(); Object newValue = evt.getNewValue(); if (oldValue != null && oldValue == newValue) { return; } else if (checkIdentity) { fireUnchecked(evt); } else { super.firePropertyChange(evt); } } /** * Reports a bound property update to any registered listeners. * No event is fired if the old and new value are the same. * If checkIdentity is true an event is fired in all * other cases. If this parameter is false, an event is fired * if old and new values are not equal. * * @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. * @param checkIdentity true to check differences using == * false to use #equals. */ public void firePropertyChange( String propertyName, Object oldValue, Object newValue, boolean checkIdentity) { if (oldValue != null && oldValue == newValue) { return; } else if (checkIdentity) { fireUnchecked( new PropertyChangeEvent( source, propertyName, oldValue, newValue)); } else { super.firePropertyChange(propertyName, oldValue, newValue); } } /** * Fires a PropertyChangeEvent to all its listeners without checking via * equals method if the old value is equal to new value. The instance * equality check is done by the calling firePropertyChange method * (to avoid instance creation of the PropertyChangeEvent).

* * If some listeners have been added with a named property, then * PropertyChangeSupport#getPropertyChangeListeners() returns * an array with a mixture of PropertyChangeListeners * and PropertyChangeListenerProxys. We notify all non-proxies * and those proxies that have a property name that is equals to the * event's property name. * * @param evt event to fire to the listeners * * @see PropertyChangeListenerProxy * @see PropertyChangeSupport#getPropertyChangeListeners() */ private void fireUnchecked(PropertyChangeEvent evt) { PropertyChangeListener[] listeners; synchronized (this) { listeners = getPropertyChangeListeners(); } String propertyName = evt.getPropertyName(); for (PropertyChangeListener listener : listeners) { if (listener instanceof PropertyChangeListenerProxy) { PropertyChangeListenerProxy proxy = (PropertyChangeListenerProxy) listener; if (proxy.getPropertyName().equals(propertyName)) { proxy.propertyChange(evt); } } else { listener.propertyChange(evt); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy