private static void addCandidatesForArgsFollowing(CommandSpec commandSpec, List<CharSequence> candidates) { if (commandSpec == null) { return; } for (Map.Entry<String, CommandLine> entry : commandSpec.subcommands().entrySet()) { candidates.add(entry.getKey()); candidates.addAll(Arrays.asList(entry.getValue().getCommandSpec().aliases())); } candidates.addAll(commandSpec.optionsMap().keySet()); for (PositionalParamSpec positional : commandSpec.positionalParameters()) { addCandidatesForArgsFollowing(positional, candidates); } } private static void addCandidatesForArgsFollowing(OptionSpec optionSpec, List<CharSequence> candidates) {
/** * Called when parsing varargs parameters for a multi-value option. * When an option is encountered, the remainder should not be interpreted as vararg elements. * @param arg the string to determine whether it is an option or not * @return true if it is an option, false otherwise */ private boolean isOption(String arg) { if (arg == null) { return false; } if ("--".equals(arg)) { return true; } // not just arg prefix: we may be in the middle of parsing -xrvfFILE if (commandSpec.optionsMap().containsKey(arg)) { // -v or -f or --file (not attached to param or other option) return true; } int separatorIndex = arg.indexOf(config().separator()); if (separatorIndex > 0) { // -f=FILE or --file==FILE (attached to param via separator) if (commandSpec.optionsMap().containsKey(arg.substring(0, separatorIndex))) { return true; } } return (arg.length() > 2 && arg.startsWith("-") && commandSpec.posixOptionsMap().containsKey(arg.charAt(1))); } private Object tryConvert(ArgSpec argSpec, int index, ITypeConverter<?> converter, String value, Class<?> type)
private boolean isStandaloneOption(String arg) { return commandSpec.optionsMap().containsKey(arg); } private void handleUnmatchedArgument(Stack<String> args) throws Exception {
@Test public void testOverwrittenOptionExceptionContainsCorrectArgSpec() { class App { @Option(names = "-s") String string; @Option(names = "-v") boolean bool; } try { CommandLine.populateCommand(new App(), "-s", "1", "-s", "2"); fail("expected exception"); } catch (OverwrittenOptionException ex) { assertEquals(ex.getCommandLine().getCommandSpec().optionsMap().get("-s"), ex.getOverwritten()); } try { CommandLine.populateCommand(new App(), "-v", "-v"); fail("expected exception"); } catch (OverwrittenOptionException ex) { assertEquals(ex.getCommandLine().getCommandSpec().optionsMap().get("-v"), ex.getOverwritten()); } }
@Test public void testMixinStandardHelpOptions_SettingToTrueAddsHelpOptions() { CommandSpec spec = CommandSpec.create(); assertTrue(spec.mixins().isEmpty()); assertTrue(spec.optionsMap().isEmpty()); assertTrue(spec.posixOptionsMap().isEmpty()); assertTrue(spec.options().isEmpty()); spec.mixinStandardHelpOptions(true); assertFalse(spec.mixins().isEmpty()); assertFalse(spec.optionsMap().isEmpty()); assertFalse(spec.posixOptionsMap().isEmpty()); assertFalse(spec.options().isEmpty()); assertTrue(spec.mixinStandardHelpOptions()); OptionSpec usageHelp = spec.posixOptionsMap().get('h'); assertSame(usageHelp, spec.optionsMap().get("--help")); assertTrue(usageHelp.usageHelp()); OptionSpec versionHelp = spec.posixOptionsMap().get('V'); assertSame(versionHelp, spec.optionsMap().get("--version")); assertTrue(versionHelp.versionHelp()); }
@Test public void testCommandSpec_forAnnotatedObjectLenient_returnsEmptyCommandSpec() { CommandSpec spec = CommandSpec.forAnnotatedObjectLenient(new Object()); assertTrue(spec.optionsMap().isEmpty()); assertTrue(spec.posixOptionsMap().isEmpty()); assertTrue(spec.options().isEmpty()); assertTrue(spec.positionalParameters().isEmpty()); assertTrue(spec.unmatchedArgsBindings().isEmpty()); assertTrue(spec.subcommands().isEmpty()); assertTrue(spec.mixins().isEmpty()); assertTrue(spec.requiredArgs().isEmpty()); assertFalse(spec.mixinStandardHelpOptions()); assertFalse(spec.helpCommand()); assertEquals("<main class>", spec.name()); assertArrayEquals(new String[0], spec.version()); assertNull(spec.versionProvider()); }
/** see <a href="https://github.com/remkop/picocli/issues/279">issue #279</a> */ @Test public void testSingleValueFieldWithOptionalParameterFollowedByOption_279() { @Command(name="sample") class Sample { @Option(names = "-x") boolean x; @Option(names="--foo", arity="0..1") String foo; } Sample sample = new Sample(); List<CommandLine> parsed3 = new CommandLine(sample).parse("--foo", "-x");// specified without value OptionSpec option3 = parsed3.get(0).getCommandSpec().optionsMap().get("--foo"); assertEquals("optional option is empty string when specified without args", "", option3.getValue()); assertEquals("optional option string value when specified without args", "", option3.stringValues().get(0)); assertEquals("optional option typed value when specified without args", "", option3.typedValues().get(0)); assertEquals("", sample.foo); assertEquals(true, sample.x); }
private void processStandaloneOption(Collection<ArgSpec> required, Set<ArgSpec> initialized, String arg, Stack<String> args, boolean paramAttachedToKey) throws Exception { ArgSpec argSpec = commandSpec.optionsMap().get(arg); required.remove(argSpec); Range arity = argSpec.arity(); if (paramAttachedToKey) { arity = arity.min(Math.max(1, arity.min)); // if key=value, minimum arity is at least 1 } LookBehind lookBehind = paramAttachedToKey ? LookBehind.ATTACHED_WITH_SEPARATOR : LookBehind.SEPARATE; if (tracer.isDebug()) {tracer.debug("Found option named '%s': %s, arity=%s%n", arg, argSpec, arity);} parseResult.nowProcessing.add(argSpec); applyOption(argSpec, lookBehind, arity, args, initialized, "option " + arg); }
@Test public void testMultiValueOptionWithMapAndAuxTypes() { CommandSpec spec = CommandSpec.create(); OptionSpec option = OptionSpec.builder("-c", "--count").arity("3").type(Map.class).auxiliaryTypes(Integer.class, Double.class).build(); assertTrue(option.isMultiValue()); spec.addOption(option); CommandLine commandLine = new CommandLine(spec); commandLine.parse("-c", "1=1.0", "2=2.0", "3=3.0"); Map<Integer, Double> expected = new LinkedHashMap<Integer, Double>(); expected.put(1, 1.0); expected.put(2, 2.0); expected.put(3, 3.0); assertEquals(expected, spec.optionsMap().get("-c").getValue()); }
@Test public void testIsUsageHelpRequested() { @Command(mixinStandardHelpOptions = true) class App { @Option(names = "-x") String x; } CommandLine cmd = new CommandLine(new App()); ParseResult parseResult = cmd.parseArgs("-h"); assertTrue(parseResult.isUsageHelpRequested()); assertFalse(parseResult.isVersionHelpRequested()); assertSame(cmd.getCommandSpec().optionsMap().get("-h"), parseResult.matchedOption('h')); assertTrue(parseResult.unmatched().isEmpty()); assertTrue(parseResult.matchedPositionals().isEmpty()); }
@Test public void testMultiValueOptionWithMapWithoutAuxTypes() { CommandSpec spec = CommandSpec.create(); OptionSpec option = OptionSpec.builder("-c", "--count").arity("3").type(Map.class).build(); assertTrue(option.isMultiValue()); spec.addOption(option); CommandLine commandLine = new CommandLine(spec); commandLine.parse("-c", "1=1.0", "2=2.0", "3=3.0"); Map<String, String> expected = new LinkedHashMap<String, String>(); expected.put("1", "1.0"); expected.put("2", "2.0"); expected.put("3", "3.0"); assertEquals(expected, spec.optionsMap().get("-c").getValue()); }
@Test public void testMatchedOptions_ReturnsOnlyMatchedOptions() { class App { @Option(names = "-a", arity = "0..1") String a; @Option(names = "-b", arity = "0..1") String b; } CommandLine cmd = new CommandLine(new App()); ParseResult parseResult = cmd.parseArgs("-a"); List<OptionSpec> options = parseResult.matchedOptions(); assertEquals(1, options.size()); Map<String, OptionSpec> optionsMap = cmd.getCommandSpec().optionsMap(); assertTrue(parseResult.hasMatchedOption(optionsMap.get("-a"))); assertFalse(parseResult.hasMatchedOption(optionsMap.get("-b"))); }
@Test public void testHasMatchedOptionByOptionSpec() { class App { @Option(names = "-x", arity = "0..1") String x; @Option(names = "-y", arity = "0..1") String y; } CommandLine cmd = new CommandLine(new App()); ParseResult parseResult = cmd.parseArgs("-x"); Map<String, OptionSpec> optionsMap = cmd.getCommandSpec().optionsMap(); assertTrue(parseResult.hasMatchedOption(optionsMap.get("-x"))); assertFalse(parseResult.hasMatchedOption(optionsMap.get("-y"))); }
@Test public void testIsVersionHelpRequested() { @Command(mixinStandardHelpOptions = true) class App { @Option(names = "-x") String x; } CommandLine cmd = new CommandLine(new App()); ParseResult parseResult = cmd.parseArgs("--version"); assertFalse(parseResult.isUsageHelpRequested()); assertTrue(parseResult.isVersionHelpRequested()); assertSame(cmd.getCommandSpec().optionsMap().get("--version"), parseResult.matchedOption('V')); }
@Test public void testModelParse() { CommandSpec spec = CommandSpec.create(); spec.addOption(OptionSpec.builder("-h", "--help").usageHelp(true).description("show help and exit").build()); spec.addOption(OptionSpec.builder("-V", "--version").versionHelp(true).description("show help and exit").build()); spec.addOption(OptionSpec.builder("-c", "--count").paramLabel("COUNT").arity("1").type(int.class).description("number of times to execute").build()); CommandLine commandLine = new CommandLine(spec); commandLine.parse("-c", "33"); assertEquals(Integer.valueOf(33), spec.optionsMap().get("-c").getValue()); } // TODO parse method should return an object offering only the options/positionals that were matched
@Test public void testMultiValueOptionWithListWithoutAuxTypes() { CommandSpec spec = CommandSpec.create(); OptionSpec option = OptionSpec.builder("-c", "--count").arity("3").type(List.class).build(); assertTrue(option.isMultiValue()); spec.addOption(option); CommandLine commandLine = new CommandLine(spec); commandLine.parse("-c", "1", "2", "3"); assertEquals(Arrays.asList("1", "2", "3"), spec.optionsMap().get("-c").getValue()); }
@Test public void testMultiValueOptionWithListAndAuxTypes() { CommandSpec spec = CommandSpec.create(); OptionSpec option = OptionSpec.builder("-c", "--count").arity("3").type(List.class).auxiliaryTypes(Integer.class).build(); assertTrue(option.isMultiValue()); spec.addOption(option); CommandLine commandLine = new CommandLine(spec); commandLine.parse("-c", "1", "2", "3"); assertEquals(Arrays.asList(1, 2, 3), spec.optionsMap().get("-c").getValue()); }
@Test public void testMultiValueOptionWithArray() { CommandSpec spec = CommandSpec.create(); OptionSpec option = OptionSpec.builder("-c", "--count").arity("3").type(int[].class).build(); assertTrue(option.isMultiValue()); spec.addOption(option); CommandLine commandLine = new CommandLine(spec); commandLine.parse("-c", "1", "2", "3"); assertArrayEquals(new int[] {1, 2, 3}, (int[]) spec.optionsMap().get("-c").getValue()); }
@Test public void testOptionConvertersOverridesRegisteredTypeConverter() { CommandSpec spec = CommandSpec.create(); spec.addOption(OptionSpec.builder("-c", "--count").paramLabel("COUNT").arity("1").type(int.class).description("number of times to execute").build()); spec.addOption(OptionSpec.builder("-s", "--sql").paramLabel("SQLTYPE").type(int.class).converters( new CommandLineTypeConversionTest.SqlTypeConverter()).description("sql type converter").build()); CommandLine commandLine = new CommandLine(spec); commandLine.parse("-c", "33", "-s", "BLOB"); assertEquals(Integer.valueOf(33), spec.optionsMap().get("-c").getValue()); assertEquals(Integer.valueOf(Types.BLOB), spec.optionsMap().get("-s").getValue()); } @Test
/** Returns the text displayed before the option list; an empty string if there are no options, * otherwise the result of {@code String.format(optionListHeading, params)}. * @param params the parameters to use to format the option list heading * @return the formatted option list heading */ public String optionListHeading(Object... params) { return commandSpec.optionsMap().isEmpty() ? "" : heading(ansi(), width(), commandSpec.usageMessage().optionListHeading(), params); }