Skip to content

Instantly share code, notes, and snippets.

@SirYwell
Last active November 9, 2019 08:33
Show Gist options
  • Save SirYwell/f520bfcae0afe367037b8d1e4fe1cbe3 to your computer and use it in GitHub Desktop.
Save SirYwell/f520bfcae0afe367037b8d1e4fe1cbe3 to your computer and use it in GitHub Desktop.
Raw draft oft Commandry
package de.eldoria.commandry;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Command {
String value();
String parent() default "";
}
package de.eldoria.commandry;
public interface CommandResult {
}
package de.eldoria.commandry;
import de.eldoria.commandry.parser.Parsers;
import de.eldoria.commandry.tree.CommandNode;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
public class Commandry {
private static CommandNode root = new CommandNode();
public static void runCommand(String command) {
StringReader reader = new StringReader(command);
run(reader, new Object[0], root);
}
private static void run(StringReader reader, Object[] params, CommandNode parent) {
String cmd = reader.readWord();
if (parent.children.containsKey(cmd)) {
var node = parent.children.get(cmd);
Parameter[] parameters = node.method.getParameters();
Object[] paramValues = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
if (params.length > i) {
paramValues[i] = params[i];
continue; // copy params of old method
}
if (!reader.canRead()) {
System.out.println("Command usage...");
return;
}
paramValues[i] = Parsers.parse(reader.readWord(), parameter.getType());
}
if (!reader.canRead()) {
try {
node.method.invoke(node.commandObject, paramValues);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
} else {
run(reader, paramValues, node);
}
}
}
public static <T> void registerCommand(Class<T> clazz) {
LinkedList<Method> methods = new LinkedList<>(findMethods(clazz));
System.out.println(methods);
while (!methods.isEmpty()) {
Method method = methods.getFirst();
Command command = method.getAnnotation(Command.class);
CommandNode node = new CommandNode();
node.commandObject = createObject(clazz);
node.name = command.value();
node.method = method;
if (command.parent().isEmpty()) {
System.out.println("root command added " + command.value());
root.children.put(command.value(), node);
methods.poll();
} else {
if (root.children.containsKey(command.parent())) {
root.children.get(command.parent()).children.put(command.value(), node);
methods.poll();
}
}
}
}
private static Object createObject(Class<?> clazz) {
try {
return clazz.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
private static <T> List<Method> findMethods(Class<T> clazz) {
Method[] methods = clazz.getMethods();
return Arrays.stream(methods)
.filter(method -> {
Command annotation = method.getAnnotation(Command.class);
return annotation != null;
})
.sorted((m1, m2) -> {
Command a1 = m1.getAnnotation(Command.class);
Command a2 = m2.getAnnotation(Command.class);
int c = a1.parent().compareTo(a2.parent());
return c != 0 ? c : a1.value().compareTo(a2.value());
})
.collect(Collectors.toList());
}
}
package de.eldoria.commandry.parser;
public interface Parser<T> {
T parse(CharSequence sequence);
}
package de.eldoria.commandry.parser;
import java.util.HashMap;
import java.util.Map;
public final class Parsers {
private static Map<Class<?>, Parser<?>> parserMap = new HashMap<>();
static {
parserMap.put(int.class, (Parser<Integer>) sequence -> Integer.parseInt(sequence.toString()));
parserMap.put(String.class, CharSequence::toString);
}
public static <T> T parse(String input, Class<T> clazz) {
return (T) parserMap.get(clazz).parse(input);
}
}
package de.eldoria.commandry;
public class StringReader {
private final String string;
private final int length;
private int position;
private int mark;
public StringReader(String string) {
this.string = string;
this.length = string.length();
this.position = 0;
this.mark = 0;
}
public String readWord() {
if (!canRead()) {
throw new StringIndexOutOfBoundsException(position);
}
while (position < length && peekChar() == ' ') { // ignore leading spaces
position++;
}
int start = position;
while (position < length && peekChar() != ' ') {
position++;
}
int end = position;
if (position < length) {
position++; // skip space
}
return string.substring(start, end);
}
public String readRemaining() {
if (position == 0) {
return string;
}
if (!canRead()) {
throw new StringIndexOutOfBoundsException(position);
}
int start = position;
position = length;
return string.substring(start);
}
public boolean canRead() {
return position < length;
}
public void reset() {
this.position = mark;
}
private char peekChar() {
return string.charAt(position);
}
}
package de.eldoria.commandry;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Commandry.registerCommand(Test.class);
Scanner scanner = new Scanner(System.in);
while (true) {
Commandry.runCommand(scanner.nextLine());
}
}
@Command("user")
public void myCommand(String user) {
System.out.println("Hi " + user);
}
@Command(value = "age", parent = "user")
public void second(String user, int age) {
System.out.println(user + " is " + age + " years old.");
}
@Command("userage")
public void third(String user, int age) {
System.out.println(user + " is " + age + " years old.");
}
}
package de.eldoria.commandry.tree;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class CommandNode {
public String name;
public Method method;
public Object commandObject;
public Map<String, CommandNode> children = new HashMap<>();
}

TODO

  • l10n
  • generating help messages
  • different arguments for one command (like teleport <playerTo> AND teleport <playerFrom> <playerTo>
  • context (for command issuer, language settings etc.)
  • aliases
  • async support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment