Dissolve gerrit-util-cli top-level directory
Change-Id: I5a3e7fcc1bc8ee5248376113f08f8dde74c3b607
This commit is contained in:
		
				
					committed by
					
						
						Dave Borowitz
					
				
			
			
				
	
			
			
			
						parent
						
							168be68cb6
						
					
				
				
					commit
					907b7ad94c
				
			
							
								
								
									
										13
									
								
								java/com/google/gerrit/util/cli/BUILD
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								java/com/google/gerrit/util/cli/BUILD
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
java_library(
 | 
			
		||||
    name = "cli",
 | 
			
		||||
    srcs = glob(["**/*.java"]),
 | 
			
		||||
    visibility = ["//visibility:public"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//java/com/google/gerrit/common:annotations",
 | 
			
		||||
        "//java/com/google/gerrit/common:server",
 | 
			
		||||
        "//lib:args4j",
 | 
			
		||||
        "//lib:guava",
 | 
			
		||||
        "//lib/guice",
 | 
			
		||||
        "//lib/guice:guice-assistedinject",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										571
									
								
								java/com/google/gerrit/util/cli/CmdLineParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										571
									
								
								java/com/google/gerrit/util/cli/CmdLineParser.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,571 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
 | 
			
		||||
 *
 | 
			
		||||
 * (Taken from JGit org.eclipse.jgit.pgm.opt.CmdLineParser.)
 | 
			
		||||
 *
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 * - Redistributions of source code must retain the above copyright notice, this
 | 
			
		||||
 * list of conditions and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 * - Redistributions in binary form must reproduce the above copyright notice,
 | 
			
		||||
 * this list of conditions and the following disclaimer in the documentation
 | 
			
		||||
 * and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * - Neither the name of the Git Development Community nor the names of its
 | 
			
		||||
 * contributors may be used to endorse or promote products derived from this
 | 
			
		||||
 * software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 | 
			
		||||
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
 * POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.google.gerrit.util.cli;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Strings;
 | 
			
		||||
import com.google.common.collect.ListMultimap;
 | 
			
		||||
import com.google.common.collect.Lists;
 | 
			
		||||
import com.google.common.collect.MultimapBuilder;
 | 
			
		||||
import com.google.inject.Inject;
 | 
			
		||||
import com.google.inject.assistedinject.Assisted;
 | 
			
		||||
import java.io.StringWriter;
 | 
			
		||||
import java.io.Writer;
 | 
			
		||||
import java.lang.annotation.Annotation;
 | 
			
		||||
import java.lang.reflect.AnnotatedElement;
 | 
			
		||||
import java.lang.reflect.Field;
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.ResourceBundle;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import org.kohsuke.args4j.Argument;
 | 
			
		||||
import org.kohsuke.args4j.CmdLineException;
 | 
			
		||||
import org.kohsuke.args4j.IllegalAnnotationError;
 | 
			
		||||
import org.kohsuke.args4j.NamedOptionDef;
 | 
			
		||||
import org.kohsuke.args4j.Option;
 | 
			
		||||
import org.kohsuke.args4j.OptionDef;
 | 
			
		||||
import org.kohsuke.args4j.spi.BooleanOptionHandler;
 | 
			
		||||
import org.kohsuke.args4j.spi.EnumOptionHandler;
 | 
			
		||||
import org.kohsuke.args4j.spi.FieldSetter;
 | 
			
		||||
import org.kohsuke.args4j.spi.MethodSetter;
 | 
			
		||||
import org.kohsuke.args4j.spi.OptionHandler;
 | 
			
		||||
import org.kohsuke.args4j.spi.Setter;
 | 
			
		||||
import org.kohsuke.args4j.spi.Setters;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Extended command line parser which handles --foo=value arguments.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>The args4j package does not natively handle --foo=value and instead prefers to see --foo value
 | 
			
		||||
 * on the command line. Many users are used to the GNU style --foo=value long option, so we convert
 | 
			
		||||
 * from the GNU style format to the args4j style format prior to invoking args4j for parsing.
 | 
			
		||||
 */
 | 
			
		||||
public class CmdLineParser {
 | 
			
		||||
  public interface Factory {
 | 
			
		||||
    CmdLineParser create(Object bean);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private final OptionHandlers handlers;
 | 
			
		||||
  private final MyParser parser;
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("rawtypes")
 | 
			
		||||
  private Map<String, OptionHandler> options;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a new command line owner that parses arguments/options and set them into the given
 | 
			
		||||
   * object.
 | 
			
		||||
   *
 | 
			
		||||
   * @param bean instance of a class annotated by {@link org.kohsuke.args4j.Option} and {@link
 | 
			
		||||
   *     org.kohsuke.args4j.Argument}. this object will receive values.
 | 
			
		||||
   * @throws IllegalAnnotationError if the option bean class is using args4j annotations
 | 
			
		||||
   *     incorrectly.
 | 
			
		||||
   */
 | 
			
		||||
  @Inject
 | 
			
		||||
  public CmdLineParser(OptionHandlers handlers, @Assisted final Object bean)
 | 
			
		||||
      throws IllegalAnnotationError {
 | 
			
		||||
    this.handlers = handlers;
 | 
			
		||||
    this.parser = new MyParser(bean);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void addArgument(Setter<?> setter, Argument a) {
 | 
			
		||||
    parser.addArgument(setter, a);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void addOption(Setter<?> setter, Option o) {
 | 
			
		||||
    parser.addOption(setter, o);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void printSingleLineUsage(Writer w, ResourceBundle rb) {
 | 
			
		||||
    parser.printSingleLineUsage(w, rb);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void printUsage(Writer out, ResourceBundle rb) {
 | 
			
		||||
    parser.printUsage(out, rb);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void printDetailedUsage(String name, StringWriter out) {
 | 
			
		||||
    out.write(name);
 | 
			
		||||
    printSingleLineUsage(out, null);
 | 
			
		||||
    out.write('\n');
 | 
			
		||||
    out.write('\n');
 | 
			
		||||
    printUsage(out, null);
 | 
			
		||||
    out.write('\n');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void printQueryStringUsage(String name, StringWriter out) {
 | 
			
		||||
    out.write(name);
 | 
			
		||||
 | 
			
		||||
    char next = '?';
 | 
			
		||||
    List<NamedOptionDef> booleans = new ArrayList<>();
 | 
			
		||||
    for (@SuppressWarnings("rawtypes") OptionHandler handler : parser.optionsList) {
 | 
			
		||||
      if (handler.option instanceof NamedOptionDef) {
 | 
			
		||||
        NamedOptionDef n = (NamedOptionDef) handler.option;
 | 
			
		||||
 | 
			
		||||
        if (handler instanceof BooleanOptionHandler) {
 | 
			
		||||
          booleans.add(n);
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!n.required()) {
 | 
			
		||||
          out.write('[');
 | 
			
		||||
        }
 | 
			
		||||
        out.write(next);
 | 
			
		||||
        next = '&';
 | 
			
		||||
        if (n.name().startsWith("--")) {
 | 
			
		||||
          out.write(n.name().substring(2));
 | 
			
		||||
        } else if (n.name().startsWith("-")) {
 | 
			
		||||
          out.write(n.name().substring(1));
 | 
			
		||||
        } else {
 | 
			
		||||
          out.write(n.name());
 | 
			
		||||
        }
 | 
			
		||||
        out.write('=');
 | 
			
		||||
 | 
			
		||||
        out.write(metaVar(handler, n));
 | 
			
		||||
        if (!n.required()) {
 | 
			
		||||
          out.write(']');
 | 
			
		||||
        }
 | 
			
		||||
        if (n.isMultiValued()) {
 | 
			
		||||
          out.write('*');
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    for (NamedOptionDef n : booleans) {
 | 
			
		||||
      if (!n.required()) {
 | 
			
		||||
        out.write('[');
 | 
			
		||||
      }
 | 
			
		||||
      out.write(next);
 | 
			
		||||
      next = '&';
 | 
			
		||||
      if (n.name().startsWith("--")) {
 | 
			
		||||
        out.write(n.name().substring(2));
 | 
			
		||||
      } else if (n.name().startsWith("-")) {
 | 
			
		||||
        out.write(n.name().substring(1));
 | 
			
		||||
      } else {
 | 
			
		||||
        out.write(n.name());
 | 
			
		||||
      }
 | 
			
		||||
      if (!n.required()) {
 | 
			
		||||
        out.write(']');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static String metaVar(OptionHandler<?> handler, NamedOptionDef n) {
 | 
			
		||||
    String var = n.metaVar();
 | 
			
		||||
    if (Strings.isNullOrEmpty(var)) {
 | 
			
		||||
      var = handler.getDefaultMetaVariable();
 | 
			
		||||
      if (handler instanceof EnumOptionHandler) {
 | 
			
		||||
        var = var.substring(1, var.length() - 1).replace(" ", "");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return var;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean wasHelpRequestedByOption() {
 | 
			
		||||
    return parser.help.value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void parseArgument(String... args) throws CmdLineException {
 | 
			
		||||
    List<String> tmp = Lists.newArrayListWithCapacity(args.length);
 | 
			
		||||
    for (int argi = 0; argi < args.length; argi++) {
 | 
			
		||||
      final String str = args[argi];
 | 
			
		||||
      if (str.equals("--")) {
 | 
			
		||||
        while (argi < args.length) {
 | 
			
		||||
          tmp.add(args[argi++]);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (str.startsWith("--")) {
 | 
			
		||||
        final int eq = str.indexOf('=');
 | 
			
		||||
        if (eq > 0) {
 | 
			
		||||
          tmp.add(str.substring(0, eq));
 | 
			
		||||
          tmp.add(str.substring(eq + 1));
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      tmp.add(str);
 | 
			
		||||
    }
 | 
			
		||||
    parser.parseArgument(tmp.toArray(new String[tmp.size()]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void parseOptionMap(Map<String, String[]> parameters) throws CmdLineException {
 | 
			
		||||
    ListMultimap<String, String> map = MultimapBuilder.hashKeys().arrayListValues().build();
 | 
			
		||||
    for (Map.Entry<String, String[]> ent : parameters.entrySet()) {
 | 
			
		||||
      for (String val : ent.getValue()) {
 | 
			
		||||
        map.put(ent.getKey(), val);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    parseOptionMap(map);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void parseOptionMap(ListMultimap<String, String> params) throws CmdLineException {
 | 
			
		||||
    List<String> tmp = Lists.newArrayListWithCapacity(2 * params.size());
 | 
			
		||||
    for (String key : params.keySet()) {
 | 
			
		||||
      String name = makeOption(key);
 | 
			
		||||
 | 
			
		||||
      if (isBoolean(name)) {
 | 
			
		||||
        boolean on = false;
 | 
			
		||||
        for (String value : params.get(key)) {
 | 
			
		||||
          on = toBoolean(key, value);
 | 
			
		||||
        }
 | 
			
		||||
        if (on) {
 | 
			
		||||
          tmp.add(name);
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        for (String value : params.get(key)) {
 | 
			
		||||
          tmp.add(name);
 | 
			
		||||
          tmp.add(value);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    parser.parseArgument(tmp.toArray(new String[tmp.size()]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean isBoolean(String name) {
 | 
			
		||||
    return findHandler(makeOption(name)) instanceof BooleanOptionHandler;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void parseWithPrefix(String prefix, Object bean) {
 | 
			
		||||
    parser.parseWithPrefix(prefix, bean);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private String makeOption(String name) {
 | 
			
		||||
    if (!name.startsWith("-")) {
 | 
			
		||||
      if (name.length() == 1) {
 | 
			
		||||
        name = "-" + name;
 | 
			
		||||
      } else {
 | 
			
		||||
        name = "--" + name;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return name;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("rawtypes")
 | 
			
		||||
  private OptionHandler findHandler(String name) {
 | 
			
		||||
    if (options == null) {
 | 
			
		||||
      options = index(parser.optionsList);
 | 
			
		||||
    }
 | 
			
		||||
    return options.get(name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("rawtypes")
 | 
			
		||||
  private static Map<String, OptionHandler> index(List<OptionHandler> in) {
 | 
			
		||||
    Map<String, OptionHandler> m = new HashMap<>();
 | 
			
		||||
    for (OptionHandler handler : in) {
 | 
			
		||||
      if (handler.option instanceof NamedOptionDef) {
 | 
			
		||||
        NamedOptionDef def = (NamedOptionDef) handler.option;
 | 
			
		||||
        if (!def.isArgument()) {
 | 
			
		||||
          m.put(def.name(), handler);
 | 
			
		||||
          for (String alias : def.aliases()) {
 | 
			
		||||
            m.put(alias, handler);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private boolean toBoolean(String name, String value) throws CmdLineException {
 | 
			
		||||
    if ("true".equals(value)
 | 
			
		||||
        || "t".equals(value)
 | 
			
		||||
        || "yes".equals(value)
 | 
			
		||||
        || "y".equals(value)
 | 
			
		||||
        || "on".equals(value)
 | 
			
		||||
        || "1".equals(value)
 | 
			
		||||
        || value == null
 | 
			
		||||
        || "".equals(value)) {
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ("false".equals(value)
 | 
			
		||||
        || "f".equals(value)
 | 
			
		||||
        || "no".equals(value)
 | 
			
		||||
        || "n".equals(value)
 | 
			
		||||
        || "off".equals(value)
 | 
			
		||||
        || "0".equals(value)) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    throw new CmdLineException(parser, String.format("invalid boolean \"%s=%s\"", name, value));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static class PrefixedOption implements Option {
 | 
			
		||||
    String prefix;
 | 
			
		||||
    Option o;
 | 
			
		||||
 | 
			
		||||
    PrefixedOption(String prefix, Option o) {
 | 
			
		||||
      this.prefix = prefix;
 | 
			
		||||
      this.o = o;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String name() {
 | 
			
		||||
      return getPrefixedName(prefix, o.name());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String[] aliases() {
 | 
			
		||||
      String[] prefixedAliases = new String[o.aliases().length];
 | 
			
		||||
      for (int i = 0; i < prefixedAliases.length; i++) {
 | 
			
		||||
        prefixedAliases[i] = getPrefixedName(prefix, o.aliases()[i]);
 | 
			
		||||
      }
 | 
			
		||||
      return prefixedAliases;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String usage() {
 | 
			
		||||
      return o.usage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String metaVar() {
 | 
			
		||||
      return o.metaVar();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean required() {
 | 
			
		||||
      return o.required();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean hidden() {
 | 
			
		||||
      return o.hidden();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("rawtypes")
 | 
			
		||||
    @Override
 | 
			
		||||
    public Class<? extends OptionHandler> handler() {
 | 
			
		||||
      return o.handler();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String[] depends() {
 | 
			
		||||
      return o.depends();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Class<? extends Annotation> annotationType() {
 | 
			
		||||
      return o.annotationType();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String getPrefixedName(String prefix, String name) {
 | 
			
		||||
      return prefix + name;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private class MyParser extends org.kohsuke.args4j.CmdLineParser {
 | 
			
		||||
    @SuppressWarnings("rawtypes")
 | 
			
		||||
    private List<OptionHandler> optionsList;
 | 
			
		||||
 | 
			
		||||
    private HelpOption help;
 | 
			
		||||
 | 
			
		||||
    MyParser(Object bean) {
 | 
			
		||||
      super(bean);
 | 
			
		||||
      parseAdditionalOptions(bean, new HashSet<>());
 | 
			
		||||
      ensureOptionsInitialized();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // NOTE: Argument annotations on bean are ignored.
 | 
			
		||||
    public void parseWithPrefix(String prefix, Object bean) {
 | 
			
		||||
      parseWithPrefix(prefix, bean, new HashSet<>());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void parseWithPrefix(String prefix, Object bean, Set<Object> parsedBeans) {
 | 
			
		||||
      if (!parsedBeans.add(bean)) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      // recursively process all the methods/fields.
 | 
			
		||||
      for (Class<?> c = bean.getClass(); c != null; c = c.getSuperclass()) {
 | 
			
		||||
        for (Method m : c.getDeclaredMethods()) {
 | 
			
		||||
          Option o = m.getAnnotation(Option.class);
 | 
			
		||||
          if (o != null) {
 | 
			
		||||
            addOption(new MethodSetter(this, bean, m), new PrefixedOption(prefix, o));
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        for (Field f : c.getDeclaredFields()) {
 | 
			
		||||
          Option o = f.getAnnotation(Option.class);
 | 
			
		||||
          if (o != null) {
 | 
			
		||||
            addOption(Setters.create(f, bean), new PrefixedOption(prefix, o));
 | 
			
		||||
          }
 | 
			
		||||
          if (f.isAnnotationPresent(Options.class)) {
 | 
			
		||||
            try {
 | 
			
		||||
              parseWithPrefix(
 | 
			
		||||
                  prefix + f.getAnnotation(Options.class).prefix(), f.get(bean), parsedBeans);
 | 
			
		||||
            } catch (IllegalAccessException e) {
 | 
			
		||||
              throw new IllegalAnnotationError(e);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void parseAdditionalOptions(Object bean, Set<Object> parsedBeans) {
 | 
			
		||||
      for (Class<?> c = bean.getClass(); c != null; c = c.getSuperclass()) {
 | 
			
		||||
        for (Field f : c.getDeclaredFields()) {
 | 
			
		||||
          if (f.isAnnotationPresent(Options.class)) {
 | 
			
		||||
            Object additionalBean = null;
 | 
			
		||||
            try {
 | 
			
		||||
              additionalBean = f.get(bean);
 | 
			
		||||
            } catch (IllegalAccessException e) {
 | 
			
		||||
              throw new IllegalAnnotationError(e);
 | 
			
		||||
            }
 | 
			
		||||
            parseWithPrefix(f.getAnnotation(Options.class).prefix(), additionalBean, parsedBeans);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings({"unchecked", "rawtypes"})
 | 
			
		||||
    @Override
 | 
			
		||||
    protected OptionHandler createOptionHandler(OptionDef option, Setter setter) {
 | 
			
		||||
      if (isHandlerSpecified(option) || isEnum(setter) || isPrimitive(setter)) {
 | 
			
		||||
        return add(super.createOptionHandler(option, setter));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      OptionHandlerFactory<?> factory = handlers.get(setter.getType());
 | 
			
		||||
      if (factory != null) {
 | 
			
		||||
        return factory.create(this, option, setter);
 | 
			
		||||
      }
 | 
			
		||||
      return add(super.createOptionHandler(option, setter));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("rawtypes")
 | 
			
		||||
    private OptionHandler add(OptionHandler handler) {
 | 
			
		||||
      ensureOptionsInitialized();
 | 
			
		||||
      optionsList.add(handler);
 | 
			
		||||
      return handler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void ensureOptionsInitialized() {
 | 
			
		||||
      if (optionsList == null) {
 | 
			
		||||
        help = new HelpOption();
 | 
			
		||||
        optionsList = new ArrayList<>();
 | 
			
		||||
        addOption(help, help);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isHandlerSpecified(OptionDef option) {
 | 
			
		||||
      return option.handler() != OptionHandler.class;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private <T> boolean isEnum(Setter<T> setter) {
 | 
			
		||||
      return Enum.class.isAssignableFrom(setter.getType());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private <T> boolean isPrimitive(Setter<T> setter) {
 | 
			
		||||
      return setter.getType().isPrimitive();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static class HelpOption implements Option, Setter<Boolean> {
 | 
			
		||||
    private boolean value;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String name() {
 | 
			
		||||
      return "--help";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String[] aliases() {
 | 
			
		||||
      return new String[] {"-h"};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String[] depends() {
 | 
			
		||||
      return new String[] {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean hidden() {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String usage() {
 | 
			
		||||
      return "display this help text";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addValue(Boolean val) {
 | 
			
		||||
      value = val;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Class<? extends OptionHandler<Boolean>> handler() {
 | 
			
		||||
      return BooleanOptionHandler.class;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String metaVar() {
 | 
			
		||||
      return "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean required() {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Class<? extends Annotation> annotationType() {
 | 
			
		||||
      return Option.class;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public FieldSetter asFieldSetter() {
 | 
			
		||||
      throw new UnsupportedOperationException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public AnnotatedElement asAnnotatedElement() {
 | 
			
		||||
      throw new UnsupportedOperationException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Class<Boolean> getType() {
 | 
			
		||||
      return Boolean.class;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean isMultiValued() {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public CmdLineException reject(String message) {
 | 
			
		||||
    return new CmdLineException(parser, message);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								java/com/google/gerrit/util/cli/EndOfOptionsHandler.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								java/com/google/gerrit/util/cli/EndOfOptionsHandler.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
// Copyright (C) 2010 The Android Open Source Project
 | 
			
		||||
//
 | 
			
		||||
// Licensed 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 com.google.gerrit.util.cli;
 | 
			
		||||
 | 
			
		||||
import org.kohsuke.args4j.CmdLineException;
 | 
			
		||||
import org.kohsuke.args4j.CmdLineParser;
 | 
			
		||||
import org.kohsuke.args4j.OptionDef;
 | 
			
		||||
import org.kohsuke.args4j.spi.OptionHandler;
 | 
			
		||||
import org.kohsuke.args4j.spi.Parameters;
 | 
			
		||||
import org.kohsuke.args4j.spi.Setter;
 | 
			
		||||
 | 
			
		||||
/** Typically used with {@code @Option(name="--")} to signal end of options. */
 | 
			
		||||
public class EndOfOptionsHandler extends OptionHandler<Boolean> {
 | 
			
		||||
  public EndOfOptionsHandler(
 | 
			
		||||
      CmdLineParser parser, OptionDef option, Setter<? super Boolean> setter) {
 | 
			
		||||
    super(parser, option, setter);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public String getDefaultMetaVariable() {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public int parseArguments(Parameters params) throws CmdLineException {
 | 
			
		||||
    owner.stopOptionParsing();
 | 
			
		||||
    setter.addValue(true);
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								java/com/google/gerrit/util/cli/OptionHandlerFactory.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								java/com/google/gerrit/util/cli/OptionHandlerFactory.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
// Copyright (C) 2009 The Android Open Source Project
 | 
			
		||||
//
 | 
			
		||||
// Licensed 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 com.google.gerrit.util.cli;
 | 
			
		||||
 | 
			
		||||
import org.kohsuke.args4j.OptionDef;
 | 
			
		||||
import org.kohsuke.args4j.spi.OptionHandler;
 | 
			
		||||
import org.kohsuke.args4j.spi.Setter;
 | 
			
		||||
 | 
			
		||||
/** Creates an args4j OptionHandler through a Guice Injector. */
 | 
			
		||||
public interface OptionHandlerFactory<T> {
 | 
			
		||||
  OptionHandler<T> create(
 | 
			
		||||
      org.kohsuke.args4j.CmdLineParser cmdLineParser, OptionDef optionDef, Setter<T> setter);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								java/com/google/gerrit/util/cli/OptionHandlerUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								java/com/google/gerrit/util/cli/OptionHandlerUtil.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
// Copyright (C) 2009 The Android Open Source Project
 | 
			
		||||
//
 | 
			
		||||
// Licensed 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 com.google.gerrit.util.cli;
 | 
			
		||||
 | 
			
		||||
import com.google.inject.Key;
 | 
			
		||||
import com.google.inject.Module;
 | 
			
		||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
 | 
			
		||||
import com.google.inject.util.Types;
 | 
			
		||||
import java.lang.reflect.Type;
 | 
			
		||||
import org.kohsuke.args4j.spi.OptionHandler;
 | 
			
		||||
 | 
			
		||||
/** Utilities to support creating OptionHandler instances. */
 | 
			
		||||
public class OptionHandlerUtil {
 | 
			
		||||
  /** Generate a key for an {@link OptionHandlerFactory} in Guice. */
 | 
			
		||||
  @SuppressWarnings("unchecked")
 | 
			
		||||
  public static <T> Key<OptionHandlerFactory<T>> keyFor(Class<T> valueType) {
 | 
			
		||||
    final Type factoryType = Types.newParameterizedType(OptionHandlerFactory.class, valueType);
 | 
			
		||||
    return (Key<OptionHandlerFactory<T>>) Key.get(factoryType);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("unchecked")
 | 
			
		||||
  private static <T> Key<OptionHandler<T>> handlerOf(Class<T> type) {
 | 
			
		||||
    final Type handlerType = Types.newParameterizedTypeWithOwner(null, OptionHandler.class, type);
 | 
			
		||||
    return (Key<OptionHandler<T>>) Key.get(handlerType);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static <T> Module moduleFor(Class<T> type, Class<? extends OptionHandler<T>> impl) {
 | 
			
		||||
    return new FactoryModuleBuilder().implement(handlerOf(type), impl).build(keyFor(type));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private OptionHandlerUtil() {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										77
									
								
								java/com/google/gerrit/util/cli/OptionHandlers.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								java/com/google/gerrit/util/cli/OptionHandlers.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
// Copyright (C) 2013 The Android Open Source Project
 | 
			
		||||
//
 | 
			
		||||
// Licensed 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 com.google.gerrit.util.cli;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableMap;
 | 
			
		||||
import com.google.gerrit.common.Nullable;
 | 
			
		||||
import com.google.inject.Binding;
 | 
			
		||||
import com.google.inject.Inject;
 | 
			
		||||
import com.google.inject.Injector;
 | 
			
		||||
import com.google.inject.Key;
 | 
			
		||||
import com.google.inject.Provider;
 | 
			
		||||
import com.google.inject.Singleton;
 | 
			
		||||
import com.google.inject.TypeLiteral;
 | 
			
		||||
import java.lang.reflect.ParameterizedType;
 | 
			
		||||
import java.util.Map.Entry;
 | 
			
		||||
 | 
			
		||||
@Singleton
 | 
			
		||||
public class OptionHandlers {
 | 
			
		||||
  public static OptionHandlers empty() {
 | 
			
		||||
    ImmutableMap<Class<?>, Provider<OptionHandlerFactory<?>>> m = ImmutableMap.of();
 | 
			
		||||
    return new OptionHandlers(m);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private final ImmutableMap<Class<?>, Provider<OptionHandlerFactory<?>>> map;
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  OptionHandlers(Injector parent) {
 | 
			
		||||
    this(build(parent));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  OptionHandlers(ImmutableMap<Class<?>, Provider<OptionHandlerFactory<?>>> m) {
 | 
			
		||||
    this.map = m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Nullable
 | 
			
		||||
  OptionHandlerFactory<?> get(Class<?> type) {
 | 
			
		||||
    Provider<OptionHandlerFactory<?>> b = map.get(type);
 | 
			
		||||
    return b != null ? b.get() : null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static ImmutableMap<Class<?>, Provider<OptionHandlerFactory<?>>> build(Injector i) {
 | 
			
		||||
    ImmutableMap.Builder<Class<?>, Provider<OptionHandlerFactory<?>>> map = ImmutableMap.builder();
 | 
			
		||||
    for (; i != null; i = i.getParent()) {
 | 
			
		||||
      for (Entry<Key<?>, Binding<?>> e : i.getBindings().entrySet()) {
 | 
			
		||||
        TypeLiteral<?> type = e.getKey().getTypeLiteral();
 | 
			
		||||
        if (type.getRawType() == OptionHandlerFactory.class
 | 
			
		||||
            && e.getKey().getAnnotation() == null
 | 
			
		||||
            && type.getType() instanceof ParameterizedType) {
 | 
			
		||||
          map.put(getType(type), cast(e.getValue()).getProvider());
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return map.build();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static Class<?> getType(TypeLiteral<?> t) {
 | 
			
		||||
    ParameterizedType p = (ParameterizedType) t.getType();
 | 
			
		||||
    return (Class<?>) p.getActualTypeArguments()[0];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("unchecked")
 | 
			
		||||
  private static Binding<OptionHandlerFactory<?>> cast(Binding<?> e) {
 | 
			
		||||
    return (Binding<OptionHandlerFactory<?>>) e;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								java/com/google/gerrit/util/cli/Options.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								java/com/google/gerrit/util/cli/Options.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
// Copyright (C) 2017 The Android Open Source Project
 | 
			
		||||
//
 | 
			
		||||
// Licensed 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 com.google.gerrit.util.cli;
 | 
			
		||||
 | 
			
		||||
import static java.lang.annotation.ElementType.FIELD;
 | 
			
		||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.Retention;
 | 
			
		||||
import java.lang.annotation.Target;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Marks a field that refers to a class with @Option annotations
 | 
			
		||||
 *
 | 
			
		||||
 * <p>Any @Option annotations found on the referred class will be handled as if they were found on
 | 
			
		||||
 * the referring class.
 | 
			
		||||
 */
 | 
			
		||||
@Retention(RUNTIME)
 | 
			
		||||
@Target({FIELD})
 | 
			
		||||
public @interface Options {
 | 
			
		||||
  String prefix() default "";
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user