io.vertx.core.cli.annotations.CLIConfigurator Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
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;
/**
* Class responsible for defining CLI using annotations and injecting values extracted by the parser.
*
* @author Clement Escoffier
*/
public class CLIConfigurator {
/**
* Creates an instance of the given class, and extracts the metadata from the given class.
*
* @param clazz the CLI class
* @return the defined CLI.
*/
public static CLI define(Class clazz) {
CLI cli = new DefaultCLI();
// Class annotations
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() == null || name.value().isEmpty()) {
throw new IllegalArgumentException("The command cannot be defined, the @Name value is empty or null.");
}
cli.setName(name.value());
if (summary != null) {
cli.setSummary(summary.value());
}
if (desc != null) {
cli.setDescription(desc.value());
}
if (hidden != null) {
cli.setHidden(true);
}
// Setter annotations
final List 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 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 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) {
// In the case of a boolean, it may be a flag, need explicit settings.
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());
}
// Default value
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 argument = method.getAnnotation(Argument.class);
arg.setIndex(argument.index());
arg.setArgName(argument.argName());
arg.setRequired(argument.required());
// Description
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()));
}
// Default 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());
}
/**
* Injects the value in the annotated setter methods ({@link Option} and {@link Argument}.
*
* @param cli the cli
* @param object the object to be injected
* @throws CLIException if an injection issue occurred.
*/
public static void inject(CommandLine cli, Object object) throws CLIException {
final List 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 Object createMultiValueContainer(Method setter, List 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;
}
}