this.function = function;
this.scope = scope;
this.name = name;
// Command name completer
String[] names = scoped ? new String[] { name } : new String[] { name, scope + ":" + name };
commandCompleter = new StringsCompleter(names);
// Build options completer
for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
for (Field field : type.getDeclaredFields()) {
Option option = field.getAnnotation(Option.class);
if (option != null) {
fields.put(option, field);
options.put(option.name(), option);
String[] aliases = option.aliases();
if (aliases != null) {
for (String alias : aliases) {
options.put(alias, option);
}
}
}
Argument argument = field.getAnnotation(Argument.class);
if (argument != null) {
Integer key = argument.index();
if (arguments.containsKey(key)) {
LOGGER.warn("Duplicate @Argument annotations on class " + type.getName() + " for index: " + key + " see: " + field);
} else {
arguments.put(key, field);
}
}
}
}
// options.put(HelpOption.HELP.name(), HelpOption.HELP);
optionsCompleter = new StringsCompleter(options.keySet());
// Build arguments completers
argsCompleters = new ArrayList<Completer>();
if (function instanceof CompletableFunction) {
Map<String, Completer> opt;
try {
//
opt = ((CompletableFunction) function).getOptionalCompleters();
} catch (Throwable t) {
opt = new HashMap<String, Completer>();
}
optionalCompleters = opt;
List<Completer> fcl = ((CompletableFunction) function).getCompleters();
if (fcl != null) {
for (Completer c : fcl) {
argsCompleters.add(c == null ? NullCompleter.INSTANCE : c);
}
} else {
argsCompleters.add(NullCompleter.INSTANCE);
}
} else {
optionalCompleters = new HashMap<String, Completer>();
final Map<Integer, Method> methods = new HashMap<Integer, Method>();
for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
for (Method method : type.getDeclaredMethods()) {
CompleterValues completerMethod = method.getAnnotation(CompleterValues.class);
if (completerMethod != null) {
int index = completerMethod.index();
Integer key = index;
if (index >= arguments.size() || index < 0) {
LOGGER.warn("Index out of range on @CompleterValues on class " + type.getName() + " for index: " + key + " see: " + method);
}
if (methods.containsKey(key)) {
LOGGER.warn("Duplicate @CompleterMethod annotations on class " + type.getName() + " for index: " + key + " see: " + method);
} else {
methods.put(key, method);
}
}
}
}
for (int i = 0, size = arguments.size(); i < size; i++) {
Completer argCompleter = NullCompleter.INSTANCE;
Method method = methods.get(i);
if (method != null) {
// lets invoke the method
Action action = function.createNewAction();
try {
Object value = method.invoke(action);
if (value instanceof String[]) {
argCompleter = new StringsCompleter((String[]) value);
} else if (value instanceof Collection) {
argCompleter = new StringsCompleter((Collection<String>) value);
} else {
LOGGER.warn("Could not use value " + value + " as set of completions!");
}
} catch (IllegalAccessException e) {
LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + e, e);
} catch (InvocationTargetException e) {
Throwable target = e.getTargetException();
if (target == null) {
target = e;
}
LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + target, target);
} finally {
try {
function.releaseAction(action);
} catch (Exception e) {
LOGGER.warn("Failed to release action: " + action + ". " + e, e);
}
}
} else {
Field field = arguments.get(i);
Class<?> type = field.getType();
if (type.isAssignableFrom(File.class)) {
argCompleter = new FileCompleter(null);
} else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
argCompleter = new StringsCompleter(new String[] {"false", "true"}, false);
} else if (type.isAssignableFrom(Enum.class)) {
Set<String> values = new HashSet<String>();
for (Object o : EnumSet.allOf((Class<Enum>) type)) {
values.add(o.toString());
}
argCompleter = new StringsCompleter(values, false);
} else {
// TODO any other completers we can add?
}
}
argsCompleters.add(argCompleter);