Add @RequiresOptions annotation for dynamic options
Add @RequiresOptions annotation for dynamic options to note one or more option dependencies so that the dynamic options will not be present unless all the @Option's on which they depend are present. Change-Id: Iadfb061a4b288bfebee34e1806d97c13e421fdb6
This commit is contained in:
committed by
Martin Fick
parent
57b553ad94
commit
99a1ad102c
@@ -53,6 +53,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
@@ -355,6 +357,10 @@ public class CmdLineParser {
|
||||
parser.parseWithPrefix(prefix, bean);
|
||||
}
|
||||
|
||||
public void drainOptionQueue() {
|
||||
parser.addOptionsWithMetRequirements();
|
||||
}
|
||||
|
||||
private String makeOption(String name) {
|
||||
if (!name.startsWith("-")) {
|
||||
if (name.length() == 1) {
|
||||
@@ -493,14 +499,54 @@ public class CmdLineParser {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private List<OptionHandler> optionsList;
|
||||
|
||||
private Map<String, QueuedOption> queuedOptionsByName = new LinkedHashMap<>();
|
||||
private HelpOption help;
|
||||
|
||||
private class QueuedOption {
|
||||
public final Option option;
|
||||
public final Setter setter;
|
||||
public final String[] requiredOptions;
|
||||
|
||||
private QueuedOption(Option option, Setter setter, RequiresOptions requiresOptions) {
|
||||
this.option = option;
|
||||
this.setter = setter;
|
||||
this.requiredOptions = requiresOptions != null ? requiresOptions.value() : new String[0];
|
||||
}
|
||||
}
|
||||
|
||||
MyParser(Object bean) {
|
||||
super(bean, ParserProperties.defaults().withAtSyntax(false));
|
||||
parseAdditionalOptions(bean, new HashSet<>());
|
||||
addOptionsWithMetRequirements();
|
||||
ensureOptionsInitialized();
|
||||
}
|
||||
|
||||
public int addOptionsWithMetRequirements() {
|
||||
int count = 0;
|
||||
for (Iterator<Map.Entry<String, QueuedOption>> it = queuedOptionsByName.entrySet().iterator();
|
||||
it.hasNext(); ) {
|
||||
QueuedOption queuedOption = it.next().getValue();
|
||||
if (hasAllRequiredOptions(queuedOption)) {
|
||||
addOption(queuedOption.setter, queuedOption.option);
|
||||
it.remove();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
count += addOptionsWithMetRequirements();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private boolean hasAllRequiredOptions(QueuedOption queuedOption) {
|
||||
for (String name : queuedOption.requiredOptions) {
|
||||
if (findOptionByName(name) == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: Argument annotations on bean are ignored.
|
||||
public void parseWithPrefix(String prefix, Object bean) {
|
||||
parseWithPrefix(prefix, bean, new HashSet<>());
|
||||
@@ -515,13 +561,19 @@ public class CmdLineParser {
|
||||
for (Method m : c.getDeclaredMethods()) {
|
||||
Option o = m.getAnnotation(Option.class);
|
||||
if (o != null) {
|
||||
addOption(new MethodSetter(this, bean, m), new PrefixedOption(prefix, o));
|
||||
queueOption(
|
||||
new PrefixedOption(prefix, o),
|
||||
new MethodSetter(this, bean, m),
|
||||
m.getAnnotation(RequiresOptions.class));
|
||||
}
|
||||
}
|
||||
for (Field f : c.getDeclaredFields()) {
|
||||
Option o = f.getAnnotation(Option.class);
|
||||
if (o != null) {
|
||||
addOption(Setters.create(f, bean), new PrefixedOption(prefix, o));
|
||||
queueOption(
|
||||
new PrefixedOption(prefix, o),
|
||||
Setters.create(f, bean),
|
||||
f.getAnnotation(RequiresOptions.class));
|
||||
}
|
||||
if (f.isAnnotationPresent(Options.class)) {
|
||||
try {
|
||||
@@ -588,6 +640,14 @@ public class CmdLineParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void queueOption(Option option, Setter setter, RequiresOptions requiresOptions) {
|
||||
if (queuedOptionsByName.put(option.name(), new QueuedOption(option, setter, requiresOptions))
|
||||
!= null) {
|
||||
throw new IllegalAnnotationError(
|
||||
"Option name " + option.name() + " is used more than once");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private OptionHandler add(OptionHandler handler) {
|
||||
ensureOptionsInitialized();
|
||||
|
||||
45
java/com/google/gerrit/util/cli/RequiresOptions.java
Normal file
45
java/com/google/gerrit/util/cli/RequiresOptions.java
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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.ElementType.METHOD;
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a field/setter annotated with {@literal @}Option as having a dependency on multiple other
|
||||
* command line option.
|
||||
*
|
||||
* <p>If any of the required command line options are not present, the {@literal @}Option will be
|
||||
* ignored.
|
||||
*
|
||||
* <p>For example:
|
||||
*
|
||||
* <pre>
|
||||
* {@literal @}RequiresOptions({"--help", "--usage"})
|
||||
* {@literal @}Option(name = "--help-as-json",
|
||||
* usage = "display help text in json format")
|
||||
* public boolean displayHelpAsJson;
|
||||
* </pre>
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target({FIELD, METHOD, PARAMETER})
|
||||
public @interface RequiresOptions {
|
||||
String[] value();
|
||||
}
|
||||
Reference in New Issue
Block a user