/** * 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 void printParser(ParserSpec parser, PrintWriter pw, String indent) { pw.printf("%sParserSpec:%n", indent); indent += " "; pw.printf("%sseparator: '%s'%n", indent, parser.separator()); pw.printf("%sendOfOptionsDelimiter: '%s'%n", indent, parser.endOfOptionsDelimiter()); pw.printf("%sexpandAtFiles: %s%n", indent, parser.expandAtFiles()); pw.printf("%satFileCommentChar: '%s'%n", indent, parser.atFileCommentChar()); pw.printf("%soverwrittenOptionsAllowed: %s%n", indent, parser.overwrittenOptionsAllowed()); pw.printf("%sunmatchedArgumentsAllowed: %s%n", indent, parser.unmatchedArgumentsAllowed()); pw.printf("%sunmatchedOptionsArePositionalParams: %s%n", indent, parser.unmatchedOptionsArePositionalParams()); pw.printf("%sstopAtUnmatched: %s%n", indent, parser.stopAtUnmatched()); pw.printf("%sstopAtPositional: %s%n", indent, parser.stopAtPositional()); pw.printf("%sposixClusteredShortOptionsAllowed: %s%n", indent, parser.posixClusteredShortOptionsAllowed()); pw.printf("%saritySatisfiedByAttachedOptionParam: %s%n", indent, parser.aritySatisfiedByAttachedOptionParam()); pw.printf("%scaseInsensitiveEnumValuesAllowed: %s%n", indent, parser.caseInsensitiveEnumValuesAllowed()); pw.printf("%scollectErrors: %s%n", indent, parser.collectErrors()); pw.printf("%slimitSplit: %s%n", indent, parser.limitSplit()); pw.printf("%stoggleBooleanFlags: %s%n", indent, parser.toggleBooleanFlags()); }
paramAttachedToOption = cluster.length() > 0; LookBehind lookBehind = paramAttachedToOption ? LookBehind.ATTACHED : LookBehind.SEPARATE; if (cluster.startsWith(config().separator())) {// attached with separator, like -f=FILE or -v=true lookBehind = LookBehind.ATTACHED_WITH_SEPARATOR; cluster = cluster.substring(config().separator().length()); arity = arity.min(Math.max(1, arity.min)); // if key=value, minimum arity is at least 1
int sep = currentArg.indexOf(spec.parser().separator()); if (sep < 0 || positionInArg < sep) { // no '=' or cursor before '=' addCandidatesForArgsFollowing(findCommandFor((OptionSpec) obj, spec), candidates); addCandidatesForArgsFollowing((OptionSpec) obj, candidates); int sepLength = spec.parser().separator().length(); if (positionInArg < sep + sepLength) { int posInSeparator = positionInArg - sep; String prefix = spec.parser().separator().substring(posInSeparator); for (int i = 0; i < candidates.size(); i++) { candidates.set(i, prefix + candidates.get(i));
for (ArgSpec missing : required) { if (missing.isOption()) { maybeThrow(MissingParameterException.create(CommandLine.this, required, config().separator())); } else { assertNoMissingParameters(missing, missing.arity(), argumentStack);
assertTrue(help.commandSpec().usageMessage().showDefaultValues()); assertFalse(help.commandSpec().usageMessage().sortOptions()); assertEquals(";", help.commandSpec().parser().separator()); assertEquals('&', help.commandSpec().usageMessage().requiredOptionMarker());
assertTrue(help.commandSpec().usageMessage().showDefaultValues()); assertFalse(help.commandSpec().usageMessage().sortOptions()); assertEquals(":", help.commandSpec().parser().separator()); assertEquals('%', help.commandSpec().usageMessage().requiredOptionMarker());
sep = append(pw, sep, indent, "hidden = %s", spec.usageMessage().hidden(), false); sep = append(pw, sep, indent, "helpCommand = %s", spec.helpCommand(), false); sep = append(pw, sep, indent, "separator = \"%s\"", spec.parser().separator(), "="); sep = append(pw, sep, indent, "usageHelpWidth = %s", spec.usageMessage().width(), 80);
assertEquals("mixmein", commandSpec.name()); assertArrayEquals(new String[] {"Mixin 1.0"}, commandSpec.version()); assertEquals(":", commandSpec.parser().separator()); assertArrayEquals(new String[] {"description from mixin"}, commandSpec.usageMessage().description()); assertEquals("Mixin Description Heading%n", commandSpec.usageMessage().descriptionHeading());
assertEquals("base param heading", help.commandSpec().usageMessage().parameterListHeading()); assertEquals(";", help.commandSpec().parser().separator()); assertEquals('&', help.commandSpec().usageMessage().requiredOptionMarker()); assertTrue(help.commandSpec().usageMessage().abbreviateSynopsis());
assertEquals("superName", commandSpec.name()); assertArrayEquals(new String[] {"MixMeInSuper 1.0"}, commandSpec.version()); assertEquals("$", commandSpec.parser().separator());
@Test public void testDefaultParameterRenderer_hideParamSyntax_on() { class App { @Parameters(index = "0") String single; @Parameters(index = "1", arity = "2") String[] multi; @Parameters(index = "2", hideParamSyntax = true) String singleHide; @Parameters(index = "3", hideParamSyntax = true, arity = "2") String[] multiHide; @Parameters(index = "4", hideParamSyntax = false, arity = "*", split = ",") String[] multiSplit; @Parameters(index = "5", hideParamSyntax = true, arity = "*", split = ",") String[] multiHideSplit; } Help withLabel = new Help(new App(), Help.Ansi.OFF); withLabel.commandSpec().parser().separator("="); Help.IParamLabelRenderer equals = withLabel.createDefaultParamLabelRenderer(); withLabel.commandSpec().parser().separator(" "); Help.IParamLabelRenderer spaced = withLabel.createDefaultParamLabelRenderer(); String[] expected = new String[] { "<single>", // "<multi> <multi>", // "<singleHide>", // "<multiHide>", // "[<multiSplit>[,<multiSplit>...]...]", // "<multiHideSplit>", // }; for (int i = 0; i < expected.length; i++) { Text withEquals = equals.renderParameterLabel(withLabel.positionalParameters().get(i), withLabel.ansi(), Collections.<IStyle>emptyList()); Text withSpace = spaced.renderParameterLabel(withLabel.positionalParameters().get(i), withLabel.ansi(), Collections.<IStyle>emptyList()); assertEquals(withEquals.toString(), expected[i], withEquals.toString()); assertEquals(withSpace.toString(), expected[i], withSpace.toString()); } }
private void verifyMixinSuperClassCommandAttributesDontOverwriteSubclassAttributes(CommandLine commandLine) throws UnsupportedEncodingException { CommandSpec commandSpec = commandLine.getCommandSpec(); assertEquals("mixmeinSub", commandSpec.name()); assertArrayEquals(new String[] {"MixinSub 1.0"}, commandSpec.version()); assertEquals("~", commandSpec.parser().separator()); assertArrayEquals(new String[] {"description from mixinSub"}, commandSpec.usageMessage().description()); assertEquals("MixinSub Description Heading%n", commandSpec.usageMessage().descriptionHeading()); assertArrayEquals(new String[] {"MixinSub Header"}, commandSpec.usageMessage().header()); assertEquals("MixinSub Header Heading%n", commandSpec.usageMessage().headerHeading()); assertArrayEquals(new String[] {"MixinSub Footer"}, commandSpec.usageMessage().footer()); assertEquals("MixinSub Footer Heading%n", commandSpec.usageMessage().footerHeading()); assertEquals("MixinSub Option List Heading%n", commandSpec.usageMessage().optionListHeading()); assertEquals("MixinSub Parameter List Heading%n", commandSpec.usageMessage().parameterListHeading()); assertEquals("MixinSub Command List Heading%n", commandSpec.usageMessage().commandListHeading()); assertEquals('#', commandSpec.usageMessage().requiredOptionMarker()); assertEquals("MixinSub Synopsis Heading%n", commandSpec.usageMessage().synopsisHeading()); assertArrayEquals(new String[] {"MixinSub custom synopsis"}, commandSpec.usageMessage().customSynopsis()); String expected = String.format("" + "MixinSub Header Heading%n" + "MixinSub Header%n" + "MixinSub Synopsis Heading%n" + "MixinSub custom synopsis%n" + "MixinSub Description Heading%n" + "description from mixinSub%n" + "MixinSub Footer Heading%n" + "MixinSub Footer%n"); assertEquals(expected, usageString(commandLine, Help.Ansi.OFF)); }
private void verifyMixinCommandAttributesDontOverwriteReceiverAttributes(CommandLine commandLine) throws UnsupportedEncodingException { CommandSpec commandSpec = commandLine.getCommandSpec(); assertEquals("receiver", commandSpec.name()); assertArrayEquals(new String[] {"Receiver 1.0"}, commandSpec.version()); assertEquals("~", commandSpec.parser().separator()); assertArrayEquals(new String[] {"Receiver description"}, commandSpec.usageMessage().description()); assertEquals("Receiver Description Heading%n", commandSpec.usageMessage().descriptionHeading()); assertArrayEquals(new String[] {"Receiver Header"}, commandSpec.usageMessage().header()); assertEquals("Receiver Header Heading%n", commandSpec.usageMessage().headerHeading()); assertArrayEquals(new String[] {"Receiver Footer"}, commandSpec.usageMessage().footer()); assertEquals("Receiver Footer Heading%n", commandSpec.usageMessage().footerHeading()); assertEquals("Receiver Option List Heading%n", commandSpec.usageMessage().optionListHeading()); assertEquals("Receiver Parameter List Heading%n", commandSpec.usageMessage().parameterListHeading()); assertEquals("Receiver Command List Heading%n", commandSpec.usageMessage().commandListHeading()); assertEquals('#', commandSpec.usageMessage().requiredOptionMarker()); assertEquals("Receiver Synopsis Heading%n", commandSpec.usageMessage().synopsisHeading()); assertArrayEquals(new String[] {"Receiver custom synopsis"}, commandSpec.usageMessage().customSynopsis()); String expected = String.format("" + "Receiver Header Heading%n" + "Receiver Header%n" + "Receiver Synopsis Heading%n" + "Receiver custom synopsis%n" + "Receiver Description Heading%n" + "Receiver description%n" + "Receiver Footer Heading%n" + "Receiver Footer%n"); assertEquals(expected, usageString(commandLine, Help.Ansi.OFF)); }
@Test public void testDefaultParameterRenderer_showsParamLabelIfPresentOrFieldNameOtherwise() { class Example { @Option(names = "--without" ) String longField; @Option(names = "--with", paramLabel = "LABEL") String otherField; } Help help = new Help(new Example()); Help.IParamLabelRenderer equalSeparatedParameterRenderer = help.createDefaultParamLabelRenderer(); Help help2 = new Help(new Example()); help2.commandSpec().parser().separator(" "); Help.IParamLabelRenderer spaceSeparatedParameterRenderer = help2.createDefaultParamLabelRenderer(); String[] expected = new String[] { "<longField>", "LABEL", }; int i = -1; for (OptionSpec option : help.options()) { i++; Text withSpace = spaceSeparatedParameterRenderer.renderParameterLabel(option, help.ansi(), Collections.<IStyle>emptyList()); assertEquals(withSpace.toString(), " " + expected[i], withSpace.toString()); Text withEquals = equalSeparatedParameterRenderer.renderParameterLabel(option, help.ansi(), Collections.<IStyle>emptyList()); assertEquals(withEquals.toString(), "=" + expected[i], withEquals.toString()); } }
/** Adds the specified mixin {@code CommandSpec} object to the map of mixins for this command. * @param name the name that can be used to later retrieve the mixin * @param mixin the mixin whose options and positional parameters and other attributes to add to this command * @return this CommandSpec for method chaining */ public CommandSpec addMixin(String name, CommandSpec mixin) { mixins.put(name, mixin); parser.initSeparator(mixin.parser.separator()); initName(mixin.name()); initVersion(mixin.version()); initHelpCommand(mixin.helpCommand()); initVersionProvider(mixin.versionProvider()); initDefaultValueProvider(mixin.defaultValueProvider()); usageMessage.initFromMixin(mixin.usageMessage, this); for (Map.Entry<String, CommandLine> entry : mixin.subcommands().entrySet()) { addSubcommand(entry.getKey(), entry.getValue()); } for (OptionSpec optionSpec : mixin.options()) { addOption(optionSpec); } for (PositionalParamSpec paramSpec : mixin.positionalParameters()) { addPositional(paramSpec); } return this; }
@Test public void testDefaultParameterRenderer_appliesToPositionalArgumentsIgnoresSeparator() { class WithLabel { @Parameters(paramLabel = "POSITIONAL_ARGS") String positional; } class WithoutLabel { @Parameters() String positional; } Help withLabel = new Help(new WithLabel()); Help.IParamLabelRenderer equals = withLabel.createDefaultParamLabelRenderer(); withLabel.commandSpec().parser().separator("="); Help.IParamLabelRenderer spaced = withLabel.createDefaultParamLabelRenderer(); Text withSpace = spaced.renderParameterLabel(withLabel.positionalParameters().get(0), withLabel.ansi(), Collections.<IStyle>emptyList()); assertEquals(withSpace.toString(), "POSITIONAL_ARGS", withSpace.toString()); Text withEquals = equals.renderParameterLabel(withLabel.positionalParameters().get(0), withLabel.ansi(), Collections.<IStyle>emptyList()); assertEquals(withEquals.toString(), "POSITIONAL_ARGS", withEquals.toString()); Help withoutLabel = new Help(new WithoutLabel()); withSpace = spaced.renderParameterLabel(withoutLabel.positionalParameters().get(0), withoutLabel.ansi(), Collections.<IStyle>emptyList()); assertEquals(withSpace.toString(), "<positional>", withSpace.toString()); withEquals = equals.renderParameterLabel(withoutLabel.positionalParameters().get(0), withoutLabel.ansi(), Collections.<IStyle>emptyList()); assertEquals(withEquals.toString(), "<positional>", withEquals.toString()); }
/** Sets the String the parser uses to separate option names from option values to the specified value. * The separator may also be set declaratively with the {@link CommandLine.Command#separator()} annotation attribute. * <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 separator the String that separates option names from option values * @see ParserSpec#separator(String) * @return this {@code CommandLine} object, to allow method chaining */ public CommandLine setSeparator(String separator) { getCommandSpec().parser().separator(Assert.notNull(separator, "separator")); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setSeparator(separator); } return this; }
public String separator() { return commandSpec.parser().separator(); } public Text renderParameterLabel(ArgSpec argSpec, Ansi ansi, List<IStyle> styles) {
/** Returns the String that separates option names from option values when parsing command line options. * @return the String the parser uses to separate option names from option values * @see ParserSpec#separator() */ public String getSeparator() { return getCommandSpec().parser().separator(); }