blob: ca0fd4e44e579939f62aac8088d35e506614c38a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.utility.internal.model;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.jpt.utility.internal.ClassTools;
import org.eclipse.jpt.utility.internal.CollectionTools;
import org.eclipse.jpt.utility.internal.StringTools;
import org.eclipse.jpt.utility.model.Model;
import org.eclipse.jpt.utility.model.event.CollectionChangeEvent;
import org.eclipse.jpt.utility.model.event.ListChangeEvent;
import org.eclipse.jpt.utility.model.event.PropertyChangeEvent;
import org.eclipse.jpt.utility.model.event.StateChangeEvent;
import org.eclipse.jpt.utility.model.event.TreeChangeEvent;
import org.eclipse.jpt.utility.model.listener.ChangeListener;
import org.eclipse.jpt.utility.model.listener.CollectionChangeListener;
import org.eclipse.jpt.utility.model.listener.ListChangeListener;
import org.eclipse.jpt.utility.model.listener.PropertyChangeListener;
import org.eclipse.jpt.utility.model.listener.StateChangeListener;
import org.eclipse.jpt.utility.model.listener.TreeChangeListener;
/**
* 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
*
* NB2: This class will check to see if, during the firing of events, a listener
* on the original, cloned, list of listeners has been removed from the master
* list of listeners *before* it is notified. If the listener has been removed
* "concurrently" it will *not* be notified. (See the code that uses the
* 'stillListening' local boolean flag.)
*
* NB3: Any listener that is added during the firing of events will *not* be
* also notified.
*
* NB4: This class is serializable, but it will only write out listeners that
* are also serializable while silently leaving behind listeners that are not.
*
* TODO fire a state change event with *every* change?
* TODO use objects (IDs?) instead of strings to identify aspects?
*/
public class ChangeSupport
implements Serializable
{
/** The object to be provided as the "source" for any generated events. */
protected final Model source;
/** Associate a listener class to a collection of "generic" listeners for that class. */
transient private GenericListenerList[] genericListeners = EMPTY_GENERIC_LISTENERS;
private static final GenericListenerList[] EMPTY_GENERIC_LISTENERS = new GenericListenerList[0];
/** Associate aspect names to child change support objects. */
private AspectChild[] aspectChildren = EMPTY_ASPECT_CHILDREN;
private static final AspectChild[] EMPTY_ASPECT_CHILDREN = new AspectChild[0];
private static final long serialVersionUID = 1L;
// ********** constructor **********
/**
* Construct support for the specified source of change events.
* The source cannot be null.
*/
public ChangeSupport(Model source) {
super();
if (source == null) {
throw new NullPointerException();
}
this.source = source;
}
// ********** internal behavior **********
/**
* Add a "generic" listener that listens to all events appropriate to that
* listener, regardless of the aspect name associated with that event.
* The listener cannot be null.
*/
protected <T extends ChangeListener> void addListener(Class<T> listenerClass, T listener) {
if (listener == null) {
throw new NullPointerException(); // better sooner than later
}
synchronized (this) {
GenericListenerList gll = this.genericListenerList(listenerClass);
if (gll == null) {
this.addGenericListenerList(listenerClass, listener);
} else {
gll.addListener(listener);
}
}
}
/**
* Return the "generic" listener list for the specified listener class.
* Return null if the list is not present.
*/
protected GenericListenerList genericListenerList(Class<? extends ChangeListener> listenerClass) {
for (GenericListenerList gll : this.genericListeners) {
if (gll.listenerClass == listenerClass) {
return gll;
}
}
return null;
}
/**
* Add the "generic" listener list for the specified listener class.
* Return the newly-built generic listener list.
*/
protected <T extends ChangeListener> GenericListenerList addGenericListenerList(Class<T> listenerClass, T listener) {
GenericListenerList gll = new GenericListenerList(listenerClass, listener);
this.genericListeners = CollectionTools.add(this.genericListeners, gll);
return gll;
}
/**
* Adds a listener that listens to all events appropriate to that listener,
* and only to those events carrying the aspect name specified.
* The aspect name cannot be null and the listener cannot be null.
*/
protected <T extends ChangeListener> void addListener(String aspectName, Class<T> listenerClass, T listener) {
if ((aspectName == null) || (listener == null)) {
throw new NullPointerException(); // better sooner than later
}
synchronized (this) {
ChangeSupport child = this.child(aspectName);
if (child == null) {
child = this.addChild(aspectName);
}
child.addListener(listenerClass, listener);
}
}
/**
* Return the child change support for the specified aspect name.
* Return null if the aspect name is null or the child is not present.
*/
protected ChangeSupport child(String aspectName) {
// put in a null check to simplify calling code
if (aspectName == null) {
return null;
}
for (AspectChild aspectChild : this.aspectChildren) {
if (aspectChild.aspectName == aspectName) {
return aspectChild.child;
}
}
return null;
}
/**
* Add the child change support for the specified aspect name.
* Return the newly-built child change support.
*/
protected ChangeSupport addChild(String aspectName) {
ChangeSupport child = this.buildChildChangeSupport();
this.aspectChildren = CollectionTools.add(this.aspectChildren, new AspectChild(aspectName, child));
return child;
}
/**
* Build and return a child change support to hold aspect-specific listeners.
*/
protected ChangeSupport buildChildChangeSupport() {
return new ChangeSupport(this.source);
}
/**
* Removes a "generic" listener that has been registered for all events
* appropriate to that listener.
*/
protected <T extends ChangeListener> void removeListener(Class<T> listenerClass, T listener) {
synchronized (this) {
GenericListenerList gll = this.genericListenerList(listenerClass);
if (gll == null) {
throw new IllegalArgumentException("listener not registered"); //$NON-NLS-1$
}
if ( ! gll.removeListener(listener)) { // leave the GLL, even if it is empty?
throw new IllegalArgumentException("listener not registered"); //$NON-NLS-1$
}
}
}
/**
* Removes a listener that has been registered for appropriate
* events carrying the specified aspect name.
*/
protected <T extends ChangeListener> void removeListener(String aspectName, Class<T> listenerClass, T listener) {
synchronized (this) {
ChangeSupport child = this.child(aspectName);
if (child == null) {
throw new IllegalArgumentException("listener not registered"); //$NON-NLS-1$
}
child.removeListener(listenerClass, listener); // leave the child, even if it is empty?
}
}
// ********** internal queries **********
/**
* Return the "generic" listeners for the specified listener class.
* Return null if there are no listeners.
*/
protected ChangeListener[] listeners(Class<? extends ChangeListener> listenerClass) {
GenericListenerList gll = this.genericListenerList(listenerClass);
return (gll == null) ? null : gll.listeners;
}
/**
* Return whether there are any "generic" listeners for the specified
* listener class.
*/
protected synchronized <T extends ChangeListener> boolean hasAnyListeners(Class<T> listenerClass) {
GenericListenerList gll = this.genericListenerList(listenerClass);
return (gll != null) && gll.hasListeners();
}
/**
* Return whether there are no "generic" listeners for the specified
* listener class.
*/
protected <T extends ChangeListener> boolean hasNoListeners(Class<T> listenerClass) {
return ! this.hasAnyListeners(listenerClass);
}
/**
* Return whether there are any listeners for the specified
* listener class and aspect name.
*/
protected synchronized boolean hasAnyListeners(Class<? extends ChangeListener> listenerClass, String aspectName) {
if (this.hasAnyListeners(listenerClass)) {
return true; // there's a "generic" listener
}
ChangeSupport child = this.child(aspectName);
return (child != null) &&
child.hasAnyListeners(listenerClass);
}
/**
* Return whether there are no "generic" listeners for the specified
* listener class and aspect name.
*/
protected <T extends ChangeListener> boolean hasNoListeners(Class<T> listenerClass, String aspectName) {
return ! this.hasAnyListeners(listenerClass, aspectName);
}
// ********** 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 ID will be null if a "state change" occurred.
*/
protected void sourceChanged(@SuppressWarnings("unused") String aspectName) {
// the default is to do nothing
}
// ********** state change support **********
protected static final Class<StateChangeListener> STATE_CHANGE_LISTENER_CLASS = StateChangeListener.class;
/**
* Add a state change listener.
*/
public void addStateChangeListener(StateChangeListener listener) {
this.addListener(STATE_CHANGE_LISTENER_CLASS, listener);
}
/**
* Remove a state change listener.
*/
public void removeStateChangeListener(StateChangeListener listener) {
this.removeListener(STATE_CHANGE_LISTENER_CLASS, listener);
}
/**
* Return whether there are any state change listeners.
*/
public boolean hasAnyStateChangeListeners() {
return this.hasAnyListeners(STATE_CHANGE_LISTENER_CLASS);
}
private StateChangeListener[] stateChangeListeners() {
return (StateChangeListener[]) this.listeners(STATE_CHANGE_LISTENER_CLASS);
}
/**
* Fire the specified state change event to any registered listeners.
*/
public void fireStateChanged(StateChangeEvent event) {
StateChangeListener[] targets = null;
synchronized (this) {
StateChangeListener[] stateChangeListeners = this.stateChangeListeners();
if (stateChangeListeners != null) {
targets = stateChangeListeners.clone();
}
}
if (targets != null) {
for (StateChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.stateChangeListeners(), target);
}
if (stillListening) {
target.stateChanged(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));
StateChangeListener[] targets = null;
synchronized (this) {
StateChangeListener[] stateChangeListeners = this.stateChangeListeners();
if (stateChangeListeners != null) {
targets = stateChangeListeners.clone();
}
}
if (targets != null) {
StateChangeEvent event = null;
for (StateChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.stateChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new StateChangeEvent(this.source);
}
target.stateChanged(event);
}
}
}
this.sourceChanged(null);
}
// ********** property change support **********
protected static final Class<PropertyChangeListener> PROPERTY_CHANGE_LISTENER_CLASS = PropertyChangeListener.class;
/**
* 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(PROPERTY_CHANGE_LISTENER_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, PROPERTY_CHANGE_LISTENER_CLASS, listener);
}
/**
* Remove a property change listener that was registered for all properties.
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.removeListener(PROPERTY_CHANGE_LISTENER_CLASS, listener);
}
/**
* Remove a property change listener that was registered for a specific property.
*/
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
this.removeListener(propertyName, PROPERTY_CHANGE_LISTENER_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(PROPERTY_CHANGE_LISTENER_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(PROPERTY_CHANGE_LISTENER_CLASS);
}
private PropertyChangeListener[] propertyChangeListeners() {
return (PropertyChangeListener[]) this.listeners(PROPERTY_CHANGE_LISTENER_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();
PropertyChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
PropertyChangeListener[] propertyChangeListeners = this.propertyChangeListeners();
if (propertyChangeListeners != null) {
targets = propertyChangeListeners.clone();
}
child = this.child(propertyName);
}
if (targets != null) {
for (PropertyChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.propertyChangeListeners(), target);
}
if (stillListening) {
target.propertyChanged(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.valuesAreEqual(oldValue, newValue)) {
return;
}
PropertyChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
PropertyChangeListener[] propertyChangeListeners = this.propertyChangeListeners();
if (propertyChangeListeners != null) {
targets = propertyChangeListeners.clone();
}
child = this.child(propertyName);
}
PropertyChangeEvent event = null;
if (targets != null) {
for (PropertyChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.propertyChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new PropertyChangeEvent(this.source, propertyName, oldValue, newValue);
}
target.propertyChanged(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;
}
PropertyChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
PropertyChangeListener[] propertyChangeListeners = this.propertyChangeListeners();
if (propertyChangeListeners != null) {
targets = propertyChangeListeners.clone();
}
child = this.child(propertyName);
}
PropertyChangeEvent event = null;
if (targets != null) {
for (PropertyChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.propertyChangeListeners(), 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));
}
target.propertyChanged(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) {
// this.firePropertyChanged(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
if (oldValue == newValue) {
return;
}
PropertyChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
PropertyChangeListener[] propertyChangeListeners = this.propertyChangeListeners();
if (propertyChangeListeners != null) {
targets = propertyChangeListeners.clone();
}
child = this.child(propertyName);
}
PropertyChangeEvent event = null;
if (targets != null) {
for (PropertyChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.propertyChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new PropertyChangeEvent(this.source, propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
target.propertyChanged(event);
}
}
}
if (child != null) {
if (event == null) {
child.firePropertyChanged(propertyName, oldValue, newValue);
} else {
child.firePropertyChanged(event);
}
}
this.sourceChanged(propertyName);
}
// ********** collection change support **********
protected static final Class<CollectionChangeListener> COLLECTION_CHANGE_LISTENER_CLASS = CollectionChangeListener.class;
/**
* Add a collection change listener that is registered for all collections.
*/
public void addCollectionChangeListener(CollectionChangeListener listener) {
this.addListener(COLLECTION_CHANGE_LISTENER_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, COLLECTION_CHANGE_LISTENER_CLASS, listener);
}
/**
* Remove a collection change listener that was registered for all collections.
*/
public void removeCollectionChangeListener(CollectionChangeListener listener) {
this.removeListener(COLLECTION_CHANGE_LISTENER_CLASS, listener);
}
/**
* Remove a collection change listener that was registered for a specific collection.
*/
public void removeCollectionChangeListener(String collectionName, CollectionChangeListener listener) {
this.removeListener(collectionName, COLLECTION_CHANGE_LISTENER_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(COLLECTION_CHANGE_LISTENER_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(COLLECTION_CHANGE_LISTENER_CLASS);
}
private CollectionChangeListener[] collectionChangeListeners() {
return (CollectionChangeListener[]) this.listeners(COLLECTION_CHANGE_LISTENER_CLASS);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireItemsAdded(CollectionChangeEvent event) {
if (event.itemsSize() == 0) {
return;
}
String collectionName = event.getCollectionName();
CollectionChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
CollectionChangeListener[] collectionChangeListeners = this.collectionChangeListeners();
if (collectionChangeListeners != null) {
targets = collectionChangeListeners.clone();
}
child = this.child(collectionName);
}
if (targets != null) {
for (CollectionChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.collectionChangeListeners(), target);
}
if (stillListening) {
target.itemsAdded(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;
}
CollectionChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
CollectionChangeListener[] collectionChangeListeners = this.collectionChangeListeners();
if (collectionChangeListeners != null) {
targets = collectionChangeListeners.clone();
}
child = this.child(collectionName);
}
CollectionChangeEvent event = null;
if (targets != null) {
for (CollectionChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.collectionChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName, addedItems);
}
target.itemsAdded(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));
CollectionChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
CollectionChangeListener[] collectionChangeListeners = this.collectionChangeListeners();
if (collectionChangeListeners != null) {
targets = collectionChangeListeners.clone();
}
child = this.child(collectionName);
}
CollectionChangeEvent event = null;
if (targets != null) {
for (CollectionChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.collectionChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName, Collections.singleton(addedItem));
}
target.itemsAdded(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.itemsSize() == 0) {
return;
}
String collectionName = event.getCollectionName();
CollectionChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
CollectionChangeListener[] collectionChangeListeners = this.collectionChangeListeners();
if (collectionChangeListeners != null) {
targets = collectionChangeListeners.clone();
}
child = this.child(collectionName);
}
if (targets != null) {
for (CollectionChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.collectionChangeListeners(), target);
}
if (stillListening) {
target.itemsRemoved(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;
}
CollectionChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
CollectionChangeListener[] collectionChangeListeners = this.collectionChangeListeners();
if (collectionChangeListeners != null) {
targets = collectionChangeListeners.clone();
}
child = this.child(collectionName);
}
CollectionChangeEvent event = null;
if (targets != null) {
for (CollectionChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.collectionChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName, removedItems);
}
target.itemsRemoved(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));
CollectionChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
CollectionChangeListener[] collectionChangeListeners = this.collectionChangeListeners();
if (collectionChangeListeners != null) {
targets = collectionChangeListeners.clone();
}
child = this.child(collectionName);
}
CollectionChangeEvent event = null;
if (targets != null) {
for (CollectionChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.collectionChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName, Collections.singleton(removedItem));
}
target.itemsRemoved(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 fireCollectionCleared(CollectionChangeEvent event) {
String collectionName = event.getCollectionName();
CollectionChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
CollectionChangeListener[] collectionChangeListeners = this.collectionChangeListeners();
if (collectionChangeListeners != null) {
targets = collectionChangeListeners.clone();
}
child = this.child(collectionName);
}
if (targets != null) {
for (CollectionChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.collectionChangeListeners(), target);
}
if (stillListening) {
target.collectionCleared(event);
}
}
}
if (child != null) {
child.fireCollectionCleared(event);
}
this.sourceChanged(collectionName);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireCollectionCleared(String collectionName) {
// this.fireCollectionCleared(new CollectionChangeEvent(this.source, collectionName));
CollectionChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
CollectionChangeListener[] collectionChangeListeners = this.collectionChangeListeners();
if (collectionChangeListeners != null) {
targets = collectionChangeListeners.clone();
}
child = this.child(collectionName);
}
CollectionChangeEvent event = null;
if (targets != null) {
for (CollectionChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.collectionChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName);
}
target.collectionCleared(event);
}
}
}
if (child != null) {
if (event == null) {
child.fireCollectionCleared(collectionName);
} else {
child.fireCollectionCleared(event);
}
}
this.sourceChanged(collectionName);
}
/**
* Report a bound collection update to any registered listeners.
*/
public void fireCollectionChanged(CollectionChangeEvent event) {
String collectionName = event.getCollectionName();
CollectionChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
CollectionChangeListener[] collectionChangeListeners = this.collectionChangeListeners();
if (collectionChangeListeners != null) {
targets = collectionChangeListeners.clone();
}
child = this.child(collectionName);
}
if (targets != null) {
for (CollectionChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.collectionChangeListeners(), target);
}
if (stillListening) {
target.collectionChanged(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));
CollectionChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
CollectionChangeListener[] collectionChangeListeners = this.collectionChangeListeners();
if (collectionChangeListeners != null) {
targets = collectionChangeListeners.clone();
}
child = this.child(collectionName);
}
CollectionChangeEvent event = null;
if (targets != null) {
for (CollectionChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.collectionChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new CollectionChangeEvent(this.source, collectionName);
}
target.collectionChanged(event);
}
}
}
if (child != null) {
if (event == null) {
child.fireCollectionChanged(collectionName);
} else {
child.fireCollectionChanged(event);
}
}
this.sourceChanged(collectionName);
}
// ********** list change support **********
protected static final Class<ListChangeListener> LIST_CHANGE_LISTENER_CLASS = ListChangeListener.class;
/**
* Add a list change listener that is registered for all lists.
*/
public void addListChangeListener(ListChangeListener listener) {
this.addListener(LIST_CHANGE_LISTENER_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, LIST_CHANGE_LISTENER_CLASS, listener);
}
/**
* Remove a list change listener that was registered for all lists.
*/
public void removeListChangeListener(ListChangeListener listener) {
this.removeListener(LIST_CHANGE_LISTENER_CLASS, listener);
}
/**
* Remove a list change listener that was registered for a specific list.
*/
public void removeListChangeListener(String listName, ListChangeListener listener) {
this.removeListener(listName, LIST_CHANGE_LISTENER_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(LIST_CHANGE_LISTENER_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(LIST_CHANGE_LISTENER_CLASS);
}
private ListChangeListener[] listChangeListeners() {
return (ListChangeListener[]) this.listeners(LIST_CHANGE_LISTENER_CLASS);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemsAdded(ListChangeEvent event) {
if (event.itemsSize() == 0) {
return;
}
String listName = event.getListName();
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
target.itemsAdded(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;
}
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
ListChangeEvent event = null;
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, addedItems);
}
target.itemsAdded(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));
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
ListChangeEvent event = null;
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, Collections.singletonList(addedItem));
}
target.itemsAdded(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.itemsSize() == 0) {
return;
}
String listName = event.getListName();
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
target.itemsRemoved(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;
}
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
ListChangeEvent event = null;
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, removedItems);
}
target.itemsRemoved(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));
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
ListChangeEvent event = null;
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, Collections.singletonList(removedItem));
}
target.itemsRemoved(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.itemsSize() == 0) {
return;
}
String listName = event.getListName();
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
target.itemsReplaced(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;
}
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
ListChangeEvent event = null;
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, index, newItems, replacedItems);
}
target.itemsReplaced(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));
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
ListChangeEvent event = null;
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), 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));
}
target.itemsReplaced(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 fireItemsMoved(ListChangeEvent event) {
if (event.getTargetIndex() == event.getSourceIndex()) {
return;
}
String listName = event.getListName();
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
target.itemsMoved(event);
}
}
}
if (child != null) {
child.fireItemsMoved(event);
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemsMoved(String listName, int targetIndex, int sourceIndex, int length) {
// this.fireItemsMoved(new ListChangeEvent(this.source, listName, targetIndex, sourceIndex, length));
if (targetIndex == sourceIndex) {
return;
}
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
ListChangeEvent event = null;
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName, targetIndex, sourceIndex, length);
}
target.itemsMoved(event);
}
}
}
if (child != null) {
if (event == null) {
child.fireItemsMoved(listName, targetIndex, sourceIndex, length);
} else {
child.fireItemsMoved(event);
}
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireItemMoved(String listName, int targetIndex, int sourceIndex) {
this.fireItemsMoved(listName, targetIndex, sourceIndex, 1);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireListCleared(ListChangeEvent event) {
String listName = event.getListName();
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
target.listCleared(event);
}
}
}
if (child != null) {
child.fireListCleared(event);
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireListCleared(String listName) {
// this.fireListCleared(new ListChangeEvent(this.source, listName));
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
ListChangeEvent event = null;
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName);
}
target.listCleared(event);
}
}
}
if (child != null) {
if (event == null) {
child.fireListCleared(listName);
} else {
child.fireListCleared(event);
}
}
this.sourceChanged(listName);
}
/**
* Report a bound list update to any registered listeners.
*/
public void fireListChanged(ListChangeEvent event) {
String listName = event.getListName();
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
target.listChanged(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));
ListChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
ListChangeListener[] listChangeListeners = this.listChangeListeners();
if (listChangeListeners != null) {
targets = listChangeListeners.clone();
}
child = this.child(listName);
}
ListChangeEvent event = null;
if (targets != null) {
for (ListChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.listChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new ListChangeEvent(this.source, listName);
}
target.listChanged(event);
}
}
}
if (child != null) {
if (event == null) {
child.fireListChanged(listName);
} else {
child.fireListChanged(event);
}
}
this.sourceChanged(listName);
}
// ********** tree change support **********
protected static final Class<TreeChangeListener> TREE_CHANGE_LISTENER_CLASS = TreeChangeListener.class;
private static final Object[] EMPTY_TREE_PATH = new Object[0];
/**
* Add a tree change listener that is registered for all trees.
*/
public void addTreeChangeListener(TreeChangeListener listener) {
this.addListener(TREE_CHANGE_LISTENER_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, TREE_CHANGE_LISTENER_CLASS, listener);
}
/**
* Remove a tree change listener that was registered for all tree.
*/
public void removeTreeChangeListener(TreeChangeListener listener) {
this.removeListener(TREE_CHANGE_LISTENER_CLASS, listener);
}
/**
* Remove a tree change listener that was registered for a specific tree.
*/
public void removeTreeChangeListener(String treeName, TreeChangeListener listener) {
this.removeListener(treeName, TREE_CHANGE_LISTENER_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(TREE_CHANGE_LISTENER_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(TREE_CHANGE_LISTENER_CLASS);
}
private TreeChangeListener[] treeChangeListeners() {
return (TreeChangeListener[]) this.listeners(TREE_CHANGE_LISTENER_CLASS);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireNodeAdded(TreeChangeEvent event) {
String treeName = event.getTreeName();
TreeChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
TreeChangeListener[] treeChangeListeners = this.treeChangeListeners();
if (treeChangeListeners != null) {
targets = treeChangeListeners.clone();
}
child = this.child(treeName);
}
if (targets != null) {
for (TreeChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.treeChangeListeners(), target);
}
if (stillListening) {
target.nodeAdded(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));
TreeChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
TreeChangeListener[] treeChangeListeners = this.treeChangeListeners();
if (treeChangeListeners != null) {
targets = treeChangeListeners.clone();
}
child = this.child(treeName);
}
TreeChangeEvent event = null;
if (targets != null) {
for (TreeChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.treeChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new TreeChangeEvent(this.source, treeName, path);
}
target.nodeAdded(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();
TreeChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
TreeChangeListener[] treeChangeListeners = this.treeChangeListeners();
if (treeChangeListeners != null) {
targets = treeChangeListeners.clone();
}
child = this.child(treeName);
}
if (targets != null) {
for (TreeChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.treeChangeListeners(), target);
}
if (stillListening) {
target.nodeRemoved(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));
TreeChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
TreeChangeListener[] treeChangeListeners = this.treeChangeListeners();
if (treeChangeListeners != null) {
targets = treeChangeListeners.clone();
}
child = this.child(treeName);
}
TreeChangeEvent event = null;
if (targets != null) {
for (TreeChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.treeChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new TreeChangeEvent(this.source, treeName, path);
}
target.nodeRemoved(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 fireTreeCleared(TreeChangeEvent event) {
String treeName = event.getTreeName();
TreeChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
TreeChangeListener[] treeChangeListeners = this.treeChangeListeners();
if (treeChangeListeners != null) {
targets = treeChangeListeners.clone();
}
child = this.child(treeName);
}
if (targets != null) {
for (TreeChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.treeChangeListeners(), target);
}
if (stillListening) {
target.treeCleared(event);
}
}
}
if (child != null) {
child.fireTreeCleared(event);
}
this.sourceChanged(treeName);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireTreeCleared(String treeName, Object[] path) {
// this.fireTreeCleared(new TreeChangeEvent(this.source, treeName, path));
TreeChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
TreeChangeListener[] treeChangeListeners = this.treeChangeListeners();
if (treeChangeListeners != null) {
targets = treeChangeListeners.clone();
}
child = this.child(treeName);
}
TreeChangeEvent event = null;
if (targets != null) {
for (TreeChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.treeChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new TreeChangeEvent(this.source, treeName, path);
}
target.treeCleared(event);
}
}
}
if (child != null) {
if (event == null) {
child.fireTreeCleared(treeName, path);
} else {
child.fireTreeCleared(event);
}
}
this.sourceChanged(treeName);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireTreeCleared(String treeName) {
this.fireTreeCleared(treeName, EMPTY_TREE_PATH);
}
/**
* Report a bound tree update to any registered listeners.
*/
public void fireTreeChanged(TreeChangeEvent event) {
String treeName = event.getTreeName();
TreeChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
TreeChangeListener[] treeChangeListeners = this.treeChangeListeners();
if (treeChangeListeners != null) {
targets = treeChangeListeners.clone();
}
child = this.child(treeName);
}
if (targets != null) {
for (TreeChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.treeChangeListeners(), target);
}
if (stillListening) {
target.treeChanged(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));
TreeChangeListener[] targets = null;
ChangeSupport child = null;
synchronized (this) {
TreeChangeListener[] treeChangeListeners = this.treeChangeListeners();
if (treeChangeListeners != null) {
targets = treeChangeListeners.clone();
}
child = this.child(treeName);
}
TreeChangeEvent event = null;
if (targets != null) {
for (TreeChangeListener target : targets) {
boolean stillListening;
synchronized (this) {
stillListening = CollectionTools.contains(this.treeChangeListeners(), target);
}
if (stillListening) {
if (event == null) {
// here's the reason for the duplicate code...
event = new TreeChangeEvent(this.source, treeName, path);
}
target.treeChanged(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, EMPTY_TREE_PATH);
}
// ********** standard methods **********
@Override
public String toString() {
return StringTools.buildToStringFor(this, this.source);
}
// ********** serialization **********
private synchronized void writeObject(ObjectOutputStream s) throws IOException {
// write out the source, children, and any hidden stuff
s.defaultWriteObject();
// only write out Serializable listeners
int len1 = this.genericListeners.length;
for (int i = 0; i < len1; i++) {
this.writeObject(s, this.genericListeners[i]);
}
s.writeObject(null);
}
private void writeObject(ObjectOutputStream s, GenericListenerList gll) throws IOException {
boolean first = true;
int len = gll.listeners.length;
for (int i = 0; i < len; i++) {
ChangeListener listener = gll.listeners[i];
if (listener instanceof Serializable) {
if (first) {
first = false;
s.writeObject(gll.listenerClass);
}
s.writeObject(listener);
}
}
if ( ! first) {
s.writeObject(null);
}
}
private synchronized void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
// read in the source, children, and any hidden stuff
s.defaultReadObject();
// read in generic listener lists
this.genericListeners = EMPTY_GENERIC_LISTENERS;
Object o;
while (null != (o = s.readObject())) {
@SuppressWarnings("unchecked")
Class<? extends ChangeListener> listenerClass = (Class<? extends ChangeListener>) o;
GenericListenerList gll = null;
while (null != (o = s.readObject())) {
if (gll == null) {
gll = this.addGenericListenerListInternal(listenerClass, (ChangeListener) o);
} else {
gll.addListener((ChangeListener) o);
}
}
}
}
@SuppressWarnings("unchecked")
private <T extends ChangeListener> GenericListenerList addGenericListenerListInternal(Class<T> listenerClass, ChangeListener listener) {
return this.addGenericListenerList(listenerClass, (T) listener);
}
// ********** member classes **********
/**
* Pair a listener class with its "generic" listeners.
*/
private static class GenericListenerList {
final Class<? extends ChangeListener> listenerClass;
ChangeListener[] listeners;
<T extends ChangeListener> GenericListenerList(Class<T> listenerClass, T listener) {
super();
this.listenerClass = listenerClass;
this.listeners = (ChangeListener[]) Array.newInstance(listenerClass, 1);
this.listeners[0] = listener;
}
void addListener(ChangeListener listener) {
this.listeners = CollectionTools.add(this.listeners, listener);
}
boolean removeListener(ChangeListener listener) {
int len = this.listeners.length;
if (len == 0) {
return false;
}
try {
this.listeners = CollectionTools.remove(this.listeners, listener);
} catch (ArrayIndexOutOfBoundsException ex) {
return false; // listener not in the list
}
return (this.listeners.length + 1) == len;
}
boolean hasListeners() {
return this.listeners.length > 0;
}
@Override
public String toString() {
return StringTools.buildToStringFor(this, ClassTools.shortNameFor(this.listenerClass));
}
}
/**
* Pair an aspect name with the change support holding its associated
* listeners.
*/
private static class AspectChild implements Serializable {
final String aspectName;
final ChangeSupport child;
private static final long serialVersionUID = 1L;
AspectChild(String aspectName, ChangeSupport child) {
super();
this.aspectName = aspectName;
this.child = child;
}
@Override
public String toString() {
return StringTools.buildToStringFor(this, this.aspectName);
}
}
}