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

com.privatejgoodies.common.bean.Bean Maven / Gradle / Ivy

/*
 * Copyright (c) 2009-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.privatejgoodies.common.bean;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.io.Serializable;

/**
 * An abstract superclass that minimizes the effort required to provide change support for bound and
 * constrained Bean properties. This class follows the conventions and recommendations as described
 * in the Java Bean Specification.

* * This class uses the standard {@link PropertyChangeSupport} to notify registered listeners about * changes. Subclasses can use different change support implementations by overriding * {@code createPropertyChangeSupport}, for example to ensure that notifications are sent in the * Event dispatch thread, or to compare old and new values with {@code ==} not {@code equals}. * * @author Karsten Lentzsch * * @see PropertyChangeEvent * @see PropertyChangeListener * @see PropertyChangeSupport * @see VetoableChangeListener * @see VetoableChangeSupport */ public abstract class Bean implements Serializable, ObservableBean2 { /** * If any{@code PropertyChangeListeners} have been registered, the {@code changeSupport} field * describes them. * * @see #addPropertyChangeListener(PropertyChangeListener) * @see #addPropertyChangeListener(String, PropertyChangeListener) * @see #removePropertyChangeListener(PropertyChangeListener) * @see #removePropertyChangeListener(String, PropertyChangeListener) * @see PropertyChangeSupport */ protected transient PropertyChangeSupport changeSupport; /** * If any {@code VetoableChangeListeners} have been registered, the {@code vetoSupport} field * describes them. * * @see #addVetoableChangeListener(VetoableChangeListener) * @see #addVetoableChangeListener(String, VetoableChangeListener) * @see #removeVetoableChangeListener(VetoableChangeListener) * @see #removeVetoableChangeListener(String, VetoableChangeListener) * @see #fireVetoableChange(String, Object, Object) */ private transient VetoableChangeSupport vetoSupport; // Managing Property Change Listeners ********************************** /** * Adds a PropertyChangeListener to the listener list. The listener is registered for all bound * properties of this class.

* * 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() */ @Override public final synchronized void addPropertyChangeListener( PropertyChangeListener listener) { if (listener == null) { return; } if (changeSupport == null) { changeSupport = createPropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(listener); } /** * Removes a PropertyChangeListener from the listener list. This method should be used to remove * PropertyChangeListeners that were registered for all bound properties of this class.

* * 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() */ @Override public final synchronized void removePropertyChangeListener( PropertyChangeListener listener) { if (listener == null || changeSupport == null) { return; } changeSupport.removePropertyChangeListener(listener); } /** * Adds a PropertyChangeListener to the listener list for a specific property. The specified * property may be user-defined.

* * Note that if this Model 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) */ @Override public final synchronized void addPropertyChangeListener( String propertyName, PropertyChangeListener listener) { if (listener == null) { return; } if (changeSupport == null) { changeSupport = createPropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(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) */ @Override public final synchronized void removePropertyChangeListener( String propertyName, PropertyChangeListener listener) { if (listener == null || changeSupport == null) { return; } changeSupport.removePropertyChangeListener(propertyName, listener); } // Managing Vetoable Change Listeners *********************************** /** * Adds a VetoableChangeListener to the listener list. The listener is registered for all bound * properties of this class.

* * If listener is {@code null}, no exception is thrown and no action is performed. * * @param listener the VetoableChangeListener to be added * * @see #removeVetoableChangeListener(String, VetoableChangeListener) * @see #addVetoableChangeListener(String, VetoableChangeListener) * @see #getVetoableChangeListeners() */ public final synchronized void addVetoableChangeListener( VetoableChangeListener listener) { if (listener == null) { return; } if (vetoSupport == null) { vetoSupport = new VetoableChangeSupport(this); } vetoSupport.addVetoableChangeListener(listener); } /** * Removes a VetoableChangeListener from the listener list. This method should be used to remove * VetoableChangeListeners that were registered for all bound properties of this class.

* * If listener is {@code null}, no exception is thrown and no action is performed. * * @param listener the VetoableChangeListener to be removed * * @see #addVetoableChangeListener(String, VetoableChangeListener) * @see #removeVetoableChangeListener(String, VetoableChangeListener) * @see #getVetoableChangeListeners() */ public final synchronized void removeVetoableChangeListener( VetoableChangeListener listener) { if (listener == null || vetoSupport == null) { return; } vetoSupport.removeVetoableChangeListener(listener); } /** * Adds a VetoableChangeListener to the listener list for a specific property. The specified * property may be user-defined.

* * Note that if this Model 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 VetoableChangeListener to be added * * @see #removeVetoableChangeListener(String, VetoableChangeListener) * @see #addVetoableChangeListener(String, VetoableChangeListener) * @see #getVetoableChangeListeners(String) */ public final synchronized void addVetoableChangeListener( String propertyName, VetoableChangeListener listener) { if (listener == null) { return; } if (vetoSupport == null) { vetoSupport = new VetoableChangeSupport(this); } vetoSupport.addVetoableChangeListener(propertyName, listener); } /** * Removes a VetoableChangeListener from the listener list for a specific property. This method * should be used to remove VetoableChangeListeners 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 VetoableChangeListener to be removed * * @see #addVetoableChangeListener(String, VetoableChangeListener) * @see #removeVetoableChangeListener(VetoableChangeListener) * @see #getVetoableChangeListeners(String) */ public final synchronized void removeVetoableChangeListener( String propertyName, VetoableChangeListener listener) { if (listener == null || vetoSupport == null) { return; } vetoSupport.removeVetoableChangeListener(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 PropertyChangeSupport#getPropertyChangeListeners() */ @Override public final synchronized PropertyChangeListener[] getPropertyChangeListeners() { if (changeSupport == null) { return new PropertyChangeListener[0]; } return changeSupport.getPropertyChangeListeners(); } /** * 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() */ @Override public final synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { if (changeSupport == null) { return new PropertyChangeListener[0]; } return changeSupport.getPropertyChangeListeners(propertyName); } /** * Returns an array of all the property change listeners registered on this component. * * @return all of this component's {@code VetoableChangeListener}s or an empty array if no * property change listeners are currently registered * * @see #addVetoableChangeListener(VetoableChangeListener) * @see #removeVetoableChangeListener(VetoableChangeListener) * @see #getVetoableChangeListeners(String) * @see VetoableChangeSupport#getVetoableChangeListeners() */ public final synchronized VetoableChangeListener[] getVetoableChangeListeners() { if (vetoSupport == null) { return new VetoableChangeListener[0]; } return vetoSupport.getVetoableChangeListeners(); } /** * 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 VetoableChangeListeners} associated with the named property or an * empty array if no listeners have been added * * @see #addVetoableChangeListener(String, VetoableChangeListener) * @see #removeVetoableChangeListener(String, VetoableChangeListener) * @see #getVetoableChangeListeners() */ public final synchronized VetoableChangeListener[] getVetoableChangeListeners(String propertyName) { if (vetoSupport == null) { return new VetoableChangeListener[0]; } return vetoSupport.getVetoableChangeListeners(propertyName); } /** * Creates and returns a PropertyChangeSupport for the given bean. Invoked by the first call to * {@link #addPropertyChangeListener} when lazily creating the sole change support instance used * throughout this bean.

* * This default implementation creates a {@code PropertyChangeSupport}. Subclasses may override * to return other change support implementations. For example to ensure that listeners are * notified in the Event dispatch thread (EDT change support). The JGoodies Binding uses an * extended change support that allows to configure whether the old and new value are compared * with {@code ==} or {@code equals}. * * @param bean the bean to create a change support for * @return the new change support */ protected PropertyChangeSupport createPropertyChangeSupport(final Object bean) { return new PropertyChangeSupport(bean); } // Firing Changes for Bound Properties ********************************** /** * General support for reporting bound property changes. Sends the given PropertyChangeEvent to * any registered PropertyChangeListener.

* * Most bean setters will invoke the fireXXX methods that get a property name and the old and * new value. However some frameworks and setters may prefer to use this general method. Also, * this method allows to fire IndexedPropertyChangeEvents that have been introduced in Java 5. * * @param event describes the property change * * @since 1.3 */ protected final void firePropertyChange(PropertyChangeEvent event) { PropertyChangeSupport aChangeSupport = this.changeSupport; if (aChangeSupport == null) { return; } aChangeSupport.firePropertyChange(event); } /** * Support for reporting bound property changes for Object properties. This method can be called * when a bound property has changed and it will send the appropriate PropertyChangeEvent to any * registered PropertyChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value */ protected final void firePropertyChange(String propertyName, Object oldValue, Object newValue) { PropertyChangeSupport aChangeSupport = this.changeSupport; if (aChangeSupport == null) { return; } aChangeSupport.firePropertyChange(propertyName, oldValue, newValue); } /** * Support for reporting bound property changes for boolean properties. This method can be * called when a bound property has changed and it will send the appropriate PropertyChangeEvent * to any registered PropertyChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value */ protected final void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { PropertyChangeSupport aChangeSupport = this.changeSupport; if (aChangeSupport == null) { return; } aChangeSupport.firePropertyChange(propertyName, oldValue, newValue); } /** * Support for reporting bound property changes for integer properties. This method can be * called when a bound property has changed and it will send the appropriate PropertyChangeEvent * to any registered PropertyChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value */ protected final void firePropertyChange(String propertyName, double oldValue, double newValue) { firePropertyChange(propertyName, Double.valueOf(oldValue), Double.valueOf(newValue)); } /** * Support for reporting bound property changes for integer properties. This method can be * called when a bound property has changed and it will send the appropriate PropertyChangeEvent * to any registered PropertyChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value */ protected final void firePropertyChange(String propertyName, float oldValue, float newValue) { firePropertyChange(propertyName, Float.valueOf(oldValue), Float.valueOf(newValue)); } /** * Support for reporting bound property changes for integer properties. This method can be * called when a bound property has changed and it will send the appropriate PropertyChangeEvent * to any registered PropertyChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value */ protected final void firePropertyChange(String propertyName, int oldValue, int newValue) { PropertyChangeSupport aChangeSupport = this.changeSupport; if (aChangeSupport == null) { return; } aChangeSupport.firePropertyChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); } /** * Support for reporting bound property changes for integer properties. This method can be * called when a bound property has changed and it will send the appropriate PropertyChangeEvent * to any registered PropertyChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value */ protected final void firePropertyChange(String propertyName, long oldValue, long newValue) { firePropertyChange(propertyName, Long.valueOf(oldValue), Long.valueOf(newValue)); } /** * Indicates that an arbitrary set of bound properties have changed. Sends a PropertyChangeEvent * with property name, old and new value set to {@code null} to any registered * PropertyChangeListeners. * * @see java.beans.PropertyChangeEvent * * @since 1.0.3 */ protected final void fireMultiplePropertiesChanged() { firePropertyChange(null, null, null); } // Firing Indexed Changes ************************************************* /** * Report a bound indexed property update to any registered listeners.

* * No event is fired if old and new values are equal and non-null. * * @param propertyName The programmatic name of the property that was changed. * @param index index of the property element that was changed. * @param oldValue The old value of the property. * @param newValue The new value of the property. * * @since 2.0 */ protected final void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) { PropertyChangeSupport aChangeSupport = this.changeSupport; if (aChangeSupport == null) { return; } aChangeSupport.fireIndexedPropertyChange(propertyName, index, oldValue, newValue); } /** * Report an {@code int} bound indexed property update to any registered listeners.

* * No event is fired if old and new values are equal and non-null.

* * This is merely a convenience wrapper around the more general fireIndexedPropertyChange method * which takes Object values. * * @param propertyName The programmatic name of the property that was changed. * @param index index of the property element that was changed. * @param oldValue The old value of the property. * @param newValue The new value of the property. * * @since 2.0 */ protected final void fireIndexedPropertyChange(String propertyName, int index, int oldValue, int newValue) { if (oldValue == newValue) { return; } fireIndexedPropertyChange(propertyName, index, Integer.valueOf(oldValue), Integer.valueOf(newValue)); } /** * Report a {@code boolean} bound indexed property update to any registered listeners.

* * No event is fired if old and new values are equal and non-null.

* * This is merely a convenience wrapper around the more general fireIndexedPropertyChange method * which takes Object values. * * @param propertyName The programmatic name of the property that was changed. * @param index index of the property element that was changed. * @param oldValue The old value of the property. * @param newValue The new value of the property. * * @since 2.0 */ protected final void fireIndexedPropertyChange(String propertyName, int index, boolean oldValue, boolean newValue) { if (oldValue == newValue) { return; } fireIndexedPropertyChange(propertyName, index, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); } // Firing Changes for Constrained Properties **************************** /** * General support for reporting constrained property changes. Sends the given * PropertyChangeEvent to any registered PropertyChangeListener.

* * Most bean setters will invoke the fireXXX methods that get a property name and the old and * new value. However some frameworks and setters may prefer to use this general method. Also, * this method allows to fire IndexedPropertyChangeEvents that have been introduced in Java 5. * * @param event describes the property change * @throws PropertyVetoException if a constrained property change is rejected * * @since 1.3 */ protected final void fireVetoableChange(PropertyChangeEvent event) throws PropertyVetoException { VetoableChangeSupport aVetoSupport = this.vetoSupport; if (aVetoSupport == null) { return; } aVetoSupport.fireVetoableChange(event); } /** * Support for reporting changes for constrained Object properties. This method can be called * before a constrained property will be changed and it will send the appropriate * PropertyChangeEvent to any registered VetoableChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value * @throws PropertyVetoException if a constrained property change is rejected */ protected final void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws PropertyVetoException { VetoableChangeSupport aVetoSupport = this.vetoSupport; if (aVetoSupport == null) { return; } aVetoSupport.fireVetoableChange(propertyName, oldValue, newValue); } /** * Support for reporting changes for constrained boolean properties. This method can be called * before a constrained property will be changed and it will send the appropriate * PropertyChangeEvent to any registered VetoableChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value * @throws PropertyVetoException if a constrained property change is rejected */ protected final void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue) throws PropertyVetoException { VetoableChangeSupport aVetoSupport = this.vetoSupport; if (aVetoSupport == null) { return; } aVetoSupport.fireVetoableChange(propertyName, oldValue, newValue); } /** * Support for reporting changes for constrained integer properties. This method can be called * before a constrained property will be changed and it will send the appropriate * PropertyChangeEvent to any registered VetoableChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value * @throws PropertyVetoException if a constrained property change is rejected */ protected final void fireVetoableChange(String propertyName, double oldValue, double newValue) throws PropertyVetoException { fireVetoableChange(propertyName, Double.valueOf(oldValue), Double.valueOf(newValue)); } /** * Support for reporting changes for constrained integer properties. This method can be called * before a constrained property will be changed and it will send the appropriate * PropertyChangeEvent to any registered VetoableChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value * @throws PropertyVetoException if a constrained property change is rejected */ protected final void fireVetoableChange(String propertyName, int oldValue, int newValue) throws PropertyVetoException { VetoableChangeSupport aVetoSupport = this.vetoSupport; if (aVetoSupport == null) { return; } aVetoSupport.fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); } /** * Support for reporting changes for constrained integer properties. This method can be called * before a constrained property will be changed and it will send the appropriate * PropertyChangeEvent to any registered VetoableChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value * @throws PropertyVetoException if a constrained property change is rejected */ protected final void fireVetoableChange(String propertyName, float oldValue, float newValue) throws PropertyVetoException { fireVetoableChange(propertyName, Float.valueOf(oldValue), Float.valueOf(newValue)); } /** * Support for reporting changes for constrained integer properties. This method can be called * before a constrained property will be changed and it will send the appropriate * PropertyChangeEvent to any registered VetoableChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value * @throws PropertyVetoException if a constrained property change is rejected */ protected final void fireVetoableChange(String propertyName, long oldValue, long newValue) throws PropertyVetoException { fireVetoableChange(propertyName, Long.valueOf(oldValue), Long.valueOf(newValue)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy