From 80d27b488b134e3fe626fd759b9b0447790b4c69 Mon Sep 17 00:00:00 2001 From: NEMESIS13cz Date: Sun, 16 Oct 2016 10:26:15 +0200 Subject: Added "multiword" strings --- CmdMgrTest.java | 118 +- README.md | 8 +- com/nemez/cmdmgr/Command.java | 26 +- com/nemez/cmdmgr/CommandManager.java | 1235 +++++++++++---------- com/nemez/cmdmgr/component/ArgumentComponent.java | 24 +- com/nemez/cmdmgr/component/BooleanComponent.java | 50 +- com/nemez/cmdmgr/component/ByteComponent.java | 56 +- com/nemez/cmdmgr/component/ChainComponent.java | 100 +- com/nemez/cmdmgr/component/ConstantComponent.java | 66 +- com/nemez/cmdmgr/component/DoubleComponent.java | 56 +- com/nemez/cmdmgr/component/FloatComponent.java | 56 +- com/nemez/cmdmgr/component/ICommandComponent.java | 18 +- com/nemez/cmdmgr/component/IntegerComponent.java | 56 +- com/nemez/cmdmgr/component/LongComponent.java | 54 +- com/nemez/cmdmgr/component/OptionalComponent.java | 38 +- com/nemez/cmdmgr/component/ShortComponent.java | 54 +- com/nemez/cmdmgr/component/StringComponent.java | 40 +- com/nemez/cmdmgr/util/BranchStack.java | 66 +- com/nemez/cmdmgr/util/Executable.java | 802 ++++++------- com/nemez/cmdmgr/util/ExecutableDefinition.java | 275 +++-- com/nemez/cmdmgr/util/HelpPageCommand.java | 36 +- com/nemez/cmdmgr/util/Property.java | 14 +- com/nemez/cmdmgr/util/Type.java | 74 +- plugin.yml | 6 +- test.cmd | 68 +- 25 files changed, 1744 insertions(+), 1652 deletions(-) diff --git a/CmdMgrTest.java b/CmdMgrTest.java index d7593df..b6f1600 100644 --- a/CmdMgrTest.java +++ b/CmdMgrTest.java @@ -1,59 +1,59 @@ - -import java.io.File; - -import org.bukkit.command.CommandSender; -import org.bukkit.plugin.java.JavaPlugin; - -import com.nemez.cmdmgr.Command; -import com.nemez.cmdmgr.CommandManager; - -public class CmdMgrTest extends JavaPlugin { - - public void onEnable() { - CommandManager.registerCommand(new File("plugins/test.cmd"), this, this); - } - - public void onDisable() { - - } - - @Command(hook="home_set") - public boolean executeSetHome(CommandSender sender, String name) { - sender.sendMessage("You executed:"); - sender.sendMessage("/home set " + name); - return true; - } - - @Command(hook="home_set_coords") - public boolean executeSetHomeBasedOnCoords(CommandSender sender, String name, int x, int y, int z) { - sender.sendMessage("You executed:"); - sender.sendMessage("/home set " + name + " " + x + " " + y + " " + z); - return true; - } - - @Command(hook="home_del") - public void executeDelHome(CommandSender sender, String name, boolean aFlag) { - sender.sendMessage("You executed:"); - sender.sendMessage("/home del " + name); - sender.sendMessage("-a - " + aFlag); - } - - @Command(hook="home_list") - public void executeListHomes(CommandSender sender) { - sender.sendMessage("You executed:"); - sender.sendMessage("/home list"); - } - - @Command(hook="home_tp") - public void executeTeleportHome(CommandSender sender, String name) { - sender.sendMessage("You executed:"); - sender.sendMessage("/home " + name); - } - - @Command(hook="noskope") - public boolean executeMagik(CommandSender sender) { - sender.sendMessage("You executed:"); - sender.sendMessage("/home yolo swag"); - return true; - } -} + +import java.io.File; + +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.java.JavaPlugin; + +import com.nemez.cmdmgr.Command; +import com.nemez.cmdmgr.CommandManager; + +public class CmdMgrTest extends JavaPlugin { + + public void onEnable() { + CommandManager.registerCommand(new File("plugins/test.cmd"), this, this); + } + + public void onDisable() { + + } + + @Command(hook="home_set") + public boolean executeSetHome(CommandSender sender, String name) { + sender.sendMessage("You executed:"); + sender.sendMessage("/home set " + name); + return true; + } + + @Command(hook="home_set_coords") + public boolean executeSetHomeBasedOnCoords(CommandSender sender, String name, int x, int y, int z) { + sender.sendMessage("You executed:"); + sender.sendMessage("/home set " + name + " " + x + " " + y + " " + z); + return true; + } + + @Command(hook="home_del") + public void executeDelHome(CommandSender sender, String name, boolean aFlag) { + sender.sendMessage("You executed:"); + sender.sendMessage("/home del " + name); + sender.sendMessage("-a - " + aFlag); + } + + @Command(hook="home_list") + public void executeListHomes(CommandSender sender) { + sender.sendMessage("You executed:"); + sender.sendMessage("/home list"); + } + + @Command(hook="home_tp") + public void executeTeleportHome(CommandSender sender, String name) { + sender.sendMessage("You executed:"); + sender.sendMessage("/home " + name); + } + + @Command(hook="noskope") + public boolean executeMagik(CommandSender sender) { + sender.sendMessage("You executed!!!!!!!!!:"); + sender.sendMessage("/home yolo swag"); + return true; + } +} diff --git a/README.md b/README.md index 55fa42d..c811548 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CommandManager -Scripting-based Spigot/Bukkit command manager - -Feel free to use and PR enhancements/customizations! If you're a nice person, you can give me credit. +# CommandManager +Scripting-based Spigot/Bukkit command manager + +Feel free to use and PR enhancements/customizations! If you're a nice person, you can give me credit. diff --git a/com/nemez/cmdmgr/Command.java b/com/nemez/cmdmgr/Command.java index 3cb61df..ff8622c 100644 --- a/com/nemez/cmdmgr/Command.java +++ b/com/nemez/cmdmgr/Command.java @@ -1,13 +1,13 @@ -package com.nemez.cmdmgr; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Command { - - String hook(); -} +package com.nemez.cmdmgr; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Command { + + String hook(); +} diff --git a/com/nemez/cmdmgr/CommandManager.java b/com/nemez/cmdmgr/CommandManager.java index 0ae90d0..5c0578c 100644 --- a/com/nemez/cmdmgr/CommandManager.java +++ b/com/nemez/cmdmgr/CommandManager.java @@ -1,609 +1,626 @@ -package com.nemez.cmdmgr; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.logging.Level; - -import org.bukkit.plugin.java.JavaPlugin; - -import com.nemez.cmdmgr.component.ArgumentComponent; -import com.nemez.cmdmgr.component.BooleanComponent; -import com.nemez.cmdmgr.component.ByteComponent; -import com.nemez.cmdmgr.component.ChainComponent; -import com.nemez.cmdmgr.component.ConstantComponent; -import com.nemez.cmdmgr.component.DoubleComponent; -import com.nemez.cmdmgr.component.FloatComponent; -import com.nemez.cmdmgr.component.ICommandComponent; -import com.nemez.cmdmgr.component.IntegerComponent; -import com.nemez.cmdmgr.component.LongComponent; -import com.nemez.cmdmgr.component.OptionalComponent; -import com.nemez.cmdmgr.component.ShortComponent; -import com.nemez.cmdmgr.component.StringComponent; -import com.nemez.cmdmgr.util.BranchStack; -import com.nemez.cmdmgr.util.Executable; -import com.nemez.cmdmgr.util.HelpPageCommand; -import com.nemez.cmdmgr.util.Property; -import com.nemez.cmdmgr.util.Type; - -/** - * Example command.cmd - * - * command home: - * set [string:name]: - * run home_set name - * help Set a new home - * perm home.set - * del [string:name]: - * run home_del name - * help Delete home\n&CCannot be undone! - * perm home.del - * list: - * run home_list - * help Show all homes - * perm home.list - * [string:name]: - * run home_tp name - * help Teleport to specified home - * perm home.tp - * - * Generated in-game command structure: - * (will only show commands the user has permission to execute) - * - * /home set - * /home del - * /home list - * /home - * /home help - * - * Java code: - * - * @Command(hook="home_set") - * public void executeHomeSet(String name) { - * ... - * } - * - * @Command(hook="home_del") - * public void executeHomeDelete(String name) { - * ... - * } - * - * @Command(hook="home_list") - * public void executeHomeList() { - * ... - * } - * - * @Command(hook="home_tp") - * public void executeHomeTeleport(String name) { - * ... - * } - */ - -public class CommandManager { - - /* Debugging toggle to generate help pages with types, e.g: str, i8, i32, fp64, etc. */ - public static boolean debugHelpMenu = false; - /* Internal boolean to keep track of errors during resolving */ - public static boolean errors = false; - - /* Switches for color and formatting in the built-in help message and pagination text */ - public static String helpDescriptionFormatting = "§b"; - public static String helpUsageFormatting = "§6"; - public static String helpPageHeaderFormatting = "§a"; - public static String helpInvalidPageFormatting = "§c"; - public static String noPermissionFormatting = "§c"; - public static String notAllowedFormatting = "§c"; - - /** - * Registers a command from a String of source code - * - * @param cmdSourceCode source code - * @param commandHandler instance of a class where your java functions are located - * @param plugin your plugin class - * @return success - if command was processed and registered successfully - */ - public static boolean registerCommand(String cmdSourceCode, Object commandHandler, JavaPlugin plugin) { - if (cmdSourceCode == null || commandHandler == null || plugin == null) { - return false; - } - /* get the class definition of the 'commandHandler' and get its functions */ - Method[] methods = commandHandler.getClass().getMethods(); - ArrayList finalMethods = new ArrayList(); - - /* extract all the functions annotated with @Command that are not static */ - for (Method m : methods) { - if (m.getAnnotationsByType(Command.class).length > 0 && (m.getModifiers() & Modifier.STATIC) == 0) { - finalMethods.add(m); - } - } - return parse(cmdSourceCode, finalMethods, plugin, commandHandler); - } - - /** - * Registers a command from a source File - * - * @param sourceFile file containing source code - * @param commandHandler instance of a class where your java functions are located - * @param plugin your plugin class - * @return success - if command was processed and registered successfully - */ - public static boolean registerCommand(File sourceFile, Object commandHandler, JavaPlugin plugin) { - StringBuilder src = new StringBuilder(); - String buf = ""; - try { - BufferedReader reader = new BufferedReader(new FileReader(sourceFile)); - while ((buf = reader.readLine()) != null) { - src.append(buf); - } - reader.close(); - } catch (Exception e) { - plugin.getLogger().log(Level.WARNING, "Error while loading command file. (" + sourceFile.getAbsolutePath() + ")"); - plugin.getLogger().log(Level.WARNING, e.getCause().toString()); - errors = true; - return false; - } - return registerCommand(src.toString(), commandHandler, plugin); - } - - /** - * Registers a command from an InputStream - * - * @param sourceStream input stream containing source code - * @param commandHandler instance of a class where your java functions are located - * @param plugin your plugin class - * @return success - if command was processed and registered successfully - */ - public static boolean registerCommand(InputStream sourceStream, Object commandHandler, JavaPlugin plugin) { - StringBuilder src = new StringBuilder(); - String buf = ""; - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(sourceStream)); - while ((buf = reader.readLine()) != null) { - src.append(buf); - } - reader.close(); - } catch (Exception e) { - plugin.getLogger().log(Level.WARNING, "Error while loading command file. (" + sourceStream.toString() + ")"); - plugin.getLogger().log(Level.WARNING, e.getCause().toString()); - errors = true; - return false; - } - return registerCommand(src.toString(), commandHandler, plugin); - } - - /** - * Parses the source code into an abstract command syntax - * - * @param source String containing command source code - * @param methods ArrayList of methods gathered from the plugin's handler class - * @param plugin plugin to register commands as - * @param methodContainer class containing method handles - * @return success - if command parsing and registration was successful - */ - private static boolean parse(String source, ArrayList methods, JavaPlugin plugin, Object methodContainer) { - /* source code */ - char[] chars = source.toCharArray(); - /* temporary buffer */ - StringBuilder buffer = new StringBuilder(); - /* name of the command we are parsing */ - String cmdName = null; - /* if we are currently gathering chars from a type definition */ - boolean insideType = false; - /* if we are currently gathering chars from the command name */ - boolean gettingName = false; - /* the previous char, used for backslash escaping */ - char previous = '\0'; - /* the current 'array' of sub-commands we are parsing */ - ChainComponent currentChain = new ChainComponent(); - /* storage for the current sub-command if it branches into more sub-commands */ - BranchStack stack = new BranchStack(); - /* the current argument we are parsing */ - ArgumentComponent currentArgComp = null; - /* the current property of the sub-command we are getting */ - Property currentProp = Property.NONE; - /* a counter for how deep in the stack we are, used to figure out if we exited a sub-command or the master command */ - int bracketCounter = 0; - /* line counter */ - int line = 0; - - /* iterate over all characters */ - for (int i = 0; i < chars.length; i++) { - /* get current char */ - char current = chars[i]; - - /* increment line counter */ - if (current == '\n') { - line++; - } - - /* current is a colon, we just switched from arguments 'type' to its 'name' */ - /* */ - /* ^ */ - if (current == ':') { - /* are we inside a type? */ - if (insideType) { - /* are we already defining an argument? */ - if (currentArgComp != null) { - /* yes, we are, throw an error */ - plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": Already defining a type."); - errors = true; - return false; - }else{ - /* okay, resolved what type this is */ - currentArgComp = resolveComponentType(buffer.toString()); - buffer = new StringBuilder(); - /* type didn't fit any definition, throw an error */ - if (currentArgComp == null) { - plugin.getLogger().log(Level.WARNING, "Type error at line " + line + ": Invalid type."); - errors = true; - return false; - } - } - }else{ - /* not inside a type, probably just a string in the help property */ - buffer.append(':'); - } - /* current is a semicolon, we just finished a property line */ - /* help this is an example; */ - /* ^ */ - }else if (current == ';') { - /* semicolon is bashslash escaped, treat it as a normal character */ - if (previous == '\\') { - buffer.append(';'); - }else{ - /* there is nothing on the stack, we are defining properties of 'nothing', throw an error */ - if (stack.get() == null) { - plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": Not in code section."); - errors = true; - return false; - } - /* we are defining the 'help' property, set it to what we just gathered */ - if (currentProp == Property.HELP) { - stack.get().help = buffer.toString(); - /* same as above, except its the function to run */ - }else if (currentProp == Property.EXECUTE) { - stack.get().execute = buffer.toString(); - /* same again, but with the permission, and as that should not contain spaces, trim it */ - }else if (currentProp == Property.PERMISSION) { - stack.get().permission = buffer.toString().trim(); - /* execution type, check if its a valid one and set it */ - }else if (currentProp == Property.TYPE) { - stack.get().type = resolveExecutionType(buffer.toString().trim()); - /* not a valid type, throw an error */ - if (stack.get().type == null) { - plugin.getLogger().log(Level.WARNING, "Attribute error at line " + line + ": Invalid attribute value. (" + buffer.toString().trim() + ")."); - errors = true; - return false; - } - /* currently not defining anything, throw an error */ - }else{ - plugin.getLogger().log(Level.WARNING, "Attribute error at line " + line + ": Invalid attribute type."); - errors = true; - return false; - } - /* reset buffer and current property */ - currentProp = Property.NONE; - buffer = new StringBuilder(); - } - /* current is an opening curly bracket, we just entered a sub-command property definition */ - }else if (current == '{') { - /* increment bracket counter */ - bracketCounter++; - /* are we getting the name of the command? */ - if (gettingName && cmdName == null) { - /* set the command name to what we just gathered (trimmed) */ - cmdName = buffer.toString().trim(); - }else{ - /* are we currently in an argument? */ - if (currentArgComp == null) { - /* no, but if there is something that looks like text, put it into the current subcommand as a constant */ - if (buffer.toString().trim().length() > 0) { - currentChain.append(new ConstantComponent(buffer.toString().trim())); - } - }else{ - /* yes, put it into the current subcommand */ - /* could happen when there are no 'spaces' */ - /* [str:example]{ */ - /* ^ */ - currentChain.append(currentArgComp); - currentArgComp = null; - } - } - /* reset buffer */ - buffer = new StringBuilder(); - /* get whatever is at the top of the stack */ - ChainComponent top = stack.get(); - if (top != null) { - /* if it's not null, add our sub-command we just finished to it */ - top.append(currentChain); - } - /* push the current sub-command onto the stack */ - stack.push(currentChain); - /* reset our current sub-command */ - currentChain = new ChainComponent(); - /* current is a closing curly bracket, we just finished a property section */ - }else if (current == '}') { - /* decrement the bracket counter */ - bracketCounter--; - /* pop whatever was on the stack */ - ChainComponent popped = stack.pop(); - /* if it's null, throw an error */ - if (popped == null) { - plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": Too many closing brackets."); - errors = true; - return false; - } - /* go through all it's sub-commands and set their properties accordingly */ - /* 'type' and 'permission' are inherited to sub-commands this way */ - for (ICommandComponent comp : popped.getComponents()) { - if (comp instanceof ChainComponent) { - if (((ChainComponent) comp).type == null) { - ((ChainComponent) comp).type = popped.type; - } - if (((ChainComponent) comp).permission == null) { - ((ChainComponent) comp).permission = popped.permission; - } - } - } - /* we just exited the main command, do more magic */ - if (bracketCounter == 0) { - postProcess(cmdName, popped, methods, plugin, methodContainer); // \o/ - /* reset everything in case the user defined another command after this one */ - buffer = new StringBuilder(); - cmdName = null; - insideType = false; - gettingName = false; - previous = '\0'; - currentChain = new ChainComponent(); - stack = new BranchStack(); - currentArgComp = null; - currentProp = Property.NONE; - continue; - } - currentChain = new ChainComponent(); - /* current is a space, we just finished defining which property we are about to set */ - }else if (current == ' ') { - /* we are already defining a property, append it as text */ - if (currentProp != Property.NONE) { - buffer.append(' '); - }else{ - /* we got the 'command' definition, the name of the command will follow */ - if (buffer.toString().equals("command") && !gettingName && cmdName == null) { - gettingName = true; - /* we got other properties, their values will follow */ - }else if (buffer.toString().equals("help")) { - currentProp = Property.HELP; - }else if (buffer.toString().equals("run")) { - currentProp = Property.EXECUTE; - }else if (buffer.toString().equals("perm")) { - currentProp = Property.PERMISSION; - }else if (buffer.toString().equals("type")) { - currentProp = Property.TYPE; - /* we didn't get any of those, we are probably in the middle of a sub-command definition */ - /* example [int:value] { */ - /* ^ ^ */ - }else{ - /* we are getting the name and we didn't set it yet, set it */ - if (gettingName && cmdName == null) { - cmdName = buffer.toString().trim(); - }else{ - /* we aren't defining a type, put the current text into the sub-command as a constant */ - if (currentArgComp == null) { - if (buffer.toString().trim().length() > 0) { - currentChain.append(new ConstantComponent(buffer.toString().trim())); - } - /* we are defining a command, put it into the sub-command */ - }else{ - currentChain.append(currentArgComp); - currentArgComp = null; - } - } - } - /* reset the buffer */ - buffer = new StringBuilder(); - } - /* current is an opening square bracket, we just started a type definition */ - }else if (current == '[') { - /* we are defining a property, treat it as text */ - if (currentProp != Property.NONE) { - buffer.append('['); - /* we are already inside of a type definition, throw an error */ - }else if (insideType) { - plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": Invalid type declaration."); - errors = true; - return false; - }else{ - /* we just entered a type definition */ - insideType = true; - } - /* current is a closing square bracket, we just finished a type definition */ - }else if (current == ']') { - /* we are defining a property, treat it as text */ - if (currentProp != Property.NONE) { - buffer.append(']'); - /* we are inside of a type */ - }else if (insideType) { - insideType = false; - /* current argument type is null, throw an error */ - if (currentArgComp == null) { - /* should never happen */ - plugin.getLogger().log(Level.WARNING, "Type error at line " + line + ": Type has no type?"); - errors = true; - return false; - }else{ - /* set the value of the current type and reset the buffer */ - currentArgComp.argName = buffer.toString(); - buffer = new StringBuilder(); - } - }else{ - /* we are not defining a type, throw an error */ - plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": Not in type declaration."); - errors = true; - return false; - } - /* typical escape sequences and such */ - }else if (current == '&' && currentProp == Property.HELP) { - if (previous == '\\') { - buffer.append('&'); - }else{ - /* for color codes and formatting */ - buffer.append('§'); - } - }else if (current == 'n' && currentProp == Property.HELP) { - if (previous == '\\') { - buffer.append('\n'); - }else{ - buffer.append('n'); - } - }else if (current == 't' && currentProp == Property.HELP) { - if (previous == '\\') { - buffer.append('\t'); - }else{ - buffer.append('t'); - } - }else if (current == '\\' && currentProp == Property.HELP) { - if (previous == '\\') { - buffer.append('\\'); - } - }else if (current != '\r' && current != '\n' && current != '\t') { - buffer.append(current); - } - previous = current; - } - - return true; - } - - /** - * Resolves the string into a type, or null if invalid - * - * @param type string you want to evaluate - * @return the type class or null if invalid - */ - private static ArgumentComponent resolveComponentType(String type) { - switch (type) { - case "string": - return new StringComponent(); - case "int": - case "integer": - return new IntegerComponent(); - case "short": - return new ShortComponent(); - case "long": - return new LongComponent(); - case "byte": - return new ByteComponent(); - case "float": - return new FloatComponent(); - case "double": - return new DoubleComponent(); - case "bool": - case "boolean": - return new BooleanComponent(); - case "optional": - case "opt": - case "flag": - return new OptionalComponent(); - } - return null; - } - - /** - * Resolves the string into a property, or null if invalid - * - * @param type string you want to evaluate - * @return the property enum or null if invalid - */ - private static Type resolveExecutionType(String type) { - switch (type) { - case "player": - return Type.PLAYER; - case "both": - case "any": - case "all": - return Type.BOTH; - case "server": - case "console": - return Type.CONSOLE; - case "none": - case "nobody": - return Type.NOBODY; - } - return null; - } - - private static void postProcess(String cmdName, ChainComponent components, ArrayList methods, JavaPlugin plugin, Object methodContainer) { - components.execute = null; - components.help = null; - components.permission = null; - components.type = null; - Executable cmd = new Executable(cmdName, constructHelpPages(cmdName, components)); - cmd.register(methods, plugin, methodContainer); - } - - private static ArrayList constructHelpPages(String cmdName, ChainComponent root) { - String[] rawLines = constructHelpPagesRecursive(root).split("\r"); - ArrayList pages = new ArrayList(); - ArrayList lines = new ArrayList(); - HelpPageCommand[] page = new HelpPageCommand[5]; - - for (int i = 0; i < rawLines.length; i++) { - if (rawLines[i].length() > 0 && !rawLines[i].equals("\0null\0null\0null\0null\0")) { - lines.add(rawLines[i]); - } - } - - boolean firstPass = true; - int i; - for (i = 0; i < lines.size(); i++) { - if (i % 5 == 0 && !firstPass) { - pages.add(page); - page = new HelpPageCommand[5]; - } - String[] cmd = lines.get(i).split("\0"); - page[i % 5] = new HelpPageCommand(cmd[1], "/" + cmdName + " " + cmd[0], cmd[3], cmd[2], Type.parse(cmd[4])); - firstPass = false; - } - if (i % 5 == 0) { - pages.add(page); - page = new HelpPageCommand[5]; - } - page[i % 5] = new HelpPageCommand(cmdName + ".help", "/" + cmdName + " help ", "Shows help.", null, Type.BOTH); - pages.add(page); - - return pages; - } - - private static String constructHelpPagesRecursive(ICommandComponent component) { - String data = ""; - - if (component instanceof ChainComponent) { - ChainComponent comp = (ChainComponent) component; - ArrayList leaves = new ArrayList(); - String chain = ""; - data += "\r"; - for (ICommandComponent c : comp.getComponents()) { - String temp = constructHelpPagesRecursive(c); - if (c instanceof ChainComponent) { - temp = temp.replaceAll("\r", "\r" + chain); - leaves.add(temp); - }else{ - chain += temp; - } - } - data += chain + "\0" + ((comp.permission == null || comp.permission.equals("-none-")) ? null : comp.permission) + "\0" + comp.execute + "\0" + comp.help + "\0" + Type.get(comp.type) + "\0"; - for (String s : leaves) { - data += s; - } - }else{ - data += component.getComponentInfo() + " "; - } - - return data; - } -} +package com.nemez.cmdmgr; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.logging.Level; + +import org.bukkit.plugin.java.JavaPlugin; + +import com.nemez.cmdmgr.component.ArgumentComponent; +import com.nemez.cmdmgr.component.BooleanComponent; +import com.nemez.cmdmgr.component.ByteComponent; +import com.nemez.cmdmgr.component.ChainComponent; +import com.nemez.cmdmgr.component.ConstantComponent; +import com.nemez.cmdmgr.component.DoubleComponent; +import com.nemez.cmdmgr.component.FloatComponent; +import com.nemez.cmdmgr.component.ICommandComponent; +import com.nemez.cmdmgr.component.IntegerComponent; +import com.nemez.cmdmgr.component.LongComponent; +import com.nemez.cmdmgr.component.OptionalComponent; +import com.nemez.cmdmgr.component.ShortComponent; +import com.nemez.cmdmgr.component.StringComponent; +import com.nemez.cmdmgr.util.BranchStack; +import com.nemez.cmdmgr.util.Executable; +import com.nemez.cmdmgr.util.HelpPageCommand; +import com.nemez.cmdmgr.util.Property; +import com.nemez.cmdmgr.util.Type; + +/** + * Example command.cmd + * + * command home: + * set [string:name]: + * run home_set name + * help Set a new home + * perm home.set + * del [string:name]: + * run home_del name + * help Delete home\n&CCannot be undone! + * perm home.del + * list: + * run home_list + * help Show all homes + * perm home.list + * [string:name]: + * run home_tp name + * help Teleport to specified home + * perm home.tp + * + * Generated in-game command structure: + * (will only show commands the user has permission to execute) + * + * /home set + * /home del + * /home list + * /home + * /home help + * + * Java code: + * + * @Command(hook="home_set") + * public void executeHomeSet(String name) { + * ... + * } + * + * @Command(hook="home_del") + * public void executeHomeDelete(String name) { + * ... + * } + * + * @Command(hook="home_list") + * public void executeHomeList() { + * ... + * } + * + * @Command(hook="home_tp") + * public void executeHomeTeleport(String name) { + * ... + * } + */ + +public class CommandManager { + + /* Debugging toggle to generate help pages with types, e.g: str, i8, i32, fp64, etc. */ + public static boolean debugHelpMenu = false; + /* Internal boolean to keep track of errors during resolving */ + public static boolean errors = false; + + /* Switches for color and formatting in the built-in help message and pagination text */ + public static String helpDescriptionFormatting = "�b"; + public static String helpUsageFormatting = "�6"; + public static String helpPageHeaderFormatting = "�a"; + public static String helpInvalidPageFormatting = "�c"; + public static String noPermissionFormatting = "�c"; + public static String notAllowedFormatting = "�c"; + + /** + * Registers a command from a String of source code + * + * @param cmdSourceCode source code + * @param commandHandler instance of a class where your java functions are located + * @param plugin your plugin class + * @return success - if command was processed and registered successfully + */ + public static boolean registerCommand(String cmdSourceCode, Object commandHandler, JavaPlugin plugin) { + if (cmdSourceCode == null || commandHandler == null || plugin == null) { + return false; + } + /* get the class definition of the 'commandHandler' and get its functions */ + Method[] methods = commandHandler.getClass().getMethods(); + ArrayList finalMethods = new ArrayList(); + + /* extract all the functions annotated with @Command that are not static */ + for (Method m : methods) { + if (m.getAnnotationsByType(Command.class).length > 0 && (m.getModifiers() & Modifier.STATIC) == 0) { + finalMethods.add(m); + } + } + return parse(cmdSourceCode, finalMethods, plugin, commandHandler); + } + + /** + * Registers a command from a source File + * + * @param sourceFile file containing source code + * @param commandHandler instance of a class where your java functions are located + * @param plugin your plugin class + * @return success - if command was processed and registered successfully + */ + public static boolean registerCommand(File sourceFile, Object commandHandler, JavaPlugin plugin) { + StringBuilder src = new StringBuilder(); + String buf = ""; + try { + BufferedReader reader = new BufferedReader(new FileReader(sourceFile)); + while ((buf = reader.readLine()) != null) { + src.append(buf); + } + reader.close(); + } catch (Exception e) { + plugin.getLogger().log(Level.WARNING, "Error while loading command file. (" + sourceFile.getAbsolutePath() + ")"); + plugin.getLogger().log(Level.WARNING, e.getCause().toString()); + errors = true; + return false; + } + return registerCommand(src.toString(), commandHandler, plugin); + } + + /** + * Registers a command from an InputStream + * + * @param sourceStream input stream containing source code + * @param commandHandler instance of a class where your java functions are located + * @param plugin your plugin class + * @return success - if command was processed and registered successfully + */ + public static boolean registerCommand(InputStream sourceStream, Object commandHandler, JavaPlugin plugin) { + StringBuilder src = new StringBuilder(); + String buf = ""; + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(sourceStream)); + while ((buf = reader.readLine()) != null) { + src.append(buf); + } + reader.close(); + } catch (Exception e) { + plugin.getLogger().log(Level.WARNING, "Error while loading command file. (" + sourceStream.toString() + ")"); + plugin.getLogger().log(Level.WARNING, e.getCause().toString()); + errors = true; + return false; + } + return registerCommand(src.toString(), commandHandler, plugin); + } + + /** + * Parses the source code into an abstract command syntax + * + * @param source String containing command source code + * @param methods ArrayList of methods gathered from the plugin's handler class + * @param plugin plugin to register commands as + * @param methodContainer class containing method handles + * @return success - if command parsing and registration was successful + */ + private static boolean parse(String source, ArrayList methods, JavaPlugin plugin, Object methodContainer) { + /* source code */ + char[] chars = source.toCharArray(); + /* temporary buffer */ + StringBuilder buffer = new StringBuilder(); + /* name of the command we are parsing */ + String cmdName = null; + /* if we are currently gathering chars from a type definition */ + boolean insideType = false; + /* if we are currently gathering chars from the command name */ + boolean gettingName = false; + /* the previous char, used for backslash escaping */ + char previous = '\0'; + /* the current 'array' of sub-commands we are parsing */ + ChainComponent currentChain = new ChainComponent(); + /* storage for the current sub-command if it branches into more sub-commands */ + BranchStack stack = new BranchStack(); + /* the current argument we are parsing */ + ArgumentComponent currentArgComp = null; + /* the current property of the sub-command we are getting */ + Property currentProp = Property.NONE; + /* a counter for how deep in the stack we are, used to figure out if we exited a sub-command or the master command */ + int bracketCounter = 0; + /* line counter */ + int line = 0; + // buffer for '...' and '"' properties of string types + StringBuilder sideBuffer = new StringBuilder(); + + /* iterate over all characters */ + for (int i = 0; i < chars.length; i++) { + /* get current char */ + char current = chars[i]; + + /* increment line counter */ + if (current == '\n') { + line++; + } + + /* current is a colon, we just switched from arguments 'type' to its 'name' */ + /* */ + /* ^ */ + if (current == ':') { + /* are we inside a type? */ + if (insideType) { + /* are we already defining an argument? */ + if (currentArgComp != null) { + /* yes, we are, throw an error */ + plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": Already defining a type."); + errors = true; + return false; + }else{ + /* okay, resolve what type this is */ + currentArgComp = resolveComponentType(buffer.toString()); + buffer = new StringBuilder(); + /* type didn't fit any definition, throw an error */ + if (currentArgComp == null) { + plugin.getLogger().log(Level.WARNING, "Type error at line " + line + ": Invalid type."); + errors = true; + return false; + } + } + }else{ + /* not inside a type, probably just a string in the help property */ + buffer.append(':'); + } + /* current is a semicolon, we just finished a property line */ + /* help this is an example; */ + /* ^ */ + }else if (current == ';') { + /* semicolon is bashslash escaped, treat it as a normal character */ + if (previous == '\\') { + buffer.append(';'); + }else{ + /* there is nothing on the stack, we are defining properties of 'nothing', throw an error */ + if (stack.get() == null) { + plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": Not in code section."); + errors = true; + return false; + } + /* we are defining the 'help' property, set it to what we just gathered */ + if (currentProp == Property.HELP) { + stack.get().help = buffer.toString(); + /* same as above, except its the function to run */ + }else if (currentProp == Property.EXECUTE) { + stack.get().execute = buffer.toString(); + /* same again, but with the permission, and as that should not contain spaces, trim it */ + }else if (currentProp == Property.PERMISSION) { + stack.get().permission = buffer.toString().trim(); + /* execution type, check if its a valid one and set it */ + }else if (currentProp == Property.TYPE) { + stack.get().type = resolveExecutionType(buffer.toString().trim()); + /* not a valid type, throw an error */ + if (stack.get().type == null) { + plugin.getLogger().log(Level.WARNING, "Attribute error at line " + line + ": Invalid attribute value. (" + buffer.toString().trim() + ")."); + errors = true; + return false; + } + /* currently not defining anything, throw an error */ + }else{ + plugin.getLogger().log(Level.WARNING, "Attribute error at line " + line + ": Invalid attribute type."); + errors = true; + return false; + } + /* reset buffer and current property */ + currentProp = Property.NONE; + buffer = new StringBuilder(); + } + /* current is an opening curly bracket, we just entered a sub-command property definition */ + }else if (current == '{') { + /* increment bracket counter */ + bracketCounter++; + /* are we getting the name of the command? */ + if (gettingName && cmdName == null) { + /* set the command name to what we just gathered (trimmed) */ + cmdName = buffer.toString().trim(); + }else{ + /* are we currently in an argument? */ + if (currentArgComp == null) { + /* no, but if there is something that looks like text, put it into the current subcommand as a constant */ + if (buffer.toString().trim().length() > 0) { + currentChain.append(new ConstantComponent(buffer.toString().trim())); + } + }else{ + /* yes, put it into the current subcommand */ + /* could happen when there are no 'spaces' */ + /* [str:example]{ */ + /* ^ */ + currentChain.append(currentArgComp); + currentArgComp = null; + } + } + /* reset buffer */ + buffer = new StringBuilder(); + /* get whatever is at the top of the stack */ + ChainComponent top = stack.get(); + if (top != null) { + /* if it's not null, add our sub-command we just finished to it */ + top.append(currentChain); + } + /* push the current sub-command onto the stack */ + stack.push(currentChain); + /* reset our current sub-command */ + currentChain = new ChainComponent(); + /* current is a closing curly bracket, we just finished a property section */ + }else if (current == '}') { + /* decrement the bracket counter */ + bracketCounter--; + /* pop whatever was on the stack */ + ChainComponent popped = stack.pop(); + /* if it's null, throw an error */ + if (popped == null) { + plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": Too many closing brackets."); + errors = true; + return false; + } + /* go through all it's sub-commands and set their properties accordingly */ + /* 'type' and 'permission' are inherited to sub-commands this way */ + for (ICommandComponent comp : popped.getComponents()) { + if (comp instanceof ChainComponent) { + if (((ChainComponent) comp).type == null) { + ((ChainComponent) comp).type = popped.type; + } + if (((ChainComponent) comp).permission == null) { + ((ChainComponent) comp).permission = popped.permission; + } + } + } + /* we just exited the main command, do more magic */ + if (bracketCounter == 0) { + postProcess(cmdName, popped, methods, plugin, methodContainer); // \o/ + /* reset everything in case the user defined another command after this one */ + buffer = new StringBuilder(); + cmdName = null; + insideType = false; + gettingName = false; + previous = '\0'; + currentChain = new ChainComponent(); + stack = new BranchStack(); + currentArgComp = null; + currentProp = Property.NONE; + continue; + } + currentChain = new ChainComponent(); + /* current is a space, we just finished defining which property we are about to set */ + }else if (current == ' ') { + /* we are already defining a property, append it as text */ + if (currentProp != Property.NONE) { + buffer.append(' '); + }else{ + /* we got the 'command' definition, the name of the command will follow */ + if (buffer.toString().equals("command") && !gettingName && cmdName == null) { + gettingName = true; + /* we got other properties, their values will follow */ + }else if (buffer.toString().equals("help")) { + currentProp = Property.HELP; + }else if (buffer.toString().equals("run")) { + currentProp = Property.EXECUTE; + }else if (buffer.toString().equals("perm")) { + currentProp = Property.PERMISSION; + }else if (buffer.toString().equals("type")) { + currentProp = Property.TYPE; + /* we didn't get any of those, we are probably in the middle of a sub-command definition */ + /* example [int:value] { */ + /* ^ ^ */ + }else{ + /* we are getting the name and we didn't set it yet, set it */ + if (gettingName && cmdName == null) { + cmdName = buffer.toString().trim(); + }else{ + /* we aren't defining a type, put the current text into the sub-command as a constant */ + if (currentArgComp == null) { + if (buffer.toString().trim().length() > 0) { + currentChain.append(new ConstantComponent(buffer.toString().trim())); + } + /* we are defining a command, put it into the sub-command */ + }else{ + currentChain.append(currentArgComp); + currentArgComp = null; + } + } + } + /* reset the buffer */ + buffer = new StringBuilder(); + } + /* current is an opening square bracket, we just started a type definition */ + }else if (current == '[') { + /* we are defining a property, treat it as text */ + if (currentProp != Property.NONE) { + buffer.append('['); + /* we are already inside of a type definition, throw an error */ + }else if (insideType) { + plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": Invalid type declaration."); + errors = true; + return false; + }else{ + /* we just entered a type definition */ + insideType = true; + } + /* current is a closing square bracket, we just finished a type definition */ + }else if (current == ']') { + /* we are defining a property, treat it as text */ + if (currentProp != Property.NONE) { + buffer.append(']'); + /* we are inside of a type */ + }else if (insideType) { + insideType = false; + /* current argument type is null, throw an error */ + if (currentArgComp == null) { + /* should never happen */ + plugin.getLogger().log(Level.WARNING, "Type error at line " + line + ": Type has no type?"); + errors = true; + return false; + }else{ + /* set the value of the current type and reset the buffer */ + currentArgComp.argName = buffer.toString(); + buffer = new StringBuilder(); + if (currentArgComp instanceof StringComponent) { + StringComponent strComp = (StringComponent) currentArgComp; + strComp.infinite = sideBuffer.toString().contains("..."); + } + sideBuffer = new StringBuilder(); + } + }else{ + /* we are not defining a type, throw an error */ + plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": Not in type declaration."); + errors = true; + return false; + } + /* typical escape sequences and such */ + }else if (current == '&' && currentProp == Property.HELP) { + if (previous == '\\') { + buffer.append('&'); + }else{ + /* for color codes and formatting */ + buffer.append('�'); + } + }else if (current == 'n' && currentProp == Property.HELP) { + if (previous == '\\') { + buffer.append('\n'); + }else{ + buffer.append('n'); + } + }else if (current == 't' && currentProp == Property.HELP) { + if (previous == '\\') { + buffer.append('\t'); + }else{ + buffer.append('t'); + } + }else if (current == '\\' && currentProp == Property.HELP) { + if (previous == '\\') { + buffer.append('\\'); + } + }else if (current != '\r' && current != '\n' && current != '\t') { + if (currentArgComp != null && current == '.') { + if (currentArgComp instanceof StringComponent) { + sideBuffer.append(current); + }else{ + plugin.getLogger().log(Level.WARNING, "Syntax error at line " + line + ": '...' is invalid for non-string types."); + errors = true; + return false; + } + }else{ + buffer.append(current); + } + } + previous = current; + } + + return true; + } + + /** + * Resolves the string into a type, or null if invalid + * + * @param type string you want to evaluate + * @return the type class or null if invalid + */ + private static ArgumentComponent resolveComponentType(String type) { + switch (type) { + case "string": + return new StringComponent(); + case "int": + case "integer": + return new IntegerComponent(); + case "short": + return new ShortComponent(); + case "long": + return new LongComponent(); + case "byte": + return new ByteComponent(); + case "float": + return new FloatComponent(); + case "double": + return new DoubleComponent(); + case "bool": + case "boolean": + return new BooleanComponent(); + case "optional": + case "opt": + case "flag": + return new OptionalComponent(); + } + return null; + } + + /** + * Resolves the string into a property, or null if invalid + * + * @param type string you want to evaluate + * @return the property enum or null if invalid + */ + private static Type resolveExecutionType(String type) { + switch (type) { + case "player": + return Type.PLAYER; + case "both": + case "any": + case "all": + return Type.BOTH; + case "server": + case "console": + return Type.CONSOLE; + case "none": + case "nobody": + return Type.NOBODY; + } + return null; + } + + private static void postProcess(String cmdName, ChainComponent components, ArrayList methods, JavaPlugin plugin, Object methodContainer) { + components.execute = null; + components.help = null; + components.permission = null; + components.type = null; + Executable cmd = new Executable(cmdName, constructHelpPages(cmdName, components)); + cmd.register(methods, plugin, methodContainer); + } + + private static ArrayList constructHelpPages(String cmdName, ChainComponent root) { + String[] rawLines = constructHelpPagesRecursive(root).split("\r"); + ArrayList pages = new ArrayList(); + ArrayList lines = new ArrayList(); + HelpPageCommand[] page = new HelpPageCommand[5]; + + for (int i = 0; i < rawLines.length; i++) { + if (rawLines[i].length() > 0 && !rawLines[i].equals("\0null\0null\0null\0null\0")) { + lines.add(rawLines[i]); + } + } + + boolean firstPass = true; + int i; + for (i = 0; i < lines.size(); i++) { + if (i % 5 == 0 && !firstPass) { + pages.add(page); + page = new HelpPageCommand[5]; + } + String[] cmd = lines.get(i).split("\0"); + page[i % 5] = new HelpPageCommand(cmd[1], "/" + cmdName + " " + cmd[0], cmd[3], cmd[2], Type.parse(cmd[4])); + firstPass = false; + } + if (i % 5 == 0) { + pages.add(page); + page = new HelpPageCommand[5]; + } + page[i % 5] = new HelpPageCommand(cmdName + ".help", "/" + cmdName + " help ", "Shows help.", null, Type.BOTH); + pages.add(page); + + return pages; + } + + private static String constructHelpPagesRecursive(ICommandComponent component) { + String data = ""; + + if (component instanceof ChainComponent) { + ChainComponent comp = (ChainComponent) component; + ArrayList leaves = new ArrayList(); + String chain = ""; + data += "\r"; + for (ICommandComponent c : comp.getComponents()) { + String temp = constructHelpPagesRecursive(c); + if (c instanceof ChainComponent) { + temp = temp.replaceAll("\r", "\r" + chain); + leaves.add(temp); + }else{ + chain += temp; + } + } + data += chain + "\0" + ((comp.permission == null || comp.permission.equals("-none-")) ? null : comp.permission) + "\0" + comp.execute + "\0" + comp.help + "\0" + Type.get(comp.type) + "\0"; + for (String s : leaves) { + data += s; + } + }else{ + data += component.getComponentInfo() + " "; + } + + return data; + } +} diff --git a/com/nemez/cmdmgr/component/ArgumentComponent.java b/com/nemez/cmdmgr/component/ArgumentComponent.java index 5ad0691..939d9cb 100644 --- a/com/nemez/cmdmgr/component/ArgumentComponent.java +++ b/com/nemez/cmdmgr/component/ArgumentComponent.java @@ -1,12 +1,12 @@ -package com.nemez.cmdmgr.component; - -public abstract class ArgumentComponent implements ICommandComponent { - - public String argName; - public int position; - - @Override - public String argName() { - return argName; - } -} +package com.nemez.cmdmgr.component; + +public abstract class ArgumentComponent implements ICommandComponent { + + public String argName; + public int position; + + @Override + public String argName() { + return argName; + } +} diff --git a/com/nemez/cmdmgr/component/BooleanComponent.java b/com/nemez/cmdmgr/component/BooleanComponent.java index 519a54f..8290fc5 100644 --- a/com/nemez/cmdmgr/component/BooleanComponent.java +++ b/com/nemez/cmdmgr/component/BooleanComponent.java @@ -1,25 +1,25 @@ -package com.nemez.cmdmgr.component; - -public class BooleanComponent extends ArgumentComponent { - - @Override - public Object get(String input) { - if (input.toLowerCase().equals("true") || input.toLowerCase().equals("yes")) { - return true; - } - return false; - } - - @Override - public boolean valid(String input) { - if (input.toLowerCase().equals("true") || input.toLowerCase().equals("false") || input.toLowerCase().equals("yes") || input.toLowerCase().equals("no")) { - return true; - } - return false; - } - - @Override - public String getComponentInfo() { - return "<" + argName + ":bool>"; - } -} +package com.nemez.cmdmgr.component; + +public class BooleanComponent extends ArgumentComponent { + + @Override + public Object get(String input) { + if (input.toLowerCase().equals("true") || input.toLowerCase().equals("yes")) { + return true; + } + return false; + } + + @Override + public boolean valid(String input) { + if (input.toLowerCase().equals("true") || input.toLowerCase().equals("false") || input.toLowerCase().equals("yes") || input.toLowerCase().equals("no")) { + return true; + } + return false; + } + + @Override + public String getComponentInfo() { + return "<" + argName + ":bool>"; + } +} diff --git a/com/nemez/cmdmgr/component/ByteComponent.java b/com/nemez/cmdmgr/component/ByteComponent.java index 4b92b5a..f0e8d74 100644 --- a/com/nemez/cmdmgr/component/ByteComponent.java +++ b/com/nemez/cmdmgr/component/ByteComponent.java @@ -1,28 +1,28 @@ -package com.nemez.cmdmgr.component; - -public class ByteComponent extends ArgumentComponent { - - @Override - public Object get(String input) { - try { - return Byte.parseByte(input); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public boolean valid(String input) { - try { - Byte.parseByte(input); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - @Override - public String getComponentInfo() { - return "<" + argName + ":i8>"; - } -} +package com.nemez.cmdmgr.component; + +public class ByteComponent extends ArgumentComponent { + + @Override + public Object get(String input) { + try { + return Byte.parseByte(input); + } catch (NumberFormatException e) { + return null; + } + } + + @Override + public boolean valid(String input) { + try { + Byte.parseByte(input); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @Override + public String getComponentInfo() { + return "<" + argName + ":i8>"; + } +} diff --git a/com/nemez/cmdmgr/component/ChainComponent.java b/com/nemez/cmdmgr/component/ChainComponent.java index a562ca0..d837d16 100644 --- a/com/nemez/cmdmgr/component/ChainComponent.java +++ b/com/nemez/cmdmgr/component/ChainComponent.java @@ -1,50 +1,50 @@ -package com.nemez.cmdmgr.component; - -import java.util.ArrayList; - -import com.nemez.cmdmgr.util.Type; - -public class ChainComponent implements ICommandComponent { - - private ArrayList components; - public String permission; - public String help; - public String execute; - public Type type; - - public ChainComponent() { - components = new ArrayList(); - } - - public void append(ICommandComponent comp) { - components.add(comp); - } - - @Override - public Object get(String input) { - return components; - } - - @Override - public boolean valid(String input) { - return true; - } - - @Override - public String argName() { - return null; - } - - @Override - public String getComponentInfo() { - return "chain[" + components.size() + "]"; - } - - public int capacity() { - return components.size(); - } - - public ArrayList getComponents() { - return components; - } -} +package com.nemez.cmdmgr.component; + +import java.util.ArrayList; + +import com.nemez.cmdmgr.util.Type; + +public class ChainComponent implements ICommandComponent { + + private ArrayList components; + public String permission; + public String help; + public String execute; + public Type type; + + public ChainComponent() { + components = new ArrayList(); + } + + public void append(ICommandComponent comp) { + components.add(comp); + } + + @Override + public Object get(String input) { + return components; + } + + @Override + public boolean valid(String input) { + return true; + } + + @Override + public String argName() { + return null; + } + + @Override + public String getComponentInfo() { + return "chain[" + components.size() + "]"; + } + + public int capacity() { + return components.size(); + } + + public ArrayList getComponents() { + return components; + } +} diff --git a/com/nemez/cmdmgr/component/ConstantComponent.java b/com/nemez/cmdmgr/component/ConstantComponent.java index d3f2490..00e255e 100644 --- a/com/nemez/cmdmgr/component/ConstantComponent.java +++ b/com/nemez/cmdmgr/component/ConstantComponent.java @@ -1,33 +1,33 @@ -package com.nemez.cmdmgr.component; - -public class ConstantComponent implements ICommandComponent { - - private String component; - - public ConstantComponent(String comp) { - component = comp; - } - - @Override - public Object get(String input) { - if (input.equals(component)) { - return input; - } - return null; - } - - @Override - public boolean valid(String input) { - return input.equals(component); - } - - @Override - public String argName() { - return null; - } - - @Override - public String getComponentInfo() { - return component; - } -} +package com.nemez.cmdmgr.component; + +public class ConstantComponent implements ICommandComponent { + + private String component; + + public ConstantComponent(String comp) { + component = comp; + } + + @Override + public Object get(String input) { + if (input.equals(component)) { + return input; + } + return null; + } + + @Override + public boolean valid(String input) { + return input.equals(component); + } + + @Override + public String argName() { + return null; + } + + @Override + public String getComponentInfo() { + return component; + } +} diff --git a/com/nemez/cmdmgr/component/DoubleComponent.java b/com/nemez/cmdmgr/component/DoubleComponent.java index 44bd638..28a5398 100644 --- a/com/nemez/cmdmgr/component/DoubleComponent.java +++ b/com/nemez/cmdmgr/component/DoubleComponent.java @@ -1,28 +1,28 @@ -package com.nemez.cmdmgr.component; - -public class DoubleComponent extends ArgumentComponent { - - @Override - public Object get(String input) { - try { - return Double.parseDouble(input); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public boolean valid(String input) { - try { - Double.parseDouble(input); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - @Override - public String getComponentInfo() { - return "<" + argName + ":fp64>"; - } -} +package com.nemez.cmdmgr.component; + +public class DoubleComponent extends ArgumentComponent { + + @Override + public Object get(String input) { + try { + return Double.parseDouble(input); + } catch (NumberFormatException e) { + return null; + } + } + + @Override + public boolean valid(String input) { + try { + Double.parseDouble(input); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @Override + public String getComponentInfo() { + return "<" + argName + ":fp64>"; + } +} diff --git a/com/nemez/cmdmgr/component/FloatComponent.java b/com/nemez/cmdmgr/component/FloatComponent.java index 95ef5e4..039378a 100644 --- a/com/nemez/cmdmgr/component/FloatComponent.java +++ b/com/nemez/cmdmgr/component/FloatComponent.java @@ -1,28 +1,28 @@ -package com.nemez.cmdmgr.component; - -public class FloatComponent extends ArgumentComponent { - - @Override - public Object get(String input) { - try { - return Float.parseFloat(input); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public boolean valid(String input) { - try { - Float.parseFloat(input); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - @Override - public String getComponentInfo() { - return "<" + argName + ":fp32>"; - } -} +package com.nemez.cmdmgr.component; + +public class FloatComponent extends ArgumentComponent { + + @Override + public Object get(String input) { + try { + return Float.parseFloat(input); + } catch (NumberFormatException e) { + return null; + } + } + + @Override + public boolean valid(String input) { + try { + Float.parseFloat(input); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @Override + public String getComponentInfo() { + return "<" + argName + ":fp32>"; + } +} diff --git a/com/nemez/cmdmgr/component/ICommandComponent.java b/com/nemez/cmdmgr/component/ICommandComponent.java index ede0978..5dbb17b 100644 --- a/com/nemez/cmdmgr/component/ICommandComponent.java +++ b/com/nemez/cmdmgr/component/ICommandComponent.java @@ -1,9 +1,9 @@ -package com.nemez.cmdmgr.component; - -public interface ICommandComponent { - - public Object get(String input); - public boolean valid(String input); - public String argName(); - public String getComponentInfo(); -} +package com.nemez.cmdmgr.component; + +public interface ICommandComponent { + + public Object get(String input); + public boolean valid(String input); + public String argName(); + public String getComponentInfo(); +} diff --git a/com/nemez/cmdmgr/component/IntegerComponent.java b/com/nemez/cmdmgr/component/IntegerComponent.java index aef1974..aafab8d 100644 --- a/com/nemez/cmdmgr/component/IntegerComponent.java +++ b/com/nemez/cmdmgr/component/IntegerComponent.java @@ -1,28 +1,28 @@ -package com.nemez.cmdmgr.component; - -public class IntegerComponent extends ArgumentComponent { - - @Override - public Object get(String input) { - try { - return Integer.parseInt(input); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public boolean valid(String input) { - try { - Integer.parseInt(input); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - @Override - public String getComponentInfo() { - return "<" + argName + ":i32>"; - } -} +package com.nemez.cmdmgr.component; + +public class IntegerComponent extends ArgumentComponent { + + @Override + public Object get(String input) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + return null; + } + } + + @Override + public boolean valid(String input) { + try { + Integer.parseInt(input); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @Override + public String getComponentInfo() { + return "<" + argName + ":i32>"; + } +} diff --git a/com/nemez/cmdmgr/component/LongComponent.java b/com/nemez/cmdmgr/component/LongComponent.java index 486fd98..d97f1bb 100644 --- a/com/nemez/cmdmgr/component/LongComponent.java +++ b/com/nemez/cmdmgr/component/LongComponent.java @@ -1,28 +1,28 @@ -package com.nemez.cmdmgr.component; - -public class LongComponent extends ArgumentComponent { - - @Override - public Object get(String input) { - try { - return Long.parseLong(input); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public boolean valid(String input) { - try { - Long.parseLong(input); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - @Override - public String getComponentInfo() { - return "<" + argName + ":i64>"; - } +package com.nemez.cmdmgr.component; + +public class LongComponent extends ArgumentComponent { + + @Override + public Object get(String input) { + try { + return Long.parseLong(input); + } catch (NumberFormatException e) { + return null; + } + } + + @Override + public boolean valid(String input) { + try { + Long.parseLong(input); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @Override + public String getComponentInfo() { + return "<" + argName + ":i64>"; + } } \ No newline at end of file diff --git a/com/nemez/cmdmgr/component/OptionalComponent.java b/com/nemez/cmdmgr/component/OptionalComponent.java index e7d791e..1d1c839 100644 --- a/com/nemez/cmdmgr/component/OptionalComponent.java +++ b/com/nemez/cmdmgr/component/OptionalComponent.java @@ -1,19 +1,19 @@ -package com.nemez.cmdmgr.component; - -public class OptionalComponent extends ArgumentComponent { - - @Override - public Object get(String input) { - return input.equals(argName); - } - - @Override - public boolean valid(String input) { - return input.equals(argName); - } - - @Override - public String getComponentInfo() { - return "<" + argName + ":flag>"; - } -} +package com.nemez.cmdmgr.component; + +public class OptionalComponent extends ArgumentComponent { + + @Override + public Object get(String input) { + return input.equals(argName); + } + + @Override + public boolean valid(String input) { + return input.equals(argName); + } + + @Override + public String getComponentInfo() { + return "<" + argName + ":flag>"; + } +} diff --git a/com/nemez/cmdmgr/component/ShortComponent.java b/com/nemez/cmdmgr/component/ShortComponent.java index f4570b1..cad6719 100644 --- a/com/nemez/cmdmgr/component/ShortComponent.java +++ b/com/nemez/cmdmgr/component/ShortComponent.java @@ -1,28 +1,28 @@ -package com.nemez.cmdmgr.component; - -public class ShortComponent extends ArgumentComponent { - - @Override - public Object get(String input) { - try { - return Short.parseShort(input); - } catch (NumberFormatException e) { - return null; - } - } - - @Override - public boolean valid(String input) { - try { - Short.parseShort(input); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - @Override - public String getComponentInfo() { - return "<" + argName + ":i16>"; - } +package com.nemez.cmdmgr.component; + +public class ShortComponent extends ArgumentComponent { + + @Override + public Object get(String input) { + try { + return Short.parseShort(input); + } catch (NumberFormatException e) { + return null; + } + } + + @Override + public boolean valid(String input) { + try { + Short.parseShort(input); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @Override + public String getComponentInfo() { + return "<" + argName + ":i16>"; + } } \ No newline at end of file diff --git a/com/nemez/cmdmgr/component/StringComponent.java b/com/nemez/cmdmgr/component/StringComponent.java index f276a33..9b7a717 100644 --- a/com/nemez/cmdmgr/component/StringComponent.java +++ b/com/nemez/cmdmgr/component/StringComponent.java @@ -1,19 +1,21 @@ -package com.nemez.cmdmgr.component; - -public class StringComponent extends ArgumentComponent { - - @Override - public Object get(String input) { - return input; - } - - @Override - public boolean valid(String input) { - return true; - } - - @Override - public String getComponentInfo() { - return "<" + argName + ":str>"; - } -} +package com.nemez.cmdmgr.component; + +public class StringComponent extends ArgumentComponent { + + public boolean infinite = false; + + @Override + public Object get(String input) { + return input; + } + + @Override + public boolean valid(String input) { + return true; + } + + @Override + public String getComponentInfo() { + return "<" + (infinite ? "..." : "") + argName + ":str>"; + } +} diff --git a/com/nemez/cmdmgr/util/BranchStack.java b/com/nemez/cmdmgr/util/BranchStack.java index aac8efb..f89094f 100644 --- a/com/nemez/cmdmgr/util/BranchStack.java +++ b/com/nemez/cmdmgr/util/BranchStack.java @@ -1,33 +1,33 @@ -package com.nemez.cmdmgr.util; - -import java.util.ArrayList; - -import com.nemez.cmdmgr.component.ChainComponent; - -public class BranchStack { - - private ArrayList components; - - public BranchStack() { - components = new ArrayList(); - } - - public void push(ChainComponent comp) { - components.add(comp); - } - - public ChainComponent pop() { - if (components.size() > 0) { - ChainComponent toPop = components.remove(components.size() - 1); - return toPop; - } - return null; - } - - public ChainComponent get() { - if (components.size() > 0) { - return components.get(components.size() - 1); - } - return null; - } -} +package com.nemez.cmdmgr.util; + +import java.util.ArrayList; + +import com.nemez.cmdmgr.component.ChainComponent; + +public class BranchStack { + + private ArrayList components; + + public BranchStack() { + components = new ArrayList(); + } + + public void push(ChainComponent comp) { + components.add(comp); + } + + public ChainComponent pop() { + if (components.size() > 0) { + ChainComponent toPop = components.remove(components.size() - 1); + return toPop; + } + return null; + } + + public ChainComponent get() { + if (components.size() > 0) { + return components.get(components.size() - 1); + } + return null; + } +} diff --git a/com/nemez/cmdmgr/util/Executable.java b/com/nemez/cmdmgr/util/Executable.java index 1db27a3..04acbae 100644 --- a/com/nemez/cmdmgr/util/Executable.java +++ b/com/nemez/cmdmgr/util/Executable.java @@ -1,398 +1,404 @@ -package com.nemez.cmdmgr.util; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.logging.Level; - -import org.bukkit.Bukkit; -import org.bukkit.command.CommandMap; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; - -import com.nemez.cmdmgr.Command; -import com.nemez.cmdmgr.CommandManager; -import com.nemez.cmdmgr.component.BooleanComponent; -import com.nemez.cmdmgr.component.ByteComponent; -import com.nemez.cmdmgr.component.ConstantComponent; -import com.nemez.cmdmgr.component.DoubleComponent; -import com.nemez.cmdmgr.component.FloatComponent; -import com.nemez.cmdmgr.component.ICommandComponent; -import com.nemez.cmdmgr.component.IntegerComponent; -import com.nemez.cmdmgr.component.LongComponent; -import com.nemez.cmdmgr.component.OptionalComponent; -import com.nemez.cmdmgr.component.ShortComponent; -import com.nemez.cmdmgr.component.StringComponent; - -public class Executable extends org.bukkit.command.Command { - - private ArrayList commands; - private ArrayList help; - private String name; - private JavaPlugin plugin; - - public Executable(String name, ArrayList help) { - super(name); - this.help = help; - this.name = name; - this.commands = new ArrayList(); - } - - public void register(ArrayList methods, JavaPlugin plugin, Object methodContainer) { - for (HelpPageCommand[] page : help) { - for (HelpPageCommand cmd : page) { - if (cmd != null) { - processLine(cmd.usage.split("\\ "), cmd.permission, cmd.method, methods, methodContainer, plugin, cmd.type); - String newUsage = ""; - String buffer = ""; - String typeBuffer = ""; - boolean ignore = false; - boolean toBuffer = false; - for (char c : cmd.usage.toCharArray()) { - if (c == '<') { - toBuffer = true; - }else if (c == ':') { - toBuffer = false; - ignore = true; - }else if (c == '>') { - ignore = false; - if (typeBuffer.equals("flag")) { - newUsage += '[' + buffer + (CommandManager.debugHelpMenu ? ':' + typeBuffer : "") + ']'; - }else{ - newUsage += '<' + buffer + (CommandManager.debugHelpMenu ? ':' + typeBuffer : "") + '>'; - } - buffer = ""; - typeBuffer = ""; - }else{ - if (toBuffer) { - buffer += c; - }else if (ignore) { - typeBuffer += c; - }else{ - newUsage += c; - } - } - } - cmd.usage = newUsage; - } - } - } - - this.plugin = plugin; - try { - final Field cmdMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); - cmdMap.setAccessible(true); - CommandMap map = (CommandMap) cmdMap.get(Bukkit.getServer()); - map.register(name, this); - } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Failed to register command '" + name + "'!"); - e.printStackTrace(); - } - - if (CommandManager.errors) { - plugin.getLogger().log(Level.WARNING, "There were parser errors, some commands may not function properly!"); - CommandManager.errors = false; - } - } - - private void processLine(String[] line, String permission, String method, ArrayList methods, Object methodContainer, JavaPlugin plugin, Type etype) { - ArrayList command = new ArrayList(); - if (method == null && line[1].equals("help")) { - command.add(new ConstantComponent("help")); - IntegerComponent pageID = new IntegerComponent(); - pageID.argName = "page"; - command.add(pageID); - ExecutableDefinition def = new ExecutableDefinition(command, null, permission, null, methodContainer, Type.BOTH); - commands.add(def); - return; - } - HashMap methodParams = new HashMap(); - method = method.trim() + " "; - String[] methodArray = method.split(" "); - Method target = null; - ArrayList links = new ArrayList(); - - for (String s : line) { - if (s.contains("/")) { - continue; - } - if (s.contains(":")) { - String[] type = s.split(":"); - String paramName = ""; - switch (type[1].substring(0, type[1].length() - 1)) { - case "i8": - ByteComponent comp1 = new ByteComponent(); - comp1.argName = type[0].substring(1); - paramName = comp1.argName; - command.add(comp1); - break; - case "i16": - ShortComponent comp2 = new ShortComponent(); - comp2.argName = type[0].substring(1); - paramName = comp2.argName; - command.add(comp2); - break; - case "i32": - IntegerComponent comp3 = new IntegerComponent(); - comp3.argName = type[0].substring(1); - paramName = comp3.argName; - command.add(comp3); - break; - case "i64": - LongComponent comp4 = new LongComponent(); - comp4.argName = type[0].substring(1); - paramName = comp4.argName; - command.add(comp4); - break; - case "fp32": - FloatComponent comp5 = new FloatComponent(); - comp5.argName = type[0].substring(1); - paramName = comp5.argName; - command.add(comp5); - break; - case "fp64": - DoubleComponent comp6 = new DoubleComponent(); - comp6.argName = type[0].substring(1); - paramName = comp6.argName; - command.add(comp6); - break; - case "str": - StringComponent comp7 = new StringComponent(); - comp7.argName = type[0].substring(1); - paramName = comp7.argName; - command.add(comp7); - break; - case "bool": - BooleanComponent comp8 = new BooleanComponent(); - comp8.argName = type[0].substring(1); - paramName = comp8.argName; - command.add(comp8); - break; - case "flag": - OptionalComponent comp9 = new OptionalComponent(); - comp9.argName = type[0].substring(1); - paramName = comp9.argName; - command.add(comp9); - break; - default: - return; - } - int index = 0; - for (int i = 1; i < methodArray.length; i++) { - if (methodArray[i] != null && !methodArray[i].trim().equals("")) { - if (methodArray[i].trim().equals(paramName)) { - methodParams.put(index, command.get(command.size() - 1)); - links.add(index); - break; - } - index++; - } - } - }else{ - command.add(new ConstantComponent(s)); - } - } - - for (Method m : methods) { - Command[] annotations = m.getAnnotationsByType(Command.class); - if (annotations == null || annotations.length != 1) { - plugin.getLogger().log(Level.WARNING, "Invalid method (" + methodArray[0] + ")"); - CommandManager.errors = true; - return; - }else{ - if (annotations[0].hook().equals(methodArray[0])) { - Class[] params = m.getParameterTypes(); - if (params.length -1 != methodParams.size()) { - plugin.getLogger().log(Level.WARNING, "Invalid method (" + methodArray[0] + "): Arguments don't match"); - CommandManager.errors = true; - return; - }else{ - for (int i = 0; i < params.length; i++) { - if (i == 0) { - if (params[0] != CommandSender.class) { - plugin.getLogger().log(Level.WARNING, "Invalid method (" + methodArray[0] + "): First argument is not CommandSender"); - CommandManager.errors = true; - return; - } - }else{ - ICommandComponent comp = methodParams.get(i - 1); - if (comp instanceof ByteComponent && params[i] == byte.class) { - - }else if (comp instanceof ShortComponent && params[i] == short.class) { - - }else if (comp instanceof IntegerComponent && params[i] == int.class) { - - }else if (comp instanceof LongComponent && params[i] == long.class) { - - }else if (comp instanceof FloatComponent && params[i] == float.class) { - - }else if (comp instanceof DoubleComponent && params[i] == double.class) { - - }else if (comp instanceof StringComponent && params[i] == String.class) { - - }else if (comp instanceof BooleanComponent && params[i] == boolean.class) { - - }else if (comp instanceof OptionalComponent && params[i] == boolean.class) { - - }else{ - plugin.getLogger().log(Level.WARNING, "Invalid method (" + methodArray[0] + "): Invalid method arguments"); - CommandManager.errors = true; - return; - } - } - } - target = m; - break; - } - } - } - } - if (target == null) { - plugin.getLogger().log(Level.WARNING, "Invalid method (" + methodArray[0] + "): Method not found"); - CommandManager.errors = true; - return; - } - if (etype == null) { - etype = Type.BOTH; - } - ExecutableDefinition def = new ExecutableDefinition(command, links, permission, target, methodContainer, etype); - commands.add(def); - } - - @Override - public boolean execute(CommandSender sender, String name, String[] args_) { - char[] rawArgs = (String.join(" ", args_) + ' ').toCharArray(); - int argSize = 0; - char last = '\0'; - boolean inString = false; - - for (char c : rawArgs) { - if (c == '"') { - if (last != '\\') { - inString = !inString; - } - }else if (c == ' ' && !inString) { - argSize++; - } - last = c; - } - last = '\0'; - String[] args = new String[argSize]; - String buffer = ""; - int index = 0; - - for (char c : rawArgs) { - if (c == '"') { - if (last != '\\') { - inString = !inString; - }else{ - buffer = buffer.substring(0, buffer.length() - 1) + '"'; - } - }else if (c == ' ' && !inString) { - args[index] = buffer; - buffer = ""; - index++; - }else{ - buffer += c; - } - last = c; - } - - ArrayList defs = new ArrayList(); - defs.addAll(commands); - defLoop: for (int j = 0; j < defs.size(); j++) { - int i = 0, k = 0; - for (; i < args.length; i++, k++) { - if (!defs.get(j).valid(k, args[i])) { - if (!defs.get(j).isOptional(k)) { - defs.remove(j); - j--; - continue defLoop; - }else{ - i--; - continue; - } - } - } - if (k != defs.get(j).getLength()) { - defs.remove(j); - j--; - } - } - if (args.length == 0 || defs.size() == 0) { - printPage(sender, 1); - }else{ - ExecutableDefinition def = defs.get(0); - for (ExecutableDefinition d : defs) { - if (d.isHelp() && args[0].equals("help")) { - try { - int page = Integer.parseInt(args[1]); - printPage(sender, page); - } catch (Exception e) { - printPage(sender, 1); - } - return true; - } - } - if (def.getPermission() != null && !sender.hasPermission(def.getPermission())) { - sender.sendMessage(CommandManager.noPermissionFormatting + "You do not have permission to execute this command."); - return true; - } - if (def.getExecType() == Type.PLAYER) { - if (!(sender instanceof Player)) { - sender.sendMessage(CommandManager.notAllowedFormatting + "Only players are allowed to run this command."); - return true; - } - }else if (def.getExecType() == Type.CONSOLE) { - if (sender instanceof Player) { - sender.sendMessage(CommandManager.notAllowedFormatting + "Only console is allowed to run this command."); - return true; - } - }else if (def.getExecType() == Type.NOBODY) { - sender.sendMessage(CommandManager.notAllowedFormatting + "Nobody can run this command."); - return true; - } - ArrayList arguments = new ArrayList(); - for (int i = 0, j = 0; i < args.length; i++, j++) { - if (def.isArgument(j)) { - if (def.valid(j, args[i])) { - arguments.add(def.get(j, args[i])); - }else if (def.isOptional(j)) { - arguments.add(false); - i--; - } - } - } - Object[] linkedArgs = new Object[arguments.size() + 1]; - for (int i = 0; i < arguments.size(); i++) { - linkedArgs[def.getLink(i) + 1] = arguments.get(i); - } - if (!def.invoke(linkedArgs, sender, plugin)) { - printPage(sender, 1); - } - } - return true; - } - - private void printPage(CommandSender sender, int page) { - page--; - if (page < 0 || page >= help.size()) { - sender.sendMessage(CommandManager.helpInvalidPageFormatting + "Non-existant page (" + (page + 1) + ").\nThere are " + help.size() + " pages."); - }else{ - HelpPageCommand[] pageData = help.get(page); - sender.sendMessage(CommandManager.helpPageHeaderFormatting + "### Help Page " + (page + 1) + "/" + (help.size()) + " ###"); - for (HelpPageCommand c : pageData) { - if (c != null) { - if (c.type == null || c.type == Type.BOTH || (c.type == Type.CONSOLE && !(sender instanceof Player)) || (c.type == Type.PLAYER && sender instanceof Player)) { - if (c.permission == null || sender.hasPermission(c.permission)) { - sender.sendMessage(CommandManager.helpUsageFormatting + c.usage); - sender.sendMessage(CommandManager.helpDescriptionFormatting + c.description); - } - } - } - } - } - } -} +package com.nemez.cmdmgr.util; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.logging.Level; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandMap; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import com.nemez.cmdmgr.Command; +import com.nemez.cmdmgr.CommandManager; +import com.nemez.cmdmgr.component.BooleanComponent; +import com.nemez.cmdmgr.component.ByteComponent; +import com.nemez.cmdmgr.component.ConstantComponent; +import com.nemez.cmdmgr.component.DoubleComponent; +import com.nemez.cmdmgr.component.FloatComponent; +import com.nemez.cmdmgr.component.ICommandComponent; +import com.nemez.cmdmgr.component.IntegerComponent; +import com.nemez.cmdmgr.component.LongComponent; +import com.nemez.cmdmgr.component.OptionalComponent; +import com.nemez.cmdmgr.component.ShortComponent; +import com.nemez.cmdmgr.component.StringComponent; + +public class Executable extends org.bukkit.command.Command { + + private ArrayList commands; + private ArrayList help; + private String name; + private JavaPlugin plugin; + + public Executable(String name, ArrayList help) { + super(name); + this.help = help; + this.name = name; + this.commands = new ArrayList(); + } + + public void register(ArrayList methods, JavaPlugin plugin, Object methodContainer) { + for (HelpPageCommand[] page : help) { + for (HelpPageCommand cmd : page) { + if (cmd != null) { + processLine(cmd.usage.split("\\ "), cmd.permission, cmd.method, methods, methodContainer, plugin, cmd.type); + String newUsage = ""; + String buffer = ""; + String typeBuffer = ""; + boolean ignore = false; + boolean toBuffer = false; + for (char c : cmd.usage.toCharArray()) { + if (c == '<') { + toBuffer = true; + }else if (c == ':') { + toBuffer = false; + ignore = true; + }else if (c == '>') { + ignore = false; + if (typeBuffer.equals("flag")) { + newUsage += '[' + buffer + (CommandManager.debugHelpMenu ? ':' + typeBuffer : "") + ']'; + }else{ + newUsage += '<' + buffer + (CommandManager.debugHelpMenu ? ':' + typeBuffer : "") + '>'; + } + buffer = ""; + typeBuffer = ""; + }else{ + if (toBuffer) { + buffer += c; + }else if (ignore) { + typeBuffer += c; + }else{ + newUsage += c; + } + } + } + cmd.usage = newUsage; + } + } + } + + this.plugin = plugin; + try { + final Field cmdMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + cmdMap.setAccessible(true); + CommandMap map = (CommandMap) cmdMap.get(Bukkit.getServer()); + map.register(name, this); + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Failed to register command '" + name + "'!"); + e.printStackTrace(); + } + + if (CommandManager.errors) { + plugin.getLogger().log(Level.WARNING, "There were parser errors, some commands may not function properly!"); + CommandManager.errors = false; + } + } + + private void processLine(String[] line, String permission, String method, ArrayList methods, Object methodContainer, JavaPlugin plugin, Type etype) { + ArrayList command = new ArrayList(); + if (method == null && line[1].equals("help")) { + command.add(new ConstantComponent("help")); + IntegerComponent pageID = new IntegerComponent(); + pageID.argName = "page"; + command.add(pageID); + ExecutableDefinition def = new ExecutableDefinition(command, null, permission, null, methodContainer, Type.BOTH); + commands.add(def); + return; + } + HashMap methodParams = new HashMap(); + method = method.trim() + " "; + String[] methodArray = method.split(" "); + Method target = null; + ArrayList links = new ArrayList(); + + for (String s : line) { + if (s.contains("/")) { + continue; + } + if (s.contains(":")) { + String[] type = s.split(":"); + String paramName = ""; + switch (type[1].substring(0, type[1].length() - 1)) { + case "i8": + ByteComponent comp1 = new ByteComponent(); + comp1.argName = type[0].substring(1); + paramName = comp1.argName; + command.add(comp1); + break; + case "i16": + ShortComponent comp2 = new ShortComponent(); + comp2.argName = type[0].substring(1); + paramName = comp2.argName; + command.add(comp2); + break; + case "i32": + IntegerComponent comp3 = new IntegerComponent(); + comp3.argName = type[0].substring(1); + paramName = comp3.argName; + command.add(comp3); + break; + case "i64": + LongComponent comp4 = new LongComponent(); + comp4.argName = type[0].substring(1); + paramName = comp4.argName; + command.add(comp4); + break; + case "fp32": + FloatComponent comp5 = new FloatComponent(); + comp5.argName = type[0].substring(1); + paramName = comp5.argName; + command.add(comp5); + break; + case "fp64": + DoubleComponent comp6 = new DoubleComponent(); + comp6.argName = type[0].substring(1); + paramName = comp6.argName; + command.add(comp6); + break; + case "str": + StringComponent comp7 = new StringComponent(); + comp7.argName = type[0].substring(1).replace("...", ""); + comp7.infinite = type[0].substring(1).contains("..."); + paramName = comp7.argName; + command.add(comp7); + break; + case "bool": + BooleanComponent comp8 = new BooleanComponent(); + comp8.argName = type[0].substring(1); + paramName = comp8.argName; + command.add(comp8); + break; + case "flag": + OptionalComponent comp9 = new OptionalComponent(); + comp9.argName = type[0].substring(1); + paramName = comp9.argName; + command.add(comp9); + break; + default: + return; + } + int index = 0; + for (int i = 1; i < methodArray.length; i++) { + if (methodArray[i] != null && !methodArray[i].trim().equals("")) { + if (methodArray[i].trim().equals(paramName)) { + methodParams.put(index, command.get(command.size() - 1)); + links.add(index); + break; + } + index++; + } + } + }else{ + command.add(new ConstantComponent(s)); + } + } + + for (Method m : methods) { + Command[] annotations = m.getAnnotationsByType(Command.class); + if (annotations == null || annotations.length != 1) { + plugin.getLogger().log(Level.WARNING, "Invalid method (" + methodArray[0] + ")"); + CommandManager.errors = true; + return; + }else{ + if (annotations[0].hook().equals(methodArray[0])) { + Class[] params = m.getParameterTypes(); + if (params.length -1 != methodParams.size()) { + plugin.getLogger().log(Level.WARNING, "Invalid method (" + methodArray[0] + "): Arguments don't match"); + CommandManager.errors = true; + return; + }else{ + for (int i = 0; i < params.length; i++) { + if (i == 0) { + if (params[0] != CommandSender.class) { + plugin.getLogger().log(Level.WARNING, "Invalid method (" + methodArray[0] + "): First argument is not CommandSender"); + CommandManager.errors = true; + return; + } + }else{ + ICommandComponent comp = methodParams.get(i - 1); + if (comp instanceof ByteComponent && params[i] == byte.class) { + + }else if (comp instanceof ShortComponent && params[i] == short.class) { + + }else if (comp instanceof IntegerComponent && params[i] == int.class) { + + }else if (comp instanceof LongComponent && params[i] == long.class) { + + }else if (comp instanceof FloatComponent && params[i] == float.class) { + + }else if (comp instanceof DoubleComponent && params[i] == double.class) { + + }else if (comp instanceof StringComponent && params[i] == String.class) { + + }else if (comp instanceof BooleanComponent && params[i] == boolean.class) { + + }else if (comp instanceof OptionalComponent && params[i] == boolean.class) { + + }else{ + plugin.getLogger().log(Level.WARNING, "Invalid method (" + methodArray[0] + "): Invalid method arguments"); + CommandManager.errors = true; + return; + } + } + } + target = m; + break; + } + } + } + } + if (target == null) { + plugin.getLogger().log(Level.WARNING, "Invalid method (" + methodArray[0] + "): Method not found"); + CommandManager.errors = true; + return; + } + if (etype == null) { + etype = Type.BOTH; + } + ExecutableDefinition def = new ExecutableDefinition(command, links, permission, target, methodContainer, etype); + commands.add(def); + } + + @Override + public boolean execute(CommandSender sender, String name, String[] args_) { + char[] rawArgs = (String.join(" ", args_) + ' ').toCharArray(); + int argSize = 0; + char last = '\0'; + boolean inString = false; + + for (char c : rawArgs) { + if (c == '"') { + if (last != '\\') { + inString = !inString; + } + }else if (c == ' ' && !inString) { + argSize++; + } + last = c; + } + last = '\0'; + String[] args = new String[argSize]; + String buffer = ""; + int index = 0; + + for (char c : rawArgs) { + if (c == '"') { + if (last != '\\') { + inString = !inString; + }else{ + buffer = buffer.substring(0, buffer.length() - 1) + '"'; + } + }else if (c == ' ' && !inString) { + args[index] = buffer; + buffer = ""; + index++; + }else{ + buffer += c; + } + last = c; + } + + ArrayList defs = new ArrayList(); + defs.addAll(commands); + defLoop: for (int j = 0; j < defs.size(); j++) { + int i = 0, k = 0; + for (; i < args.length; i++, k++) { + if (!defs.get(j).valid(k, args[i])) { + if (!defs.get(j).isOptional(k)) { + defs.remove(j); + j--; + continue defLoop; + }else{ + i--; + continue; + } + } + } + if (k != defs.get(j).getLength(k)) { + defs.remove(j); + j--; + } + } + if (args.length == 0 || defs.size() == 0) { + printPage(sender, 1); + }else{ + ExecutableDefinition def = defs.get(0); + for (ExecutableDefinition d : defs) { + if (d.isHelp() && args[0].equals("help")) { + try { + int page = Integer.parseInt(args[1]); + printPage(sender, page); + } catch (Exception e) { + printPage(sender, 1); + } + return true; + } + } + if (def.getPermission() != null && !sender.hasPermission(def.getPermission())) { + sender.sendMessage(CommandManager.noPermissionFormatting + "You do not have permission to execute this command."); + return true; + } + if (def.getExecType() == Type.PLAYER) { + if (!(sender instanceof Player)) { + sender.sendMessage(CommandManager.notAllowedFormatting + "Only players are allowed to run this command."); + return true; + } + }else if (def.getExecType() == Type.CONSOLE) { + if (sender instanceof Player) { + sender.sendMessage(CommandManager.notAllowedFormatting + "Only console is allowed to run this command."); + return true; + } + }else if (def.getExecType() == Type.NOBODY) { + sender.sendMessage(CommandManager.notAllowedFormatting + "Nobody can run this command."); + return true; + } + ArrayList arguments = new ArrayList(); + for (int i = 0, j = 0; i < args.length; i++, j++) { + if (def.isArgument(j)) { + if (def.valid(j, args[i])) { + arguments.add(def.get(j, args[i])); + }else if (def.isOptional(j)) { + arguments.add(false); + i--; + } + } + } + Object[] linkedArgs = new Object[def.getNumOfArgs() + 1]; + for (int i = 0; i < arguments.size(); i++) { + int link = def.getLink(i) + 1; + if (linkedArgs[link] != null) { + linkedArgs[link] = linkedArgs[link].toString() + " " + arguments.get(i).toString(); + }else{ + linkedArgs[link] = arguments.get(i); + } + } + if (!def.invoke(linkedArgs, sender, plugin)) { + printPage(sender, 1); + } + } + return true; + } + + private void printPage(CommandSender sender, int page) { + page--; + if (page < 0 || page >= help.size()) { + sender.sendMessage(CommandManager.helpInvalidPageFormatting + "Non-existant page (" + (page + 1) + ").\nThere are " + help.size() + " pages."); + }else{ + HelpPageCommand[] pageData = help.get(page); + sender.sendMessage(CommandManager.helpPageHeaderFormatting + "### Help Page " + (page + 1) + "/" + (help.size()) + " ###"); + for (HelpPageCommand c : pageData) { + if (c != null) { + if (c.type == null || c.type == Type.BOTH || (c.type == Type.CONSOLE && !(sender instanceof Player)) || (c.type == Type.PLAYER && sender instanceof Player)) { + if (c.permission == null || sender.hasPermission(c.permission)) { + sender.sendMessage(CommandManager.helpUsageFormatting + c.usage); + sender.sendMessage(CommandManager.helpDescriptionFormatting + c.description); + } + } + } + } + } + } +} diff --git a/com/nemez/cmdmgr/util/ExecutableDefinition.java b/com/nemez/cmdmgr/util/ExecutableDefinition.java index e140689..ae5a7f9 100644 --- a/com/nemez/cmdmgr/util/ExecutableDefinition.java +++ b/com/nemez/cmdmgr/util/ExecutableDefinition.java @@ -1,104 +1,171 @@ -package com.nemez.cmdmgr.util; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.logging.Level; - -import org.bukkit.command.CommandSender; -import org.bukkit.plugin.java.JavaPlugin; - -import com.nemez.cmdmgr.CommandManager; -import com.nemez.cmdmgr.component.ArgumentComponent; -import com.nemez.cmdmgr.component.ICommandComponent; -import com.nemez.cmdmgr.component.OptionalComponent; - -public class ExecutableDefinition { - - private ArrayList components; - private String permission; - private Method target; - private Object methodContainer; - private Type type; - private ArrayList paramLinks; - - public ExecutableDefinition(ArrayList cmd, ArrayList paramLinks, String perm, Method method, Object methodContainer, Type type) { - this.components = cmd; - this.permission = perm; - this.target = method; - this.methodContainer = methodContainer; - this.type = type; - this.paramLinks = paramLinks; - } - - public boolean valid(int index, String arg) { - if (index < 0 || index >= components.size()) { - return false; - } - return components.get(index).valid(arg); - } - - public Object get(int index, String arg) { - if (index < 0 || index >= components.size()) { - return null; - } - return components.get(index).get(arg); - } - - public boolean isArgument(int index) { - if (index < 0 || index >= components.size()) { - return false; - } - return components.get(index) instanceof ArgumentComponent; - } - - public boolean isOptional(int index) { - if (index < 0 || index >= components.size()) { - return false; - } - return components.get(index) instanceof OptionalComponent; - } - - public boolean isHelp() { - return target == null && components.get(0).valid("help") && components.get(1).getComponentInfo().equals(""); - } - - public String getPermission() { - return permission; - } - - public Type getExecType() { - return type; - } - - public int getLength() { - return components.size(); - } - - public int getLink(int i) { - if (i < 0 || i > paramLinks.size()) { - return i; - } - return paramLinks.get(i); - } - - public boolean invoke(Object[] args, CommandSender sender, JavaPlugin plugin) { - if (target == null) { - return false; - } - args[0] = sender; - try { - if (target.getReturnType() == void.class) { - target.invoke(methodContainer, args); - return true; - }else if (target.getReturnType() == boolean.class) { - return (boolean) target.invoke(methodContainer, args); - } - } catch (Exception e) { - sender.sendMessage(CommandManager.helpInvalidPageFormatting + "An internal error occured, please contact the server administrator and/or report a bug."); - plugin.getLogger().log(Level.WARNING, "Runtime Error: invalid method"); - e.printStackTrace(); - return true; - } - return false; - } -} +package com.nemez.cmdmgr.util; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.logging.Level; + +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.java.JavaPlugin; + +import com.nemez.cmdmgr.CommandManager; +import com.nemez.cmdmgr.component.ArgumentComponent; +import com.nemez.cmdmgr.component.ICommandComponent; +import com.nemez.cmdmgr.component.OptionalComponent; +import com.nemez.cmdmgr.component.StringComponent; + +public class ExecutableDefinition { + + private ArrayList components; + private String permission; + private Method target; + private Object methodContainer; + private Type type; + private ArrayList paramLinks; + + public ExecutableDefinition(ArrayList cmd, ArrayList paramLinks, String perm, Method method, Object methodContainer, Type type) { + this.components = cmd; + this.permission = perm; + this.target = method; + this.methodContainer = methodContainer; + this.type = type; + this.paramLinks = paramLinks; + } + + public boolean valid(int index, String arg) { + if (index < 0) { + return false; + } + if (index >= components.size()) { + if (components.get(components.size() - 1) instanceof StringComponent) { + StringComponent strComp = (StringComponent) components.get(components.size() - 1); + if (strComp.infinite) { + return strComp.valid(arg); + }else{ + return false; + } + }else{ + return false; + } + } + return components.get(index).valid(arg); + } + + public Object get(int index, String arg) { + if (index < 0) { + return null; + } + if (index >= components.size()) { + if (components.get(components.size() - 1) instanceof StringComponent) { + StringComponent strComp = (StringComponent) components.get(components.size() - 1); + if (strComp.infinite) { + return strComp.get(arg); + }else{ + return null; + } + }else{ + return null; + } + } + return components.get(index).get(arg); + } + + public boolean isArgument(int index) { + if (index < 0) { + return false; + } + if (index >= components.size()) { + if (components.get(components.size() - 1) instanceof StringComponent) { + StringComponent strComp = (StringComponent) components.get(components.size() - 1); + if (strComp.infinite) { + return true; + }else{ + return false; + } + }else{ + return false; + } + } + return components.get(index) instanceof ArgumentComponent; + } + + public boolean isOptional(int index) { + if (index < 0 || index >= components.size()) { + return false; + } + return components.get(index) instanceof OptionalComponent; + } + + public boolean isHelp() { + return target == null && components.get(0).valid("help") && components.get(1).getComponentInfo().equals(""); + } + + public String getPermission() { + return permission; + } + + public Type getExecType() { + return type; + } + + public int getLength(int argSize) { + if (argSize >= components.size()) { + if (components.get(components.size() - 1) instanceof StringComponent) { + StringComponent strComp = (StringComponent) components.get(components.size() - 1); + if (strComp.infinite) { + return argSize; + } + } + } + return components.size(); + } + + public int getNumOfArgs() { + int counter = 0; + for (ICommandComponent c : components) { + if (c instanceof ArgumentComponent) { + counter++; + } + } + return counter; + } + + public int getLink(int i) { + if (i < 0) { + return i; + } + if (i >= paramLinks.size()) { + if (components.get(components.size() - 1) instanceof StringComponent) { + StringComponent strComp = (StringComponent) components.get(components.size() - 1); + if (strComp.infinite) { + return paramLinks.get(paramLinks.size() - 1); + }else{ + return i; + } + }else{ + return i; + } + } + return paramLinks.get(i); + } + + public boolean invoke(Object[] args, CommandSender sender, JavaPlugin plugin) { + if (target == null) { + return false; + } + args[0] = sender; + try { + if (target.getReturnType() == void.class) { + target.invoke(methodContainer, args); + return true; + }else if (target.getReturnType() == boolean.class) { + return (boolean) target.invoke(methodContainer, args); + } + } catch (Exception e) { + sender.sendMessage(CommandManager.helpInvalidPageFormatting + "An internal error occured, please contact the server administrator and/or report a bug."); + plugin.getLogger().log(Level.WARNING, "Runtime Error: invalid method"); + e.printStackTrace(); + return true; + } + return false; + } +} diff --git a/com/nemez/cmdmgr/util/HelpPageCommand.java b/com/nemez/cmdmgr/util/HelpPageCommand.java index 984022f..a3ee138 100644 --- a/com/nemez/cmdmgr/util/HelpPageCommand.java +++ b/com/nemez/cmdmgr/util/HelpPageCommand.java @@ -1,18 +1,18 @@ -package com.nemez.cmdmgr.util; - -public class HelpPageCommand { - - public String permission; - public String usage; - public String description; - public String method; - public Type type; - - public HelpPageCommand(String perm, String usage, String description, String method, Type type) { - this.permission = perm; - this.usage = usage; - this.description = description; - this.method = method; - this.type = type; - } -} +package com.nemez.cmdmgr.util; + +public class HelpPageCommand { + + public String permission; + public String usage; + public String description; + public String method; + public Type type; + + public HelpPageCommand(String perm, String usage, String description, String method, Type type) { + this.permission = perm; + this.usage = usage; + this.description = description; + this.method = method; + this.type = type; + } +} diff --git a/com/nemez/cmdmgr/util/Property.java b/com/nemez/cmdmgr/util/Property.java index 4c4ab96..4d681b0 100644 --- a/com/nemez/cmdmgr/util/Property.java +++ b/com/nemez/cmdmgr/util/Property.java @@ -1,7 +1,7 @@ -package com.nemez.cmdmgr.util; - -public enum Property { - - NONE, PERMISSION, HELP, EXECUTE, TYPE; - -} +package com.nemez.cmdmgr.util; + +public enum Property { + + NONE, PERMISSION, HELP, EXECUTE, TYPE; + +} diff --git a/com/nemez/cmdmgr/util/Type.java b/com/nemez/cmdmgr/util/Type.java index fd60a9e..f2fc37b 100644 --- a/com/nemez/cmdmgr/util/Type.java +++ b/com/nemez/cmdmgr/util/Type.java @@ -1,37 +1,37 @@ -package com.nemez.cmdmgr.util; - -public enum Type { - - BOTH, PLAYER, CONSOLE, NOBODY; - - public static Type parse(String string) { - if (string.equals("both")) { - return BOTH; - }else if (string.equals("player")) { - return PLAYER; - }else if (string.equals("console")) { - return CONSOLE; - }else if (string.equals("nobody")) { - return NOBODY; - }else{ - return null; - } - } - - public static String get(Type t) { - if (t == null) { - return "null"; - } - switch (t) { - case BOTH: - return "both"; - case PLAYER: - return "player"; - case CONSOLE: - return "console"; - case NOBODY: - return "nobody"; - } - return "null"; - } -} +package com.nemez.cmdmgr.util; + +public enum Type { + + BOTH, PLAYER, CONSOLE, NOBODY; + + public static Type parse(String string) { + if (string.equals("both")) { + return BOTH; + }else if (string.equals("player")) { + return PLAYER; + }else if (string.equals("console")) { + return CONSOLE; + }else if (string.equals("nobody")) { + return NOBODY; + }else{ + return null; + } + } + + public static String get(Type t) { + if (t == null) { + return "null"; + } + switch (t) { + case BOTH: + return "both"; + case PLAYER: + return "player"; + case CONSOLE: + return "console"; + case NOBODY: + return "nobody"; + } + return "null"; + } +} diff --git a/plugin.yml b/plugin.yml index eaefecc..f752976 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,4 +1,4 @@ -name: CommandManagerTest -main: CmdMgrTest -version: 13.37 +name: CommandManagerTest +main: CmdMgrTest +version: 13.37 author: Nemes \ No newline at end of file diff --git a/test.cmd b/test.cmd index 050e7d0..7e0ce89 100644 --- a/test.cmd +++ b/test.cmd @@ -1,35 +1,35 @@ - -command home { - - set [string:name] { - [int:x] [int:y] [int:z] { - run home_set_coords name x y z; - perm home.set.xyz; - help Sets a new home at coordinates XYZ; - } - run home_set name; - perm home.set; - help Sets a new home; - type none; - } - del [optional:-a] [string:name] { - run home_del name -a; - help Deletes a home\n&cCannot be undone!; - perm home.del; - } - list { - help Shows all homes; - run home_list; - perm home.list; - } - [string:name] { - perm home.tp; - help Teleports to a home; - run home_tp name; - } - yolo swag { - perm yo.mamma; - help Reks you; - run noskope; - } + +command home { + + set [string:name] { + [int:x] [int:y] [int:z] { + run home_set_coords name x y z; + perm home.set.xyz; + help Sets a new home at coordinates XYZ; + } + run home_set name; + perm home.set; + help Sets a new home; + type none; + } + del [optional:-a] [string:name] { + run home_del name -a; + help Deletes a home\n&cCannot be undone!; + perm home.del; + } + list { + help Shows all homes; + run home_list; + perm home.list; + } + [string:name] { + perm home.tp; + help Teleports to a home; + run home_tp name; + } + yolo swag { + perm yo.mamma; + help Reks you; + run noskope; + } } \ No newline at end of file -- cgit v1.2.3