blob: f71e12d2f775c0793adc0b56378958c1b9046cbe [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2012 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.common.utility.internal.model.value;
import org.eclipse.jpt.common.utility.internal.model.ChangeSupport;
import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent;
import org.eclipse.jpt.common.utility.model.listener.StateChangeListener;
import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel;
/**
* Abstract class that provides support for wrapping a {@link ModifiablePropertyValueModel}
* and listening for changes to <em>aspects</em> of the <em>value</em> held
* by the {@link ModifiablePropertyValueModel}. Changes to the
* {@link ModifiablePropertyValueModel}'s value are also monitored.
* <p>
* This is useful if you have a value that may change, but whose aspects can also
* change in a fashion that might be of interest to the client.
* <p>
* <strong>NB:</strong> Clients will need to listen for two different change
* notifications:<ul>
* <li>a property change event will be be fired when the <em>value</em> changes;
* <li>a state change event will be fired when an <em>aspect</em> of the value changes.
* </ul>
* Subclasses need to override two methods:<ul>
* <li>{@link #engageValue_()}<p>
* begin listening to the appropriate aspect of the value and call
* {@link #valueAspectChanged()} whenever the aspect changes
* (this will fire a state change event)
* <li>{@link #disengageValue_()}<p>
* stop listening to the appropriate aspect of the value
* </ul>
*/
public abstract class ValueAspectAdapter<V>
extends PropertyValueModelWrapper<V>
implements ModifiablePropertyValueModel<V>
{
/** Cache the value so we can disengage. */
protected volatile V value;
// ********** constructors/initialization **********
/**
* Constructor - the value model is required.
*/
protected ValueAspectAdapter(ModifiablePropertyValueModel<V> valueModel) {
super(valueModel);
this.value = null;
}
/**
* Override to allow both property and state change listeners.
*/
@Override
protected ChangeSupport buildChangeSupport() {
return new ChangeSupport(this);
}
// ********** PropertyValueModel implementation **********
public V getValue() {
return this.value;
}
// ********** WritablePropertyValueModel implementation **********
public void setValue(V value) {
this.getValueModel().setValue(value);
}
// ********** PropertyValueModelWrapper implementation **********
@Override
protected void wrappedValueChanged(PropertyChangeEvent event) {
this.disengageValue();
this.engageValue();
this.firePropertyChanged(event.clone(this));
}
// ********** listeners **********
/**
* Extend to start listening to the underlying model if necessary.
*/
@Override
public synchronized void addStateChangeListener(StateChangeListener listener) {
if (this.hasNoListeners()) {
this.engageModel();
}
super.addStateChangeListener(listener);
}
/**
* Extend to stop listening to the underlying model if necessary.
*/
@Override
public synchronized void removeStateChangeListener(StateChangeListener listener) {
super.removeStateChangeListener(listener);
if (this.hasNoListeners()) {
this.disengageModel();
}
}
/**
* Extend to check for state change listeners.
*/
@Override
protected boolean hasListeners() {
return this.hasAnyStateChangeListeners() || super.hasListeners();
}
/**
* Extend to engage an aspect of the value model's value.
*/
@Override
protected void engageModel() {
super.engageModel();
this.engageValue();
}
/**
* Extend to disengage an aspect of the value model's value.
*/
@Override
protected void disengageModel() {
this.disengageValue();
super.disengageModel();
}
// ********** behavior **********
/**
* Start listening to an aspect of the current value.
*/
protected void engageValue() {
this.value = this.valueModel.getValue();
if (this.value != null) {
this.engageValue_();
}
}
/**
* Start listening to some aspect of the current value.
* At this point we can be sure the value is not <code>null</code>.
*/
protected abstract void engageValue_();
/**
* Stop listening to an aspect of the current value.
*/
protected void disengageValue() {
if (this.value != null) {
this.disengageValue_();
this.value = null;
}
}
/**
* Stop listening to an aspect of the current value.
* At this point we can be sure the value is not <code>null</code>.
*/
protected abstract void disengageValue_();
/**
* Subclasses should call this method whenever the value's aspect changes.
*/
protected void valueAspectChanged() {
this.fireStateChanged();
}
/**
* Our constructor accepts only a {@link ModifiablePropertyValueModel}{@code<V>}.
*/
@SuppressWarnings("unchecked")
protected ModifiablePropertyValueModel<V> getValueModel() {
return (ModifiablePropertyValueModel<V>) this.valueModel;
}
@Override
public void toString(StringBuilder sb) {
sb.append(this.getValue());
}
}