package org.marketcetera.photon.ui.databinding; import java.util.Collection; import org.apache.commons.lang.ObjectUtils; import org.eclipse.core.databinding.observable.DisposeEvent; import org.eclipse.core.databinding.observable.IDisposeListener; import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.IValueChangeListener; import org.eclipse.core.databinding.observable.value.ValueChangeEvent; import org.marketcetera.photon.commons.databinding.ITypedObservableValue; import org.marketcetera.util.misc.ClassVersion; import com.google.common.collect.ImmutableList; /* $License$ */ /** * Manages an observable whose value is made up of other child values. * Generally, a {@link MasterDetailObservables master/detail observable} is * better, but this class is for cases where all children must be set for the * master to take a value (e.g. the master observes an immutable object). * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: CompoundObservableManager.java 16154 2012-07-14 16:34:05Z colin $ * @since 2.0.0 */ @ClassVersion("$Id: CompoundObservableManager.java 16154 2012-07-14 16:34:05Z colin $") public abstract class CompoundObservableManager<T extends IObservableValue> { private final T mParent; private ImmutableList<IObservableValue> mChildren; private boolean mUpdatingParent; private boolean mUpdatingChildren; /** * Constructor. * * @param parent * the parent observable to be managed */ public CompoundObservableManager(T parent) { mParent = parent; } /** * Initialize this class with the collection of child observables. This * class is responsible for the children and will dispose them when the * parent is disposed. * * @param children * the child observables */ protected final void init(Collection<? extends IObservableValue> children) { mChildren = ImmutableList.copyOf(children); mParent.addDisposeListener(new IDisposeListener() { @Override public void handleDispose(DisposeEvent staleEvent) { for (IObservableValue child : mChildren) { child.dispose(); } } }); mParent.addValueChangeListener(new IValueChangeListener() { @Override public void handleValueChange(ValueChangeEvent event) { if (!mUpdatingParent) { internalUpdateChildren(); } } }); for (IObservableValue child : mChildren) { child.addValueChangeListener(new IValueChangeListener() { @Override public void handleValueChange(ValueChangeEvent event) { if (!mUpdatingChildren) { internalUpdateParent(); } } }); } internalUpdateChildren(); } private void internalUpdateChildren() { mUpdatingChildren = true; updateChildren(); mUpdatingChildren = false; } private void internalUpdateParent() { mUpdatingParent = true; updateParent(); mUpdatingParent = false; } /** * Get the parent observable. * * @return the parent observable */ protected T getParent() { return mParent; } /** * Update the child observables since the parent has changed. */ protected abstract void updateChildren(); /** * Update the parent observable since a child has changed. */ protected abstract void updateParent(); /** * Utility method to only set the observable to the new value if the new * value is different than the existing one. * * @param <T> the observable's type * @param observable the observable * @param newValue the new value */ protected static <T> void setIfChanged(ITypedObservableValue<T> observable, T newValue) { if (!ObjectUtils.equals(observable.getValue(), newValue)) { observable.setValue(newValue); } } }