diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/DynamicOptions.java b/gerrit-server/src/main/java/com/google/gerrit/server/DynamicOptions.java index 6267dcad0b..485411294e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/DynamicOptions.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/DynamicOptions.java @@ -138,7 +138,7 @@ public class DynamicOptions { public void parseDynamicBeans(CmdLineParser clp) { for (Entry e : beansByPlugin.entrySet()) { - clp.parseWithPrefix(e.getKey(), e.getValue()); + clp.parseWithPrefix("--" + e.getKey(), e.getValue()); } } diff --git a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java index bb293ccedf..2d1574b7ea 100644 --- a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java +++ b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java @@ -48,9 +48,11 @@ 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; @@ -381,7 +383,7 @@ public class CmdLineParser { } private static String getPrefixedName(String prefix, String name) { - return "--" + prefix + name; + return prefix + name; } } @@ -393,11 +395,19 @@ public class CmdLineParser { 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 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()) { @@ -411,6 +421,31 @@ public class CmdLineParser { 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(String prefix, Object bean, Set 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( + prefix + f.getAnnotation(Options.class).prefix(), additionalBean, parsedBeans); + } } } } diff --git a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/Options.java b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/Options.java new file mode 100644 index 0000000000..96613df6a5 --- /dev/null +++ b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/Options.java @@ -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 + * + *

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 ""; +}