content: package hip.util;
  
  import com.google.common.base.Preconditions;
  import com.google.common.collect.ImmutableMap;
  import joptsimple.*;
  import org.apache.hadoop.util.ToolRunner;
  
  import java.io.IOException;
  import java.io.PrintStream;
  import java.util.Map;
  
  /**
   * A command-line tool for making calls in the HAServiceProtocol.
   * For example,. this can be used to force a service to standby or active
   * mode, or to trigger a health-check.
   */
  public final class Cli {
  
    /**
     * Output stream for errors.
     */
    private final PrintStream errOut = System.err;
  
    private final OptionParser parser;
    private final String[] args;
    private OptionSet options;
  
    private Cli(final OptionParser parser, final String[] args) {
      this.parser = parser;
      this.args = args;
    }
  
    /**
     * Write the usage to stderr.
     *
     * @throws IOException if writing fails
     */
    public void printUsage() throws IOException {
      parser.printHelpOn(errOut);
      errOut.println();
      ToolRunner.printGenericCommandUsage(errOut);
    }
  
    /**
     * Parse the arguments.
     *
     * @return 0 if the arguments were parsed, otherwise non-zero
     * @throws IOException on error
     */
    public int runCmd() throws IOException {
  
      boolean parseException = false;
      options = null;
      try {
        options = parser.parse(args);
      } catch (OptionException e) {
        e.printStackTrace();
        parseException = true;
      }
  
      if (parseException || options.has("help")) {
        printUsage();
        return 1;
      }
  
      return 0;
    }
  
    public String getArgValueAsString(ArgInfo opt) {
      return (String) options.valueOf(opt.getArgName());
    }
  
    public Integer getArgValueAsInt(ArgInfo opt) {
      String s  = getArgValueAsString(opt);
      if (s == null) {
        return null;
      }
      return Integer.valueOf(s);
    }
  
    public String getArgValueAsString(ArgGetter opt) {
      return (String) options.valueOf(opt.getArgInfo().getArgName());
    }
  
    public Integer getArgValueAsInt(ArgGetter opt) {
      String s  = getArgValueAsString(opt);
      if (s == null) {
        return null;
      }
      return Integer.valueOf(s);
    }
  
    public boolean doesArgExist(ArgInfo opt) {
      return options.has(opt.getArgName());
    }
  
    public boolean doesArgExist(ArgGetter opt) {
      return options.has(opt.getArgInfo().getArgName());
    }
  
    public static CliBuilder builder() {
      return new CliBuilder();
    }
  
    public static interface ArgInfo {
      String getArgName();
  
      String getArgDescription();
  
      boolean isRequired();
  
      boolean hasArg();
    }
  
    public static interface ArgGetter {
      ArgInfo getArgInfo();
    }
  
    public static class ArgInfoImpl implements ArgInfo {
      private final String argName;
      private final boolean hasArgument;
      private final boolean isRequired;
      private final String description;
  
      public ArgInfoImpl(String argName, boolean hasArgument, boolean isRequired, String description) {
        Preconditions.checkNotNull(argName);
        this.argName = argName.toLowerCase().replaceAll("_", "-");
        this.hasArgument = hasArgument;
        this.isRequired = isRequired;
        this.description = description;
      }
  
      @Override
      public String getArgName() {
        return argName;
      }
  
      @Override
      public String getArgDescription() {
        return description;
      }
  
      @Override
      public boolean isRequired() {
        return isRequired;
      }
  
      @Override
      public boolean hasArg() {
        return hasArgument;
      }
    }
  
    public static class ArgBuilder {
      private String argName;
      private boolean hasArgument;
      private boolean isRequired;
      private String description;
  
      public static ArgBuilder builder() {
        return new ArgBuilder();
      }
  
      public ArgBuilder setArgName(String argName) {
        this.argName = argName;
        return this;
      }
  
      public ArgBuilder hasArgument(boolean hasArgument) {
        this.hasArgument = hasArgument;
        return this;
      }
  
      public ArgBuilder required(boolean isRequired) {
        this.isRequired = isRequired;
        return this;
      }
  
      public ArgBuilder description(String description) {
        this.description = description;
        return this;
      }
  
      public ArgInfo build() {
        return new ArgInfoImpl(argName, hasArgument, isRequired, description);
      }
    }
  
    public static class CliBuilder {
      private String[] args;
      private ImmutableMap.Builder<String, ArgInfo> opts = ImmutableMap.builder();
  
      public CliBuilder setArgs(String[] args) {
        this.args = args;
        return this;
      }
  
      public CliBuilder addOption(ArgInfo argInfo) {
        opts.put(argInfo.getArgName(), argInfo);
        return this;
      }
  
      public CliBuilder addOptions(ArgInfo... argInfos) {
        for (ArgInfo argInfo : argInfos) {
          addOption(argInfo);
        }
        return this;
      }
  
      public CliBuilder addOptions(ArgGetter... getters) {
        for (ArgGetter gettter : getters) {
          addOption(gettter.getArgInfo());
        }
        return this;
      }
  
      public Cli build() {
        OptionParser parser = new OptionParser();
        Map<String, ArgInfo> options = opts.build();
        for (ArgInfo opt : options.values()) {
          OptionSpecBuilder builder = parser.accepts(opt.getArgName());
          if (opt.hasArg()) {
            ArgumentAcceptingOptionSpec<String> optSpec = builder.withRequiredArg();
            if (opt.isRequired()) {
              optSpec.required();
            }
            optSpec.describedAs(opt.getArgDescription());
          }
        }
  
        parser.accepts("help").forHelp();
        parser.allowsUnrecognizedOptions();
  
        return new Cli(parser, args);
      }
    }
  }
  