io.github.palexdev.mfxcore.base.bindings.MappingSource Maven / Gradle / Ivy
Show all versions of materialfx-all Show documentation
/*
* Copyright (C) 2022 Parisi Alessandro - [email protected]
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
*
* MaterialFX 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 3 of the License,
* or (at your option) any later version.
*
* MaterialFX 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 MaterialFX. If not, see .
*/
package io.github.palexdev.mfxcore.base.bindings;
import io.github.palexdev.mfxcore.base.bindings.base.Updater;
import io.github.palexdev.mfxcore.enums.BindingType;
import javafx.beans.value.ObservableValue;
/**
* Concrete implementation of {@link AbstractSource}. This type of {@code Source} is capable of
* mapping the source's type S to the target's type and vice-versa to perform both {@link #updateTarget(Object, Object)}
* and {@link #updateSource(Object, Object)}.
*
* @param the source's observable type
* @param the target's observable type
*/
public class MappingSource extends AbstractSource {
//================================================================================
// Properties
//================================================================================
protected MappedUpdater targetUpdater; // From source to target
protected MappedUpdater sourceUpdater; // From target to source
//================================================================================
// Constructors
//================================================================================
protected MappingSource() {
}
public MappingSource(ObservableValue observable) {
super(observable);
}
public static MappingSource of(ObservableValue observable) {
return new MappingSource<>(observable);
}
//================================================================================
// Methods
//================================================================================
/**
* {@inheritDoc}
*
* Operates differently depending on which binding activated this.
*
* For unidirectional bindings the target instance is null so the {@link #getTargetUpdater()} is called
* then exits.
*
* For bidirectional bindings the target instance is not null. Before calling the {@link #getTargetUpdater()}
* we first must check that the update is not has not been invoked because of a "bounce" effect, {@link Target#isFromSource()}.
*/
@Override
public void updateTarget(S oldValue, S newValue) {
if (target.bindingType() == BindingType.UNIDIRECTIONAL) {
try {
target.ignoreBinding = true;
targetUpdater.update(oldValue, newValue);
} finally {
target.ignoreBinding = false;
}
return;
}
if (target.isFromSource()) return;
targetUpdater.update(oldValue, newValue);
}
/**
* {@inheritDoc}
*
* Operates differently depending on which binding activated this.
*
* For unidirectional bindings the target instance is null so the {@link #getTargetUpdater()} is called
* then exits.
*
* For bidirectional bindings the target instance is not null. The call to {@link #getSourceUpdater()}
* is surrounded by a try-finally block in which we also set {@link Target#isFromSource()} to true, if anything goes wrong
* the finally block ensures to reset {@link Target#isFromSource()} back to false.
*/
@Override
public void updateSource(T oldValue, T newValue) {
if (target == null) {
sourceUpdater.update(oldValue, newValue);
return;
}
try {
target.fromSource = true;
sourceUpdater.update(oldValue, newValue);
} finally {
target.fromSource = false;
}
}
/**
* {@inheritDoc}
*
* For unidirectional bindings. The listener added to this source's observable is responsible for
* triggering {@link #updateTarget(Object, Object)}.
*/
@Override
protected void listen() {
if (obvListener == null) obvListener = (ov, o, n) -> updateTarget(o, n);
observable.addListener(obvListener);
}
/**
* {@inheritDoc}
*
* For bidirectional bindings. The source's target is set to the given one.
* Then {@link #listen()} is called. Then a listener to the given target is added and is responsible for
* triggering {@link #updateSource(Object, Object)}.
*/
@Override
protected void listen(Target target) {
listen();
this.target = target;
if (tgtListener == null) tgtListener = (ov, o, n) -> updateSource(o, n);
target.getObservable().addListener(tgtListener);
}
/**
* {@inheritDoc}
*
* Removes the listeners added to this source's observable and the target's observable (if not null).
* Then sets all its properties and listeners to null.
*/
@Override
public void dispose() {
observable.removeListener(obvListener);
if (target != null && tgtListener != null) target.getObservable().removeListener(tgtListener);
observable = null;
target = null;
targetUpdater = null;
sourceUpdater = null;
obvListener = null;
tgtListener = null;
}
//================================================================================
// Getters/Setters
//================================================================================
/**
* @return the {@link MappedUpdater} responsible for mapping the source's values to a compatible type and then
* update the target
*/
public MappedUpdater getTargetUpdater() {
return targetUpdater;
}
/**
* Sets the {@link MappedUpdater} for the target.
*/
public MappingSource setTargetUpdater(MappedUpdater targetUpdater) {
this.targetUpdater = targetUpdater;
return this;
}
/**
* Sets the {@link MappedUpdater} for the target.
*/
public MappingSource setTargetUpdater(Mapper mapper, Updater updater) {
this.targetUpdater = new MappedUpdater<>(mapper, updater);
return this;
}
/**
* @return the {@link MappedUpdater} responsible for mapping the target's values to a compatible type and then
* update the source
*/
public MappedUpdater getSourceUpdater() {
return sourceUpdater;
}
/**
* Sets the {@link MappedUpdater} for the source.
*/
public MappingSource setSourceUpdater(MappedUpdater sourceUpdater) {
this.sourceUpdater = sourceUpdater;
return this;
}
/**
* Sets the {@link MappedUpdater} for the source.
*/
public MappingSource setSourceUpdater(Mapper mapper, Updater updater) {
this.sourceUpdater = new MappedUpdater<>(mapper, updater);
return this;
}
//================================================================================
// Builder
//================================================================================
public static class Builder {
private final MappingSource source = new MappingSource<>();
public Builder observable(ObservableValue observable) {
source.observable = observable;
return this;
}
public Builder targetUpdater(MappedUpdater targetUpdater) {
source.targetUpdater = targetUpdater;
return this;
}
public Builder sourceUpdater(MappedUpdater sourceUpdater) {
source.sourceUpdater = sourceUpdater;
return this;
}
public MappingSource get() {
if (source.observable == null) throw new NullPointerException("Source is invalid as observable is null");
return source;
}
}
}