forked from SteamWar/SteamWar
Initial copy from other repository (nothing is fixed here)
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.command;
|
||||
|
||||
import de.steamwar.command.graph.NodeData;
|
||||
import de.steamwar.command.graph.RootNode;
|
||||
import de.steamwar.command.utils.Pair;
|
||||
import de.steamwar.command.utils.Triple;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class AbstractCommand<T> {
|
||||
|
||||
private static final Map<Class<? extends AbstractCommand<?>>, Map<Class<?>, Function<?, ?>>> EXECUTOR_MAPPER = new HashMap<>();
|
||||
|
||||
public static <E, T> void addExecutorMapper(Class<? extends AbstractCommand<E>> wrapper, Class<T> executorType, Function<E, T> mapper) {
|
||||
EXECUTOR_MAPPER.computeIfAbsent(wrapper, __ -> new HashMap<>())
|
||||
.putIfAbsent(executorType, mapper);
|
||||
}
|
||||
|
||||
protected final String command;
|
||||
protected final String permission;
|
||||
private final Consumer<CommandLoader<T>> initializer;
|
||||
protected final String[] aliases;
|
||||
protected final List<String> descriptions = new ArrayList<>();
|
||||
|
||||
private boolean initialized = false;
|
||||
protected final RootNode<T> rootNode = new RootNode<>();
|
||||
|
||||
private final Map<String, AbstractTypeMapper<T, ?>> localTypeMapper = new HashMap<>();
|
||||
private final Map<String, AbstractTypeValidator<T, ?>> localValidators = new HashMap<>();
|
||||
private final Map<String, AbstractTypeSupplier<T, ?>> localSupplier = new HashMap<>();
|
||||
|
||||
protected AbstractCommand(String command, Consumer<CommandLoader<T>> initializer, String... aliases) {
|
||||
this(command, null, initializer, aliases);
|
||||
}
|
||||
|
||||
protected AbstractCommand(String command, String permission, Consumer<CommandLoader<T>> initializer, String... aliases) {
|
||||
this.command = command;
|
||||
this.permission = permission;
|
||||
this.initializer = initializer;
|
||||
this.aliases = aliases;
|
||||
initCommand();
|
||||
unregister();
|
||||
register();
|
||||
}
|
||||
|
||||
protected synchronized void initCommand() {
|
||||
}
|
||||
|
||||
public abstract void unregister();
|
||||
|
||||
public abstract void register();
|
||||
|
||||
protected void commandSystemError(T sender, CommandFrameworkException e) {
|
||||
Logger.getGlobal().log(Level.WARNING, "An unexpected error occurred", e);
|
||||
}
|
||||
|
||||
protected void sendMessage(T sender, String message, Object[] args) {
|
||||
}
|
||||
|
||||
public final void execute(T sender, String alias, String[] args) {
|
||||
initialize();
|
||||
List<Runnable> errors = new ArrayList<>();
|
||||
try {
|
||||
boolean executed = rootNode.execute(sender, new NodeData(alias, fixArgs(args), (s, args1) -> {
|
||||
errors.add(() -> sendMessage(sender, s, args1));
|
||||
}));
|
||||
if (executed) return;
|
||||
if (!errors.isEmpty()) {
|
||||
errors.forEach(Runnable::run);
|
||||
return;
|
||||
}
|
||||
descriptions.forEach(s -> sendMessage(sender, s, new Object[0]));
|
||||
} catch (CommandFrameworkException e) {
|
||||
commandSystemError(sender, e);
|
||||
}
|
||||
}
|
||||
|
||||
public final List<String> tabComplete(T sender, String alias, String[] args) throws IllegalArgumentException {
|
||||
initialize();
|
||||
List<Collection<String>> tabCompletes = new ArrayList<>();
|
||||
rootNode.tabComplete(sender, new NodeData(alias, fixArgs(args), (s, args1) -> {}), tabCompletes);
|
||||
return tabCompletes.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String[] fixArgs(String[] args) {
|
||||
int removedCount = 0;
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String arg = args[i];
|
||||
if (arg.isEmpty() && i != args.length - 1) {
|
||||
removedCount++;
|
||||
continue;
|
||||
}
|
||||
args[i] = args[i - removedCount];
|
||||
}
|
||||
if (removedCount != 0) {
|
||||
args = Arrays.copyOf(args, args.length - removedCount);
|
||||
}
|
||||
if (args.length == 0) {
|
||||
return new String[]{""};
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private synchronized void initialize() {
|
||||
if (initialized) return;
|
||||
|
||||
List<Object> commandObjects = new ArrayList<>();
|
||||
initializer.accept(new CommandLoader<>() {
|
||||
@Override
|
||||
public AbstractCommand<T> get() {
|
||||
return AbstractCommand.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Object command) {
|
||||
commandObjects.add(command);
|
||||
}
|
||||
});
|
||||
|
||||
List<Pair<DataImpl, Method>> methods = new ArrayList<>();
|
||||
for (Object commandObject : commandObjects) {
|
||||
DataImpl dataImpl = new DataImpl(this, commandObject, rootNode, EXECUTOR_MAPPER.getOrDefault(this.getClass(), Collections.emptyMap()), (Map) localTypeMapper, CommandUtils.MAPPER_FUNCTIONS, (Map) localValidators, CommandUtils.VALIDATOR_FUNCTIONS, (Map) localSupplier, CommandUtils.SUPPLIER_FUNCTIONS);
|
||||
methods(commandObject).stream()
|
||||
.filter(method -> Arrays.stream(method.getAnnotations()).anyMatch(anno -> anno.annotationType().isAnnotationPresent(Handler.Implementation.class)))
|
||||
.forEach(method -> methods.add(new Pair<>(dataImpl, method)));
|
||||
}
|
||||
|
||||
Map<Integer, Map<Triple<DataImpl, Method, Annotation>, List<Handler.HandlerMethod<Annotation>>>> handlerMap = new HashMap<>();
|
||||
for (Pair<DataImpl, Method> commandMethod : methods) {
|
||||
List<Annotation> annotations = CommandUtils.getAnnotations(commandMethod.b);
|
||||
for (Annotation annotation : annotations) {
|
||||
Handler.Implementation handler = annotation.annotationType().getAnnotation(Handler.Implementation.class);
|
||||
Handler handlerObject;
|
||||
try {
|
||||
handlerObject = handler.value().getConstructor().newInstance();
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException("Handler " + handler.value().getName() + " cannot be used to check the argument validity", e);
|
||||
}
|
||||
if (!(handlerObject instanceof Handler.HandlerMethod)) {
|
||||
throw new UnsupportedOperationException("Handler " + handlerObject.getClass().getName() + " is not a HandlerMethod");
|
||||
}
|
||||
handlerMap.computeIfAbsent(((Handler.HandlerMethod<?>) handlerObject).getRunPriority(), k -> new LinkedHashMap<>())
|
||||
.computeIfAbsent(new Triple<>(commandMethod.a, commandMethod.b, annotation), k -> new ArrayList<>())
|
||||
.add((Handler.HandlerMethod<Annotation>) handlerObject);
|
||||
}
|
||||
}
|
||||
|
||||
List<Integer> runPriorities = new ArrayList<>(handlerMap.keySet());
|
||||
runPriorities.sort(Comparator.naturalOrder());
|
||||
for (int runPriority : runPriorities) {
|
||||
handlerMap.get(runPriority).forEach((dataMethodAnnotationTriple, handlerMethods) -> {
|
||||
handlerMethods.forEach(annotationHandlerMethod -> {
|
||||
try {
|
||||
annotationHandlerMethod.check(dataMethodAnnotationTriple.c, dataMethodAnnotationTriple.b, dataMethodAnnotationTriple.a);
|
||||
} catch (Exception e) {
|
||||
throw new UnsupportedOperationException("Method check failed for " + dataMethodAnnotationTriple.b.getName(), e);
|
||||
}
|
||||
annotationHandlerMethod.run(dataMethodAnnotationTriple.c, dataMethodAnnotationTriple.b, dataMethodAnnotationTriple.a);
|
||||
});
|
||||
});
|
||||
}
|
||||
rootNode.sort();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// TODO: Implement this when Message System is ready
|
||||
/*
|
||||
public void addDefaultHelpMessage(String message) {
|
||||
defaultHelpMessages.add(message);
|
||||
}
|
||||
*/
|
||||
|
||||
private List<Method> methods(Object commandObject) {
|
||||
List<Method> methods = new ArrayList<>();
|
||||
Class<?> current = commandObject.getClass();
|
||||
while (current != null) {
|
||||
methods.addAll(Arrays.asList(current.getDeclaredMethods()));
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user