/*******************************************************************************
* 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.uitools.app;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import org.eclipse.persistence.tools.workbench.utility.AbstractModel;
import org.eclipse.persistence.tools.workbench.utility.events.ChangeSupport;
import org.eclipse.persistence.tools.workbench.utility.events.CollectionChangeListener;
import org.eclipse.persistence.tools.workbench.utility.events.ListChangeListener;
import org.eclipse.persistence.tools.workbench.utility.events.StateChangeListener;
import org.eclipse.persistence.tools.workbench.utility.events.TreeChangeListener;
/**
* This abstract extension of AbstractModel provides a base for adding
* change listeners (PropertyChange, CollectionChange, ListChange, TreeChange)
* to a subject and converting the subject's change notifications into a single
* set of change notifications for the aspect VALUE.
*
* The adapter will only listen to the subject (and subject holder) when the
* adapter itself actually has listeners. This will allow the adapter to be
* garbage collected when appropriate
*/
public abstract class AspectAdapter
extends AbstractModel
implements ValueModel
{
/**
* The subject that holds the aspect and fires
* change notification when the aspect changes.
* We need to hold on to this directly so we can
* disengage it when it changes.
*/
protected Object subject;
/**
* A value model that holds the subject
* that holds the aspect and provides change notification.
* This is useful when there are a number of AspectAdapters
* that have the same subject and that subject can change.
* All the AspectAdapters should share the same subject holder.
* For now, this is can only be set upon construction and is
* immutable.
*/
protected ValueModel subjectHolder;
/** A listener that keeps us in synch with the subjectHolder. */
protected PropertyChangeListener subjectChangeListener;
// ********** constructors **********
/**
* Construct an AspectAdapter for the specified subject.
*/
protected AspectAdapter(Object subject) {
this(new ReadOnlyPropertyValueModel(subject));
}
/**
* Construct an AspectAdapter for the specified subject holder.
* The subject holder cannot be null.
*/
protected AspectAdapter(ValueModel subjectHolder) {
super();
if (subjectHolder == null) {
throw new NullPointerException();
}
this.subjectHolder = subjectHolder;
// the subject is null when we are not listening to it
// this will typically result in our value being null
this.subject = null;
}
// ********** initialization **********
protected void initialize() {
super.initialize();
this.subjectChangeListener = this.buildSubjectChangeListener();
}
/**
* @see org.eclipse.persistence.tools.workbench.utility.AbstractModel#buildDefaultChangeSupport()
*/
protected ChangeSupport buildDefaultChangeSupport() {
return new ValueModelChangeSupport(this);
}
/**
* The subject holder's value has changed, keep our subject in synch.
*/
protected PropertyChangeListener buildSubjectChangeListener() {
return new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
AspectAdapter.this.subjectChanged();
}
public String toString() {
return "subject change listener";
}
};
}
// ********** behavior **********
/**
* The subject has changed. Notify listeners that the value has changed.
*/
protected synchronized void subjectChanged() {
Object oldValue = this.getValue();
boolean hasListeners = this.hasListeners();
if (hasListeners) {
this.disengageSubject();
}
this.subject = this.subjectHolder.getValue();
if (hasListeners) {
this.engageSubject();
this.fireAspectChange(oldValue, this.getValue());
}
}
/**
* Return whether there are any listeners for the aspect.
*/
protected abstract boolean hasListeners();
/**
* Return whether there are any listeners for the aspect.
*/
protected boolean hasNoListeners() {
return ! this.hasListeners();
}
/**
* The aspect has changed, notify listeners appropriately.
*/
protected abstract void fireAspectChange(Object oldValue, Object newValue);
/**
* The subject is not null - add our listener.
*/
protected abstract void engageNonNullSubject();
protected void engageSubject() {
// check for nothing to listen to
if (this.subject != null) {
this.engageNonNullSubject();
}
}
/**
* The subject is not null - remove our listener.
*/
protected abstract void disengageNonNullSubject();
protected void disengageSubject() {
// check for nothing to listen to
if (this.subject != null) {
this.disengageNonNullSubject();
}
}
protected void engageSubjectHolder() {
this.subjectHolder.addPropertyChangeListener(VALUE, this.subjectChangeListener);
// synch our subject *after* we start listening to the subject holder,
// since its value might change when a listener is added
this.subject = this.subjectHolder.getValue();
}
protected void disengageSubjectHolder() {
this.subjectHolder.removePropertyChangeListener(VALUE, this.subjectChangeListener);
// clear out the subject when we are not listening to its holder
this.subject = null;
}
protected void engageModels() {
this.engageSubjectHolder();
this.engageSubject();
}
protected void disengageModels() {
this.disengageSubject();
this.disengageSubjectHolder();
}
// ********** extend change support **********
/**
* Extend to start listening to the models if necessary.
* @see org.eclipse.persistence.tools.workbench.utility.Model#addStateChangeListener(StateChangeListener)
*/
public synchronized void addStateChangeListener(StateChangeListener listener) {
if (this.hasNoListeners()) {
this.engageModels();
}
super.addStateChangeListener(listener);
}
/**
* Extend to stop listening to the models if appropriate.
* @see org.eclipse.persistence.tools.workbench.utility.Model#removeStateChangeListener(StateChangeListener)
*/
public synchronized void removeStateChangeListener(StateChangeListener listener) {
super.removeStateChangeListener(listener);
if (this.hasNoListeners()) {
this.disengageModels();
}
}
/**
* Extend to start listening to the models if necessary.
* @see org.eclipse.persistence.tools.workbench.utility.Model#addPropertyChangeListener(PropertyChangeListener)
*/
public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
if (this.hasNoListeners()) {
this.engageModels();
}
super.addPropertyChangeListener(listener);
}
/**
* Extend to start listening to the models if necessary.
* @see org.eclipse.persistence.tools.workbench.utility.Model#addPropertyChangeListener(String, PropertyChangeListener)
*/
public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
if (propertyName == VALUE && this.hasNoListeners()) {
this.engageModels();
}
super.addPropertyChangeListener(propertyName, listener);
}
/**
* Extend to stop listening to the models if appropriate.
* @see org.eclipse.persistence.tools.workbench.utility.Model#removePropertyChangeListener(PropertyChangeListener)
*/
public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
super.removePropertyChangeListener(listener);
if (this.hasNoListeners()) {
this.disengageModels();
}
}
/**
* Extend to stop listening to the models if appropriate.
* @see org.eclipse.persistence.tools.workbench.utility.Model#removePropertyChangeListener(String, PropertyChangeListener)
*/
public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
super.removePropertyChangeListener(propertyName, listener);
if (propertyName == VALUE && this.hasNoListeners()) {
this.disengageModels();
}
}
/**
* Extend to start listening to the models if necessary.
* @see org.eclipse.persistence.tools.workbench.utility.Model#addCollectionChangeListener(CollectionChangeListener)
*/
public synchronized void addCollectionChangeListener(CollectionChangeListener listener) {
if (this.hasNoListeners()) {
this.engageModels();
}
super.addCollectionChangeListener(listener);
}
/**
* Extend to start listening to the models if necessary.
* @see org.eclipse.persistence.tools.workbench.utility.Model#addCollectionChangeListener(String, CollectionChangeListener)
*/
public synchronized void addCollectionChangeListener(String collectionName, CollectionChangeListener listener) {
if (collectionName == VALUE && this.hasNoListeners()) {
this.engageModels();
}
super.addCollectionChangeListener(collectionName, listener);
}
/**
* Extend to stop listening to the models if appropriate.
* @see org.eclipse.persistence.tools.workbench.utility.Model#removeCollectionChangeListener(CollectionChangeListener)
*/
public synchronized void removeCollectionChangeListener(CollectionChangeListener listener) {
super.removeCollectionChangeListener(listener);
if (this.hasNoListeners()) {
this.disengageModels();
}
}
/**
* Extend to stop listening to the models if appropriate.
* @see org.eclipse.persistence.tools.workbench.utility.Model#removeCollectionChangeListener(String, CollectionChangeListener)
*/
public synchronized void removeCollectionChangeListener(String collectionName, CollectionChangeListener listener) {
super.removeCollectionChangeListener(collectionName, listener);
if (collectionName == VALUE && this.hasNoListeners()) {
this.disengageModels();
}
}
/**
* Extend to start listening to the models if necessary.
* @see org.eclipse.persistence.tools.workbench.utility.Model#addListChangeListener(ListChangeListener)
*/
public synchronized void addListChangeListener(ListChangeListener listener) {
if (this.hasNoListeners()) {
this.engageModels();
}
super.addListChangeListener(listener);
}
/**
* Extend to start listening to the models if necessary.
* @see org.eclipse.persistence.tools.workbench.utility.Model#addListChangeListener(String, ListChangeListener)
*/
public synchronized void addListChangeListener(String listName, ListChangeListener listener) {
if (listName == VALUE && this.hasNoListeners()) {
this.engageModels();
}
super.addListChangeListener(listName, listener);
}
/**
* Extend to stop listening to the models if appropriate.
* @see org.eclipse.persistence.tools.workbench.utility.Model#removeListChangeListener(ListChangeListener)
*/
public synchronized void removeListChangeListener(ListChangeListener listener) {
super.removeListChangeListener(listener);
if (this.hasNoListeners()) {
this.disengageModels();
}
}
/**
* Extend to stop listening to the models if appropriate.
* @see org.eclipse.persistence.tools.workbench.utility.Model#removeListChangeListener(String, ListChangeListener)
*/
public synchronized void removeListChangeListener(String listName, ListChangeListener listener) {
super.removeListChangeListener(listName, listener);
if (listName == VALUE && this.hasNoListeners()) {
this.disengageModels();
}
}
/**
* Extend to start listening to the models if necessary.
* @see org.eclipse.persistence.tools.workbench.utility.Model#addTreeChangeListener(TreeChangeListener)
*/
public synchronized void addTreeChangeListener(TreeChangeListener listener) {
if (this.hasNoListeners()) {
this.engageModels();
}
super.addTreeChangeListener(listener);
}
/**
* Extend to start listening to the models if necessary.
* @see org.eclipse.persistence.tools.workbench.utility.Model#addTreeChangeListener(String, TreeChangeListener)
*/
public synchronized void addTreeChangeListener(String treeName, TreeChangeListener listener) {
if (treeName == VALUE && this.hasNoListeners()) {
this.engageModels();
}
super.addTreeChangeListener(treeName, listener);
}
/**
* Extend to stop listening to the models if appropriate.
* @see org.eclipse.persistence.tools.workbench.utility.Model#removeTreeChangeListener(TreeChangeListener)
*/
public synchronized void removeTreeChangeListener(TreeChangeListener listener) {
super.removeTreeChangeListener(listener);
if (this.hasNoListeners()) {
this.disengageModels();
}
}
/**
* Extend to stop listening to the models if appropriate.
* @see org.eclipse.persistence.tools.workbench.utility.Model#removeTreeChangeListener(String, TreeChangeListener)
*/
public synchronized void removeTreeChangeListener(String treeName, TreeChangeListener listener) {
super.removeTreeChangeListener(treeName, listener);
if (treeName == VALUE && this.hasNoListeners()) {
this.disengageModels();
}
}
}