/** * GRANITE DATA SERVICES * Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S. * * This file is part of the Granite Data Services Platform. * * Granite Data Services is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * Granite Data Services is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA, or see <http://www.gnu.org/licenses/>. */ package org.granite.client.javafx.util; import java.util.ArrayList; import java.util.List; import org.granite.client.javafx.util.ChangeWatcher.Trigger; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; public class ChainedObservableValue<T, P> implements ObservableValue<P>, ChangeListener<T>, Trigger<T, P> { private final ObservableValueGetter<T, P> targetObservableValueGetter; private ObservableValue<P> targetObservableValue = null; private List<ChangeListener<? super P>> changeListeners = new ArrayList<ChangeListener<? super P>>(); private List<InvalidationListener> invalidationListeners = new ArrayList<InvalidationListener>(); public static <T, P> ChainedObservableValue<T, P> chain(ObservableValue<T> sourceObservableValue, ObservableValueGetter<T, P> targetPropertyGetter) { return new ChainedObservableValue<T, P>(sourceObservableValue, targetPropertyGetter); } public static <T, P> ChainedObservableValue<T, P> chain(ChangeWatcher<T> sourceWatcher, ObservableValueGetter<T, P> targetPropertyGetter) { return new ChainedObservableValue<T, P>(sourceWatcher, targetPropertyGetter); } public ChainedObservableValue(ObservableValue<T> sourceObservableValue, ObservableValueGetter<T, P> targetPropertyGetter) { this.targetObservableValueGetter = targetPropertyGetter; sourceObservableValue.addListener(this); afterChange(sourceObservableValue.getValue(), null); } public ChainedObservableValue(ChangeWatcher<T> sourceWatcher, ObservableValueGetter<T, P> targetPropertyGetter) { this.targetObservableValueGetter = targetPropertyGetter; sourceWatcher.addTrigger(this); } public <X> ChainedObservableValue<P, X> chain(ObservableValueGetter<P, X> nextPropertyGetter) { return new ChainedObservableValue<P, X>(this, nextPropertyGetter); } public P beforeChange(T oldSource) { P oldValue = targetObservableValue != null ? targetObservableValue.getValue() : null; if (targetObservableValue != null) { targetObservableValue.removeListener(targetChangeListener); targetObservableValue.removeListener(targetInvalidationListener); } return oldValue; } public void afterChange(T newSource, P oldValue) { if (newSource != null) targetObservableValue = targetObservableValueGetter.getObservableValue(newSource); P newValue = targetObservableValue != null ? targetObservableValue.getValue() : null; if (targetObservableValue != null) { targetObservableValue.addListener(targetChangeListener); targetObservableValue.addListener(targetInvalidationListener); } if (newValue != oldValue) { targetInvalidationListener.invalidated(ChainedObservableValue.this); targetChangeListener.changed(ChainedObservableValue.this, oldValue, newValue); } } @Override public void changed(ObservableValue<? extends T> source, T oldSource, T newSource) { P oldValue = beforeChange(oldSource); afterChange(newSource, oldValue); } private ChangeListener<? super P> targetChangeListener = new ChangeListener<P>() { @Override public void changed(ObservableValue<? extends P> target, P oldTarget, P newTarget) { for (ChangeListener<? super P> listener : changeListeners) { listener.changed(target, oldTarget, newTarget); } } }; private InvalidationListener targetInvalidationListener = new InvalidationListener() { @Override public void invalidated(Observable observable) { for (InvalidationListener listener : invalidationListeners) listener.invalidated(observable); } }; @Override public P getValue() { if (targetObservableValue == null) return null; return targetObservableValue.getValue(); } @Override public void addListener(ChangeListener<? super P> listener) { changeListeners.add(listener); } @Override public void removeListener(ChangeListener<? super P> listener) { changeListeners.remove(listener); } @Override public void addListener(InvalidationListener listener) { invalidationListeners.add(listener); } @Override public void removeListener(InvalidationListener listener) { invalidationListeners.remove(listener); } public static interface ObservableValueGetter<B, T> { ObservableValue<T> getObservableValue(B bean); } }