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

bayern.steinbrecher.wizard.EmbeddedWizardPage Maven / Gradle / Ivy

Go to download

Contains a library to create dynamic and branching JavaFX wizards in an abstract way. Comes with a predefined collection of typical wizard pages.

There is a newer version: 1.60
Show newest version
package bayern.steinbrecher.wizard;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.LoadException;
import javafx.scene.layout.Pane;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Represents a page of the wizard.
 *
 * @param  The return type of the result represented by the page.
 * @author Stefan Huber
 * @since 1.0
 */
public final class EmbeddedWizardPage> {

    private static final Logger LOGGER = Logger.getLogger(EmbeddedWizardPage.class.getName());
    /**
     * The key of the page to be used as first one.
     */
    public static final String FIRST_PAGE_KEY = "first";
    private final WizardPage page;
    private final Pane root;
    private final ReadOnlyBooleanWrapper hasNextFunction = new ReadOnlyBooleanWrapper(this, "hasNextFunction");
    private final ObjectProperty> nextFunction = new SimpleObjectProperty<>(this, "nextFunction");
    private boolean finish;
    private Wizard containingWizard = null;

    EmbeddedWizardPage(@NotNull WizardPage page, @Nullable Supplier nextFunction, boolean finish)
            throws LoadException {
        this.page = Objects.requireNonNull(page);
        this.root = page.loadFXML();
        hasNextFunction.bind(this.nextFunction.isNotNull());
        setFinishAndNext(finish, nextFunction);
    }

    /**
     * Returns the pane containing all controls.
     *
     * @return The pane containing all controls.
     */
    @NotNull
    public Pane getRoot() {
        return root;
    }

    /**
     * The property containing the function calculating which page to show next.
     *
     * @return The property containing the function calculating which page to show next.
     */
    @NotNull
    public ReadOnlyProperty> nextFunctionProperty() {
        return nextFunction;
    }

    /**
     * Returns the function calculating the key of the next page.
     *
     * @return The function calculating the key of the next page. Returns {@code null} if this page has no next one.
     */
    @Nullable
    public Supplier getNextFunction() {
        return nextFunctionProperty().getValue();
    }

    /**
     * Returns whether this page is a last one.
     *
     * @return {@code true} only if this page is a last one.
     */
    public boolean isFinish() {
        return finish;
    }

    /*
     * @param finish         {@code true} only if this page is a last one.
     * @param nextFunction   The function calculating the name of the next page. In case {@code finish} is
     *                       {@code true} this value is allowed to be {@code null}.
     * @since 1.27
     */
    public void setFinishAndNext(boolean finish, @Nullable Supplier nextFunction) {
        if (!finish) {
            Objects.requireNonNull(nextFunction,
                    "A non-last page must define a function which calculates the next page.");
        }
        this.finish = finish;
        this.nextFunction.setValue(nextFunction);
    }

    /**
     * This method must be called by a {@link Wizard} if it registers this page as one of its visitable pages.
     * NOTE Currently (2020-09-01) the reference to the containing wizard is solely needed in case this pages next
     * function dynamically creates a {@link WizardPage}.
     */
    void setContainingWizard(@NotNull Wizard wizard) {
        if (containingWizard != null) {
            throw new IllegalStateException("This page is already registered to a wizard");
        }
        containingWizard = Objects.requireNonNull(wizard);
    }

    /**
     * @return A {@link CompletableFuture} object which contains the dynamically generated next page as soon as the
     * {@link Wizard} this page belongs to tries to access its next page.
     * @see #setFinishAndNext(boolean, Supplier)
     * @since 1.27
     */
    public , C extends WizardPageController> CompletableFuture> setFinishAndDynamicNext(
            boolean finish, @Nullable Supplier> dynamicNextFunction, @NotNull String pageID) {
        CompletableFuture> wizardPageCreation = new CompletableFuture<>();
        Supplier nextFunction;
        if (dynamicNextFunction == null) {
            nextFunction = null;
            wizardPageCreation.completeExceptionally(
                    new NoSuchElementException(
                            "This wizard page has no next function which could have dynamically created the next "
                                    + "wizard page."));
        } else {
            nextFunction = () -> {
                if (containingWizard == null) {
                    wizardPageCreation.completeExceptionally(
                            new IllegalStateException(
                                    "This pages next function can not register a dynamically created next page since "
                                            + "no wizard registered for containing this page"));
                } else {
                    try {
                        EmbeddedWizardPage wizardPage = dynamicNextFunction.get()
                                .generateEmbeddableWizardPage();
                        containingWizard.putPage(pageID, wizardPage);
                        wizardPageCreation.complete(wizardPage);
                    } catch (LoadException ex) {
                        wizardPageCreation.completeExceptionally(ex);
                    }
                }
                if (wizardPageCreation.isCompletedExceptionally()) {
                    Throwable creationException;
                    try {
                        creationException = wizardPageCreation.handle((noResult, ex) -> ex)
                                .get();
                    } catch (InterruptedException | ExecutionException ex) {
                        creationException = ex;
                    }
                    LOGGER.log(Level.SEVERE, "The dynamic creation of a wizard page failed", creationException);
                }
                return pageID;
            };
        }
        setFinishAndNext(finish, nextFunction);
        return wizardPageCreation;
    }

    public T getResult() {
        return page.getResult();
    }

    /**
     * Returns the property representing whether this page has valid input.
     *
     * @return The property representing whether this page has valid input.
     */
    @NotNull
    public ReadOnlyBooleanProperty validProperty() {
        return page.validProperty();
    }

    /**
     * Returns whether the current input of this page is valid.
     *
     * @return {@code true} only if the current input of this page is valid.
     */
    public boolean isValid() {
        return validProperty().get();
    }

    /**
     * Returns the property holding {@code true} only if this page has a {@code nextFunction}.
     *
     * @return The property holding {@code true} only if this page has a {@code nextFunction}.
     * @see #nextFunctionProperty()
     */
    @NotNull
    public ReadOnlyBooleanProperty hasNextFunctionProperty() {
        return hasNextFunction.getReadOnlyProperty();
    }

    /**
     * Checks whether this page has a {@code nextFunction}.
     *
     * @return Returns {@code true} only if this page has a {@code nextFunction}.
     * @see #nextFunctionProperty()
     */
    public boolean isHasNextFunction() {
        return hasNextFunctionProperty().get();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy