08855df46a
Reviewed-by: vlivanov, erikj, mseledtsov, gthornbr
328 lines
13 KiB
Java
328 lines
13 KiB
Java
/*
|
|
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
package vm.share.options;
|
|
|
|
import java.util.Map;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.ArrayList;
|
|
import java.util.LinkedList;
|
|
import java.util.Iterator;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Modifier;
|
|
import java.io.PrintStream;
|
|
import nsk.share.TestBug;
|
|
import nsk.share.log.LogSupport;
|
|
|
|
class OptionsSetup {
|
|
private LogSupport log = new LogSupport();
|
|
private boolean verbose = true;
|
|
private Object test;
|
|
private String[] args;
|
|
private OptionHandler unknownOptionHandler;
|
|
|
|
private int argIndex = 0;
|
|
private Map<String, OptionDefinition> optionDefs = new LinkedHashMap<String, OptionDefinition>(); // Use LinkedHashMap to ensure order of options
|
|
private List<OptionDefinition> unconfiguredOptionsList = new ArrayList<OptionDefinition>();
|
|
private List<OptionDefinition> unconfiguredOptionList = new ArrayList<OptionDefinition>();
|
|
private Map<String, Object> optionValues = new LinkedHashMap<String, Object>();
|
|
|
|
public OptionsSetup(Object test, String[] args, OptionHandler unknownOptionHandler) {
|
|
this.test = test;
|
|
this.args = args;
|
|
this.unknownOptionHandler = unknownOptionHandler;
|
|
log.setDebugEnabled(verbose);
|
|
}
|
|
|
|
public void run() {
|
|
searchAnnotations(test, null);
|
|
while (argIndex < args.length) {
|
|
process1Arg();
|
|
}
|
|
setDefaultValues();
|
|
checkMandatoryOptions();
|
|
if (unconfiguredOptionsList.size() > 0) {
|
|
for (OptionDefinition optDef : unconfiguredOptionsList)
|
|
log.info("Unconfigured option: " + optDef);
|
|
throw new TestBug("Some options are unconfigured");
|
|
}
|
|
}
|
|
|
|
private void checkMandatoryOptions() {
|
|
for (Map.Entry<String, OptionDefinition> e : optionDefs.entrySet()) {
|
|
String name = e.getKey();
|
|
OptionDefinition optDef = e.getValue();
|
|
if (optDef.getDefaultValue() == null && !optionValues.containsKey(name))
|
|
throw new TestBug("Mandatory option is not specified: -" + name);
|
|
}
|
|
}
|
|
|
|
private void setDefaultValues() {
|
|
for (Iterator<OptionDefinition> it = unconfiguredOptionList.iterator(); it.hasNext(); ) {
|
|
OptionDefinition optDef = it.next();
|
|
String value = optDef.getDefaultValue();
|
|
if (value == null)
|
|
continue;
|
|
setOptionValue(optDef, value);
|
|
it.remove();
|
|
if (unconfiguredOptionsList.contains(optDef))
|
|
unconfiguredOptionsList.remove(optDef);
|
|
}
|
|
|
|
for (Iterator<OptionDefinition> it = unconfiguredOptionsList.iterator(); it.hasNext(); ) {
|
|
OptionDefinition optDef = it.next();
|
|
if (optionsAnnotation(optDef.getOwner(), optDef.getField(), null, optDef, true))
|
|
it.remove();
|
|
}
|
|
}
|
|
|
|
private void process1Arg() {
|
|
String arg = args[argIndex++];
|
|
//log.debug("Processing argument: " + arg);
|
|
if (!arg.startsWith("-")) {
|
|
processUnknownArg(arg);
|
|
return;
|
|
}
|
|
String opt = arg.substring(1);
|
|
String value = null;
|
|
int i = opt.indexOf('=');
|
|
if (i != -1) {
|
|
value = opt.substring(i + 1);
|
|
opt = opt.substring(0, i);
|
|
}
|
|
if (opt.equals("help")) {
|
|
printHelp();
|
|
throw new TestBug("-help was specified");
|
|
}
|
|
if (!optionDefs.containsKey(opt)) {
|
|
if (value == null && argIndex < args.length)
|
|
value = args[argIndex++];
|
|
// We need to try to resolve default values of all unconfigured fields because one of them may potentially have this option
|
|
setDefaultValues();
|
|
if (!optionDefs.containsKey(opt)) {
|
|
processUnknownOpt(opt, value);
|
|
return;
|
|
}
|
|
}
|
|
OptionDefinition optDef = optionDefs.get(opt);
|
|
Field f = optDef.getField();
|
|
// Handle boolean omitted value
|
|
if (value == null && (argIndex >= args.length || args[argIndex].startsWith("-"))) {
|
|
if (f.getType() == boolean.class || f.getType() == Boolean.class) {
|
|
value = "true";
|
|
}
|
|
}
|
|
if (value == null) {
|
|
if (argIndex >= args.length)
|
|
throw new TestBug("Missing value for option -" + opt);
|
|
value = args[argIndex++];
|
|
}
|
|
setOptionValue(optDef, value);
|
|
if (unconfiguredOptionList.contains(optDef)){
|
|
unconfiguredOptionList.remove(optDef);
|
|
}
|
|
}
|
|
|
|
private void setOptionValue(OptionDefinition optDef, String value) {
|
|
Object ovalue = null;
|
|
if (optDef.hasFactory()) {
|
|
ovalue = optDef.getFactory().getObject(value);
|
|
} else {
|
|
ovalue = PrimitiveParser.parse(value, optDef.getField().getType());
|
|
}
|
|
optionValues.put(optDef.getName(), ovalue);
|
|
try {
|
|
Field f = optDef.getField();
|
|
Object o = optDef.getOwner();
|
|
f.set(o, ovalue);
|
|
if (f.isAnnotationPresent(Options.class)) {
|
|
if (!optionsAnnotation(o, f, optDef.getPrefix(), optDef, false))
|
|
throw new TestBug("Unexpected (bug in framework?): optionsAnnotation returned null: " + optDef);
|
|
if (unconfiguredOptionsList.contains(optDef))
|
|
unconfiguredOptionsList.remove(optDef);
|
|
}
|
|
} catch (IllegalArgumentException e) {
|
|
throw new TestBug("Exception setting field value for option " + optDef.getName(), e);
|
|
} catch (IllegalAccessException e) {
|
|
throw new TestBug("Exception setting field value for option " + optDef.getName(), e);
|
|
}
|
|
}
|
|
|
|
private void processUnknownArg(String arg) {
|
|
if (unknownOptionHandler != null)
|
|
unknownOptionHandler.argument(arg);
|
|
else
|
|
throw new TestBug("Invalid argument: " + arg);
|
|
}
|
|
|
|
private void processUnknownOpt(String opt, String value) {
|
|
if (unknownOptionHandler != null)
|
|
unknownOptionHandler.option(opt, value);
|
|
else
|
|
throw new TestBug("Invalid option: '" + opt + "', value: '" + value + "'");
|
|
}
|
|
|
|
private void searchAnnotations(Object o, String prefix) {
|
|
Class<?> cl0 = o.getClass();
|
|
//log.debug("Looking for annotations for object " + o + ", class " + cl0);
|
|
List<Class> classes = new LinkedList<Class>();
|
|
while (cl0.getSuperclass() != null) {
|
|
classes.add(0, cl0); // Add to the beginning to ensure the option order is from superclass to subclass
|
|
cl0 = cl0.getSuperclass();
|
|
}
|
|
for (Class<?> cl : classes) {
|
|
for (Field f : cl.getDeclaredFields()) {
|
|
OptionDefinition optDef = null;
|
|
if (f.isAnnotationPresent(Option.class)) {
|
|
optDef = optionAnnotation(o, f, prefix);
|
|
if (optDef != null) {
|
|
unconfiguredOptionList.add(optDef);
|
|
}
|
|
}
|
|
if (f.isAnnotationPresent(Options.class)) {
|
|
if (!optionsAnnotation(o, f, prefix, optDef, false)) {
|
|
if (!unconfiguredOptionsList.contains(optDef))
|
|
unconfiguredOptionsList.add(optDef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean optionsAnnotation(Object o, Field f, String prefix, OptionDefinition optDef, boolean useDefault) {
|
|
if (Modifier.isStatic(f.getModifiers()))
|
|
throw new OptionError("@Options annotation is not allowed at static field", optDef);
|
|
if (!Object.class.isAssignableFrom(f.getDeclaringClass()))
|
|
throw new OptionError("@Options annotation is only allowed on object types", optDef);
|
|
//log.debug("Processing @Options annotation: object " + o + ", field " + f + ", prefix " + prefix);
|
|
Object v = null;
|
|
try {
|
|
f.setAccessible(true);
|
|
v = f.get(o);
|
|
} catch (IllegalAccessException e) {
|
|
throw new OptionError("Exception getting value of field ", e, optDef);
|
|
}
|
|
if (v == null) {
|
|
if (optDef == null)
|
|
throw new OptionError("Value of field is null and no @Option annotation is present", optDef);
|
|
if (!optDef.hasFactory())
|
|
throw new OptionError("Value of field is null and no @Option annotation does not have factory", optDef);
|
|
if (useDefault) {
|
|
setOptionValue(optDef, optDef.getDefaultValue());
|
|
try {
|
|
v = f.get(o);
|
|
} catch (IllegalAccessException e) {
|
|
throw new OptionError("Exception getting value of field ", e, optDef);
|
|
}
|
|
}
|
|
if (v == null) {
|
|
// We cannot setup it right away, so it is stored until value is set
|
|
return false;
|
|
} else
|
|
return true; // setOption Value already searched annotations
|
|
}
|
|
Options opts = f.getAnnotation(Options.class);
|
|
String vprefix = opts.prefix();
|
|
if (vprefix.equals(Options.defPrefix))
|
|
vprefix = null;
|
|
if (vprefix != null) {
|
|
if (prefix != null)
|
|
prefix = prefix + "." + vprefix;
|
|
else
|
|
prefix = vprefix;
|
|
}
|
|
searchAnnotations(v, prefix);
|
|
return true;
|
|
}
|
|
|
|
private OptionDefinition optionAnnotation(Object o, Field f, String prefix) {
|
|
//log.debug("Processing @Option annotation: object " + o + ", field " + f + ", prefix " + prefix);
|
|
f.setAccessible(true);
|
|
Option opt = f.getAnnotation(Option.class);
|
|
String name = opt.name();
|
|
if (name.equals(Option.defName))
|
|
name = f.getName(); // option name defaults to field name
|
|
if (prefix != null)
|
|
name = prefix + "." + name;
|
|
if (optionDefs.containsKey(name))
|
|
throw new TestBug("Option is already defined: " + name);
|
|
String defaultValue = opt.default_value();
|
|
if (defaultValue.equals(Option.defDefaultValue))
|
|
defaultValue = null; // default value defaults to null
|
|
String description = opt.description();
|
|
if (description.equals(Option.defDescription))
|
|
description = null;
|
|
if (description == null) {
|
|
if (name.equals("log") || name.endsWith(".log")) {
|
|
try {
|
|
f.set(o, log);
|
|
} catch (IllegalAccessException e) {
|
|
throw new TestBug("Exception setting log field of " + o, e);
|
|
}
|
|
return null;
|
|
} else
|
|
throw new TestBug("@Option annotation should always have description set: " + name + ", field: " + f);
|
|
}
|
|
Class<? extends OptionObjectFactory> factory = opt.factory();
|
|
//log.debug("Factory: " + factory);
|
|
if (factory.equals(OptionObjectFactory.class))
|
|
factory = null;
|
|
OptionDefinition optDef = new OptionDefinition(
|
|
prefix,
|
|
name,
|
|
description,
|
|
defaultValue,
|
|
factory,
|
|
f,
|
|
o
|
|
);
|
|
optionDefs.put(name, optDef);
|
|
//log.debug("Added option definition: " + optDef);
|
|
return optDef;
|
|
}
|
|
|
|
private void printHelp() {
|
|
PrintStream out = System.out;
|
|
out.println(" Supported options:");
|
|
out.println(" -help");
|
|
out.println(" Show this help screen");
|
|
for (Map.Entry<String, OptionDefinition> entry : optionDefs.entrySet()) {
|
|
String opt = entry.getKey();
|
|
OptionDefinition optDef = entry.getValue();
|
|
out.println(" -" + opt + " <" + optDef.getPlaceHolder() + ">");
|
|
out.print(" " + optDef.getDescription() + " ");
|
|
if (optDef.getDefaultValue() != null) {
|
|
out.println("(default: " + optDef.getDefaultValue() + ")");
|
|
} else {
|
|
out.println("(mandatory)");
|
|
}
|
|
if (optDef.hasFactory()) {
|
|
OptionObjectFactory factory = optDef.getFactory();
|
|
for (String key : factory.getPossibleValues()) {
|
|
out.println(" " + key + ": " + factory.getParameterDescription(key));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|