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

com.dua3.utility.fx.controls.FileInputBuilder Maven / Gradle / Ivy

There is a newer version: 15.0.2
Show newest version
package com.dua3.utility.fx.controls;

import org.jspecify.annotations.Nullable;
import javafx.beans.value.ObservableValue;
import javafx.stage.FileChooser;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Builder for creating instances of {@link FileInput} with customizable options.
 * The builder provides methods to configure the file dialog mode, initial path,
 * extension filters, validation, and disabled state.
 */
public final class FileInputBuilder {

    private final FileDialogMode mode;
    private final List extensionFilters = new ArrayList<>();
    private Supplier<@Nullable Path> initialPath = () -> null;
    private boolean existingOnly = true;
    private Function> validate;
    private @Nullable ObservableValue disabled;

    /**
     * Creates a new instance of FileInputBuilder with the specified file dialog mode.
     *
     * @param mode the {@link FileDialogMode} indicating the type of file dialog (OPEN, SAVE, or DIRECTORY)
     */
    FileInputBuilder(FileDialogMode mode) {
        this.mode = mode;
        this.validate = this::defaultValidate;
    }

    /**
     * Sets the 'disabled' property for the FileInput control.
     *
     * @param disabled an ObservableValue object representing the disabled state of the FileInput control
     * @return the current instance of FileInputBuilder with the 'disabled' property set
     */
    public FileInputBuilder disabled(ObservableValue disabled) {
        this.disabled = disabled;
        return this;
    }

    /**
     * Sets the validation function for the file input.
     *
     * @param validate a function that takes a Path and returns an Optional containing
     *                 an error message if validation fails, or an empty Optional if
     *                 validation succeeds
     * @return the updated FileInputBuilder instance
     */
    public FileInputBuilder validate(Function> validate) {
        this.validate = validate;
        return this;
    }

    /**
     * Sets the initial path for the file input.
     *
     * @param initialPath the initial path to set, which may be null
     * @return an instance of FileInputBuilder with the specified initial path
     */
    public FileInputBuilder initialPath(@Nullable Path initialPath) {
        this.initialPath = () -> initialPath;
        return this;
    }

    /**
     * Sets the initial path for the FileInputBuilder using the provided {@code Supplier}.
     *
     * @param initialPath a Supplier that provides the initial Path
     * @return the FileInputBuilder instance with the updated initial path
     */
    public FileInputBuilder initialPath(Supplier initialPath) {
        this.initialPath = initialPath;
        return this;
    }

    /**
     * Adds the specified file extension filters to the FileChooser.
     *
     * @param filter One or more FileChooser.ExtensionFilter objects representing
     *               the file extension filters to be added.
     * @return The current instance of FileInputBuilder, allowing for method chaining.
     */
    public FileInputBuilder filter(FileChooser.ExtensionFilter... filter) {
        extensionFilters.addAll(Arrays.asList(filter));
        return this;
    }

    /**
     * Sets the flag indicating whether only existing files or directories should be selectable.
     *
     * @param flag a boolean flag; if true, only existing files or directories can be selected, otherwise new ones can also be selected
     * @return the current instance of FileInputBuilder for method chaining
     */
    public FileInputBuilder existingOnly(boolean flag) {
        this.existingOnly = flag;
        return this;
    }

    /**
     * Builds a {@link FileInput} object using the properties specified in the {@code FileInputBuilder}.
     *
     * @return a constructed {@link FileInput} control based on the current configuration.
     */
    public FileInput build() {
        FileInput control = new FileInput(mode, existingOnly, initialPath, extensionFilters, validate);
        if (disabled != null) {
            control.disableProperty().bind(disabled);
        }

        return control;
    }

    /**
     * Returns a string representation of the item based on the mode and whether the text should be capitalized.
     *
     * @param captitalize boolean indicating whether the text should be capitalized
     * @return a string representing the item, either "Directory" or "directory" when the mode is DIRECTORY,
     * and "File" or "file" when the mode is OPEN or SAVE
     */
    private String itemText(boolean captitalize) {
        return switch (mode) {
            case DIRECTORY -> captitalize ? "Directory" : "directory";
            case OPEN, SAVE -> captitalize ? "File" : "file";
        };
    }

    /**
     * Validates a given file path based on the specified file dialog mode and whether
     * only existing files or directories should be selectable.
     *
     * @param p the path to validate, can be null
     * @return an Optional containing an error message if validation fails, or an empty Optional if validation succeeds
     */
    private Optional defaultValidate(@Nullable Path p) {
        if (p == null) {
            return Optional.of("No " + itemText(false) + " selected");
        }
        if (existingOnly && !Files.exists(p)) {
            return Optional.of(itemText(true) + " does not exist");
        }
        if (!existingOnly) {
            return Optional.empty();
        }
        final boolean isDirectory = Files.isDirectory(p);
        return switch (mode) {
            case DIRECTORY -> isDirectory
                    ? Optional.empty()
                    : Optional.of("Selection is not a " + itemText(false));
            case OPEN -> isDirectory
                    ? Optional.of("Selection is a directory")
                    : Optional.empty();
            case SAVE -> isDirectory
                    ? Optional.of("Selection is a directory")
                    : (!Files.isWritable(p) ? Optional.of(itemText(true) + "is not writeable") : Optional.empty());
        };
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy