Mon, 01 Dec 2025 21:27:09 +0100
resolve dependencies between standard config vars
fixes #768
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(); } }