package org.codefx.libfx.nesting.testhelper; import java.util.Objects; import java.util.Optional; import javafx.beans.Observable; import javafx.beans.property.IntegerProperty; import javafx.beans.property.Property; import javafx.beans.property.ReadOnlyProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import org.codefx.libfx.nesting.Nesting; /** * Provides simple usability functions to access an outer observable's nesting hierarchy in a more readable way. To that * end, many arguments and return types can be null if the {@link Nesting#innerObservableProperty() innerObservable} is * not present.(see method comments) */ public class NestingAccess { // #begin NESTING /** * @param <O> * the type of observable the nesting contains * @param nesting * the nesting whose observable will be returned * @return the observable in the nesting's {@code innerObservable} property: * {@link Nesting#innerObservableProperty()}.{@link ReadOnlyProperty#getValue() getValue()}. * {@link Optional#get() get()}; will be null if no observable is present */ public static <O extends Observable> O getNestingObservable(Nesting<O> nesting) { Objects.requireNonNull(nesting, "The argument 'nesting' must not be null."); Optional<O> nestingObservable = nesting.innerObservableProperty().getValue(); return nestingObservable.orElse(null); } /** * Sets the specified nesting's observable value to the specified new observable. * * @param <O> * the type of observable the nesting contains * @param nesting * the nesting whose observable will be set * @param newObservable * the nesting's new observable; can be null */ public static <O extends Observable> void setNestingObservable(EditableNesting<O> nesting, O newObservable) { Objects.requireNonNull(nesting, "The argument 'nesting' must not be null."); nesting.setInnerObservable(Optional.ofNullable(newObservable)); } /** * @param <T> * the type of the nesting's observable's value * @param <O> * the type of observable the nesting contains * @param nesting * the nesting whose observable's value will be returned * @return the value of the observable in the nesting's {@code innerObservable} property: * {@link Nesting#innerObservableProperty()}.{@link ReadOnlyProperty#getValue() getValue()}. * {@link Optional#get() get()}.{@link ObservableValue#getValue() getValue()}; can be null * @throws NullPointerException * if the nesting's inner observable is not present */ public static <T, O extends ObservableValue<T>> T getNestingValue(Nesting<O> nesting) { Objects.requireNonNull(nesting, "The argument 'nesting' must not be null."); return getNestingObservable(nesting).getValue(); } /** * Sets the specified nesting's observable's value to the specified new value. * * @param <T> * the type of the nesting's observable's value * @param <O> * the type of observable the nesting contains * @param nesting * the nesting whose observable's value will be set * @param newValue * the nesting's observable's new value; can be null. * @throws NullPointerException * if the nesting's inner observable is not present */ public static <T, O extends Property<T>> void setNestingValue(Nesting<O> nesting, T newValue) { Objects.requireNonNull(nesting, "The argument 'nesting' must not be null."); getNestingObservable(nesting).setValue(newValue); } //#end NESTING // #begin NESTED HIERARCHY /** * @param outerObservable * the outer observable whose outer value will be returned * @return outerObservable -> outerValue; can be null */ public static OuterValue getOuterValue(ObservableValue<OuterValue> outerObservable) { Objects.requireNonNull(outerObservable, "The argument 'outerObservable' must not be null."); return outerObservable.getValue(); } /** * Sets a new outer value. * * @param outerObservable * the outer observable whose outer value will be set * @param outerValue * the new outer value; can be null */ public static void setOuterValue(Property<OuterValue> outerObservable, OuterValue outerValue) { Objects.requireNonNull(outerObservable, "The argument 'outerObservable' must not be null."); outerObservable.setValue(outerValue); } /** * @param outerObservable * the outer observable whose inner value will be returned * @return outerObservable -> outerValue -> innerValue; can be null * @throws NullPointerException * if the outer observable's value is null */ public static InnerValue getInnerValue(ObservableValue<OuterValue> outerObservable) { Objects.requireNonNull(outerObservable, "The argument 'outerObservable' must not be null."); return outerObservable.getValue().getInnerValue(); } /** * Sets a new inner value. * * @param outerObservable * the outer observable whose inner value will be set * @param innerValue * the new inner value * @throws NullPointerException * if the outer observable's value is null */ public static void setInnerValue(ObservableValue<OuterValue> outerObservable, InnerValue innerValue) { Objects.requireNonNull(outerObservable, "The argument 'outerObservable' must not be null."); outerObservable.getValue().setInnerValue(innerValue); } /** * @param outerObservable * the outer observable whose inner observable will be returned * @return outerObservable -> outerType -> innerType -> observable * @throws NullPointerException * if the outer observable's value or the inner value is null */ public static Observable getInnerObservable(ObservableValue<OuterValue> outerObservable) { Objects.requireNonNull(outerObservable, "The argument 'outerObservable' must not be null."); return getInnerValue(outerObservable).observable(); } /** * @param outerObservable * the outer observable whose inner property will be returned * @return outerObservable -> outerType -> innerType -> property * @throws NullPointerException * if the outer observable's value or the inner value is null */ public static Property<SomeValue> getInnerProperty(ObservableValue<OuterValue> outerObservable) { Objects.requireNonNull(outerObservable, "The argument 'outerObservable' must not be null."); return getInnerValue(outerObservable).property(); } /** * @param outerObservable * the outer observable whose inner integer property will be returned * @return outerObservable -> outerType -> innerType -> integerProperty * @throws NullPointerException * if the outer observable's value or the inner value is null */ public static IntegerProperty getInnerIntegerProperty(ObservableValue<OuterValue> outerObservable) { return getInnerValue(outerObservable).integerProperty(); } //#end NESTED HIERARCHY // #begin INNER CLASSES /** * An implementation of {@link Nesting} which does no real nesting. Instead it allows to directly edit the * {@link Nesting#innerObservableProperty()}. * * @param <O> * the type of the inner observable */ public static class EditableNesting<O extends Observable> implements Nesting<O> { /** * The property holding the inner observable. */ private final Property<Optional<O>> innerObservable; /** * Creates a new editable nesting. */ private EditableNesting() { this.innerObservable = new SimpleObjectProperty<Optional<O>>(this, "innerObservable", Optional.empty()); } /** * Creates a new editable nesting with the specified inner observable. * * @param <O> * the type of the inner observable * @param innerObservable * the inner observable of the returned nesting * @return a new editable nesting instance */ public static <O extends Observable> EditableNesting<O> createWithInnerObservable(O innerObservable) { Objects.requireNonNull(innerObservable, "The argument 'innerObservable' must not be null."); EditableNesting<O> nesting = new EditableNesting<>(); Optional<O> innerObservableOptional = Optional.of(innerObservable); nesting.setInnerObservable(innerObservableOptional); return nesting; } /** * Creates a new editable nesting with null as its inner observable. * * @param <O> * the type of the inner observable * @return a new editable nesting instance */ public static <O extends Observable> EditableNesting<O> createWithInnerObservableNull() { return new EditableNesting<>(); } /** * The property holding the inner observable. * * @return the innerObservable as a property */ @Override public Property<Optional<O>> innerObservableProperty() { return innerObservable; } /** * The property holding the inner observable. * * @return the innerObservable */ public Optional<O> getInnerObservable() { return innerObservableProperty().getValue(); } /** * The property holding the inner observable. * * @param innerObservable * the innerObservable to set */ public void setInnerObservable(Optional<O> innerObservable) { Objects.requireNonNull(innerObservable, "The argument 'innerObservable' must not be null."); innerObservableProperty().setValue(innerObservable); } } //#end INNER CLASSES }