package org.argeo.api.cli; import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; import java.util.function.Function; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; /** A special command that can describe {@link DescribedCommand}. */ public class HelpCommand implements DescribedCommand { /** * System property forcing the root command to this value (typically the name of * a script). */ public final static String ROOT_COMMAND_PROPERTY = "org.argeo.api.cli.rootCommand"; final static String HELP = "help"; final static Option HELP_OPTION = Option.builder().longOpt(HELP).desc("print this help").build(); private CommandsCli commandsCli; private CommandsCli parentCommandsCli; // Help formatting private static int helpWidth = 80; private static int helpLeftPad = 4; private static int helpDescPad = 20; public HelpCommand(CommandsCli parentCommandsCli, CommandsCli commandsCli) { super(); this.parentCommandsCli = parentCommandsCli; this.commandsCli = commandsCli; } @Override public String apply(List args) { StringWriter out = new StringWriter(); if (args.size() == 0) {// overview printHelp(commandsCli, out); } else { String cmd = args.get(0); Function, ?> function = commandsCli.getCommand(cmd); if (function == null) return "Command " + cmd + " not found."; Options options; String examples; DescribedCommand command = null; if (function instanceof DescribedCommand) { command = (DescribedCommand) function; options = command.getOptions(); examples = command.getExamples(); } else { options = new Options(); examples = null; } String description = getShortDescription(function); String commandCall = getCommandUsage(cmd, command); HelpFormatter formatter = new HelpFormatter(); formatter.printHelp(new PrintWriter(out), helpWidth, commandCall, description, options, helpLeftPad, helpDescPad, examples, false); } return out.toString(); } private static String getShortDescription(Function, ?> function) { if (function instanceof DescribedCommand) { return ((DescribedCommand) function).getDescription(); } else { return function.toString(); } } public String getCommandUsage(String cmd, DescribedCommand command) { String commandCall = getCommandCall(commandsCli) + " " + cmd; assert command != null; if (command != null && command.getUsage() != null) { commandCall = commandCall + " " + command.getUsage(); } return commandCall; } @Override public String getDescription() { return "Shows this help or describes a command"; } @Override public String getUsage() { return "[command]"; } public CommandsCli getParentCommandsCli() { return parentCommandsCli; } protected String getCommandCall(CommandsCli commandsCli) { HelpCommand hc = commandsCli.getHelpCommand(); if (hc.getParentCommandsCli() != null) { return getCommandCall(hc.getParentCommandsCli()) + " " + commandsCli.getCommandName(); } else { String rootCommand = System.getProperty(ROOT_COMMAND_PROPERTY); if (rootCommand != null) return rootCommand; return commandsCli.getCommandName(); } } public static void printHelp(DescribedCommand command, StringWriter out) { String usage = "java " + command.getClass().getName() + (command.getUsage() != null ? " " + command.getUsage() : ""); HelpFormatter formatter = new HelpFormatter(); Options options = command.getOptions(); options.addOption(HelpCommand.HELP_OPTION); formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), options, helpLeftPad, helpDescPad, command.getExamples(), false); } public static void printHelp(CommandsCli commandsCli, String commandName, StringWriter out) { if (commandName == null) { printHelp(commandsCli, out); return; } DescribedCommand command = (DescribedCommand) commandsCli.getCommand(commandName); String usage = commandsCli.getHelpCommand().getCommandUsage(commandName, command); HelpFormatter formatter = new HelpFormatter(); Options options = command.getOptions(); options.addOption(HelpCommand.HELP_OPTION); formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), options, helpLeftPad, helpDescPad, command.getExamples(), false); } public static void printHelp(CommandsCli commandsCli, StringWriter out) { out.append(commandsCli.getDescription()).append('\n'); String leftPad = spaces(helpLeftPad); for (String cmd : commandsCli.getSubCommands()) { Function, ?> function = commandsCli.getCommand(cmd); assert function != null; out.append(leftPad); out.append(cmd); // TODO deal with long commands out.append(spaces(helpDescPad - cmd.length())); out.append(getShortDescription(function)); out.append('\n'); } } private static String spaces(int count) { // Java 11 // return " ".repeat(count); if (count <= 0) return ""; else { StringBuilder sb = new StringBuilder(count); for (int i = 0; i < count; i++) sb.append(' '); return sb.toString(); } } }