String separator = config().separator(); while (!args.isEmpty()) { if (endOfOptions) { if (commandSpec.parser.endOfOptionsDelimiter().equals(arg)) { tracer.info("Found end-of-options delimiter '--'. Treating remainder as positional parameters.%n"); endOfOptions = true; else if (config().posixClusteredShortOptionsAllowed() && arg.length() > 2 && arg.startsWith("-")) { if (tracer.isDebug()) {tracer.debug("Trying to process '%s' as clustered short options%n", arg, args);} processClusteredShortOptions(required, initialized, arg, args);
String argDescription) throws Exception { updateHelpRequested(argSpec); boolean consumeOnlyOne = commandSpec.parser().aritySatisfiedByAttachedOptionParam() && lookBehind.isAttached(); Stack<String> workingStack = args; if (consumeOnlyOne) {
/** Sets whether the parser should ignore case when converting arguments to {@code enum} values. The default is {@code false}. * When set to true, for example, for an option of type <a href="https://docs.oracle.com/javase/8/docs/api/java/time/DayOfWeek.html">java.time.DayOfWeek</a>, * values {@code MonDaY}, {@code monday} and {@code MONDAY} are all recognized if {@code true}. * <p>The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its * subcommands and nested sub-subcommands <em>at the moment this method is called</em>. Subcommands added * later will have the default setting. To ensure a setting is applied to all * subcommands, call the setter last, after adding subcommands.</p> * @param newValue the new setting * @return this {@code CommandLine} object, to allow method chaining * @since 3.4 */ public CommandLine setCaseInsensitiveEnumValuesAllowed(boolean newValue) { getCommandSpec().parser().caseInsensitiveEnumValuesAllowed(newValue); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setCaseInsensitiveEnumValuesAllowed(newValue); } return this; }
spec.parser().limitSplit(true); String actual = usageString(new CommandLine(spec), Help.Ansi.OFF); String expected = String.format("" +
String argDescription) throws Exception { updateHelpRequested(argSpec); boolean consumeOnlyOne = commandSpec.parser().aritySatisfiedByAttachedOptionParam() && lookBehind.isAttached(); Stack<String> workingStack = args; if (consumeOnlyOne) {
@Test public void testMapFieldHelpSplit_with_limitSplit() { class App { @Parameters(arity = "2", split = "\\|", paramLabel = "FIXTAG=VALUE", description = "Exactly two lists of vertical bar '|'-separated FIXTAG=VALUE pairs.") Map<Integer,String> message; @Option(names = {"-P", "-map"}, split = ",", paramLabel = "TIMEUNIT=VALUE", description = "Any number of TIMEUNIT=VALUE pairs. These may be specified separately (-PTIMEUNIT=VALUE) or as a comma-separated list.") Map<TimeUnit, String> map; } CommandSpec spec = CommandSpec.forAnnotatedObject(new App()); spec.parser().limitSplit(true); String actual = usageString(new CommandLine(spec), Help.Ansi.OFF); String expected = String.format("" + "Usage: <main class> [-P=TIMEUNIT=VALUE[,TIMEUNIT=VALUE]...]...%n" + " (FIXTAG=VALUE\\|FIXTAG=VALUE)...%n" + " (FIXTAG=VALUE\\|FIXTAG=VALUE)...%n" + " Exactly two lists of vertical bar '|'-separated FIXTAG=VALUE pairs.%n" + " -P, -map=TIMEUNIT=VALUE[,TIMEUNIT=VALUE]...%n" + " Any number of TIMEUNIT=VALUE pairs. These may be specified separately%n" + " (-PTIMEUNIT=VALUE) or as a comma-separated list.%n"); assertEquals(expected, actual); }
/** Sets whether the parser should ignore case when converting arguments to {@code enum} values. The default is {@code false}. * When set to true, for example, for an option of type <a href="https://docs.oracle.com/javase/8/docs/api/java/time/DayOfWeek.html">java.time.DayOfWeek</a>, * values {@code MonDaY}, {@code monday} and {@code MONDAY} are all recognized if {@code true}. * <p>The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its * subcommands and nested sub-subcommands <em>at the moment this method is called</em>. Subcommands added * later will have the default setting. To ensure a setting is applied to all * subcommands, call the setter last, after adding subcommands.</p> * @param newValue the new setting * @return this {@code CommandLine} object, to allow method chaining * @since 3.4 */ public CommandLine setCaseInsensitiveEnumValuesAllowed(boolean newValue) { getCommandSpec().parser().caseInsensitiveEnumValuesAllowed(newValue); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setCaseInsensitiveEnumValuesAllowed(newValue); } return this; }
@Test public void testParserPosixClustedShortOptions_false_disallowsShortOptionsAttachedToOptionParam() { String[] args = {"-oFILE"}; CompactFields compact = CommandLine.populateCommand(new CompactFields(), args); verifyCompact(compact, false, false, "FILE", null); CompactFields unclustered = new CompactFields(); CommandLine cmd = new CommandLine(unclustered); cmd.getCommandSpec().parser().posixClusteredShortOptionsAllowed(false); try { cmd.parse(args); fail("Expected exception"); } catch (UnmatchedArgumentException ex) { assertEquals("Unknown option: -oFILE", ex.getMessage()); } } @Test
@Test public void testResemblesOption_WithOptionsDash() { CommandSpec spec = CommandSpec.wrapWithoutInspection(null); spec.addOption(OptionSpec.builder("-x").build()); spec.parser().unmatchedOptionsArePositionalParams(false); assertFalse(spec.resemblesOption("blah", null)); System.setProperty("picocli.trace", "DEBUG"); Tracer tracer = new Tracer(); System.clearProperty("picocli.trace"); assertFalse(spec.resemblesOption("blah", tracer)); assertTrue(spec.resemblesOption("-a", tracer)); assertFalse(spec.resemblesOption("/a", tracer)); Tracer tracer2 = new Tracer(); assertFalse(spec.resemblesOption("blah", tracer2)); assertTrue(spec.resemblesOption("-a", tracer)); assertFalse(spec.resemblesOption("/a", tracer)); }
/** Sets whether short options like {@code -x -v -f SomeFile} can be clustered together like {@code -xvfSomeFile}. The default is {@code true}. * <p>The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its * subcommands and nested sub-subcommands <em>at the moment this method is called</em>. Subcommands added * later will have the default setting. To ensure a setting is applied to all * subcommands, call the setter last, after adding subcommands.</p> * @param newValue the new setting * @return this {@code CommandLine} object, to allow method chaining * @since 3.0 */ public CommandLine setPosixClusteredShortOptionsAllowed(boolean newValue) { getCommandSpec().parser().posixClusteredShortOptionsAllowed(newValue); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setPosixClusteredShortOptionsAllowed(newValue); } return this; }
@Test public void testParserPosixClustedShortOptions_false_resultsInShortClusteredOptionsNotRecognized() { CompactFields compact = CommandLine.populateCommand(new CompactFields(), "-rvoFILE"); verifyCompact(compact, true, true, "FILE", null); CommandLine cmd = new CommandLine(new CompactFields()); cmd.getCommandSpec().parser().posixClusteredShortOptionsAllowed(false); try { cmd.parse("-rvoFILE"); fail("Expected exception"); } catch (UnmatchedArgumentException ex) { assertEquals("Unknown option: -rvoFILE", ex.getMessage()); } } @Test
private boolean splitFirst() { return limitSplit(); } /** Returns true if arguments should be split first before any further processing and the number of
@Test public void testResemblesOption_WithoutOptions() { CommandSpec spec = CommandSpec.wrapWithoutInspection(null); spec.parser().unmatchedOptionsArePositionalParams(false); assertFalse(spec.resemblesOption("blah", null)); System.setProperty("picocli.trace", "DEBUG"); Tracer tracer = new Tracer(); System.clearProperty("picocli.trace"); assertFalse(spec.resemblesOption("blah", tracer)); assertTrue(spec.resemblesOption("-a", tracer)); Tracer tracer2 = new Tracer(); assertFalse(spec.resemblesOption("blah", tracer2)); assertTrue(spec.resemblesOption("-a", tracer)); }
@SuppressWarnings("unchecked") public Object convert(String value) throws Exception { if (commandSpec.parser().caseInsensitiveEnumValuesAllowed()) { String upper = value.toUpperCase(); for (Object enumConstant : type.getEnumConstants()) { if (upper.equals(String.valueOf(enumConstant).toUpperCase())) { return enumConstant; } } } try { return Enum.valueOf((Class<Enum>) type, value); } catch (Exception ex) { throw new TypeConversionException( String.format("expected one of %s but was '%s'", Arrays.asList(type.getEnumConstants()), value)); } } };
@Test public void testCommandSpecParserSetter() { CommandSpec spec = CommandSpec.wrapWithoutInspection(null); ParserSpec old = spec.parser(); assertSame(old, spec.parser()); assertFalse(spec.parser().collectErrors()); assertFalse(spec.parser().caseInsensitiveEnumValuesAllowed()); ParserSpec update = new ParserSpec().collectErrors(true).caseInsensitiveEnumValuesAllowed(true); spec.parser(update); assertSame(old, spec.parser()); assertTrue(spec.parser().collectErrors()); assertTrue(spec.parser().caseInsensitiveEnumValuesAllowed()); }
/** Returns whether the parser accepts clustered short options. The default is {@code true}. * @return {@code true} if short options like {@code -x -v -f SomeFile} can be clustered together like {@code -xvfSomeFile}, {@code false} otherwise * @since 3.0 */ public boolean isPosixClusteredShortOptionsAllowed() { return getCommandSpec().parser().posixClusteredShortOptionsAllowed(); }
@Test public void testInterpreterProcessClusteredShortOptions_complex() { class App { @Option(names = "-x", arity = "1", split = ",") String x; @Parameters List<String> remainder; } App app = new App(); CommandLine cmd = new CommandLine(app); cmd.getCommandSpec().parser().aritySatisfiedByAttachedOptionParam(true); cmd.parseArgs("-xa,b,c", "d", "e"); assertEquals("a,b,c", app.x); assertEquals(Arrays.asList("d", "e"), app.remainder); }
@Test public void testResemblesOption_WhenUnmatchedArePositional() { CommandSpec spec = CommandSpec.wrapWithoutInspection(null); spec.parser().unmatchedOptionsArePositionalParams(true); assertFalse(spec.resemblesOption("blah", null)); System.setProperty("picocli.trace", "DEBUG"); Tracer tracer = new Tracer(); System.clearProperty("picocli.trace"); assertFalse(spec.resemblesOption("blah", tracer)); Tracer tracer2 = new Tracer(); assertFalse(spec.resemblesOption("blah", tracer2)); }
private <T> T parseCommonsCliCompatible(T obj, String[] args) { CommandLine cmd = new CommandLine(obj); cmd.getCommandSpec().parser() .limitSplit(true) .aritySatisfiedByAttachedOptionParam(true); cmd.parseArgs(args); return obj; }
private String unquote(String value) { if (!commandSpec.parser().trimQuotes()) { return value; } return value == null ? null : (value.length() > 1 && value.startsWith("\"") && value.endsWith("\"")) ? value.substring(1, value.length() - 1) : value; }