All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.reactfx.value.FlatMapped Maven / Gradle / Ivy

package org.reactfx.value;

import java.util.function.Consumer;
import java.util.function.Function;

import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;

import org.reactfx.Subscription;

abstract class FlatMapped>
extends ValBase {
    final Val src;

    private Subscription selectedSubscription = null; // irrelevant when not connected

    public FlatMapped(ObservableValue src, Function f) {
        this.src = Val.map(src, f);
    }

    @Override
    protected final Subscription connect() {
        return Val.observeInvalidations(src, obs -> srcInvalidated())
                .and(this::stopObservingSelected);
    }

    @Override
    protected final U computeValue() {
        if(isObservingInputs()) {
            startObservingSelected();
        }
        return src.getOpt().map(O::getValue).orElse(null);
    }

    private void startObservingSelected() {
        assert isObservingInputs();
        if(selectedSubscription == null) {
            src.ifPresent(sel -> {
                selectedSubscription = Val.observeInvalidations(
                        sel, obs -> selectedInvalidated());
            });
        }
    }

    private void stopObservingSelected() {
        if(selectedSubscription != null) {
            selectedSubscription.unsubscribe();
            selectedSubscription = null;
        }
    }

    private void selectedInvalidated() {
        invalidate();
    }

    private void srcInvalidated() {
        stopObservingSelected();
        invalidate();
    }
}

class FlatMappedVal>
extends FlatMapped {

    public FlatMappedVal(ObservableValue src, Function f) {
        super(src, f);
    }
}

class FlatMappedVar>
extends FlatMapped
implements Var {
    private final ChangeListener srcListenerWhenBound;

    private ObservableValue boundTo = null;

    public FlatMappedVar(
            ObservableValue src,
            Function f,
            U resetToOnUnbind) {
        this(src, f, oldProperty -> oldProperty.setValue(resetToOnUnbind));
    }

    public FlatMappedVar(ObservableValue src, Function f) {
        this(src, f, oldProperty -> {});
    }

    private FlatMappedVar(
            ObservableValue src,
            Function f,
            Consumer onUnbind) {
        super(src, f);
        srcListenerWhenBound = (obs, oldProperty, newProperty) -> {
            assert boundTo != null;
            if(oldProperty != null) {
                oldProperty.unbind();
                onUnbind.accept(oldProperty);
            }
            if(newProperty != null) {
                newProperty.bind(boundTo);
            }
        };
    }

    @Override
    public void setValue(U value) {
        src.ifPresent(sel -> {
            sel.setValue(value);
            invalidate();
        });
    }

    @Override
    public void bind(ObservableValue other) {
        if(other == null) {
            throw new IllegalArgumentException("Cannot bind to null");
        }

        if(boundTo == null) {
            src.addListener(srcListenerWhenBound);
        }
        src.ifPresent(sel -> sel.bind(other));
        boundTo = other;
    }

    @Override
    public void unbind() {
        if(boundTo != null) {
            src.removeListener(srcListenerWhenBound);
            src.ifPresent(Property::unbind);
            boundTo = null;
        }
    }

    @Override
    public boolean isBound() {
        return boundTo != null ||
                (src.isPresent() && src.getOrThrow().isBound());
    }
}