package com.github.ompc.greys.core.command;
import com.github.ompc.greys.core.command.annotation.Cmd;
import com.github.ompc.greys.core.command.annotation.IndexArg;
import com.github.ompc.greys.core.command.annotation.NamedArg;
import com.github.ompc.greys.core.exception.CommandException;
import com.github.ompc.greys.core.exception.CommandInitializationException;
import com.github.ompc.greys.core.exception.CommandNotFoundException;
import com.github.ompc.greys.core.util.GaClassUtils;
import com.github.ompc.greys.core.util.GaReflectUtils;
import com.github.ompc.greys.core.util.GaStringUtils;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpecBuilder;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class Commands {
private final Map<String, Class<?>> commands = new HashMap<String, Class<?>>();
private Commands() {
for (final Class<?> clazz : GaClassUtils.scanPackage(Commands.class.getClassLoader(), "com.github.ompc.greys.core.command")) {
if (!Command.class.isAssignableFrom(clazz)
|| Modifier.isAbstract(clazz.getModifiers())) {
continue;
}
if (clazz.isAnnotationPresent(Cmd.class)) {
final Cmd cmd = clazz.getAnnotation(Cmd.class);
commands.put(cmd.name(), clazz);
}
}
}
/**
* 根据命令行所输入的内容构建一个命令
*
* @param line 命令行输入的一行命令
* @return 解析出的命令
* @throws CommandException 命令失败(不存在/初始化失败/参数校验等)
*/
public Command newCommand(String line) throws CommandException {
final String[] splitOfLine = GaStringUtils.splitForArgument(line);
final String cmdName = splitOfLine[0];
final Class<?> clazz = getInstance().commands.get(cmdName);
if (null == clazz) {
throw new CommandNotFoundException(cmdName);
}
final Command command;
try {
command = (Command) clazz.newInstance();
} catch (Throwable t) {
throw new CommandInitializationException(cmdName, t);
}
try {
final OptionSet opt = getOptionParser(clazz).parse(splitOfLine);
for (final Field field : clazz.getDeclaredFields()) {
// 处理命名参数
if (field.isAnnotationPresent(NamedArg.class)) {
final NamedArg arg = field.getAnnotation(NamedArg.class);
if (arg.hasValue()) {
if (opt.has(arg.name())) {
final boolean isCollection = field.getType().isAssignableFrom(Collection.class);
final Object value;
// 处理一参多值的情况
if (isCollection) {
value = opt.valuesOf(arg.name());
}
// 处理一参单值的情况
else {
// 待定的返回值,稍后可能用枚举值修正
Object valueOfUndetermined = opt.valueOf(arg.name());
//如果是枚举类型,则根据枚举信息赋值
if (field.getType().isEnum()) {
final Enum<?>[] enums = (Enum[]) field.getType().getEnumConstants();
if (enums != null) {
for (Enum<?> e : enums) {
if (e.name().equals(valueOfUndetermined)) {
valueOfUndetermined = e;
break;
}
}
}
}
value = valueOfUndetermined;
}
try {
GaReflectUtils.setValue(field, value, command);
} catch (IllegalArgumentException e) {
throw new CommandInitializationException(cmdName, e);
} catch (IllegalAccessException e) {
throw new CommandInitializationException(cmdName, e);
}
}
}
// 设置boolean类型,一般只有boolean类型hasValue才为false
else {
try {
GaReflectUtils.setValue(field, opt.has(arg.name()), command);
} catch (IllegalArgumentException e) {
throw new CommandInitializationException(cmdName, e);
} catch (IllegalAccessException e) {
throw new CommandInitializationException(cmdName, e);
}
}
}
// 处理顺序参数
else if (field.isAnnotationPresent(IndexArg.class)) {
final IndexArg arg = field.getAnnotation(IndexArg.class);
final int index = arg.index() + 1;
if (arg.isRequired()
&& opt.nonOptionArguments().size() <= index) {
throw new IllegalArgumentException(arg.name() + " argument was missing.");
}
if (opt.nonOptionArguments().size() > index) {
try {
GaReflectUtils.setValue(field, opt.nonOptionArguments().get(index), command);
} catch (IllegalArgumentException e) {
throw new CommandInitializationException(cmdName, e);
} catch (IllegalAccessException e) {
throw new CommandInitializationException(cmdName, e);
}
}
}
}//for
} catch (Throwable t) {
throw new CommandException(cmdName, t);
}
return command;
}
private static OptionParser getOptionParser(Class<?> clazz) {
final OptionParser parser = new OptionParser();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(NamedArg.class)) {
final NamedArg arg = field.getAnnotation(NamedArg.class);
final OptionSpecBuilder osb = parser.accepts(arg.name(), arg.summary());
if (arg.hasValue()) {
final boolean isCollection = field.getType().isAssignableFrom(Collection.class);
if (isCollection) {
osb.withOptionalArg();
} else {
osb.withOptionalArg().ofType(field.getType());
}
}
}
}
return parser;
}
/**
* 列出所有精简命令
*
* @return 返回当前版本所支持的精简命令集合
*/
public Map<String, Class<?>> listCommands() {
return new HashMap<String, Class<?>>(commands);
}
private static final Commands instance = new Commands();
public static synchronized Commands getInstance() {
return instance;
}
}