/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.elasticsearch.common.cli; import com.google.common.collect.ImmutableMap; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionGroup; import org.apache.commons.cli.Options; import java.util.Collection; /** * */ public class CliToolConfig { public static Builder config(String name, Class<? extends CliTool> toolType) { return new Builder(name, toolType); } private final Class<? extends CliTool> toolType; private final String name; private final ImmutableMap<String, Cmd> cmds; private static final HelpPrinter helpPrinter = new HelpPrinter(); private CliToolConfig(String name, Class<? extends CliTool> toolType, Cmd[] cmds) { this.name = name; this.toolType = toolType; ImmutableMap.Builder<String, Cmd> cmdsBuilder = ImmutableMap.builder(); for (int i = 0; i < cmds.length; i++) { cmdsBuilder.put(cmds[i].name, cmds[i]); } this.cmds = cmdsBuilder.build(); } public boolean isSingle() { return cmds.size() == 1; } public Cmd single() { assert isSingle() : "Requesting single command on a multi-command tool"; return cmds.values().iterator().next(); } public Class<? extends CliTool> toolType() { return toolType; } public String name() { return name; } public Collection<Cmd> cmds() { return cmds.values(); } public Cmd cmd(String name) { return cmds.get(name); } public void printUsage(Terminal terminal) { helpPrinter.print(this, terminal); } public static class Builder { public static Cmd.Builder cmd(String name, Class<? extends CliTool.Command> cmdType) { return new Cmd.Builder(name, cmdType); } public static OptionBuilder option(String shortName, String longName) { return new OptionBuilder(shortName, longName); } public static Option.Builder optionBuilder(String shortName, String longName) { return Option.builder(shortName).argName(longName).longOpt(longName); } public static OptionGroupBuilder optionGroup(boolean required) { return new OptionGroupBuilder(required); } private final Class<? extends CliTool> toolType; private final String name; private Cmd[] cmds; private Builder(String name, Class<? extends CliTool> toolType) { this.name = name; this.toolType = toolType; } public Builder cmds(Cmd.Builder... cmds) { this.cmds = new Cmd[cmds.length]; for (int i = 0; i < cmds.length; i++) { this.cmds[i] = cmds[i].build(); this.cmds[i].toolName = name; } return this; } public Builder cmds(Cmd... cmds) { for (int i = 0; i < cmds.length; i++) { cmds[i].toolName = name; } this.cmds = cmds; return this; } public CliToolConfig build() { return new CliToolConfig(name, toolType, cmds); } } public static class Cmd { private String toolName; private final String name; private final Class<? extends CliTool.Command> cmdType; private final Options options; private final boolean stopAtNonOption; private Cmd(String name, Class<? extends CliTool.Command> cmdType, Options options, boolean stopAtNonOption) { this.name = name; this.cmdType = cmdType; this.options = options; this.stopAtNonOption = stopAtNonOption; OptionsSource.VERBOSITY.populate(options); } public Class<? extends CliTool.Command> cmdType() { return cmdType; } public String name() { return name; } public Options options() { return options; } public boolean isStopAtNonOption() { return stopAtNonOption; } public void printUsage(Terminal terminal) { helpPrinter.print(toolName, this, terminal); } public static class Builder { private final String name; private final Class<? extends CliTool.Command> cmdType; private Options options = new Options(); private boolean stopAtNonOption = false; private Builder(String name, Class<? extends CliTool.Command> cmdType) { this.name = name; this.cmdType = cmdType; } public Builder options(OptionBuilder... optionBuilder) { for (int i = 0; i < optionBuilder.length; i++) { options.addOption(optionBuilder[i].build()); } return this; } public Builder options(Option.Builder... optionBuilders) { for (int i = 0; i < optionBuilders.length; i++) { options.addOption(optionBuilders[i].build()); } return this; } public Builder optionGroups(OptionGroupBuilder... optionGroupBuilders) { for (OptionGroupBuilder builder : optionGroupBuilders) { options.addOptionGroup(builder.build()); } return this; } /** * @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops * the parsing and the remaining arguments are added to the * args list. If <tt>false</tt> an unrecognized * argument triggers a ParseException. */ public Builder stopAtNonOption(boolean stopAtNonOption) { this.stopAtNonOption = stopAtNonOption; return this; } public Cmd build() { return new Cmd(name, cmdType, options, stopAtNonOption); } } } public static class OptionBuilder { private final Option option; private OptionBuilder(String shortName, String longName) { option = new Option(shortName, ""); option.setLongOpt(longName); option.setArgName(longName); } public OptionBuilder required(boolean required) { option.setRequired(required); return this; } public OptionBuilder hasArg(boolean optional) { option.setOptionalArg(optional); option.setArgs(1); return this; } public Option build() { return option; } } public static class OptionGroupBuilder { private OptionGroup group; private OptionGroupBuilder(boolean required) { group = new OptionGroup(); group.setRequired(required); } public OptionGroupBuilder options(OptionBuilder... optionBuilders) { for (OptionBuilder builder : optionBuilders) { group.addOption(builder.build()); } return this; } public OptionGroup build() { return group; } } static abstract class OptionsSource { static final OptionsSource HELP = new OptionsSource() { @Override void populate(Options options) { options.addOption(new OptionBuilder("h", "help").required(false).build()); } }; static final OptionsSource VERBOSITY = new OptionsSource() { @Override void populate(Options options) { OptionGroup verbosityGroup = new OptionGroup(); verbosityGroup.setRequired(false); verbosityGroup.addOption(new OptionBuilder("s", "silent").required(false).build()); verbosityGroup.addOption(new OptionBuilder("v", "verbose").required(false).build()); options.addOptionGroup(verbosityGroup); } }; private Options options; Options options() { if (options == null) { options = new Options(); populate(options); } return options; } abstract void populate(Options options); } }