package io.vertx.core.cli.annotations;
import io.vertx.core.cli.*;
import io.vertx.core.cli.impl.DefaultCLI;
import io.vertx.core.cli.impl.ReflectionUtils;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class CLIConfigurator {
public static CLI define(Class<?> clazz) {
CLI cli = new DefaultCLI();
final Summary summary = clazz.getAnnotation(Summary.class);
final Description desc = clazz.getAnnotation(Description.class);
final Hidden hidden = clazz.getAnnotation(Hidden.class);
final Name name = clazz.getAnnotation(Name.class);
if (name == null) {
throw new IllegalArgumentException("The command cannot be defined, the @Name annotation is missing.");
}
if (name.value().isEmpty()) {
throw new IllegalArgumentException("The command cannot be defined, the @Name value is empty or null.");
}
cli.setName(name.value());
cli.setPriority(name.priority());
if (summary != null) {
cli.setSummary(summary.value());
}
if (desc != null) {
cli.setDescription(desc.value());
}
if (hidden != null) {
cli.setHidden(true);
}
final List<Method> methods = ReflectionUtils.getSetterMethods(clazz);
for (Method method : methods) {
final Option option = method.getAnnotation(Option.class);
final Argument argument = method.getAnnotation(Argument.class);
if (option != null) {
cli.addOption(createOption(method));
}
if (argument != null) {
cli.addArgument(createArgument(method));
}
}
return cli;
}
@SuppressWarnings("unchecked")
private static io.vertx.core.cli.Option createOption(Method method) {
TypedOption opt = new TypedOption();
Option option = method.getAnnotation(Option.class);
opt.setLongName(option.longName())
.setShortName(option.shortName())
.setMultiValued(option.acceptMultipleValues())
.setSingleValued(option.acceptValue())
.setArgName(option.argName())
.setFlag(option.flag())
.setHelp(option.help())
.setRequired(option.required());
Description description = method.getAnnotation(Description.class);
if (description != null) {
opt.setDescription(description.value());
}
Hidden hidden = method.getAnnotation(Hidden.class);
if (hidden != null) {
opt.setHidden(true);
}
if (ReflectionUtils.isMultiple(method)) {
opt
.setType(ReflectionUtils.getComponentType(method.getParameters()[0]))
.setMultiValued(true);
} else {
final Class<?> type = method.getParameters()[0].getType();
opt.setType(type);
if (type != Boolean.TYPE && type != Boolean.class) {
opt.setSingleValued(true);
}
}
ConvertedBy convertedBy = method.getAnnotation(ConvertedBy.class);
if (convertedBy != null) {
opt.setConverter(ReflectionUtils.newInstance(convertedBy.value()));
}
ParsedAsList parsedAsList = method.getAnnotation(ParsedAsList.class);
if (parsedAsList != null) {
opt.setParsedAsList(true).setListSeparator(parsedAsList.separator());
}
DefaultValue defaultValue = method.getAnnotation(DefaultValue.class);
if (defaultValue != null) {
opt.setDefaultValue(defaultValue.value());
}
opt.ensureValidity();
return opt;
}
@SuppressWarnings("unchecked")
private static io.vertx.core.cli.Argument createArgument(Method method) {
TypedArgument arg = new TypedArgument();
Argument argument = method.getAnnotation(Argument.class);
arg.setIndex(argument.index());
arg.setArgName(argument.argName());
arg.setRequired(argument.required());
Description description = method.getAnnotation(Description.class);
if (description != null) {
arg.setDescription(description.value());
}
if (ReflectionUtils.isMultiple(method)) {
arg
.setType(ReflectionUtils.getComponentType(method.getParameters()[0]))
.setMultiValued(true);
} else {
final Class<?> type = method.getParameters()[0].getType();
arg.setType(type);
}
Hidden hidden = method.getAnnotation(Hidden.class);
if (hidden != null) {
arg.setHidden(true);
}
ConvertedBy convertedBy = method.getAnnotation(ConvertedBy.class);
if (convertedBy != null) {
arg.setConverter(ReflectionUtils.newInstance(convertedBy.value()));
}
DefaultValue defaultValue = method.getAnnotation(DefaultValue.class);
if (defaultValue != null) {
arg.setDefaultValue(defaultValue.value());
}
return arg;
}
private static Object getOptionValue(Method method, String name, CommandLine commandLine) {
final io.vertx.core.cli.Option option = commandLine.cli().getOption(name);
if (option == null) {
return null;
}
boolean multiple = ReflectionUtils.isMultiple(method);
if (multiple) {
return createMultiValueContainer(method, commandLine.getOptionValues(name));
}
return commandLine.getOptionValue(name);
}
private static Object getArgumentValue(Method method, int index, CommandLine commandLine) {
final io.vertx.core.cli.Argument argument = commandLine.cli().getArgument(index);
if (argument == null) {
return null;
}
boolean multiple = ReflectionUtils.isMultiple(method);
if (multiple) {
return createMultiValueContainer(method, commandLine.getArgumentValues(argument.getIndex()));
}
return commandLine.getArgumentValue(argument.getIndex());
}
public static void inject(CommandLine cli, Object object) throws CLIException {
final List<Method> methods = ReflectionUtils.getSetterMethods(object.getClass());
for (Method method : methods) {
Option option = method.getAnnotation(Option.class);
Argument argument = method.getAnnotation(Argument.class);
if (option != null) {
String name = option.longName();
if (name == null) {
name = option.shortName();
}
try {
Object injected = getOptionValue(method, name, cli);
if (injected != null) {
method.setAccessible(true);
method.invoke(object, injected);
}
} catch (Exception e) {
throw new CLIException("Cannot inject value for option '" + name + "'", e);
}
}
if (argument != null) {
int index = argument.index();
try {
Object injected = getArgumentValue(method, index, cli);
if (injected != null) {
method.setAccessible(true);
method.invoke(object, injected);
}
} catch (Exception e) {
throw new CLIException("Cannot inject value for argument '" + index + "'", e);
}
}
}
}
private static <T> Object createMultiValueContainer(Method setter, List<T> values) {
final Class<?> type = setter.getParameterTypes()[0];
if (type.isArray()) {
Object array = Array.newInstance(type.getComponentType(), values.size());
for (int i = 0; i < values.size(); i++) {
Array.set(array, i, values.get(i));
}
return array;
}
if (Set.class.isAssignableFrom(type)) {
return new LinkedHashSet<>(values);
}
if (List.class.isAssignableFrom(type) || Collection.class.isAssignableFrom(type)) {
return values;
}
return null;
}
}