From 80fa47685f13d4d2baeb05b90b0f1c247a31dbd5 Mon Sep 17 00:00:00 2001 From: Aikar Date: Fri, 5 May 2017 21:57:15 -0400 Subject: [PATCH] supper inner classes and parent up subcommand bases - Resolves #20 --- .../java/co/aikar/acfexample/SomeCommand.java | 34 +++++++++- .../java/co/aikar/commands/BaseCommand.java | 64 +++++++++++++++++-- .../co/aikar/commands/BaseSubCommand.java | 53 +++++++++++++++ 3 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 src/main/java/co/aikar/commands/BaseSubCommand.java diff --git a/example/src/main/java/co/aikar/acfexample/SomeCommand.java b/example/src/main/java/co/aikar/acfexample/SomeCommand.java index 7c4b24a4..92acd701 100644 --- a/example/src/main/java/co/aikar/acfexample/SomeCommand.java +++ b/example/src/main/java/co/aikar/acfexample/SomeCommand.java @@ -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); + } + } + } } diff --git a/src/main/java/co/aikar/commands/BaseCommand.java b/src/main/java/co/aikar/commands/BaseCommand.java index 948afd71..c9662210 100644 --- a/src/main/java/co/aikar/commands/BaseCommand.java +++ b/src/main/java/co/aikar/commands/BaseCommand.java @@ -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 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 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) { diff --git a/src/main/java/co/aikar/commands/BaseSubCommand.java b/src/main/java/co/aikar/commands/BaseSubCommand.java new file mode 100644 index 00000000..01f8ef72 --- /dev/null +++ b/src/main/java/co/aikar/commands/BaseSubCommand.java @@ -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(); + } +}