/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.utility.events;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.persistence.tools.workbench.utility.IdentityHashBag;
import org.eclipse.persistence.tools.workbench.utility.string.StringTools;
/**
* Support object that can be used by implementors of the Model interface.
* It provides for state, property, collection, list, and tree change notifications to
* listeners.
*
* NB: There is lots of copy-n-paste code in this class. Nearly all of this duplication
* is an effort to prevent the unnecessary creation of new objects (typically event
* objects). Since many events are fired when there are no listeners, we postpone
* the creation of event objects until we know we have interested listeners.
* Most methods have the "non-duplicated" version of the method body commented
* out at the top of the current method body.
* The hope was that this class would prove to be fairly static and the duplicated
* code would not prove onerous; but that has not proven to be
* the case, as we have added support for "state" changes, "dirty" notification,
* and custom "notifiers", with more to come, I'm sure.... ~bjv
*/
public class ChangeSupport
implements Serializable
{
/** The object to be provided as the "source" for any generated events. */
protected final Object source;
/** Maps a listener class to a collection of listeners for that class. */
transient private Map listeners;
/** Maps property names to child ChangeSupport objects. */
private Map children;
private static final long serialVersionUID = 1L;
// ******************** constructor ********************
/**
* Construct support for the specified source of change events.
* The source cannot be null.
*/
public ChangeSupport(Object source) {
super();
if (source == null) {
throw new NullPointerException();
}
this.source = source;
}
// ******************** internal behavior ********************
/**
* Adds a listener that listens to all events appropriate to that listener,
* regardless of the property name associated with that event.
* The listener cannot be null.
*/
protected void addListener(Class listenerClass, Object listener) {
if (listener == null) {
throw new NullPointerException(); // better sooner than later
}
synchronized (this) {
if (this.listeners == null) {
this.listeners = new IdentityHashMap();
}
IdentityHashBag listenerClassSpecificListeners = (IdentityHashBag) this.listeners.get(listenerClass);
if (listenerClassSpecificListeners == null) {
listenerClassSpecificListeners = new IdentityHashBag();
this.listeners.put(listenerClass, listenerClassSpecificListeners);
}
listenerClassSpecificListeners.add(listener);
}
}
/**
* Adds a listener that listens to all events appropriate to that listener,
* and only to those events carrying the property name specified.
* The aspect name cannot be null and the listener cannot be null.
*/
protected void addListener(String aspectName, Class listenerClass, Object listener) {
if ((aspectName == null) || (listener == null)) {
throw new NullPointerException(); // better sooner than later
}
synchronized (this) {
if (this.children == null) {
this.children = new IdentityHashMap();
}
ChangeSupport child = (ChangeSupport) this.children.get(aspectName);
if (child == null) {
child = this.buildChildChangeSupport();
this.children.put(aspectName, child);
}
child.addListener(listenerClass, listener);
}
}
/**
* Build and return a child change support to hold aspect-specific listeners.
*/
protected ChangeSupport buildChildChangeSupport() {
return new ChangeSupport(this.source);
}
/**
* Removes a listener that has been registered for all events appropriate to that listener.
*/
protected void removeListener(Class listenerClass, Object listener) {
if (this.listeners == null) {
throw new IllegalArgumentException("listener not registered");
}
synchronized (this) {
IdentityHashBag listenerClassSpecificListeners = (IdentityHashBag) this.listeners.get(listenerClass);
if (listenerClassSpecificListeners == null) {
throw new IllegalArgumentException("listener not registered");
}
if ( ! listenerClassSpecificListeners.remove(listener)) {
throw new IllegalArgumentException("listener not registered");
}
}
}
/**
* Removes a listener that has been registered for appropriate
* events carrying the specified property name.
*/
protected void removeListener(String aspectName, Class listenerClass, Object listener) {
if (this.children == null) {
throw new IllegalArgumentException("listener not registered");
}
synchronized (this) {
ChangeSupport child = (ChangeSupport) this.children.get(aspectName);
if (child == null) {
throw new IllegalArgumentException("listener not registered");
}
child.removeListener(listenerClass, listener);
}
}
// ******************** internal queries ********************
/**
* Return a notifier that will forward change notifications to the listeners.
*/
protected ChangeNotifier notifier() {
return DefaultChangeNotifier.instance();
}
/**
* Return whether there are any "generic" listeners for the specified
* listener class.
*/
protected synchronized boolean hasAnyListeners(Class listenerClass) {
if (this.listeners == null) {
return false;
}
IdentityHashBag listenerClassSpecificListeners = (IdentityHashBag) this.listeners.get(listenerClass);
return (listenerClassSpecificListeners != null) &&
! listenerClassSpecificListeners.isEmpty();
}
/**
* Return whether there are any listeners for the specified
* listener class and property name.
*/
protected synchronized boolean hasAnyListeners(Class listenerClass, String aspectName) {
if (this.hasAnyListeners(listenerClass)) {
return true; // there's a "generic" listener
}
if (this.children == null) {
return false;
}
ChangeSupport child = (ChangeSupport) this.children.get(aspectName);
return (child != null) &&
child.hasAnyListeners(listenerClass);
}
// ******************** behavior ********************
/**
* The specified aspect of the source has changed;
* override this method to perform things like setting a
* dirty flag or validating the source's state.
* The aspect name will be null if a "state change" occurred.
*/
protected void sourceChanged(String aspectName) {
// the default is to do nothing
}
// ******************** state change support ********************
/**
* Add a state change listener.
*/
public void addStateChangeListener(StateChangeListener listener) {
this.addListener(StateChangeListener.class, listener);
}
/**
* Remove a state change listener.
*/
public void removeStateChangeListener(StateChangeListener listener) {
this.removeListener(StateChangeListener.class, listener);
}
/**
* Return whether there are any state change listeners.
*/
public boolean hasAnyStateChangeListeners() {
return this.hasAnyListeners(StateChangeListener.class);
}
/**
* Fire the specified state change event to any registered listeners.
*/
public void fireStateChanged(StateChangeEvent event) {
IdentityHashBag stateChangeListeners = null;
IdentityHashBag targets = null;
synchronized (this) {
if (this.listeners != null) {
stateChangeListeners = (IdentityHashBag) this.listeners.get(StateChangeListener.class);
if (stateChangeListeners != null) {
targets = (IdentityHashBag) stateChangeListeners.clone();
}
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
StateChangeListener target = (StateChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = stateChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().stateChanged(target, event);
}
}
}
this.sourceChanged(null);
}
/**
* Report a generic state change event to any registered state change listeners.
*/
public void fireStateChanged() {
// this.fireStateChange(new StateChangeEvent(this.source));
IdentityHashBag stateChangeListeners = null;
IdentityHashBag targets = null;
synchronized (this) {
if (this.listeners != null) {
stateChangeListeners = (IdentityHashBag) this.listeners.get(StateChangeListener.class);
if (stateChangeListeners != null) {
targets = (IdentityHashBag) stateChangeListeners.clone();
}
}
}
if (targets != null) {
StateChangeEvent event = null;
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
StateChangeListener target = (StateChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = stateChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new StateChangeEvent(this.source);
}
this.notifier().stateChanged(target, event);
}
}
}
this.sourceChanged(null);
}
// ******************** property change support ********************
/**
* Return whether the values are equal, with the appropriate null checks.
* Convenience method for checking whether an attribute value has changed.
*/
public boolean valuesAreEqual(Object value1, Object value2) {
if ((value1 == null) && (value2 == null)) {
return true; // both are null
}
if ((value1 == null) || (value2 == null)) {
return false; // one is null but the other is not
}
return value1.equals(value2);
}
/**
* Return whether the values are different, with the appropriate null checks.
* Convenience method for checking whether an attribute value has changed.
*/
public boolean valuesAreDifferent(Object value1, Object value2) {
return ! this.valuesAreEqual(value1, value2);
}
/**
* Add a property change listener that is registered for all properties.
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
this.addListener(PropertyChangeListener.class, listener);
}
/**
* Add a property change listener for the specified property. The listener
* will be notified only for changes to the specified property.
*/
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
this.addListener(propertyName, PropertyChangeListener.class, listener);
}
/**
* Remove a property change listener that was registered for all properties.
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.removeListener(PropertyChangeListener.class, listener);
}
/**
* Remove a property change listener that was registered for a specific property.
*/
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
this.removeListener(propertyName, PropertyChangeListener.class, listener);
}
/**
* Return whether there are any property change listeners that will
* be notified when the specified property has changed.
*/
public boolean hasAnyPropertyChangeListeners(String propertyName) {
return this.hasAnyListeners(PropertyChangeListener.class, propertyName);
}
/**
* Return whether there are any property change listeners that will
* be notified when any property has changed.
*/
public boolean hasAnyPropertyChangeListeners() {
return this.hasAnyListeners(PropertyChangeListener.class);
}
/**
* Fire the specified property change event to any registered listeners.
* No event is fired if the given event's old and new values are the same;
* this includes when both values are null. Use a state change event
* for general purpose notification of changes.
*/
public void firePropertyChanged(PropertyChangeEvent event) {
if (this.valuesAreEqual(event.getOldValue(), event.getNewValue())) {
return;
}
String propertyName = event.getPropertyName();
IdentityHashBag propertyChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
propertyChangeListeners = (IdentityHashBag) this.listeners.get(PropertyChangeListener.class);
if (propertyChangeListeners != null) {
targets = (IdentityHashBag) propertyChangeListeners.clone();
}
}
if ((propertyName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(propertyName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
PropertyChangeListener target = (PropertyChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = propertyChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().propertyChange(target, event);
}
}
}
if (child != null) {
child.firePropertyChanged(event);
}
this.sourceChanged(propertyName);
}
/**
* Report a bound property update to any registered property change listeners.
* No event is fired if the given old and new values are the same;
* this includes when both values are null. Use a state change event
* for general purpose notification of changes.
*/
public void firePropertyChanged(String propertyName, Object oldValue, Object newValue) {
// this.firePropertyChanged(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue));
if (this.valuesAreDifferent(oldValue, newValue)) {
this.firePropertyChangedInternal(propertyName, oldValue, newValue);
}
}
/**
* The old and new values have been verified as different.
* Fire a property change event if there are any listeners.
*/
protected void firePropertyChangedInternal(String propertyName, Object oldValue, Object newValue) {
IdentityHashBag propertyChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
propertyChangeListeners = (IdentityHashBag) this.listeners.get(PropertyChangeListener.class);
if (propertyChangeListeners != null) {
targets = (IdentityHashBag) propertyChangeListeners.clone();
}
}
if ((propertyName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(propertyName);
}
}
PropertyChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
PropertyChangeListener target = (PropertyChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = propertyChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new PropertyChangeEvent(this.source, propertyName, oldValue, newValue);
}
this.notifier().propertyChange(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.firePropertyChanged(propertyName, oldValue, newValue);
} else {
child.firePropertyChanged(event);
}
}
this.sourceChanged(propertyName);
}
/**
* Report an int bound property update to any registered listeners.
* No event is fired if old and new are equal.
* <p>
* This is merely a convenience wrapper around the more general
* firePropertyChange method that takes Object values.
*/
public void firePropertyChanged(String propertyName, int oldValue, int newValue) {
// this.firePropertyChanged(propertyName, new Integer(oldValue), new Integer(newValue));
if (oldValue == newValue) {
return;
}
IdentityHashBag propertyChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
propertyChangeListeners = (IdentityHashBag) this.listeners.get(PropertyChangeListener.class);
if (propertyChangeListeners != null) {
targets = (IdentityHashBag) propertyChangeListeners.clone();
}
}
if ((propertyName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(propertyName);
}
}
PropertyChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
PropertyChangeListener target = (PropertyChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = propertyChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new PropertyChangeEvent(this.source, propertyName, new Integer(oldValue), new Integer(newValue));
}
this.notifier().propertyChange(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.firePropertyChanged(propertyName, oldValue, newValue);
} else {
child.firePropertyChanged(event);
}
}
this.sourceChanged(propertyName);
}
/**
* Report a boolean bound property update to any registered listeners.
* No event is fired if old and new are equal.
* <p>
* This is merely a convenience wrapper around the more general
* firePropertyChange method that takes Object values.
*/
public void firePropertyChanged(String propertyName, boolean oldValue, boolean newValue) {
if (oldValue != newValue) {
this.firePropertyChangedInternal(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
}
// ******************** collection change support ********************
/**
* Add a collection change listener that is registered for all collections.
*/
public void addCollectionChangeListener(CollectionChangeListener listener) {
this.addListener(CollectionChangeListener.class, listener);
}
/**
* Add a collection change listener for the specified collection. The listener
* will be notified only for changes to the specified collection.
*/
public void addCollectionChangeListener(String collectionName, CollectionChangeListener listener) {
this.addListener(collectionName, CollectionChangeListener.class, listener);
}
/**
* Remove a collection change listener that was registered for all collections.
*/
public void removeCollectionChangeListener(CollectionChangeListener listener) {
this.removeListener(CollectionChangeListener.class, listener);
}
/**
* Remove a collection change listener that was registered for a specific collection.
*/
public void removeCollectionChangeListener(String collectionName, CollectionChangeListener listener) {
this.removeListener(collectionName, CollectionChangeListener.class, listener);
}
/**
* Return whether there are any collection change listeners that will
* be notified when the specified collection has changed.
*/
public boolean hasAnyCollectionChangeListeners(String collectionName) {
return this.hasAnyListeners(CollectionChangeListener.class, collectionName);
}
/**
* Return whether there are any collection change listeners that will
* be notified when any collection has changed.
*/
public boolean hasAnyCollectionChangeListeners() {
return this.hasAnyListeners(CollectionChangeListener.class);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireItemsAdded(CollectionChangeEvent event) {
if (event.size() == 0) {
return;
}
String collectionName = event.getCollectionName();
IdentityHashBag collectionChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
collectionChangeListeners = (IdentityHashBag) this.listeners.get(CollectionChangeListener.class);
if (collectionChangeListeners != null) {
targets = (IdentityHashBag) collectionChangeListeners.clone();
}
}
if ((collectionName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(collectionName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
CollectionChangeListener target = (CollectionChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = collectionChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().itemsAdded(target, event);
}
}
}
if (child != null) {
child.fireItemsAdded(event);
}
this.sourceChanged(collectionName);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireItemsAdded(String collectionName, Collection addedItems) {
// this.fireItemsAdded(new CollectionChangeEvent(this.source, collectionName, addedItems));
if (addedItems.size() == 0) {
return;
}
IdentityHashBag collectionChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
collectionChangeListeners = (IdentityHashBag) this.listeners.get(CollectionChangeListener.class);
if (collectionChangeListeners != null) {
targets = (IdentityHashBag) collectionChangeListeners.clone();
}
}
if ((collectionName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(collectionName);
}
}
CollectionChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
CollectionChangeListener target = (CollectionChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = collectionChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName, addedItems);
}
this.notifier().itemsAdded(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemsAdded(collectionName, addedItems);
} else {
child.fireItemsAdded(event);
}
}
this.sourceChanged(collectionName);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireItemAdded(String collectionName, Object addedItem) {
// this.fireItemsAdded(collectionName, Collections.singleton(addedItem));
IdentityHashBag collectionChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
collectionChangeListeners = (IdentityHashBag) this.listeners.get(CollectionChangeListener.class);
if (collectionChangeListeners != null) {
targets = (IdentityHashBag) collectionChangeListeners.clone();
}
}
if ((collectionName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(collectionName);
}
}
CollectionChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
CollectionChangeListener target = (CollectionChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = collectionChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName, Collections.singleton(addedItem));
}
this.notifier().itemsAdded(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemAdded(collectionName, addedItem);
} else {
child.fireItemsAdded(event);
}
}
this.sourceChanged(collectionName);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireItemsRemoved(CollectionChangeEvent event) {
if (event.size() == 0) {
return;
}
String collectionName = event.getCollectionName();
IdentityHashBag collectionChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
collectionChangeListeners = (IdentityHashBag) this.listeners.get(CollectionChangeListener.class);
if (collectionChangeListeners != null) {
targets = (IdentityHashBag) collectionChangeListeners.clone();
}
}
if ((collectionName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(collectionName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
CollectionChangeListener target = (CollectionChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = collectionChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().itemsRemoved(target, event);
}
}
}
if (child != null) {
child.fireItemsRemoved(event);
}
this.sourceChanged(collectionName);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireItemsRemoved(String collectionName, Collection removedItems) {
// this.fireItemsRemoved(new CollectionChangeEvent(this.source, collectionName, removedItems));
if (removedItems.size() == 0) {
return;
}
IdentityHashBag collectionChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
collectionChangeListeners = (IdentityHashBag) this.listeners.get(CollectionChangeListener.class);
if (collectionChangeListeners != null) {
targets = (IdentityHashBag) collectionChangeListeners.clone();
}
}
if ((collectionName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(collectionName);
}
}
CollectionChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
CollectionChangeListener target = (CollectionChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = collectionChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName, removedItems);
}
this.notifier().itemsRemoved(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemsRemoved(collectionName, removedItems);
} else {
child.fireItemsRemoved(event);
}
}
this.sourceChanged(collectionName);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireItemRemoved(String collectionName, Object removedItem) {
// this.fireItemsRemoved(collectionName, Collections.singleton(removedItem));
IdentityHashBag collectionChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
collectionChangeListeners = (IdentityHashBag) this.listeners.get(CollectionChangeListener.class);
if (collectionChangeListeners != null) {
targets = (IdentityHashBag) collectionChangeListeners.clone();
}
}
if ((collectionName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(collectionName);
}
}
CollectionChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
CollectionChangeListener target = (CollectionChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = collectionChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName, Collections.singleton(removedItem));
}
this.notifier().itemsRemoved(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemRemoved(collectionName, removedItem);
} else {
child.fireItemsRemoved(event);
}
}
this.sourceChanged(collectionName);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireCollectionChanged(CollectionChangeEvent event) {
String collectionName = event.getCollectionName();
IdentityHashBag collectionChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
collectionChangeListeners = (IdentityHashBag) this.listeners.get(CollectionChangeListener.class);
if (collectionChangeListeners != null) {
targets = (IdentityHashBag) collectionChangeListeners.clone();
}
}
if ((collectionName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(collectionName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
CollectionChangeListener target = (CollectionChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = collectionChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().collectionChanged(target, event);
}
}
}
if (child != null) {
child.fireCollectionChanged(event);
}
this.sourceChanged(collectionName);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireCollectionChanged(String collectionName) {
// this.fireCollectionChanged(new CollectionChangeEvent(this.source, collectionName));
IdentityHashBag collectionChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
collectionChangeListeners = (IdentityHashBag) this.listeners.get(CollectionChangeListener.class);
if (collectionChangeListeners != null) {
targets = (IdentityHashBag) collectionChangeListeners.clone();
}
}
if ((collectionName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(collectionName);
}
}
CollectionChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
CollectionChangeListener target = (CollectionChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = collectionChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName);
}
this.notifier().collectionChanged(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireCollectionChanged(collectionName);
} else {
child.fireCollectionChanged(event);
}
}
this.sourceChanged(collectionName);
}
// ******************** list change support ********************
/**
* Add a list change listener that is registered for all lists.
*/
public void addListChangeListener(ListChangeListener listener) {
this.addListener(ListChangeListener.class, listener);
}
/**
* Add a list change listener for the specified list. The listener
* will be notified only for changes to the specified list.
*/
public void addListChangeListener(String listName, ListChangeListener listener) {
this.addListener(listName, ListChangeListener.class, listener);
}
/**
* Remove a list change listener that was registered for all lists.
*/
public void removeListChangeListener(ListChangeListener listener) {
this.removeListener(ListChangeListener.class, listener);
}
/**
* Remove a list change listener that was registered for a specific list.
*/
public void removeListChangeListener(String listName, ListChangeListener listener) {
this.removeListener(listName, ListChangeListener.class, listener);
}
/**
* Return whether there are any list change listeners that will
* be notified when the specified list has changed.
*/
public boolean hasAnyListChangeListeners(String listName) {
return this.hasAnyListeners(ListChangeListener.class, listName);
}
/**
* Return whether there are any list change listeners that will
* be notified when any list has changed.
*/
public boolean hasAnyListChangeListeners() {
return this.hasAnyListeners(ListChangeListener.class);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemsAdded(ListChangeEvent event) {
if (event.size() == 0) {
return;
}
String listName = event.getListName();
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().itemsAdded(target, event);
}
}
}
if (child != null) {
child.fireItemsAdded(event);
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemsAdded(String listName, int index, List addedItems) {
// this.fireItemsAdded(new ListChangeEvent(this.source, listName, index, addedItems));
if (addedItems.size() == 0) {
return;
}
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
ListChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, addedItems);
}
this.notifier().itemsAdded(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemsAdded(listName, index, addedItems);
} else {
child.fireItemsAdded(event);
}
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemAdded(String listName, int index, Object addedItem) {
// this.fireItemsAdded(listName, index, Collections.singletonList(addedItem));
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
ListChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, Collections.singletonList(addedItem));
}
this.notifier().itemsAdded(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemAdded(listName, index, addedItem);
} else {
child.fireItemsAdded(event);
}
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemsRemoved(ListChangeEvent event) {
if (event.size() == 0) {
return;
}
String listName = event.getListName();
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().itemsRemoved(target, event);
}
}
}
if (child != null) {
child.fireItemsRemoved(event);
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemsRemoved(String listName, int index, List removedItems) {
// this.fireItemsRemoved(new ListChangeEvent(this.source, listName, index, removedItems));
if (removedItems.size() == 0) {
return;
}
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
ListChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, removedItems);
}
this.notifier().itemsRemoved(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemsRemoved(listName, index, removedItems);
} else {
child.fireItemsRemoved(event);
}
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemRemoved(String listName, int index, Object removedItem) {
// this.fireItemsRemoved(listName, index, Collections.singletonList(removedItem));
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
ListChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, Collections.singletonList(removedItem));
}
this.notifier().itemsRemoved(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemRemoved(listName, index, removedItem);
} else {
child.fireItemsRemoved(event);
}
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemsReplaced(ListChangeEvent event) {
if (event.size() == 0) {
return;
}
String listName = event.getListName();
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().itemsReplaced(target, event);
}
}
}
if (child != null) {
child.fireItemsReplaced(event);
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemsReplaced(String listName, int index, List newItems, List replacedItems) {
// this.fireItemsReplaced(new ListChangeEvent(this.source, listName, index, newItems, replacedItems));
if (newItems.size() == 0) {
return;
}
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
ListChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, newItems, replacedItems);
}
this.notifier().itemsReplaced(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemsReplaced(listName, index, newItems, replacedItems);
} else {
child.fireItemsReplaced(event);
}
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemReplaced(String listName, int index, Object newItem, Object replacedItem) {
// this.fireItemsReplaced(listName, index, Collections.singletonList(newItem), Collections.singletonList(replacedItem));
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
ListChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, Collections.singletonList(newItem), Collections.singletonList(replacedItem));
}
this.notifier().itemsReplaced(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemReplaced(listName, index, newItem, replacedItem);
} else {
child.fireItemsReplaced(event);
}
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireListChanged(ListChangeEvent event) {
String listName = event.getListName();
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().listChanged(target, event);
}
}
}
if (child != null) {
child.fireListChanged(event);
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireListChanged(String listName) {
// this.fireListChanged(new ListChangeEvent(this.source, listName));
IdentityHashBag listChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
listChangeListeners = (IdentityHashBag) this.listeners.get(ListChangeListener.class);
if (listChangeListeners != null) {
targets = (IdentityHashBag) listChangeListeners.clone();
}
}
if ((listName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(listName);
}
}
ListChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
ListChangeListener target = (ListChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = listChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName);
}
this.notifier().listChanged(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireListChanged(listName);
} else {
child.fireListChanged(event);
}
}
this.sourceChanged(listName);
}
// ******************** tree change support ********************
/**
* Add a tree change listener that is registered for all trees.
*/
public void addTreeChangeListener(TreeChangeListener listener) {
this.addListener(TreeChangeListener.class, listener);
}
/**
* Add a tree change listener for the specified tree. The listener
* will be notified only for changes to the specified tree.
*/
public void addTreeChangeListener(String treeName, TreeChangeListener listener) {
this.addListener(treeName, TreeChangeListener.class, listener);
}
/**
* Remove a tree change listener that was registered for all tree.
*/
public void removeTreeChangeListener(TreeChangeListener listener) {
this.removeListener(TreeChangeListener.class, listener);
}
/**
* Remove a tree change listener that was registered for a specific tree.
*/
public void removeTreeChangeListener(String treeName, TreeChangeListener listener) {
this.removeListener(treeName, TreeChangeListener.class, listener);
}
/**
* Return whether there are any tree change listeners that will
* be notified when the specified tree has changed.
*/
public boolean hasAnyTreeChangeListeners(String treeName) {
return this.hasAnyListeners(TreeChangeListener.class, treeName);
}
/**
* Return whether there are any tree change listeners that will
* be notified when any tree has changed.
*/
public boolean hasAnyTreeChangeListeners() {
return this.hasAnyListeners(TreeChangeListener.class);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireNodeAdded(TreeChangeEvent event) {
String treeName = event.getTreeName();
IdentityHashBag treeChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
treeChangeListeners = (IdentityHashBag) this.listeners.get(TreeChangeListener.class);
if (treeChangeListeners != null) {
targets = (IdentityHashBag) treeChangeListeners.clone();
}
}
if ((treeName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(treeName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
TreeChangeListener target = (TreeChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = treeChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().nodeAdded(target, event);
}
}
}
if (child != null) {
child.fireNodeAdded(event);
}
this.sourceChanged(treeName);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireNodeAdded(String treeName, Object[] path) {
// this.fireNodeAdded(new TreeChangeEvent(this.source, treeName, path));
IdentityHashBag treeChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
treeChangeListeners = (IdentityHashBag) this.listeners.get(TreeChangeListener.class);
if (treeChangeListeners != null) {
targets = (IdentityHashBag) treeChangeListeners.clone();
}
}
if ((treeName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(treeName);
}
}
TreeChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
TreeChangeListener target = (TreeChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = treeChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new TreeChangeEvent(this.source, treeName, path);
}
this.notifier().nodeAdded(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireNodeAdded(treeName, path);
} else {
child.fireNodeAdded(event);
}
}
this.sourceChanged(treeName);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireNodeRemoved(TreeChangeEvent event) {
String treeName = event.getTreeName();
IdentityHashBag treeChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
treeChangeListeners = (IdentityHashBag) this.listeners.get(TreeChangeListener.class);
if (treeChangeListeners != null) {
targets = (IdentityHashBag) treeChangeListeners.clone();
}
}
if ((treeName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(treeName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
TreeChangeListener target = (TreeChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = treeChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().nodeRemoved(target, event);
}
}
}
if (child != null) {
child.fireNodeRemoved(event);
}
this.sourceChanged(treeName);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireNodeRemoved(String treeName, Object[] path) {
// this.fireNodeRemoved(new TreeChangeEvent(this.source, treeName, path));
IdentityHashBag treeChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
treeChangeListeners = (IdentityHashBag) this.listeners.get(TreeChangeListener.class);
if (treeChangeListeners != null) {
targets = (IdentityHashBag) treeChangeListeners.clone();
}
}
if ((treeName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(treeName);
}
}
TreeChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
TreeChangeListener target = (TreeChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = treeChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new TreeChangeEvent(this.source, treeName, path);
}
this.notifier().nodeRemoved(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireNodeRemoved(treeName, path);
} else {
child.fireNodeRemoved(event);
}
}
this.sourceChanged(treeName);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireTreeChanged(TreeChangeEvent event) {
String treeName = event.getTreeName();
IdentityHashBag treeChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
treeChangeListeners = (IdentityHashBag) this.listeners.get(TreeChangeListener.class);
if (treeChangeListeners != null) {
targets = (IdentityHashBag) treeChangeListeners.clone();
}
}
if ((treeName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(treeName);
}
}
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
TreeChangeListener target = (TreeChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = treeChangeListeners.contains(target);
}
if (stillListening) {
this.notifier().treeChanged(target, event);
}
}
}
if (child != null) {
child.fireTreeChanged(event);
}
this.sourceChanged(treeName);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireTreeChanged(String treeName, Object[] path) {
// this.fireTreeChanged(new TreeChangeEvent(this.source, treeName, path));
IdentityHashBag treeChangeListeners = null;
IdentityHashBag targets = null;
ChangeSupport child = null;
synchronized (this) {
if (this.listeners != null) {
treeChangeListeners = (IdentityHashBag) this.listeners.get(TreeChangeListener.class);
if (treeChangeListeners != null) {
targets = (IdentityHashBag) treeChangeListeners.clone();
}
}
if ((treeName != null) && (this.children != null)) {
child = (ChangeSupport) this.children.get(treeName);
}
}
TreeChangeEvent event = null;
if (targets != null) {
for (Iterator stream = targets.iterator(); stream.hasNext(); ) {
TreeChangeListener target = (TreeChangeListener) stream.next();
boolean stillListening;
synchronized (this) {
stillListening = treeChangeListeners.contains(target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new TreeChangeEvent(this.source, treeName, path);
}
this.notifier().treeChanged(target, event);
}
}
}
if (child != null) {
if (event == null) {
child.fireTreeChanged(treeName, path);
} else {
child.fireTreeChanged(event);
}
}
this.sourceChanged(treeName);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireTreeChanged(String treeName) {
this.fireTreeChanged(treeName, null);
}
// ******************** standard methods ********************
public String toString() {
return StringTools.buildToStringFor(this, this.source);
}
}