blob: 487dcc65a7a26e5665703ca4a89ed5cf2eade262 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.beans;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
/**
* This utility class
*
*/
public class PropertyChangeSupport implements Serializable {
private static final long serialVersionUID = 6401253773779951803l;
private transient Object sourceBean;
private transient List<PropertyChangeListener> allPropertiesChangeListeners =
new ArrayList<PropertyChangeListener>();
private transient Map<String, List<PropertyChangeListener>>
selectedPropertiesChangeListeners =
new HashMap<String, List<PropertyChangeListener>>();
// fields for serialization compatibility
private Hashtable<String, List<PropertyChangeListener>> children;
private Object source;
private int propertyChangeSupportSerializedDataVersion = 1;
/**
* Creates a new instance that uses the source bean as source for any event.
*
* @param sourceBean
* the bean used as source for all events.
*/
public PropertyChangeSupport(Object sourceBean) {
if (sourceBean == null) {
throw new NullPointerException();
}
this.sourceBean = sourceBean;
}
/**
* Fires a {@link PropertyChangeEvent} with the given name, old value and
* new value. As source the bean used to initialize this instance is used.
* If the old value and the new value are not null and equal the event will
* not be fired.
*
* @param propertyName
* the name of the property
* @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) {
PropertyChangeEvent event = createPropertyChangeEvent(propertyName,
oldValue, newValue);
doFirePropertyChange(event);
}
/**
* Fires an {@link IndexedPropertyChangeEvent} with the given name, old
* value, new value and index. As source the bean used to initialize this
* instance is used. If the old value and the new value are not null and
* equal the event will not be fired.
*
* @param propertyName
* the name of the property
* @param index
* the index
* @param oldValue
* the old value of the property
* @param newValue
* the new value of the property
*/
public void fireIndexedPropertyChange(String propertyName, int index,
Object oldValue, Object newValue) {
// nulls and equals check done in doFire...
doFirePropertyChange(new IndexedPropertyChangeEvent(sourceBean,
propertyName, oldValue, newValue, index));
}
/**
* Removes the listener from the specific property. This only happens if it
* was registered to this property. Nothing happens if it was not
* registered with this property or if the property name or the listener is
* null.
*
* @param propertyName
* the property name the listener is listening to
* @param listener
* the listener to remove
*/
public synchronized void removePropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
if ((propertyName != null) && (listener != null)) {
List<PropertyChangeListener> listeners =
selectedPropertiesChangeListeners.get(propertyName);
if (listeners != null) {
listeners.remove(listener);
}
}
}
/**
* Adds a listener to a specific property. Nothing happens if the property
* name or the listener is null.
*
* @param propertyName
* the name of the property
* @param listener
* the listener to register for the property with the given name
*/
public synchronized void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
if ((listener != null) && (propertyName != null)) {
List<PropertyChangeListener> listeners =
selectedPropertiesChangeListeners.get(propertyName);
if (listeners == null) {
listeners = new ArrayList<PropertyChangeListener>();
selectedPropertiesChangeListeners.put(propertyName, listeners);
}
// RI compatibility
if (listener instanceof PropertyChangeListenerProxy) {
PropertyChangeListenerProxy proxy =
(PropertyChangeListenerProxy) listener;
listeners.add(new PropertyChangeListenerProxy(
proxy.getPropertyName(),
(PropertyChangeListener) proxy.getListener()));
} else {
listeners.add(listener);
}
}
}
/**
* Returns an array of listeners that registered to the property with the
* given name. If the property name is null an empty array is returned.
*
* @param propertyName
* the name of the property whose listeners should be returned
* @return the array of listeners to the property with the given name.
*/
public synchronized PropertyChangeListener[] getPropertyChangeListeners(
String propertyName) {
List<PropertyChangeListener> listeners = null;
if (propertyName != null) {
listeners = selectedPropertiesChangeListeners.get(propertyName);
}
return (listeners == null) ? new PropertyChangeListener[] {}
: listeners.toArray(
new PropertyChangeListener[listeners.size()]);
}
/**
* Fires a property change of a boolean property with the given name. If the
* old value and the new value are not null and equal the event will not be
* fired.
*
* @param propertyName
* the property name
* @param oldValue
* the old value
* @param newValue
* the new value
*/
public void firePropertyChange(String propertyName, boolean oldValue,
boolean newValue) {
PropertyChangeEvent event = createPropertyChangeEvent(propertyName,
oldValue, newValue);
doFirePropertyChange(event);
}
/**
* Fires a property change of a boolean property with the given name. If the
* old value and the new value are not null and equal the event will not be
* fired.
*
* @param propertyName
* the property name
* @param index
* the index of the changed property
* @param oldValue
* the old value
* @param newValue
* the new value
*/
public void fireIndexedPropertyChange(String propertyName, int index,
boolean oldValue, boolean newValue) {
if (oldValue != newValue) {
fireIndexedPropertyChange(propertyName, index, Boolean
.valueOf(oldValue), Boolean.valueOf(newValue));
}
}
/**
* Fires a property change of an integer property with the given name. If
* the old value and the new value are not null and equal the event will not
* be fired.
*
* @param propertyName
* the property name
* @param oldValue
* the old value
* @param newValue
* the new value
*/
public void firePropertyChange(String propertyName, int oldValue,
int newValue) {
PropertyChangeEvent event = createPropertyChangeEvent(propertyName,
oldValue, newValue);
doFirePropertyChange(event);
}
/**
* Fires a property change of an integer property with the given name. If
* the old value and the new value are not null and equal the event will not
* be fired.
*
* @param propertyName
* the property name
* @param index
* the index of the changed property
* @param oldValue
* the old value
* @param newValue
* the new value
*/
public void fireIndexedPropertyChange(String propertyName, int index,
int oldValue, int newValue) {
if (oldValue != newValue) {
fireIndexedPropertyChange(propertyName, index,
Integer.valueOf(oldValue), Integer.valueOf(newValue));
}
}
/**
* Returns true if there are listeners registered to the property with the
* given name.
*
* @param propertyName
* the name of the property
* @return true if there are listeners registered to that property, false
* otherwise.
*/
public synchronized boolean hasListeners(String propertyName) {
boolean result = allPropertiesChangeListeners.size() > 0;
if (!result && (propertyName != null)) {
List<PropertyChangeListener> listeners =
selectedPropertiesChangeListeners.get(propertyName);
if (listeners != null) {
result = listeners.size() > 0;
}
}
return result;
}
/**
* removes a property change listener that was registered to all properties.
*
* @param listener
* the listener to remove
*/
public synchronized void removePropertyChangeListener(
PropertyChangeListener listener) {
if (listener != null) {
if (listener instanceof PropertyChangeListenerProxy) {
String name = ((PropertyChangeListenerProxy) listener)
.getPropertyName();
PropertyChangeListener lst = (PropertyChangeListener)
((PropertyChangeListenerProxy) listener).getListener();
removePropertyChangeListener(name, lst);
} else {
allPropertiesChangeListeners.remove(listener);
}
}
}
/**
* Registers a listener with all properties.
*
* @param listener
* the listener to register
*/
public synchronized void addPropertyChangeListener(
PropertyChangeListener listener) {
if (listener != null) {
if (listener instanceof PropertyChangeListenerProxy) {
String name = ((PropertyChangeListenerProxy) listener)
.getPropertyName();
PropertyChangeListener lst = (PropertyChangeListener)
((PropertyChangeListenerProxy) listener).getListener();
addPropertyChangeListener(name, lst);
} else {
allPropertiesChangeListeners.add(listener);
}
}
}
/**
* Returns an array with the listeners that registered to all properties.
*
* @return the array of listeners
*/
public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
ArrayList<PropertyChangeListener> result =
new ArrayList<PropertyChangeListener>(
allPropertiesChangeListeners);
for (String propertyName : selectedPropertiesChangeListeners.keySet()) {
List<PropertyChangeListener> selectedListeners =
selectedPropertiesChangeListeners.get(propertyName);
if (selectedListeners != null) {
for (PropertyChangeListener listener : selectedListeners) {
result.add(new PropertyChangeListenerProxy(propertyName,
listener));
}
}
}
return result.toArray(new PropertyChangeListener[result.size()]);
}
private void writeObject(ObjectOutputStream oos) throws IOException {
List<PropertyChangeListener> allSerializedPropertiesChangeListeners =
new ArrayList<PropertyChangeListener>();
for (PropertyChangeListener pcl : allPropertiesChangeListeners) {
if (pcl instanceof Serializable) {
allSerializedPropertiesChangeListeners.add(pcl);
}
}
Map<String, List<PropertyChangeListener>>
selectedSerializedPropertiesChangeListeners =
new HashMap<String, List<PropertyChangeListener>>();
for (String propertyName : selectedPropertiesChangeListeners.keySet()) {
List<PropertyChangeListener> keyValues =
selectedPropertiesChangeListeners.get(propertyName);
if (keyValues != null) {
List<PropertyChangeListener> serializedPropertiesChangeListeners
= new ArrayList<PropertyChangeListener>();
for (PropertyChangeListener pcl : keyValues) {
if (pcl instanceof Serializable) {
serializedPropertiesChangeListeners.add(pcl);
}
}
if (!serializedPropertiesChangeListeners.isEmpty()) {
selectedSerializedPropertiesChangeListeners.put(
propertyName, serializedPropertiesChangeListeners);
}
}
}
children = new Hashtable<String, List<PropertyChangeListener>>(
selectedSerializedPropertiesChangeListeners);
children.put("", allSerializedPropertiesChangeListeners);
oos.writeObject(children);
Object source = null;
if (sourceBean instanceof Serializable) {
source = sourceBean;
}
oos.writeObject(source);
oos.writeInt(propertyChangeSupportSerializedDataVersion);
}
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream ois) throws IOException,
ClassNotFoundException {
children = (Hashtable<String, List<PropertyChangeListener>>) ois
.readObject();
selectedPropertiesChangeListeners = new HashMap<String, List<PropertyChangeListener>>(
children);
allPropertiesChangeListeners = selectedPropertiesChangeListeners
.remove("");
if (allPropertiesChangeListeners == null) {
allPropertiesChangeListeners = new ArrayList<PropertyChangeListener>();
}
sourceBean = ois.readObject();
propertyChangeSupportSerializedDataVersion = ois.readInt();
}
/**
* Fires a property change event to all listeners of that property.
*
* @param event
* the event to fire
*/
public void firePropertyChange(PropertyChangeEvent event) {
doFirePropertyChange(event);
}
private PropertyChangeEvent createPropertyChangeEvent(String propertyName,
Object oldValue, Object newValue) {
return new PropertyChangeEvent(sourceBean, propertyName, oldValue,
newValue);
}
private PropertyChangeEvent createPropertyChangeEvent(String propertyName,
boolean oldValue, boolean newValue) {
return new PropertyChangeEvent(sourceBean, propertyName, oldValue,
newValue);
}
private PropertyChangeEvent createPropertyChangeEvent(String propertyName,
int oldValue, int newValue) {
return new PropertyChangeEvent(sourceBean, propertyName, oldValue,
newValue);
}
private void doFirePropertyChange(PropertyChangeEvent event) {
String propertyName = event.getPropertyName();
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
if ((newValue != null) && (oldValue != null)
&& newValue.equals(oldValue)) {
return;
}
/*
* Copy the listeners collections so they can be modified while we fire
* events.
*/
// Listeners to all property change events
PropertyChangeListener[] listensToAll;
// Listens to a given property change
PropertyChangeListener[] listensToOne = null;
synchronized (this) {
listensToAll = allPropertiesChangeListeners
.toArray(new PropertyChangeListener[allPropertiesChangeListeners
.size()]);
List<PropertyChangeListener> listeners = selectedPropertiesChangeListeners
.get(propertyName);
if (listeners != null) {
listensToOne = listeners
.toArray(new PropertyChangeListener[listeners.size()]);
}
}
// Fire the listeners
for (PropertyChangeListener listener : listensToAll) {
listener.propertyChange(event);
}
if (listensToOne != null) {
for (PropertyChangeListener listener : listensToOne) {
listener.propertyChange(event);
}
}
}
}