public void verify(PrintStream out) { List<String> errors = new ArrayList<>(); if (!missing.isEmpty()) { StringBuilder missingMessage = new StringBuilder(80); List<String> missingSwitches = new ArrayList<>(missing.size()); for (CliOption option : missing) { missingSwitches.add(last(option.getSwitchNames())); } missingMessage.append("Missing required option"); if (missing.size() > 1) { missingMessage.append('s'); } missingMessage.append(": ").append(join(", ", missingSwitches)); errors.add(missingMessage.toString()); } if (!unexpected.isEmpty()) { String unexpectedMessage = "Unexpected parameters: " + join(" ", unexpected); errors.add(unexpectedMessage); } if (!duplicates.isEmpty()) { StringBuilder duplicatesMessage = new StringBuilder(80); List<String> duplicateSwitches = new ArrayList<>(duplicates.size()); for (Map.Entry<String, String> duplicate : duplicates) { duplicateSwitches.add(duplicate.getKey() + "=" + duplicate.getValue()); } duplicatesMessage.append("Duplicate options: ").append(join(", ", duplicateSwitches)); errors.add(duplicatesMessage.toString()); } if (!errors.isEmpty()) { out.println(command.getDocumentation()); out.println(); out.flush(); throw new ErrorMessage(join("\n", errors)); } }
/** * Add a <code>CliCommand</code>. * * <p>Called by the extending application class to add their custom commands. * * <p>if the given <code>CliCommand.commandName</code> is null then a non-command simple switch * only application is declared. Otherwise a command driven application is declared. * * <p>Commands added here are the first level commands expected at <code>args[0]</code>. * * <p>Nested commands are supported to any level. */ protected void addCommand(CliCommand command) { if (nonCommandCli != null) { if (nonCommandCli.get() && command.getCommandName() != null) throw new RuntimeException("Trying to add a command when already in non-command mode"); if (nonCommandCli.get() && command.getCommandName() == null) throw new RuntimeException("Trying to add two non-commands"); if (!nonCommandCli.get() && command.getCommandName() == null) throw new RuntimeException("Trying to add a non-command when already set to command mode"); } if (command.getCommandName() != null) nonCommandCli = new AtomicBoolean(false); else nonCommandCli = new AtomicBoolean(true); commands.put(command.getCommandName(), command); command.cliSetup(); }
public CliParser(CliCommand command, List<String> arguments) { this.command = command; // getOption removes options as it parses them ArgumentList argumentList = new ArgumentList(arguments); for (CliOption option : command.getOptions()) { switches.addAll(option.getSwitchNames()); List<Map.Entry<String, String>> parsedValues = getOption(option, argumentList); if (parsedValues.isEmpty()) { if (option.isRequired()) { missing.add(option); } } else if (parsedValues.size() > 1) { if (option.isUnique()) { duplicates.addAll(parsedValues); } else { for (String switchName : option.getSwitchNames()) { for (Map.Entry<String, String> parsedValue : parsedValues) { List<String> switchValues = multiValues.get(switchName); if (switchValues == null) { switchValues = new ArrayList<>(); multiValues.put(switchName, switchValues); } switchValues.add(parsedValue.getValue()); } } } } else { String value = parsedValues.get(0).getValue(); for (String switchName : option.getSwitchNames()) { values.put(switchName, value); } } } Iterator<CliParameter> parameters = command.getParameters().iterator(); Iterator<String> trailingArguments = argumentList.trailing(); while (parameters.hasNext() && trailingArguments.hasNext()) { String name = parameters.next().getName(); values.put(name, trailingArguments.next()); trailingArguments.remove(); switches.add(name); } while (parameters.hasNext()) { CliParameter parameter = parameters.next(); switches.add(parameter.getName()); if (parameter.isRequired()) { CliOption option = new CliOption.SwitchBuilder().withSwitch(parameter.getName()).build(); missing.add(option); } } if (command.allowsTrailingParameter()) { while (trailingArguments.hasNext()) { trailing.add(trailingArguments.next()); trailingArguments.remove(); } } Iterator<String> unexpected = argumentList.remaining(); while (unexpected.hasNext()) { this.unexpected.add(unexpected.next()); } }
/** * Validate command line <code>args</code>. * <p>The given command line <code>args</code> are validated against the * custom <code>CliCommand</code>'s added by the application. Help is * displayed and the application exits if there are errors. Otherwise, the * specified <code>activeCommand<code> is set, making its commons-cli * <code>CommandLine</code> available for the application. * @return the selected <code>CliCommand</code> */ protected CliCommand validateCommands() { // check for no arguments help if (originalArgs.length == 0) { if (nonCommandCli.get()) commands.get(null).help(0, null); commandHelp(0, null); } // check for simple no commands command line if (nonCommandCli.get()) { CliCommand command = commands.get(null); command.parseCommandLine(originalArgs); boolean gotConfig = command.handleConfiguration(); handleLogger(command.getCommandLine(), gotConfig); command.customInit(); activeCommand = command; return command; } // check for -v --version at first argument if (originalArgs[0].equals("-" + VersionShortCl) || originalArgs[0].equals("--" + VersionLongCl)) version(); if (originalArgs[0].equals("-" + CliBase.HelpShortCl) || originalArgs[0].equals("--" + CliBase.HelpLongCl)) commandHelp(0, null); // check if first command given is known CliCommand cmd = null; for (Entry<String, CliCommand> entry : commands.entrySet()) { if (entry.getKey().equals(originalArgs[0])) { cmd = entry.getValue(); break; } } if (cmd == null) commandHelp(InvalidCommand, "Unknown command: " + originalArgs[0]); // check for first command given but no arguments help if (originalArgs.length == 1) { if (cmd.hasChildren()) cmd.commandHelp(0, null); if (cmd.hasOptions()) cmd.help(0, null); return cmd; } // must be more than 1 argument given, see if 2nd is another level command CliCommand command = null; for (Entry<String, CliCommand> entry : commands.entrySet()) { command = entry.getValue(); activeCommand = command.validateCommands(0); if (activeCommand != null) break; } boolean gotConfig = command.handleConfiguration(); handleLogger(command.getCommandLine(), gotConfig); if (log.isDebugEnabled()) { StrH.ttl(initsb, 1, "java.class.path:"); String path = System.getProperty("java.class.path"); do { int index = path.indexOf(';'); if (index == -1) break; String element = path.substring(0, index); StrH.ttl(initsb, 2, element); path = path.substring(++index); } while (true); path = path.replace(';', '\n'); } log.debug(initsb.toString()); return activeCommand; }
/** * Return Command Line. * * @return the <code>CommandLine</code>from the active <code>CliCommand</code> specified in the * given command line arguments. Will never be null. * @see CliCommand */ public CommandLine getCommandLine() { return activeCommand.getCommandLine(); }