src/main/java/de/unixwork/uwproj/AbstractOption.java

Sun, 30 Nov 2025 22:31:04 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 30 Nov 2025 22:31:04 +0100
changeset 173
16324bb74fb1
parent 165
f4f11f755ee7
permissions
-rw-r--r--

fix missing blank line after config vars in config.mk

package de.unixwork.uwproj;

public abstract class AbstractOption {
    protected String arg = "";
    protected String desc = "";

    public AbstractOption() {
    }

    /**
     * Indicates whether the option can be overridden by command line arguments.
     *
     * @return {@code true} if the option can be overridden, {@code false} otherwise
     */
    public final boolean isOverridable() {
        return !arg.isEmpty();
    }

    /**
     * The name of the argument.
     *
     * @return the argument name
     * @see #getHelpTextArg()
     */
    public final String getArg() {
        return arg;
    }

    /**
     * The display text for the argument.
     * <p>
     * This is not necessarily the same as {@link #getArg()},
     * e.g. for options that have --enable-opt / --disable-opt variants.
     * Also, the help text may show a set of selecatable values or other info.
     *
     * @return the text for the argument to include in the help text
     * @see #getArg()
     */
    public abstract String getHelpTextArg();

    /**
     * The description of the option.
     * Rarely used, better use {@link #getHelpText()} instead.
     * @return the description without any formatting
     * @see #getHelpText()
     */
    public final String getDesc() {
        return desc;
    }

    /**
     * The internal variable name for the option.
     * @return the variable name where the option's value will be stored
     */
    public abstract String getVarName();

    /**
     * Generates help text for the feature option.
     * <p>
     * If no description is available via {@link #getDesc()}, only the option name is generated
     * via {@link #getHelpTextArg()}. Otherwise, the description is added according to the following rules:
     * <p>
     * When the option name does not consume more than 25 characters, the description starts in the same line.
     * Otherwise, a line break is added, first. The description will be placed in a block starting from column
     * 27 to 80 and automatically break when necessary. The description must not contain a single word
     * that is longer than 54 characters, or it will break the layout.
     *
     * @return a help text for terminal output
     */
    public String getHelpText() {
        final var builder = new StringBuilder();

        // Get the option name and prepend it with indentation
        final var opt = "  "+getHelpTextArg();

        // Add option name
        builder.append(opt);

        // Stop, if there is no description
        if (desc.isBlank()) return builder.toString();

        // Prepare the description by replacing some unwanted spaces
        final var hdesc = desc.trim()
                .replaceAll("\\r", "")
                .replaceAll("\\t", "  ");

        // Declare our frame where the description shall be placed
        final int startx = 26;
        final int endx = 80;

        // Move to startx (break, if we already past that)
        if (opt.length() >= startx) {
            builder.append("\n");
            builder.append(" ".repeat(startx));
        } else {
            builder.append(" ".repeat(startx-opt.length()));
        }

        // Append the description keeping the layout intact
        int x = startx;
        for (int i = 0 ; i < hdesc.length() ;) {
            // get the next word and see where we would end
            int s = hdesc.indexOf(' ', i);
            if (s < 0) s = hdesc.length();
            int n = hdesc.indexOf('\n', i);
            if (n < 0) n = hdesc.length();
            s = Math.min(s, n);
            int l = s-i;
            if (x + l > endx) {
                // does not fit, break into next line
                builder.append('\n');
                builder.append(" ".repeat(startx));
                x = startx;
            }
            // append the word
            builder.append(hdesc, i, s);
            x += l;
            i += l;
            // append the next spaces
            while (i < hdesc.length()) {
                int c = hdesc.charAt(i);
                if (c == ' ') {
                    // as long as we don't need to break, add the spaces
                    i++;
                    x++;
                    if (x < endx) builder.append(' ');
                } else if (c == '\n') {
                    // if user wants us to break, comply
                    i++;
                    // if we have still space, just move to the end of the line
                    if (x < endx) {
                        x = endx;
                    } else {
                        // otherwise, we need to add an extra blank line
                        builder.append('\n');
                    }
                } else {
                    // we have found the next word, so continue with the outer loop
                    break;
                }
            }
            // break to new line, when spaces moved us outside the frame
            if (x > endx) {
                builder.append('\n');
                builder.append(" ".repeat(startx));
                x = startx;
            }
        }

        return builder.toString();
    }
}

mercurial