/**
* 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.property.Property;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class ChainedProperty<T, P> implements Property<P>, ChangeListener<T>, Trigger<T, P> {
private final PropertyGetter<T, P> targetPropertyGetter;
private Property<P> targetProperty = null;
private List<ChangeListener<? super P>> changeListeners = new ArrayList<ChangeListener<? super P>>();
private List<InvalidationListener> invalidationListeners = new ArrayList<InvalidationListener>();
public static <T, P> ChainedProperty<T, P> chain(ObservableValue<T> sourceObservableValue, PropertyGetter<T, P> targetPropertyGetter) {
return new ChainedProperty<T, P>(sourceObservableValue, targetPropertyGetter);
}
public static <T, P> ChainedProperty<T, P> chain(ChangeWatcher<T> sourceWatcher, PropertyGetter<T, P> targetPropertyGetter) {
return new ChainedProperty<T, P>(sourceWatcher, targetPropertyGetter);
}
public ChainedProperty(ObservableValue<T> sourceObservableValue, PropertyGetter<T, P> targetPropertyGetter) {
this.targetPropertyGetter = targetPropertyGetter;
sourceObservableValue.addListener(this);
afterChange(sourceObservableValue.getValue(), null);
}
public ChainedProperty(ChangeWatcher<T> sourceWatcher, PropertyGetter<T, P> targetPropertyGetter) {
this.targetPropertyGetter = targetPropertyGetter;
sourceWatcher.addTrigger(this);
}
public <X> ChainedProperty<P, X> chain(PropertyGetter<P, X> nextPropertyGetter) {
return new ChainedProperty<P, X>(this, nextPropertyGetter);
}
public P beforeChange(T oldSource) {
P oldValue = targetProperty != null ? targetProperty.getValue() : null;
if (targetProperty != null) {
targetProperty.removeListener(targetChangeListener);
targetProperty.removeListener(targetInvalidationListener);
}
return oldValue;
}
public void afterChange(T newSource, P oldValue) {
targetProperty = newSource != null ? targetPropertyGetter.getProperty(newSource) : null;
P newValue = targetProperty != null ? targetProperty.getValue() : null;
if (targetProperty != null) {
targetProperty.addListener(targetChangeListener);
targetProperty.addListener(targetInvalidationListener);
if (newValue != oldValue) {
targetInvalidationListener.invalidated(ChainedProperty.this);
targetChangeListener.changed(ChainedProperty.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 Object getBean() {
if (targetProperty == null)
return null;
return targetProperty.getBean();
}
@Override
public String getName() {
if (targetProperty == null)
return null;
return targetProperty.getName();
}
@Override
public P getValue() {
if (targetProperty == null)
return null;
return targetProperty.getValue();
}
@Override
public void setValue(P value) {
if (targetProperty == null)
return;
targetProperty.setValue(value);
}
@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);
}
@Override
public void bind(ObservableValue<? extends P> observableValue) {
throw new UnsupportedOperationException();
}
@Override
public void bindBidirectional(Property<P> property) {
throw new UnsupportedOperationException();
}
@Override
public boolean isBound() {
throw new UnsupportedOperationException();
}
@Override
public void unbind() {
throw new UnsupportedOperationException();
}
@Override
public void unbindBidirectional(Property<P> property) {
throw new UnsupportedOperationException();
}
public static interface PropertyGetter<B, T> {
Property<T> getProperty(B bean);
}
}