void visitCommandSpec(CommandSpec spec) throws NoSuchFieldException, IllegalAccessException { if (spec.userObject() != null) { if (spec.userObject() instanceof Method) { Method method = (Method) spec.userObject(); ReflectedClass cls = getOrCreateClass(method.getDeclaringClass()); cls.addMethod(method.getName(), method.getParameterTypes()); } else if (Proxy.isProxyClass(spec.userObject().getClass())) { visitAnnotatedFields(spec.userObject().getClass()); visitObjectType(spec.versionProvider()); visitObjectType(spec.defaultValueProvider()); for (UnmatchedArgsBinding binding : spec.unmatchedArgsBindings()) { visitGetter(binding.getter()); visitSetter(binding.setter()); for (OptionSpec option : spec.options()) { visitArgSpec(option); for (PositionalParamSpec positional : spec.positionalParameters()) { visitArgSpec(positional); for (CommandSpec mixin : spec.mixins().values()) { visitCommandSpec(mixin); for (CommandLine sub : spec.subcommands().values()) { visitCommandSpec(sub.getCommandSpec());
private void printCommandSpec(CommandSpec spec, String label, PrintWriter pw, String initialIndent, String indent) { pw.printf("%s%n", label); pw.printf("%sname: '%s'%n", initialIndent, spec.name()); pw.printf("%saliases: %s%n", indent, Arrays.toString(spec.aliases())); pw.printf("%suserObject: %s%n", indent, spec.userObject()); pw.printf("%shelpCommand: %s%n", indent, spec.helpCommand()); pw.printf("%sdefaultValueProvider: %s%n", indent, spec.defaultValueProvider()); pw.printf("%sversionProvider: %s%n", indent, spec.versionProvider()); pw.printf("%sversion: %s%n", indent, Arrays.toString(spec.version())); List<OptionSpec> options = new ArrayList<OptionSpec>(spec.options()); Collections.sort(options, new Comparator<OptionSpec>() { public int compare(OptionSpec o1, OptionSpec o2) { return o1.shortestName().compareTo(o2.shortestName()); } }); printOptionList(options, pw, indent); printPositionalList(spec.positionalParameters(), pw, indent); printUnmatchedArgsBindingList(spec.unmatchedArgsBindings(), pw, indent); printMixinList(spec.mixins(), pw, indent); printUsageMessage(spec.usageMessage(), pw, indent); printParser(spec.parser(), pw, indent); printResourceBundle(spec.resourceBundle(), pw, indent); printSubcommandList(spec.subcommands(), pw, indent); }
/** 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 testVersionHelp_helpCommand() { CommandSpec helpCommand = CommandSpec.create().helpCommand(true); assertTrue(helpCommand.helpCommand()); CommandSpec parent = CommandSpec.create().addOption(OptionSpec.builder("-x").required(true).build()); parent.addSubcommand("help", helpCommand); CommandLine commandLine = new CommandLine(parent); commandLine.parse("help"); // no missing param exception try { commandLine.parse(); } catch (MissingParameterException ex) { assertEquals("Missing required option '-x=PARAM'", ex.getMessage()); assertEquals(1, ex.getMissing().size()); assertSame(ex.getMissing().get(0).toString(), parent.posixOptionsMap().get('x'), ex.getMissing().get(0)); } }
@Test public void testInitHelpCommand() { CommandSpec spec = CommandSpec.wrapWithoutInspection(null); assertFalse(spec.helpCommand()); CommandSpec mixin = CommandSpec.wrapWithoutInspection(null); mixin.helpCommand(true); spec.addMixin("helper", mixin); assertTrue(spec.helpCommand()); }
private void updateHelpRequested(CommandSpec command) { isHelpRequested |= command.helpCommand(); } private void updateHelpRequested(ArgSpec argSpec) {
parsed.printVersionHelp(out, colorScheme.ansi); return true; } else if (parsed.getCommandSpec().helpCommand()) { if (parsed.getCommand() instanceof IHelpCommandInitializable) { ((IHelpCommandInitializable) parsed.getCommand()).init(parsed, colorScheme.ansi, out, err);
sep = append(pw, sep, indent, "name = \"%s\"", spec.name(), "<main class>"); sep = appendStringArray(pw, sep, indent, "aliases = %s", spec.aliases(), EMPTY_ARRAY); sep = append(pw, sep, indent, "mixinStandardHelpOptions = %s", spec.mixinStandardHelpOptions(), false); sep = append(pw, sep, indent, "headerHeading = \"%s\"", spec.usageMessage().headerHeading(), ""); sep = appendStringArray(pw, sep, indent, "header = %s", spec.usageMessage().header(), EMPTY_ARRAY); sep = append(pw, sep, indent, "descriptionHeading = \"%s\"", spec.usageMessage().descriptionHeading(), ""); sep = appendStringArray(pw, sep, indent, "description = %s", spec.usageMessage().description(), EMPTY_ARRAY); sep = append(pw, sep, indent, "synopsisHeading = \"%s\"", spec.usageMessage().synopsisHeading(), "Usage: "); sep = append(pw, sep, indent, "abbreviateSynopsis = %s", spec.usageMessage().abbreviateSynopsis(), false); sep = appendStringArray(pw, sep, indent, "customSynopsis = %s", spec.usageMessage().customSynopsis(), EMPTY_ARRAY); sep = append(pw, sep, indent, "optionListHeading = \"%s\"", spec.usageMessage().optionListHeading(), ""); sep = append(pw, sep, indent, "parameterListHeading = \"%s\"", spec.usageMessage().parameterListHeading(), ""); sep = append(pw, sep, indent, "commandListHeading = \"%s\"", spec.usageMessage().commandListHeading(), "Commands:%n"); sep = append(pw, sep, indent, "footerHeading = \"%s\"", spec.usageMessage().footerHeading(), ""); sep = appendStringArray(pw, sep, indent, "footer = %s", spec.usageMessage().footer(), EMPTY_ARRAY); sep = append(pw, sep, indent, "requiredOptionMarker = \'%s\'", spec.usageMessage().requiredOptionMarker(), ' '); sep = append(pw, sep, indent, "addMethodSubcommands = %s", spec.isAddMethodSubcommands(), !isCommandMethod(spec)); sep = appendSubcommandClasses(pw, sep, indent, spec.subcommands()); sep = appendStringArray(pw, sep, indent, "version = %s", spec.version(), EMPTY_ARRAY); sep = appendClassName(pw, sep, indent, "versionProvider = %s", spec.versionProvider()); sep = append(pw, sep, indent, "showDefaultValues = %s", spec.usageMessage().showDefaultValues(), false); sep = appendClassName(pw, sep, indent, "defaultValueProvider = %s", spec.defaultValueProvider()); sep = append(pw, sep, indent, "resourceBundle = \"%s\"", spec.resourceBundleBaseName(), "null"); sep = append(pw, sep, indent, "sortOptions = %s", spec.usageMessage().sortOptions(), true); 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);
/** Returns the default value String displayed in the description. If this ArgSpec is part of a * CommandSpec with a {@link IDefaultValueProvider}, this method will first try to obtain * the default value from the default value provider; if the provider is {@code null} or if it * returns a {@code null} value, then next any value set to {@link ArgSpec#defaultValue()} * is returned, and if this is also {@code null}, finally the {@linkplain ArgSpec#initialValue() initial value} is returned. * @see CommandSpec#defaultValueProvider() * @see ArgSpec#defaultValue() */ public String defaultValueString() { String fromProvider = null; IDefaultValueProvider defaultValueProvider = null; try { defaultValueProvider = commandSpec.defaultValueProvider(); fromProvider = defaultValueProvider == null ? null : defaultValueProvider.defaultValue(this); } catch (Exception ex) { new Tracer().info("Error getting default value for %s from %s: %s", this, defaultValueProvider, ex); } String defaultVal = fromProvider == null ? this.defaultValue() : fromProvider; Object value = defaultVal == null ? initialValue() : defaultVal; if (value != null && value.getClass().isArray()) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < Array.getLength(value); i++) { sb.append(i > 0 ? ", " : "").append(Array.get(value, i)); } return sb.insert(0, "[").append("]").toString(); } return String.valueOf(value); }
private void updateCommandAttributes(CommandSpec result, Command cmd) { // null factory to prevent // javax.lang.model.type.MirroredTypeException: Attempt to access Class object for TypeMirror picocli.CommandLine.NoVersionProvider result.updateCommandAttributes(cmd, null); try { cmd.versionProvider(); } catch (MirroredTypeException ex) { VersionProviderMetaData provider = new VersionProviderMetaData(ex.getTypeMirror()); if (!provider.isDefault()) { result.versionProvider(provider); } } try { cmd.defaultValueProvider(); } catch (MirroredTypeException ex) { DefaultValueProviderMetaData provider = new DefaultValueProviderMetaData(ex.getTypeMirror()); if (!provider.isDefault()) { result.defaultValueProvider(provider); } } }
/** * Updates the following attributes from the specified {@code @Command} annotation: * aliases, {@link ParserSpec#separator() parser separator}, command name, version, help command, * version provider, default provider and {@link UsageMessageSpec usage message spec}. * @param cmd the {@code @Command} annotation to get attribute values from * @param factory factory used to instantiate classes * @since 3.7 */ public void updateCommandAttributes(Command cmd, IFactory factory) { aliases(cmd.aliases()); parser().updateSeparator(cmd.separator()); updateName(cmd.name()); updateVersion(cmd.version()); updateHelpCommand(cmd.helpCommand()); updateAddMethodSubcommands(cmd.addMethodSubcommands()); usageMessage().updateFromCommand(cmd, this); if (factory != null) { updateVersionProvider(cmd.versionProvider(), factory); initDefaultValueProvider(cmd.defaultValueProvider(), factory); } }
/** Adds the specified subcommand with the specified name. * If the specified subcommand does not have a ResourceBundle set, it is initialized to the ResourceBundle of this command spec. * @param name subcommand name - when this String is encountered in the command line arguments the subcommand is invoked * @param subCommandLine the subcommand to envoke when the name is encountered on the command line * @return this {@code CommandSpec} object for method chaining */ public CommandSpec addSubcommand(String name, CommandLine subCommandLine) { CommandLine previous = commands.put(name, subCommandLine); if (previous != null && previous != subCommandLine) { throw new InitializationException("Another subcommand named '" + name + "' already exists for command '" + this.name() + "'"); } CommandSpec subSpec = subCommandLine.getCommandSpec(); if (subSpec.name == null) { subSpec.name(name); } subSpec.parent(this); for (String alias : subSpec.aliases()) { previous = commands.put(alias, subCommandLine); if (previous != null && previous != subCommandLine) { throw new InitializationException("Alias '" + alias + "' for subcommand '" + name + "' is already used by another subcommand of '" + this.name() + "'"); } } subSpec.initCommandHierarchyWithResourceBundle(resourceBundleBaseName()); return this; } private void initCommandHierarchyWithResourceBundle(String bundleBaseName) {
@Test public void testVersionHelp_helpCommand() { CommandSpec helpCommand = CommandSpec.create().helpCommand(true); assertTrue(helpCommand.helpCommand()); CommandSpec parent = CommandSpec.create().addOption(OptionSpec.builder("-x").required(true).build()); parent.addSubcommand("help", helpCommand); CommandLine commandLine = new CommandLine(parent); commandLine.parse("help"); // no missing param exception try { commandLine.parse(); } catch (MissingParameterException ex) { assertEquals("Missing required option '-x=PARAM'", ex.getMessage()); assertEquals(1, ex.getMissing().size()); assertSame(ex.getMissing().get(0).toString(), parent.posixOptionsMap().get('x'), ex.getMissing().get(0)); } }
@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()); }
private int calcLongOptionColumnWidth() { int max = 0; IOptionRenderer optionRenderer = new DefaultOptionRenderer(false, " "); for (OptionSpec option : commandSpec.options()) { Text[][] values = optionRenderer.render(option, parameterLabelRenderer(), colorScheme); int len = values[0][3].length; if (len < Help.defaultOptionsColumnWidth - 3) { max = Math.max(max, len); } } IParameterRenderer paramRenderer = new DefaultParameterRenderer(false, " "); for (PositionalParamSpec positional : commandSpec.positionalParameters()) { Text[][] values = paramRenderer.render(positional, parameterLabelRenderer(), colorScheme); int len = values[0][3].length; if (len < Help.defaultOptionsColumnWidth - 3) { max = Math.max(max, len); } } return max + 3; }
@Test public void testDefaultValueProvider() { IDefaultValueProvider provider1 = new IDefaultValueProvider() { public String defaultValue(ArgSpec argSpec) { return null; } }; IDefaultValueProvider provider2 = new IDefaultValueProvider() { public String defaultValue(ArgSpec argSpec) { return null; } }; CommandSpec spec = CommandSpec.wrapWithoutInspection(null); spec.defaultValueProvider(provider1); CommandSpec mixin = CommandSpec.wrapWithoutInspection(null); mixin.defaultValueProvider(provider2); spec.addMixin("helper", mixin); assertSame(provider1, spec.defaultValueProvider()); }
/** Sets a default value provider for the command and sub-commands * <p>The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its * sub-commands and nested sub-subcommands <em>at the moment this method is called</em>. Sub-commands added * later will have the default setting. To ensure a setting is applied to all * sub-commands, call the setter last, after adding sub-commands.</p> * @param newValue the default value provider to use * @return this {@code CommandLine} object, to allow method chaining * @since 3.6 */ public CommandLine setDefaultValueProvider(IDefaultValueProvider newValue) { getCommandSpec().defaultValueProvider(newValue); for (CommandLine command : getCommandSpec().subcommands().values()) { command.setDefaultValueProvider(newValue); } return this; }
List<OptionSpec> options() { return commandSpec.options(); } List<PositionalParamSpec> positionalParameters() { return commandSpec.positionalParameters(); }
/** * <p>Returns a description of the {@linkplain Option options} supported by the application. * This implementation {@linkplain #createShortOptionNameComparator() sorts options alphabetically}, and shows * only the {@linkplain Option#hidden() non-hidden} options in a {@linkplain TextTable tabular format} * using the {@linkplain #createDefaultOptionRenderer() default renderer} and {@linkplain Layout default layout}.</p> * @return the fully formatted option list * @see #optionList(Layout, Comparator, IParamLabelRenderer) */ public String optionList() { Comparator<OptionSpec> sortOrder = commandSpec.usageMessage().sortOptions() ? createShortOptionNameComparator() : createOrderComparatorIfNecessary(commandSpec.options()); return optionList(createLayout(calcLongOptionColumnWidth()), sortOrder, parameterLabelRenderer()); }
/** Returns the option with the specified name, or {@code null} if no option with that name is defined for this command. * @param name used to search the options. May include option name prefix characters or not. */ public OptionSpec findOption(String name) { return findOption(name, options()); }