supper inner classes and parent up subcommand bases - Resolves #20

This commit is contained in:
Aikar
2017-05-05 21:57:15 -04:00
parent 4e801ba012
commit 80fa47685f
3 changed files with 145 additions and 6 deletions
@@ -24,6 +24,7 @@
package co.aikar.acfexample;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.BaseSubCommand;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
@@ -38,7 +39,7 @@ import org.bukkit.entity.Player;
@CommandAlias("acf|somecommand|sc|somcom")
public class SomeCommand extends BaseCommand {
@Subcommand("test")
@Subcommand("test4")
@CommandAlias("acftest|acft")
public void onCommand(CommandSender sender, SomeObject someObject) {
sender.sendMessage("You got an object of type: " + someObject.getClass().getName() + " with a value of: " + someObject.getValue());
@@ -79,4 +80,35 @@ public class SomeCommand extends BaseCommand {
public void onTestSub1(CommandSender sender, String hi) {
sender.sendMessage(hi);
}
@Subcommand("test|txt|tfoo")
public class Test extends BaseSubCommand {
@Subcommand("test1|td1")
@CommandCompletion("FOO")
public void onTest1(Player player, String testX) {
player.sendMessage("You got test inner test1: " + testX);
}
@Subcommand("test2|td2")
@CommandCompletion("BAR")
public void onTest2(Player player, String testY) {
player.sendMessage("You got test inner test2: " + testY);
}
@Subcommand("next")
public class TestInner extends BaseSubCommand {
@Subcommand("test3|td4")
@CommandCompletion("FOO")
public void onTest1(Player player, String testX) {
player.sendMessage("You got test inner inner test3: " + testX);
}
@CommandAlias("deepinner")
@Subcommand("test4|td4")
@CommandCompletion("BAR")
public void onTest2(Player player, String testY) {
player.sendMessage("You got test inner inner test4: " + testY);
}
}
}
}
@@ -40,8 +40,11 @@ import org.bukkit.command.CommandSender;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.util.StringUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -52,9 +55,10 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@SuppressWarnings("unused")
public abstract class BaseCommand extends Command {
public class BaseCommand extends Command {
public static final String UNKNOWN = "__unknown";
public static final String DEFAULT = "__default";
@@ -102,10 +106,12 @@ public abstract class BaseCommand extends Command {
}
void onRegister(CommandManager manager) {
onRegister(manager, getName());
}
void onRegister(CommandManager manager, String cmd) {
this.manager = manager;
final Class<? extends BaseCommand> self = this.getClass();
CommandAlias rootCmdAlias = self.getAnnotation(CommandAlias.class);
String cmd = this.getName();
if (cmd == null) {
if (rootCmdAlias == null) {
cmd = "__" + self.getSimpleName();
@@ -141,7 +147,7 @@ public abstract class BaseCommand extends Command {
for (Method method : self.getDeclaredMethods()) {
method.setAccessible(true);
String sublist = null;
final Subcommand sub = method.getAnnotation(Subcommand.class);
String sub = getSubcommandValue(method);
final Default def = method.getAnnotation(Default.class);
final CommandAlias commandAliases = method.getAnnotation(CommandAlias.class);
@@ -155,7 +161,7 @@ public abstract class BaseCommand extends Command {
}
}
if (sub != null) {
sublist = sub.value();
sublist = sub;
} else if (commandAliases != null) {
sublist = commandAliases.value();
}
@@ -185,7 +191,55 @@ public abstract class BaseCommand extends Command {
}
}
register(getName(), this);
register(cmd, this);
for (Class<?> clazz : this.getClass().getDeclaredClasses()) {
if (BaseSubCommand.class.isAssignableFrom(clazz)) {
try {
BaseSubCommand subCommand = null;
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
declaredConstructor.setAccessible(true);
Parameter[] parameters = declaredConstructor.getParameters();
if (parameters.length == 1) {
subCommand = (BaseSubCommand) declaredConstructor.newInstance(this);
} else {
ACFLog.info("Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining(", ")) + ")");
}
}
if (subCommand != null) {
subCommand.setParentCommand(this);
subCommand.onRegister(manager, cmd);
this.subCommands.putAll(subCommand.subCommands);
this.registeredCommands.putAll(subCommand.registeredCommands);
} else {
ACFLog.severe("Could not find a subcommand ctor for " + clazz.getName());
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
private String getSubcommandValue(Method method) {
final Subcommand sub = method.getAnnotation(Subcommand.class);
if (sub == null) {
return null;
}
List<String> subList = new ArrayList<>();
subList.add(sub.value());
Class<?> clazz = method.getDeclaringClass();
while (clazz != null) {
Subcommand classSub = clazz.getAnnotation(Subcommand.class);
if (classSub != null) {
subList.add(classSub.value());
}
clazz = clazz.getEnclosingClass();
}
Collections.reverse(subList);
return ACFUtil.join(subList, " ");
}
private void register(String name, BaseCommand cmd) {
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package co.aikar.commands;
public class BaseSubCommand extends BaseCommand {
private BaseCommand parentCommand;
public BaseSubCommand() {}
void setParentCommand(BaseCommand command) {
this.parentCommand = command;
}
@Override
public String getName() {
return parentCommand.getName();
}
@Override
public String getLabel() {
return parentCommand.getLabel();
}
@Override
public String getDescription() {
return parentCommand.getDescription();
}
@Override
public String getUsage() {
return parentCommand.getUsage();
}
}