From 05c4072d7df2975febcbca14ad598a90d4e56567 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Mon, 23 May 2016 10:07:13 +0200 Subject: [PATCH 1/4] * NEW: First draft for the new Commands API. --- .../zlib/components/commands2/Command.java | 12 ++++ .../components/commands2/CommandData.java | 70 +++++++++++++++++++ .../commands2/CommandException.java | 12 ++++ .../components/commands2/CommandGroup.java | 11 +++ .../components/commands2/CommandNode.java | 26 +++++++ .../commands2/CommandTreeBuilder.java | 45 ++++++++++++ .../zlib/components/commands2/Commands.java | 15 ++++ .../components/commands2/CommandsTest.java | 26 +++++++ .../components/commands2/CustomGroup.java | 23 ++++++ .../zlib/components/commands2/MyCommand.java | 12 ++++ .../resources/help/belovedblocks.fr_FR.txt | 1 + src/test/resources/help/belovedblocks.txt | 1 + .../help/belovedblocks/give.fr_FR.txt | 1 + .../resources/help/belovedblocks/give.txt | 1 + .../help/belovedblocks/give/block.fr_FR.txt | 1 + .../help/belovedblocks/give/block.txt | 1 + .../help/belovedblocks/give/tool.fr_FR.txt | 1 + .../help/belovedblocks/give/tool.txt | 1 + 18 files changed, 260 insertions(+) create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/Command.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandData.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandGroup.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandNode.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandTreeBuilder.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/Commands.java create mode 100644 src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java create mode 100644 src/test/java/fr/zcraft/zlib/components/commands2/CustomGroup.java create mode 100644 src/test/java/fr/zcraft/zlib/components/commands2/MyCommand.java create mode 100644 src/test/resources/help/belovedblocks.fr_FR.txt create mode 100644 src/test/resources/help/belovedblocks.txt create mode 100644 src/test/resources/help/belovedblocks/give.fr_FR.txt create mode 100644 src/test/resources/help/belovedblocks/give.txt create mode 100644 src/test/resources/help/belovedblocks/give/block.fr_FR.txt create mode 100644 src/test/resources/help/belovedblocks/give/block.txt create mode 100644 src/test/resources/help/belovedblocks/give/tool.fr_FR.txt create mode 100644 src/test/resources/help/belovedblocks/give/tool.txt diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Command.java b/src/main/java/fr/zcraft/zlib/components/commands2/Command.java new file mode 100644 index 00000000..7acb041a --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Command.java @@ -0,0 +1,12 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package fr.zcraft.zlib.components.commands2; + +public class Command { + + +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandData.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandData.java new file mode 100644 index 00000000..a3111ccb --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandData.java @@ -0,0 +1,70 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package fr.zcraft.zlib.components.commands2; + +import java.util.Collection; +import org.apache.commons.lang.NotImplementedException; +import org.bukkit.command.CommandSender; + +public class CommandData implements CommandNode +{ + + @Override + public T getParent() + { + throw new NotImplementedException(); + } + + @Override + public Collection getChildren() + { + throw new NotImplementedException(); + } + + @Override + public String getName() + { + throw new NotImplementedException(); + } + + @Override + public String[] getAliases() + { + throw new NotImplementedException(); + } + + @Override + public String getShortDescription() + { + throw new NotImplementedException(); + } + + @Override + public String getUsage() + { + throw new NotImplementedException(); + } + + @Override + public void run(CommandSender sender, String... arguments) throws CommandException + { + throw new NotImplementedException(); + } + + @Override + public Collection complete(CommandSender sender, String... arguments) throws CommandException + { + throw new NotImplementedException(); + } + + @Override + public boolean canExecute(CommandSender sender) + { + throw new NotImplementedException(); + } + +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandException.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandException.java new file mode 100644 index 00000000..1b988380 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandException.java @@ -0,0 +1,12 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package fr.zcraft.zlib.components.commands2; + +public class CommandException extends Exception +{ + +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandGroup.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandGroup.java new file mode 100644 index 00000000..6c2427a6 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandGroup.java @@ -0,0 +1,11 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package fr.zcraft.zlib.components.commands2; + +public class CommandGroup extends CommandData { + +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandNode.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandNode.java new file mode 100644 index 00000000..d14cd5bf --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandNode.java @@ -0,0 +1,26 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package fr.zcraft.zlib.components.commands2; + +import java.util.Collection; +import org.bukkit.command.CommandSender; + +public interface CommandNode +{ + public T getParent(); + public Collection getChildren(); + + public String getName(); + public String[] getAliases(); + public String getShortDescription(); + public String getUsage(); + + public void run(CommandSender sender, String... arguments) throws CommandException; + public Collection complete(CommandSender sender, String... arguments) throws CommandException; + public boolean canExecute(CommandSender sender); + +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandTreeBuilder.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandTreeBuilder.java new file mode 100644 index 00000000..b90ea99c --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandTreeBuilder.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package fr.zcraft.zlib.components.commands2; + +public class CommandTreeBuilder +{ + public CommandTreeBuilder command(Class command) + { + return this; + } + + public CommandTreeBuilder command(String name, Class command) + { + return this; + } + + public CommandTreeBuilder commands(Class... command) + { + return this; + } + + public CommandTreeBuilder subCommand(String name, String... aliases) + { + return this; + } + + public CommandTreeBuilder subCommand(String name, CommandGroup group, String... aliases) + { + return this; + } + + public CommandTreeBuilder endGroup() + { + return this; + } + + public CommandTreeBuilder end() + { + return this; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java b/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java new file mode 100644 index 00000000..837d33c4 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java @@ -0,0 +1,15 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package fr.zcraft.zlib.components.commands2; + +public abstract class Commands +{ + static public CommandTreeBuilder register() + { + return new CommandTreeBuilder(); + } +} diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java b/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java new file mode 100644 index 00000000..b95600cd --- /dev/null +++ b/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java @@ -0,0 +1,26 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package fr.zcraft.zlib.components.commands2; + +import org.junit.Test; + +public class CommandsTest +{ + @Test + public void simpleTest() + { + Commands.register() + .subCommand("belovedblocks", "bb") + .subCommand("give", "g") + .subCommand("tool", new CustomGroup(1), "t") + .command(MyCommand.class) + .endGroup() + .subCommand("block", new CustomGroup(2), "b") + .command(MyCommand.class) + .end(); + } +} diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/CustomGroup.java b/src/test/java/fr/zcraft/zlib/components/commands2/CustomGroup.java new file mode 100644 index 00000000..9aff4ce1 --- /dev/null +++ b/src/test/java/fr/zcraft/zlib/components/commands2/CustomGroup.java @@ -0,0 +1,23 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package fr.zcraft.zlib.components.commands2; + +public class CustomGroup extends CommandGroup +{ + private final int veryImportantNumber; + + public CustomGroup(int veryImportantNumber) + { + this.veryImportantNumber = veryImportantNumber; + } + + public int getVeryImportantNumber() + { + return veryImportantNumber; + } + +} diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/MyCommand.java b/src/test/java/fr/zcraft/zlib/components/commands2/MyCommand.java new file mode 100644 index 00000000..262d4092 --- /dev/null +++ b/src/test/java/fr/zcraft/zlib/components/commands2/MyCommand.java @@ -0,0 +1,12 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package fr.zcraft.zlib.components.commands2; + +public class MyCommand extends Command +{ + +} diff --git a/src/test/resources/help/belovedblocks.fr_FR.txt b/src/test/resources/help/belovedblocks.fr_FR.txt new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/src/test/resources/help/belovedblocks.fr_FR.txt @@ -0,0 +1 @@ + diff --git a/src/test/resources/help/belovedblocks.txt b/src/test/resources/help/belovedblocks.txt new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/src/test/resources/help/belovedblocks.txt @@ -0,0 +1 @@ + diff --git a/src/test/resources/help/belovedblocks/give.fr_FR.txt b/src/test/resources/help/belovedblocks/give.fr_FR.txt new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/src/test/resources/help/belovedblocks/give.fr_FR.txt @@ -0,0 +1 @@ + diff --git a/src/test/resources/help/belovedblocks/give.txt b/src/test/resources/help/belovedblocks/give.txt new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/src/test/resources/help/belovedblocks/give.txt @@ -0,0 +1 @@ + diff --git a/src/test/resources/help/belovedblocks/give/block.fr_FR.txt b/src/test/resources/help/belovedblocks/give/block.fr_FR.txt new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/src/test/resources/help/belovedblocks/give/block.fr_FR.txt @@ -0,0 +1 @@ + diff --git a/src/test/resources/help/belovedblocks/give/block.txt b/src/test/resources/help/belovedblocks/give/block.txt new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/src/test/resources/help/belovedblocks/give/block.txt @@ -0,0 +1 @@ + diff --git a/src/test/resources/help/belovedblocks/give/tool.fr_FR.txt b/src/test/resources/help/belovedblocks/give/tool.fr_FR.txt new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/src/test/resources/help/belovedblocks/give/tool.fr_FR.txt @@ -0,0 +1 @@ + diff --git a/src/test/resources/help/belovedblocks/give/tool.txt b/src/test/resources/help/belovedblocks/give/tool.txt new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/src/test/resources/help/belovedblocks/give/tool.txt @@ -0,0 +1 @@ + From 1aadfe7f67983b178b0e24d8456e8125768cde81 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Sun, 4 Mar 2018 15:35:40 +0100 Subject: [PATCH 2/4] * NEW: Commands: I changed everything --- pom.xml | 4 +- .../zlib/components/commands2/Command.java | 82 ++++++++++++- .../components/commands2/CommandData.java | 70 ----------- .../commands2/CommandException.java | 12 -- .../components/commands2/CommandExecutor.java | 55 +++++++++ .../commands2/CommandGenerator.java | 87 ++++++++++++++ .../components/commands2/CommandGroup.java | 11 -- .../components/commands2/CommandNode.java | 26 ----- .../components/commands2/CommandRunnable.java | 41 +++++++ .../components/commands2/CommandSender.java | 37 ++++++ .../commands2/CommandTreeBuilder.java | 45 -------- .../zlib/components/commands2/Commands.java | 109 ++++++++++++++++-- .../zlib/components/commands2/Context.java | 81 +++++++++++++ .../commands2/ContextGenerator.java | 70 +++++++++++ .../commands2/ParameterTypeConverter.java | 42 +++++++ .../zlib/components/commands2/SubCommand.java | 62 ++++++++++ .../commands2/annotations/About.java | 42 +++++++ .../commands2/annotations/Flag.java | 45 ++++++++ .../commands2/annotations/Subcommand.java | 46 ++++++++ .../exceptions/CommandException.java | 36 ++++++ .../exceptions/CommandNotFoundException.java | 43 +++++++ .../exceptions/InvalidArgumentException.java | 35 ++++++ .../components/commands2/CommandsTest.java | 62 +++++++--- .../components/commands2/CustomGroup.java | 23 ---- .../zlib/components/commands2/MyCommand.java | 12 -- .../components/commands2/bb/BBCommand.java | 63 ++++++++++ .../commands2/iom/CreateCommand.java | 35 ++++++ .../components/commands2/iom/IoMCommand.java | 22 ++++ 28 files changed, 1071 insertions(+), 227 deletions(-) delete mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandData.java delete mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandExecutor.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java delete mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandGroup.java delete mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandNode.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandRunnable.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandSender.java delete mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/CommandTreeBuilder.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/Context.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/ParameterTypeConverter.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/SubCommand.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/annotations/About.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/annotations/Flag.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/annotations/Subcommand.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandNotFoundException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java delete mode 100644 src/test/java/fr/zcraft/zlib/components/commands2/CustomGroup.java delete mode 100644 src/test/java/fr/zcraft/zlib/components/commands2/MyCommand.java create mode 100644 src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java create mode 100644 src/test/java/fr/zcraft/zlib/components/commands2/iom/CreateCommand.java create mode 100644 src/test/java/fr/zcraft/zlib/components/commands2/iom/IoMCommand.java diff --git a/pom.xml b/pom.xml index a1f4babf..0d0dc9dd 100644 --- a/pom.xml +++ b/pom.xml @@ -43,8 +43,8 @@ UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Command.java b/src/main/java/fr/zcraft/zlib/components/commands2/Command.java index 7acb041a..02ab6daa 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/Command.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Command.java @@ -1,12 +1,84 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. */ package fr.zcraft.zlib.components.commands2; -public class Command { +import java.util.List; +import java.util.Optional; - +/** + * This class represents a registered command. + * @param The command runnable type this command is bound to. + */ +public class Command { + + private final String name; + private final Class runnableClass; + private final boolean isCommandGroup; + private final List> subCommands; + + Command(Class runnableClass, String name, boolean isCommandGroup, List> subCommands) { + this.runnableClass = runnableClass; + this.name = name; + this.isCommandGroup = isCommandGroup; + this.subCommands = subCommands; + } + + public Context makeContext(CommandSender sender, String[] arguments) { + return ContextGenerator.makeContext(this, sender, arguments, Optional.empty()); + } + + public String getName() { + return name; + } + + public boolean nameMatches(String string) { + return string.equalsIgnoreCase(name); + } + + public Class getRunnableClass() { + return runnableClass; + } + + public boolean isCommandGroup() { + return isCommandGroup; + } + + public List> getSubCommands() { + return subCommands; + } + + public Optional> getSubCommand(String name) { + for(SubCommand subCommand : subCommands) { + if(subCommand.getCommand().nameMatches(name)) return Optional.of(subCommand); + } + return Optional.empty(); + } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandData.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandData.java deleted file mode 100644 index a3111ccb..00000000 --- a/src/main/java/fr/zcraft/zlib/components/commands2/CommandData.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package fr.zcraft.zlib.components.commands2; - -import java.util.Collection; -import org.apache.commons.lang.NotImplementedException; -import org.bukkit.command.CommandSender; - -public class CommandData implements CommandNode -{ - - @Override - public T getParent() - { - throw new NotImplementedException(); - } - - @Override - public Collection getChildren() - { - throw new NotImplementedException(); - } - - @Override - public String getName() - { - throw new NotImplementedException(); - } - - @Override - public String[] getAliases() - { - throw new NotImplementedException(); - } - - @Override - public String getShortDescription() - { - throw new NotImplementedException(); - } - - @Override - public String getUsage() - { - throw new NotImplementedException(); - } - - @Override - public void run(CommandSender sender, String... arguments) throws CommandException - { - throw new NotImplementedException(); - } - - @Override - public Collection complete(CommandSender sender, String... arguments) throws CommandException - { - throw new NotImplementedException(); - } - - @Override - public boolean canExecute(CommandSender sender) - { - throw new NotImplementedException(); - } - -} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandException.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandException.java deleted file mode 100644 index 1b988380..00000000 --- a/src/main/java/fr/zcraft/zlib/components/commands2/CommandException.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package fr.zcraft.zlib.components.commands2; - -public class CommandException extends Exception -{ - -} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandExecutor.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandExecutor.java new file mode 100644 index 00000000..9b8ee528 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandExecutor.java @@ -0,0 +1,55 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +import java.util.List; + +class CommandExecutor implements TabCompleter, org.bukkit.command.CommandExecutor { + + public CommandExecutor(Command command) { + + } + + @Override + public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings) { + throw new NotImplementedException(); + } + + @Override + public List onTabComplete(CommandSender commandSender, Command command, String s, String[] strings) { + throw new NotImplementedException(); + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java new file mode 100644 index 00000000..5eb5a930 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java @@ -0,0 +1,87 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2; + +import fr.zcraft.zlib.components.commands2.annotations.Subcommand; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * This class contains various utilities to generate Command objects from their bound runnable class. + */ +class CommandGenerator { + static public Command fromClass(Class runnableClass, String name) { + if(isCommandGroup(runnableClass)) { + return fromEnumClass(runnableClass, name); + } else { + return fromPlainClass(runnableClass, name); + } + } + + static private boolean isCommandGroup(Class runnableClass) { + return Enum.class.isAssignableFrom(runnableClass); + } + + static private Command fromEnumClass(Class runnableClass, String name) { + List> subcommands = Arrays.stream(runnableClass.getDeclaredFields()) + .filter(Field::isEnumConstant) + .map(CommandGenerator::fromField) + .collect(Collectors.toList()); + + return new Command(runnableClass, name, true, subcommands); + } + + static private Command fromPlainClass(Class runnableClass, String name) { + return new Command(runnableClass, name, false, new ArrayList<>()); + } + + static private SubCommand fromField(Field field) { + Subcommand subcommand = field.getAnnotation(Subcommand.class); + if(subcommand == null) throw new RuntimeException("No subcommand annotation"); //TODO: Better exception + + String commandName = subcommand.name(); + if(commandName.isEmpty()) commandName = field.getName().toLowerCase(); + Command innerCommand = fromClass(subcommand.value(), commandName); + T parentValue; + try { + parentValue = (T) field.get(null); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); //TODO: Better exception + } + + return new SubCommand(innerCommand, parentValue, field); + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandGroup.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandGroup.java deleted file mode 100644 index 6c2427a6..00000000 --- a/src/main/java/fr/zcraft/zlib/components/commands2/CommandGroup.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package fr.zcraft.zlib.components.commands2; - -public class CommandGroup extends CommandData { - -} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandNode.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandNode.java deleted file mode 100644 index d14cd5bf..00000000 --- a/src/main/java/fr/zcraft/zlib/components/commands2/CommandNode.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package fr.zcraft.zlib.components.commands2; - -import java.util.Collection; -import org.bukkit.command.CommandSender; - -public interface CommandNode -{ - public T getParent(); - public Collection getChildren(); - - public String getName(); - public String[] getAliases(); - public String getShortDescription(); - public String getUsage(); - - public void run(CommandSender sender, String... arguments) throws CommandException; - public Collection complete(CommandSender sender, String... arguments) throws CommandException; - public boolean canExecute(CommandSender sender); - -} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandRunnable.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandRunnable.java new file mode 100644 index 00000000..92476cbd --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandRunnable.java @@ -0,0 +1,41 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2; + +import fr.zcraft.zlib.components.commands2.exceptions.CommandException; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + + +public interface CommandRunnable { + default void run(Context context) throws CommandException { + throw new NotImplementedException(); + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandSender.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandSender.java new file mode 100644 index 00000000..d87097de --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandSender.java @@ -0,0 +1,37 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2; + +public class CommandSender { + private org.bukkit.command.CommandSender bukkitSender; + + CommandSender() {} +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandTreeBuilder.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandTreeBuilder.java deleted file mode 100644 index b90ea99c..00000000 --- a/src/main/java/fr/zcraft/zlib/components/commands2/CommandTreeBuilder.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package fr.zcraft.zlib.components.commands2; - -public class CommandTreeBuilder -{ - public CommandTreeBuilder command(Class command) - { - return this; - } - - public CommandTreeBuilder command(String name, Class command) - { - return this; - } - - public CommandTreeBuilder commands(Class... command) - { - return this; - } - - public CommandTreeBuilder subCommand(String name, String... aliases) - { - return this; - } - - public CommandTreeBuilder subCommand(String name, CommandGroup group, String... aliases) - { - return this; - } - - public CommandTreeBuilder endGroup() - { - return this; - } - - public CommandTreeBuilder end() - { - return this; - } -} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java b/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java index 837d33c4..bfd2b908 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java @@ -1,15 +1,110 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. */ package fr.zcraft.zlib.components.commands2; -public abstract class Commands +import fr.zcraft.zlib.components.commands2.exceptions.CommandException; +import fr.zcraft.zlib.components.commands2.exceptions.CommandNotFoundException; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * The entry point for the zLib Commands component. + */ +public abstract class Commands { - static public CommandTreeBuilder register() - { - return new CommandTreeBuilder(); + private Commands() {} + + static private Map> commandRegistry = new HashMap<>(); + static private Map, ParameterTypeConverter> typeConverterRegistry = new HashMap<>(); + + /** + * Registers a new command. + * @param command The command runnable to be registered + * @param name The name of the command + */ + static public void register(Class command, String name) { + name = name.toLowerCase(); + if(commandRegistry.containsKey(name)) throw new IllegalArgumentException("Command already registered : " + name); + + commandRegistry.put(name, CommandGenerator.fromClass(command, name)); + } + + /** + * Registers a new command parameter type converter. + * You must register a new converter before using any types in your commands that zLib doesn't support out of the box. + * Additional type converters must be registered before any commands using the type are. + * @param parameterTypeConverter The type converter to register + */ + static public void registerParameterTypeConverter(ParameterTypeConverter parameterTypeConverter) { + //TODO: Raise exception if already registered + typeConverterRegistry.put(parameterTypeConverter.getType(), parameterTypeConverter); + } + + /** + * Finds the registered command matching the given name. + * @param commandName The name of the command + * @return The matching command + * @throws CommandNotFoundException If no matching command is found + */ + static public Command findCommand(String commandName) throws CommandNotFoundException { + Command command = commandRegistry.get(commandName.toLowerCase()); + if(command == null) throw new CommandNotFoundException(commandName); + return command; + } + + /** + * Finds the registered command parameter type converter handling the given type. + * @param type The type used to look up the matching type converter + * @param The type the command parameter type converter handles + * @return The registered command parameter type converter, if found + */ + static public Optional> findTypeConverter(Class type) { + @SuppressWarnings("unchecked") + ParameterTypeConverter typeConverter = (ParameterTypeConverter) typeConverterRegistry.get(type); + return Optional.ofNullable(typeConverter); + } + + /** + * Creates a new Command execution context for the given command. + * @param commandName The name of the command + * @param sender The command sender the command originated from + * @param arguments The command arguments + * @return The newly created context + * @throws CommandException If the command is not found, or if an error occured when parsing the arguments + */ + static public Context makeContext(String commandName, CommandSender sender, String[] arguments) throws CommandException { + return findCommand(commandName).makeContext(sender, arguments); } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Context.java b/src/main/java/fr/zcraft/zlib/components/commands2/Context.java new file mode 100644 index 00000000..498e9717 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Context.java @@ -0,0 +1,81 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2; + +import java.util.Optional; + +/** + * This class represents a command execution context. + * It contains all the data related to command execution, such as the command sender, arguments, etc. + * @param The command runnable type this command is bound to. + */ +public class Context { + + private final T runnable; + private final CommandSender sender; + private final String[] args; + private final Command parentCommand; + private final Optional parentContext; + private final Optional> matchedSubCommand; + + Context(T runnable, CommandSender sender, String[] args, Command command, Optional parentContext, Optional> matchedSubCommand) { + this.runnable = runnable; + this.sender = sender; + this.args = args; + this.parentCommand = command; + this.parentContext = parentContext; + this.matchedSubCommand = matchedSubCommand; + } + + public T getCommandRunnable() { + return this.runnable; + } + + public CommandSender sender() { + return this.sender; + } + + public String[] getArgs() { + return args; + } + + public Command getParentCommand() { + return parentCommand; + } + + public Optional getParentContext() { + return parentContext; + } + + public Optional> getMatchedSubCommand() { + return matchedSubCommand; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java b/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java new file mode 100644 index 00000000..11556441 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java @@ -0,0 +1,70 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2; + +import fr.zcraft.zlib.tools.reflection.Reflection; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +import java.util.Arrays; +import java.util.Optional; + +/** + * This class contains various utilities to generate Context objects from arguments matched against their bound Command class. + */ +abstract class ContextGenerator { + private ContextGenerator() {} + + private static Context makeEnumContext(Command command, CommandSender sender, String[] arguments, Optional parentContext) throws Exception { + if(arguments.length < 1) throw new Exception("not enough arguments");//TODO: Better exceptions + SubCommand subCommand = command.getSubCommand(arguments[0]).orElseThrow(Exception::new); + return new Context<>(subCommand.getParentEnumValue(), sender, arguments, command, parentContext, Optional.of(subCommand)); + } + + static Context makeContext(Command command, CommandSender sender, String[] arguments, Optional parentContext) { + try { + if(command.isCommandGroup()) { + Context context = makeEnumContext(command, sender, arguments, parentContext); + SubCommand subCommand = context.getMatchedSubCommand().orElseThrow(NullPointerException::new); + return makeContext(subCommand.getCommand(), sender, arguments, Optional.of(context)); + } else { + return makePlainClassContext(command, sender, arguments, parentContext); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + + private static Context makePlainClassContext(Command command, CommandSender sender, String[] arguments, Optional parentContext) throws Exception { + T runnable = Reflection.instantiate(command.getRunnableClass()); + return new Context<>(runnable, sender, arguments, command, parentContext, Optional.empty()); + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/ParameterTypeConverter.java b/src/main/java/fr/zcraft/zlib/components/commands2/ParameterTypeConverter.java new file mode 100644 index 00000000..78ca9eb7 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/ParameterTypeConverter.java @@ -0,0 +1,42 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2; + +import fr.zcraft.zlib.components.commands2.exceptions.InvalidArgumentException; + +/** + * An interface used to parse types from command arguments. + * @param + */ +public interface ParameterTypeConverter { + public Class getType(); + public T fromArgument(String argument) throws InvalidArgumentException; +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/SubCommand.java b/src/main/java/fr/zcraft/zlib/components/commands2/SubCommand.java new file mode 100644 index 00000000..a14454c3 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/SubCommand.java @@ -0,0 +1,62 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2; + +import java.lang.reflect.Field; + +/** + * This class contains information about a command's subcommand. + * @param + * @param + */ +public class SubCommand { + private final Command command; + private final Parent parentEnumValue; + private final Field parentField; + + SubCommand(Command command, Parent parentEnumValue, Field parentField) { + this.command = command; + this.parentEnumValue = parentEnumValue; + this.parentField = parentField; + } + + public Command getCommand() { + return command; + } + + public Parent getParentEnumValue() { + return parentEnumValue; + } + + public Field getParentField() { + return parentField; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/annotations/About.java b/src/main/java/fr/zcraft/zlib/components/commands2/annotations/About.java new file mode 100644 index 00000000..1af8b9d7 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/annotations/About.java @@ -0,0 +1,42 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface About { + String value(); +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/annotations/Flag.java b/src/main/java/fr/zcraft/zlib/components/commands2/annotations/Flag.java new file mode 100644 index 00000000..d1dce9c5 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/annotations/Flag.java @@ -0,0 +1,45 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface Flag { + String shortName() default ""; + String name() default ""; + String[] names() default {}; + String[] additionalNames() default {}; +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/annotations/Subcommand.java b/src/main/java/fr/zcraft/zlib/components/commands2/annotations/Subcommand.java new file mode 100644 index 00000000..2eef68a3 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/annotations/Subcommand.java @@ -0,0 +1,46 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2.annotations; + +import fr.zcraft.zlib.components.commands2.CommandRunnable; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface Subcommand { + Class value(); + String name() default ""; + String[] aliases() default {}; +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandException.java new file mode 100644 index 00000000..12cff7a0 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandException.java @@ -0,0 +1,36 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2.exceptions; + +public class CommandException extends Exception +{ + +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandNotFoundException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandNotFoundException.java new file mode 100644 index 00000000..04e9e5b7 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandNotFoundException.java @@ -0,0 +1,43 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2.exceptions; + +public class CommandNotFoundException extends CommandException { + private final String commandName; + + public CommandNotFoundException(String commandName) { + this.commandName = commandName; + } + + public String getCommandName() { + return commandName; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java new file mode 100644 index 00000000..5c86bf7d --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java @@ -0,0 +1,35 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2.exceptions; + +public class InvalidArgumentException extends CommandException { + +} diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java b/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java index b95600cd..73fc2f6e 100644 --- a/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java +++ b/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java @@ -1,26 +1,60 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. */ package fr.zcraft.zlib.components.commands2; +import fr.zcraft.zlib.components.commands2.bb.BBCommand; +import fr.zcraft.zlib.components.commands2.exceptions.CommandException; +import fr.zcraft.zlib.components.commands2.iom.CreateCommand; +import fr.zcraft.zlib.components.commands2.iom.IoMCommand; +import org.junit.Assert; import org.junit.Test; +import java.util.Optional; + public class CommandsTest { + static private final CommandSender sender = new CommandSender(); + @Test + public void iomTest() throws CommandException { + Commands.register(IoMCommand.class, "maptool"); + Commands.register(CreateCommand.class, "tomap"); + + Context mapToolContext = Commands.makeContext("maptool", sender, new String[]{"list"}); + Assert.assertEquals(mapToolContext.getParentContext().map(Context::getCommandRunnable), Optional.of(IoMCommand.LIST)); + Assert.assertTrue(mapToolContext.getCommandRunnable() instanceof IoMCommand.ListCommand); + } + @Test - public void simpleTest() - { - Commands.register() - .subCommand("belovedblocks", "bb") - .subCommand("give", "g") - .subCommand("tool", new CustomGroup(1), "t") - .command(MyCommand.class) - .endGroup() - .subCommand("block", new CustomGroup(2), "b") - .command(MyCommand.class) - .end(); + public void bbTest() { + Commands.registerParameterTypeConverter(new BBCommand.BBItemParamConverter()); + Commands.register(BBCommand.class, "bb"); } } diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/CustomGroup.java b/src/test/java/fr/zcraft/zlib/components/commands2/CustomGroup.java deleted file mode 100644 index 9aff4ce1..00000000 --- a/src/test/java/fr/zcraft/zlib/components/commands2/CustomGroup.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package fr.zcraft.zlib.components.commands2; - -public class CustomGroup extends CommandGroup -{ - private final int veryImportantNumber; - - public CustomGroup(int veryImportantNumber) - { - this.veryImportantNumber = veryImportantNumber; - } - - public int getVeryImportantNumber() - { - return veryImportantNumber; - } - -} diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/MyCommand.java b/src/test/java/fr/zcraft/zlib/components/commands2/MyCommand.java deleted file mode 100644 index 262d4092..00000000 --- a/src/test/java/fr/zcraft/zlib/components/commands2/MyCommand.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package fr.zcraft.zlib.components.commands2; - -public class MyCommand extends Command -{ - -} diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java b/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java new file mode 100644 index 00000000..4ba2d694 --- /dev/null +++ b/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java @@ -0,0 +1,63 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2.bb; + +import fr.zcraft.zlib.components.commands2.CommandRunnable; +import fr.zcraft.zlib.components.commands2.ParameterTypeConverter; +import fr.zcraft.zlib.components.commands2.exceptions.InvalidArgumentException; + +import java.util.Arrays; +import java.util.Optional; + +public class BBCommand implements CommandRunnable { + public BBItem item; + public Optional amount; + + static public class BBItem { + static public final String[] items = {"saw", "stonecutter"}; + public String itemType; + } + + static public class BBItemParamConverter implements ParameterTypeConverter { + + @Override + public Class getType() { + return BBItem.class; + } + + @Override + public BBItem fromArgument(String argument) throws InvalidArgumentException { + argument = argument.toLowerCase(); + if(!Arrays.asList(BBItem.items).contains(argument)) throw new InvalidArgumentException(); + return null; + } + } +} diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/iom/CreateCommand.java b/src/test/java/fr/zcraft/zlib/components/commands2/iom/CreateCommand.java new file mode 100644 index 00000000..003331e9 --- /dev/null +++ b/src/test/java/fr/zcraft/zlib/components/commands2/iom/CreateCommand.java @@ -0,0 +1,35 @@ +package fr.zcraft.zlib.components.commands2.iom; + +import fr.zcraft.zlib.components.commands2.CommandRunnable; +import fr.zcraft.zlib.components.commands2.Context; +import fr.zcraft.zlib.components.commands2.annotations.About; +import fr.zcraft.zlib.components.commands2.annotations.Flag; + +import java.net.URI; +import java.util.Optional; + +public class CreateCommand implements CommandRunnable { + + @About("The URI") + public URI imageURI; + + @About("The width") + @Flag(shortName = "w") + public Optional width; + + @About("The height") + @Flag(shortName = "h") + public Optional height; + + @Flag + public boolean stretch; + + @Flag + public boolean cover; + + @Override + public void run(Context context) { + System.out.println("Hello world !"); + } + +} diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/iom/IoMCommand.java b/src/test/java/fr/zcraft/zlib/components/commands2/iom/IoMCommand.java new file mode 100644 index 00000000..de6d592e --- /dev/null +++ b/src/test/java/fr/zcraft/zlib/components/commands2/iom/IoMCommand.java @@ -0,0 +1,22 @@ +package fr.zcraft.zlib.components.commands2.iom; + +import fr.zcraft.zlib.components.commands2.exceptions.CommandException; +import fr.zcraft.zlib.components.commands2.CommandRunnable; +import fr.zcraft.zlib.components.commands2.Context; +import fr.zcraft.zlib.components.commands2.annotations.Subcommand; + +public enum IoMCommand implements CommandRunnable { + @Subcommand(CreateCommand.class) + CREATE, + @Subcommand(ListCommand.class) + LIST, + ; + + static public class ListCommand implements CommandRunnable { + + @Override + public void run(Context context) throws CommandException { + + } + } +} From b84a81a8160f7dbad8aad605a298092bd310d6c5 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Sun, 4 Mar 2018 18:40:04 +0100 Subject: [PATCH 3/4] * NEW: Commands: Added arguments and flags parsing support. --- .../zlib/components/commands2/Command.java | 28 ++++++- .../commands2/CommandGenerator.java | 84 ++++++++++++++++--- .../zlib/components/commands2/Commands.java | 10 ++- .../commands2/ContextGenerator.java | 60 ++++++++++++- .../zlib/components/commands2/Field.java | 45 ++++++++++ .../zlib/components/commands2/Flag.java | 21 +++++ .../zlib/components/commands2/Parameter.java | 7 ++ .../converters/IntegerTypeConverter.java | 20 +++++ .../converters/URITypeConverter.java | 23 +++++ .../exceptions/InvalidArgumentException.java | 12 +++ .../exceptions/UnhandledParameterType.java | 26 ++++++ .../components/commands2/CommandsTest.java | 21 ++++- .../components/commands2/bb/BBCommand.java | 2 +- 13 files changed, 338 insertions(+), 21 deletions(-) create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/Field.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/Flag.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/Parameter.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/converters/IntegerTypeConverter.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/converters/URITypeConverter.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnhandledParameterType.java diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Command.java b/src/main/java/fr/zcraft/zlib/components/commands2/Command.java index 02ab6daa..2f6aabab 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/Command.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Command.java @@ -43,12 +43,16 @@ public class Command { private final Class runnableClass; private final boolean isCommandGroup; private final List> subCommands; + private final List> parameters; + private final List> flags; - Command(Class runnableClass, String name, boolean isCommandGroup, List> subCommands) { + Command(Class runnableClass, String name, boolean isCommandGroup, List> subCommands, List> parameters, List> flags) { this.runnableClass = runnableClass; this.name = name; this.isCommandGroup = isCommandGroup; this.subCommands = subCommands; + this.parameters = parameters; + this.flags = flags; } public Context makeContext(CommandSender sender, String[] arguments) { @@ -81,4 +85,26 @@ public Optional> getSubCommand(String name) { } return Optional.empty(); } + + public List> getParameters() { + return parameters; + } + + public List> getFlags() { + return flags; + } + + public Optional> getFlag(String shortName) { + for(Flag flag: flags) { + if(flag.getName().equals(shortName)) return Optional.of(flag); + } + return Optional.empty(); + } + + public Optional> getShortFlag(String shortName) { + for(Flag flag: flags) { + if(flag.getShortName().equals(shortName)) return Optional.of(flag); + } + return Optional.empty(); + } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java index 5eb5a930..bc1b3202 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java @@ -31,19 +31,20 @@ package fr.zcraft.zlib.components.commands2; import fr.zcraft.zlib.components.commands2.annotations.Subcommand; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import fr.zcraft.zlib.components.commands2.exceptions.CommandException; +import fr.zcraft.zlib.components.commands2.exceptions.UnhandledParameterType; import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; +import java.util.Optional; /** * This class contains various utilities to generate Command objects from their bound runnable class. */ class CommandGenerator { - static public Command fromClass(Class runnableClass, String name) { + static public Command fromClass(Class runnableClass, String name) throws CommandException { if(isCommandGroup(runnableClass)) { return fromEnumClass(runnableClass, name); } else { @@ -55,20 +56,76 @@ static private boolean isCommandGroup(Class runnableC return Enum.class.isAssignableFrom(runnableClass); } - static private Command fromEnumClass(Class runnableClass, String name) { - List> subcommands = Arrays.stream(runnableClass.getDeclaredFields()) - .filter(Field::isEnumConstant) - .map(CommandGenerator::fromField) - .collect(Collectors.toList()); + static private Command fromEnumClass(Class runnableClass, String name) throws CommandException { + List> subcommands = new ArrayList<>(); - return new Command(runnableClass, name, true, subcommands); + for(Field f : runnableClass.getDeclaredFields()) { + if(!f.isEnumConstant()) continue; + subcommands.add(subcommandFromField(f)); + } + + return new Command(runnableClass, name, true, subcommands, new ArrayList<>(), new ArrayList<>()); + } + + static private List> getCommandParameters(Class runnableClass) throws UnhandledParameterType { + List> parameters = new ArrayList<>(); + + for(Field f : runnableClass.getDeclaredFields()) { + if(isFlagField(f)) continue; + parameters.add(parameterFromField(f)); + } + + return parameters; + } + + static private List> getCommandFlags(Class runnableClass) throws UnhandledParameterType { + List> parameters = new ArrayList<>(); + + for(Field f : runnableClass.getDeclaredFields()) { + if(!isFlagField(f)) continue; + parameters.add(flagFromField(f)); + } + + return parameters; + } + + static private boolean isFlagField(Field field) { + return field.getAnnotation(fr.zcraft.zlib.components.commands2.annotations.Flag.class) != null; + } + + static private Parameter parameterFromField(Field field) throws UnhandledParameterType { + Class type = field.getType(); + ParameterTypeConverter typeConverter = Commands.findTypeConverter(type).orElseThrow(() -> new UnhandledParameterType(type, field)); + return new Parameter<>(type, field, typeConverter, field.getName(), null, true); + } + + static private Flag flagFromField(Field field) throws UnhandledParameterType { + @SuppressWarnings("unchecked") + Class type = (Class) field.getType(); + fr.zcraft.zlib.components.commands2.annotations.Flag flagAnnotation = field.getAnnotation(fr.zcraft.zlib.components.commands2.annotations.Flag.class); + String shortName = flagAnnotation.shortName(); + + if(boolean.class.isAssignableFrom(type)) { + return new Flag<>(type, field, null, field.getName(), shortName,null, false, false); + } + + boolean isRequired = true; + if(Optional.class.isAssignableFrom(type)) { + ParameterizedType ptype = (ParameterizedType) field.getGenericType(); + type = (Class) ptype.getActualTypeArguments()[0]; + isRequired = false; + } + + Optional> typeConverter = Commands.findTypeConverter(type); + if(!typeConverter.isPresent()) throw new UnhandledParameterType(type, field); + return new Flag<>(type, field, typeConverter.get(), field.getName(), shortName, null, isRequired, true); } - static private Command fromPlainClass(Class runnableClass, String name) { - return new Command(runnableClass, name, false, new ArrayList<>()); + static private Command fromPlainClass(Class runnableClass, String name) throws CommandException { + return new Command<>(runnableClass, name, false, new ArrayList<>(), getCommandParameters(runnableClass), getCommandFlags(runnableClass)); } - static private SubCommand fromField(Field field) { + static private SubCommand subcommandFromField(Field field) throws CommandException { Subcommand subcommand = field.getAnnotation(Subcommand.class); if(subcommand == null) throw new RuntimeException("No subcommand annotation"); //TODO: Better exception @@ -77,6 +134,7 @@ static private Command fromPlainClass(Class ru Command innerCommand = fromClass(subcommand.value(), commandName); T parentValue; try { + field.setAccessible(true); parentValue = (T) field.get(null); } catch (IllegalAccessException e) { throw new RuntimeException(e); //TODO: Better exception diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java b/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java index bfd2b908..b183ccf6 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java @@ -30,6 +30,8 @@ package fr.zcraft.zlib.components.commands2; +import fr.zcraft.zlib.components.commands2.converters.IntegerTypeConverter; +import fr.zcraft.zlib.components.commands2.converters.URITypeConverter; import fr.zcraft.zlib.components.commands2.exceptions.CommandException; import fr.zcraft.zlib.components.commands2.exceptions.CommandNotFoundException; import sun.reflect.generics.reflectiveObjects.NotImplementedException; @@ -54,7 +56,7 @@ private Commands() {} * @param command The command runnable to be registered * @param name The name of the command */ - static public void register(Class command, String name) { + static public void register(Class command, String name) throws CommandException { name = name.toLowerCase(); if(commandRegistry.containsKey(name)) throw new IllegalArgumentException("Command already registered : " + name); @@ -107,4 +109,10 @@ static public Optional> findTypeConverter(Class static public Context makeContext(String commandName, CommandSender sender, String[] arguments) throws CommandException { return findCommand(commandName).makeContext(sender, arguments); } + + // REGISTER ALL THE TYPES + static { + registerParameterTypeConverter(new IntegerTypeConverter()); + registerParameterTypeConverter(new URITypeConverter()); + } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java b/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java index 11556441..8b339bcc 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java @@ -33,7 +33,9 @@ import fr.zcraft.zlib.tools.reflection.Reflection; import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Optional; /** @@ -53,7 +55,7 @@ static Context makeContext(Command context = makeEnumContext(command, sender, arguments, parentContext); SubCommand subCommand = context.getMatchedSubCommand().orElseThrow(NullPointerException::new); - return makeContext(subCommand.getCommand(), sender, arguments, Optional.of(context)); + return makeContext(subCommand.getCommand(), sender, Arrays.copyOfRange(arguments, 1, arguments.length), Optional.of(context)); } else { return makePlainClassContext(command, sender, arguments, parentContext); } @@ -65,6 +67,62 @@ static Context makeContext(Command Context makePlainClassContext(Command command, CommandSender sender, String[] arguments, Optional parentContext) throws Exception { T runnable = Reflection.instantiate(command.getRunnableClass()); + List> parameters = command.getParameters(); + List> flags = command.getFlags(); + List> remainingFlags = new ArrayList<>(flags); + + int argumentsI = 0; + int parametersI = 0; + + for(; argumentsI < arguments.length; ++argumentsI) { + String argument = arguments[argumentsI]; + Flag flag = null; + if(argument.startsWith("--")) { + argument = argument.substring(2); + Optional> oflag = command.getFlag(argument); + if (!oflag.isPresent()) throw new RuntimeException("unknown flag: " + argument); + flag = oflag.get(); + } else if(argument.startsWith("-")) { + argument = argument.substring(1); + Optional> oflag = command.getShortFlag(argument); + if (!oflag.isPresent()) throw new RuntimeException("unknown flag: " + argument); + flag = oflag.get(); + } + + if(flag != null) { + if (!remainingFlags.remove(flag)) throw new RuntimeException("flag already defined: " + argument); + if (!flag.hasValue()) { + flag.getRunnableField().set(runnable, true); + } else { + if (argumentsI + 1 >= arguments.length) throw new RuntimeException("Missing value for flag :" + argument); + ++argumentsI; + String flagValue = arguments[argumentsI]; + if (flag.isRequired()) { + flag.getRunnableField().set(runnable, flag.getTypeConverter().fromArgument(flagValue)); + } else { + flag.getRunnableField().set(runnable, Optional.of(flag.getTypeConverter().fromArgument(flagValue))); + } + } + continue; + } + + if(parametersI >= parameters.size()) throw new RuntimeException("too many arguments"); + Parameter parameter = parameters.get(argumentsI); + parameter.getRunnableField().set(runnable, parameter.getTypeConverter().fromArgument(arguments[argumentsI])); + ++parametersI; + } + + if(!remainingFlags.isEmpty()) { + for(Flag f: remainingFlags) { + if(f.isRequired()) throw new RuntimeException("missing required flag : " + f.getName()); + if(f.hasValue()) { + f.getRunnableField().set(runnable, Optional.empty()); + } else { + f.getRunnableField().set(runnable, false); + } + } + } + return new Context<>(runnable, sender, arguments, command, parentContext, Optional.empty()); } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Field.java b/src/main/java/fr/zcraft/zlib/components/commands2/Field.java new file mode 100644 index 00000000..6ca72c82 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Field.java @@ -0,0 +1,45 @@ +package fr.zcraft.zlib.components.commands2; + +public class Field { + private final Class parameterType; + private final java.lang.reflect.Field runnableField; + private final ParameterTypeConverter typeConverter; + + private final String name; + private final String about; + private final boolean isRequired; + + Field(Class parameterType, java.lang.reflect.Field runnableField, ParameterTypeConverter typeConverter, String name, String about, boolean isRequired) { + this.parameterType = parameterType; + this.runnableField = runnableField; + this.typeConverter = typeConverter; + + this.name = name; + this.about = about; + this.isRequired = isRequired; + } + + public Class getParameterType() { + return parameterType; + } + + public ParameterTypeConverter getTypeConverter() { + return typeConverter; + } + + public java.lang.reflect.Field getRunnableField() { + return runnableField; + } + + public String getName() { + return name; + } + + public String getAbout() { + return about; + } + + public boolean isRequired() { + return isRequired; + } +} \ No newline at end of file diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Flag.java b/src/main/java/fr/zcraft/zlib/components/commands2/Flag.java new file mode 100644 index 00000000..bde60f41 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Flag.java @@ -0,0 +1,21 @@ +package fr.zcraft.zlib.components.commands2; + +class Flag extends Field { + private final boolean hasValue; + private final String shortName; + + Flag(Class parameterType, java.lang.reflect.Field runnableField, ParameterTypeConverter typeConverter, String name, String shortName, String about, boolean isRequired, boolean hasValue) { + super(parameterType, runnableField, typeConverter, name, about, isRequired); + this.hasValue = hasValue; + this.shortName = shortName; + } + + public boolean hasValue() { + return hasValue; + } + + public String getShortName() { + return shortName; + } +} + diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Parameter.java b/src/main/java/fr/zcraft/zlib/components/commands2/Parameter.java new file mode 100644 index 00000000..cc1a8096 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Parameter.java @@ -0,0 +1,7 @@ +package fr.zcraft.zlib.components.commands2; + +class Parameter extends Field { + Parameter(Class parameterType, java.lang.reflect.Field runnableField, ParameterTypeConverter typeConverter, String name, String about, boolean isRequired) { + super(parameterType, runnableField, typeConverter, name, about, isRequired); + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/converters/IntegerTypeConverter.java b/src/main/java/fr/zcraft/zlib/components/commands2/converters/IntegerTypeConverter.java new file mode 100644 index 00000000..4ccb8c45 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/converters/IntegerTypeConverter.java @@ -0,0 +1,20 @@ +package fr.zcraft.zlib.components.commands2.converters; + +import fr.zcraft.zlib.components.commands2.ParameterTypeConverter; +import fr.zcraft.zlib.components.commands2.exceptions.InvalidArgumentException; + +public class IntegerTypeConverter implements ParameterTypeConverter { + @Override + public Class getType() { + return Integer.class; + } + + @Override + public Integer fromArgument(String argument) throws InvalidArgumentException { + try { + return Integer.parseInt(argument); + } catch(NumberFormatException ex) { + throw new InvalidArgumentException("Invalid integer"); + } + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/converters/URITypeConverter.java b/src/main/java/fr/zcraft/zlib/components/commands2/converters/URITypeConverter.java new file mode 100644 index 00000000..8c7d04dd --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/converters/URITypeConverter.java @@ -0,0 +1,23 @@ +package fr.zcraft.zlib.components.commands2.converters; + +import fr.zcraft.zlib.components.commands2.ParameterTypeConverter; +import fr.zcraft.zlib.components.commands2.exceptions.InvalidArgumentException; + +import java.net.URI; +import java.net.URISyntaxException; + +public class URITypeConverter implements ParameterTypeConverter { + @Override + public Class getType() { + return URI.class; + } + + @Override + public URI fromArgument(String argument) throws InvalidArgumentException { + try { + return new URI(argument); + } catch (URISyntaxException e) { + throw new InvalidArgumentException("Invalid URI"); + } + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java index 5c86bf7d..742b14e1 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java @@ -31,5 +31,17 @@ package fr.zcraft.zlib.components.commands2.exceptions; public class InvalidArgumentException extends CommandException { + private final String argument; + public InvalidArgumentException() { + this(null); + } + + public InvalidArgumentException(String argument) { + this.argument = argument; + } + + public String getArgument() { + return argument; + } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnhandledParameterType.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnhandledParameterType.java new file mode 100644 index 00000000..bb0f2e0b --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnhandledParameterType.java @@ -0,0 +1,26 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +import java.lang.reflect.Field; + +public class UnhandledParameterType extends CommandException { + private final Class type; + private final Field field; + + public UnhandledParameterType(Class type, Field field) { + this.type = type; + this.field = field; + } + + public Class getType() { + return type; + } + + private Field getField() { + return field; + } + + @Override + public String getMessage() { + return "Type error in " + field.getDeclaringClass().getName() + "." + field.getName() + " : no parameter type converters found for type " + type.getName(); + } +} diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java b/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java index 73fc2f6e..15c82387 100644 --- a/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java +++ b/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java @@ -37,23 +37,36 @@ import org.junit.Assert; import org.junit.Test; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Optional; -public class CommandsTest +public class CommandsTest { static private final CommandSender sender = new CommandSender(); @Test - public void iomTest() throws CommandException { + public void iomTest() throws CommandException, URISyntaxException { Commands.register(IoMCommand.class, "maptool"); Commands.register(CreateCommand.class, "tomap"); Context mapToolContext = Commands.makeContext("maptool", sender, new String[]{"list"}); - Assert.assertEquals(mapToolContext.getParentContext().map(Context::getCommandRunnable), Optional.of(IoMCommand.LIST)); + Assert.assertEquals(Optional.of(IoMCommand.LIST), mapToolContext.getParentContext().map(Context::getCommandRunnable)); Assert.assertTrue(mapToolContext.getCommandRunnable() instanceof IoMCommand.ListCommand); + + Context createContext = Commands.makeContext("maptool", sender, new String[]{"create", "http://example.com/test.png", "-w", "42", "--stretch"}); + Assert.assertTrue(createContext.getCommandRunnable() instanceof CreateCommand); + CreateCommand createRunnable = (CreateCommand) createContext.getCommandRunnable(); + + Assert.assertEquals(new URI("http://example.com/test.png"), createRunnable.imageURI); + Assert.assertEquals(Optional.of(42), createRunnable.width); + Assert.assertEquals(Optional.empty(), createRunnable.height); + Assert.assertEquals(true, createRunnable.stretch); + Assert.assertEquals(false, createRunnable.cover); + } @Test - public void bbTest() { + public void bbTest() throws CommandException { Commands.registerParameterTypeConverter(new BBCommand.BBItemParamConverter()); Commands.register(BBCommand.class, "bb"); } diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java b/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java index 4ba2d694..44ec8e97 100644 --- a/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java +++ b/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java @@ -56,7 +56,7 @@ public Class getType() { @Override public BBItem fromArgument(String argument) throws InvalidArgumentException { argument = argument.toLowerCase(); - if(!Arrays.asList(BBItem.items).contains(argument)) throw new InvalidArgumentException(); + if(!Arrays.asList(BBItem.items).contains(argument)) throw new InvalidArgumentException(argument); return null; } } From 61d34aaab26232cccb4fd868b9940b0eab8bb818 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Tue, 6 Mar 2018 02:04:08 +0100 Subject: [PATCH 4/4] Better exception handling, minor refactorings. * NEW: Added exception types for all raised command parsing errors. * NEW: Removed type parameter on Field, Flag and Parameter types. * NEW: Overall better error handling. --- .../zlib/components/commands2/Command.java | 22 ++-- .../commands2/CommandGenerator.java | 103 ++++++++------- .../zlib/components/commands2/Commands.java | 15 +-- .../commands2/ContextGenerator.java | 117 +++++++++++------- .../zlib/components/commands2/Field.java | 12 +- .../zlib/components/commands2/Flag.java | 4 +- .../zlib/components/commands2/Parameter.java | 4 +- .../commands2/ParameterTypeConverter.java | 6 +- .../converters/IntegerTypeConverter.java | 8 +- .../converters/URITypeConverter.java | 6 +- .../exceptions/ArgumentException.java | 67 ++++++++++ .../CommandConfigurationException.java | 19 +++ .../exceptions/ExtraArgumentException.java | 7 ++ .../FlagAlreadyDefinedException.java | 16 +++ .../exceptions/FlagMissingValueException.java | 18 +++ .../exceptions/InvalidArgumentException.java | 51 ++------ .../exceptions/MissingArgumentException.java | 5 + .../exceptions/MissingParameterException.java | 15 +++ .../MissingRequiredFlagException.java | 15 +++ .../MissingSubcommandException.java | 15 +++ .../ParameterTypeConverterException.java | 29 +++++ .../exceptions/UnhandledParameterType.java | 3 +- .../exceptions/UnknownFlagException.java | 7 ++ .../UnknownSubcommandException.java | 16 +++ .../components/commands2/CommandsTest.java | 6 + .../components/commands2/bb/BBCommand.java | 15 ++- 26 files changed, 426 insertions(+), 175 deletions(-) create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ArgumentException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandConfigurationException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ExtraArgumentException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/FlagAlreadyDefinedException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/FlagMissingValueException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingArgumentException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingParameterException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingRequiredFlagException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingSubcommandException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ParameterTypeConverterException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnknownFlagException.java create mode 100644 src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnknownSubcommandException.java diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Command.java b/src/main/java/fr/zcraft/zlib/components/commands2/Command.java index 2f6aabab..2a9706b4 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/Command.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Command.java @@ -30,6 +30,8 @@ package fr.zcraft.zlib.components.commands2; +import fr.zcraft.zlib.components.commands2.exceptions.CommandException; + import java.util.List; import java.util.Optional; @@ -43,10 +45,10 @@ public class Command { private final Class runnableClass; private final boolean isCommandGroup; private final List> subCommands; - private final List> parameters; - private final List> flags; + private final List parameters; + private final List flags; - Command(Class runnableClass, String name, boolean isCommandGroup, List> subCommands, List> parameters, List> flags) { + Command(Class runnableClass, String name, boolean isCommandGroup, List> subCommands, List parameters, List flags) { this.runnableClass = runnableClass; this.name = name; this.isCommandGroup = isCommandGroup; @@ -55,7 +57,7 @@ public class Command { this.flags = flags; } - public Context makeContext(CommandSender sender, String[] arguments) { + public Context makeContext(CommandSender sender, String[] arguments) throws CommandException { return ContextGenerator.makeContext(this, sender, arguments, Optional.empty()); } @@ -86,23 +88,23 @@ public Optional> getSubCommand(String name) { return Optional.empty(); } - public List> getParameters() { + public List getParameters() { return parameters; } - public List> getFlags() { + public List getFlags() { return flags; } - public Optional> getFlag(String shortName) { - for(Flag flag: flags) { + public Optional getFlag(String shortName) { + for(Flag flag: flags) { if(flag.getName().equals(shortName)) return Optional.of(flag); } return Optional.empty(); } - public Optional> getShortFlag(String shortName) { - for(Flag flag: flags) { + public Optional getShortFlag(String shortName) { + for(Flag flag: flags) { if(flag.getShortName().equals(shortName)) return Optional.of(flag); } return Optional.empty(); diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java b/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java index bc1b3202..f8b7fee5 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/CommandGenerator.java @@ -31,20 +31,22 @@ package fr.zcraft.zlib.components.commands2; import fr.zcraft.zlib.components.commands2.annotations.Subcommand; -import fr.zcraft.zlib.components.commands2.exceptions.CommandException; +import fr.zcraft.zlib.components.commands2.exceptions.CommandConfigurationException; import fr.zcraft.zlib.components.commands2.exceptions.UnhandledParameterType; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; /** * This class contains various utilities to generate Command objects from their bound runnable class. */ class CommandGenerator { - static public Command fromClass(Class runnableClass, String name) throws CommandException { + static public Command fromClass(Class runnableClass, String name) { if(isCommandGroup(runnableClass)) { return fromEnumClass(runnableClass, name); } else { @@ -56,90 +58,95 @@ static private boolean isCommandGroup(Class runnableC return Enum.class.isAssignableFrom(runnableClass); } - static private Command fromEnumClass(Class runnableClass, String name) throws CommandException { - List> subcommands = new ArrayList<>(); + static private Command fromEnumClass(final Class runnableClass, String name) { + List> subcommands = Arrays.stream(runnableClass.getDeclaredFields()) + .filter(Field::isEnumConstant) + .map(f -> subcommandFromField(f, runnableClass)) + .collect(Collectors.toList()); - for(Field f : runnableClass.getDeclaredFields()) { - if(!f.isEnumConstant()) continue; - subcommands.add(subcommandFromField(f)); - } - - return new Command(runnableClass, name, true, subcommands, new ArrayList<>(), new ArrayList<>()); + return new Command<>(runnableClass, name, true, subcommands, new ArrayList<>(), new ArrayList<>()); } - static private List> getCommandParameters(Class runnableClass) throws UnhandledParameterType { - List> parameters = new ArrayList<>(); - - for(Field f : runnableClass.getDeclaredFields()) { - if(isFlagField(f)) continue; - parameters.add(parameterFromField(f)); - } - - return parameters; + static private List getCommandParameters(Class runnableClass) throws UnhandledParameterType { + return Arrays.stream(runnableClass.getDeclaredFields()) + .filter(f -> !isFlagField(f)) + .map(CommandGenerator::parameterFromField) + .collect(Collectors.toList()); } - static private List> getCommandFlags(Class runnableClass) throws UnhandledParameterType { - List> parameters = new ArrayList<>(); - - for(Field f : runnableClass.getDeclaredFields()) { - if(!isFlagField(f)) continue; - parameters.add(flagFromField(f)); - } - - return parameters; + static private List getCommandFlags(Class runnableClass) throws UnhandledParameterType { + return Arrays.stream(runnableClass.getDeclaredFields()) + .filter(CommandGenerator::isFlagField) + .map(CommandGenerator::flagFromField) + .collect(Collectors.toList()); } static private boolean isFlagField(Field field) { return field.getAnnotation(fr.zcraft.zlib.components.commands2.annotations.Flag.class) != null; } - static private Parameter parameterFromField(Field field) throws UnhandledParameterType { - Class type = field.getType(); - ParameterTypeConverter typeConverter = Commands.findTypeConverter(type).orElseThrow(() -> new UnhandledParameterType(type, field)); - return new Parameter<>(type, field, typeConverter, field.getName(), null, true); + static private Parameter parameterFromField(Field field) throws UnhandledParameterType { + return new Parameter(field.getType(), field, findTypeConverter(field), field.getName(), null, isFieldRequired(field)); } - static private Flag flagFromField(Field field) throws UnhandledParameterType { - @SuppressWarnings("unchecked") - Class type = (Class) field.getType(); + static private Flag flagFromField(Field field) throws UnhandledParameterType { + Class type = field.getType(); fr.zcraft.zlib.components.commands2.annotations.Flag flagAnnotation = field.getAnnotation(fr.zcraft.zlib.components.commands2.annotations.Flag.class); String shortName = flagAnnotation.shortName(); if(boolean.class.isAssignableFrom(type)) { - return new Flag<>(type, field, null, field.getName(), shortName,null, false, false); + return new Flag(type, field, null, field.getName(), shortName,null, false, false); } - boolean isRequired = true; + boolean isRequired = isFieldRequired(field); + return new Flag(type, field, findTypeConverter(field), field.getName(), shortName, null, isRequired, true); + } + + static private Class getFieldType(Field field) { + Class type = field.getType(); if(Optional.class.isAssignableFrom(type)) { ParameterizedType ptype = (ParameterizedType) field.getGenericType(); - type = (Class) ptype.getActualTypeArguments()[0]; - isRequired = false; + type = (Class) ptype.getActualTypeArguments()[0]; } + return type; + } - Optional> typeConverter = Commands.findTypeConverter(type); + static private ParameterTypeConverter findTypeConverter(Field field) throws UnhandledParameterType { + Class type = getFieldType(field); + Optional> typeConverter = Commands.findTypeConverter(type); if(!typeConverter.isPresent()) throw new UnhandledParameterType(type, field); - return new Flag<>(type, field, typeConverter.get(), field.getName(), shortName, null, isRequired, true); + return typeConverter.get(); + } + + static private boolean isFieldRequired(Field f) { + return !Optional.class.isAssignableFrom(f.getType()); } - static private Command fromPlainClass(Class runnableClass, String name) throws CommandException { + static private Command fromPlainClass(Class runnableClass, String name) { return new Command<>(runnableClass, name, false, new ArrayList<>(), getCommandParameters(runnableClass), getCommandFlags(runnableClass)); } - static private SubCommand subcommandFromField(Field field) throws CommandException { + static private SubCommand subcommandFromField(Field field, Class parentCommandClass) { Subcommand subcommand = field.getAnnotation(Subcommand.class); - if(subcommand == null) throw new RuntimeException("No subcommand annotation"); //TODO: Better exception + if(subcommand == null) throw new CommandConfigurationException(parentCommandClass, "Missing subcommand annotation on field: " + field.getName()); String commandName = subcommand.name(); if(commandName.isEmpty()) commandName = field.getName().toLowerCase(); Command innerCommand = fromClass(subcommand.value(), commandName); - T parentValue; + Object parentFieldValue; try { field.setAccessible(true); - parentValue = (T) field.get(null); + parentFieldValue = field.get(null); + if(!parentCommandClass.isInstance(parentFieldValue)) { + throw new CommandConfigurationException(parentCommandClass, "Invalid enum field: " + field.getName()); + } } catch (IllegalAccessException e) { - throw new RuntimeException(e); //TODO: Better exception + throw new CommandConfigurationException(parentCommandClass, "Unable to access field: " + field.getName()); } - return new SubCommand(innerCommand, parentValue, field); + @SuppressWarnings ("unchecked") + T parentValue = (T) parentFieldValue; + + return new SubCommand<>(innerCommand, parentValue, field); } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java b/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java index b183ccf6..f2a00e42 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Commands.java @@ -34,11 +34,9 @@ import fr.zcraft.zlib.components.commands2.converters.URITypeConverter; import fr.zcraft.zlib.components.commands2.exceptions.CommandException; import fr.zcraft.zlib.components.commands2.exceptions.CommandNotFoundException; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.Optional; /** @@ -48,15 +46,15 @@ public abstract class Commands { private Commands() {} - static private Map> commandRegistry = new HashMap<>(); - static private Map, ParameterTypeConverter> typeConverterRegistry = new HashMap<>(); + static private final Map> commandRegistry = new HashMap<>(); + static private final Map, ParameterTypeConverter> typeConverterRegistry = new HashMap<>(); /** * Registers a new command. * @param command The command runnable to be registered * @param name The name of the command */ - static public void register(Class command, String name) throws CommandException { + static public void register(Class command, String name) { name = name.toLowerCase(); if(commandRegistry.containsKey(name)) throw new IllegalArgumentException("Command already registered : " + name); @@ -89,13 +87,10 @@ static public Command findCommand(String commandName) throws CommandNotFoundExce /** * Finds the registered command parameter type converter handling the given type. * @param type The type used to look up the matching type converter - * @param The type the command parameter type converter handles * @return The registered command parameter type converter, if found */ - static public Optional> findTypeConverter(Class type) { - @SuppressWarnings("unchecked") - ParameterTypeConverter typeConverter = (ParameterTypeConverter) typeConverterRegistry.get(type); - return Optional.ofNullable(typeConverter); + static public Optional> findTypeConverter(Class type) { + return Optional.ofNullable(typeConverterRegistry.get(type)); } /** diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java b/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java index 8b339bcc..d9f54982 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/ContextGenerator.java @@ -30,8 +30,8 @@ package fr.zcraft.zlib.components.commands2; +import fr.zcraft.zlib.components.commands2.exceptions.*; import fr.zcraft.zlib.tools.reflection.Reflection; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.util.ArrayList; import java.util.Arrays; @@ -44,85 +44,118 @@ abstract class ContextGenerator { private ContextGenerator() {} - private static Context makeEnumContext(Command command, CommandSender sender, String[] arguments, Optional parentContext) throws Exception { - if(arguments.length < 1) throw new Exception("not enough arguments");//TODO: Better exceptions - SubCommand subCommand = command.getSubCommand(arguments[0]).orElseThrow(Exception::new); + private static Context makeEnumContext(Command command, CommandSender sender, String[] arguments, Optional parentContext) throws ArgumentException { + if(arguments.length < 1) throw new MissingSubcommandException(command); + SubCommand subCommand = command.getSubCommand(arguments[0]).orElseThrow(() -> new UnknownSubcommandException(command, arguments[0], 0)); return new Context<>(subCommand.getParentEnumValue(), sender, arguments, command, parentContext, Optional.of(subCommand)); } - static Context makeContext(Command command, CommandSender sender, String[] arguments, Optional parentContext) { + static Context makeContext(Command command, CommandSender sender, String[] arguments, Optional parentContext) throws ArgumentException { + if(!command.isCommandGroup()) { + return makePlainClassContext(command, sender, arguments, parentContext); + } + + Context context = makeEnumContext(command, sender, arguments, parentContext); + SubCommand subCommand = context.getMatchedSubCommand().orElseThrow(NullPointerException::new); try { - if(command.isCommandGroup()) { - Context context = makeEnumContext(command, sender, arguments, parentContext); - SubCommand subCommand = context.getMatchedSubCommand().orElseThrow(NullPointerException::new); - return makeContext(subCommand.getCommand(), sender, Arrays.copyOfRange(arguments, 1, arguments.length), Optional.of(context)); - } else { - return makePlainClassContext(command, sender, arguments, parentContext); - } - } catch (Throwable e) { - throw new RuntimeException(e); + return makeContext(subCommand.getCommand(), sender, Arrays.copyOfRange(arguments, 1, arguments.length), Optional.of(context)); + } catch(ArgumentException e) { + e.setArgumentPosition(e.getArgumentPosition().map(i -> i + 1)); + throw e; } } - - private static Context makePlainClassContext(Command command, CommandSender sender, String[] arguments, Optional parentContext) throws Exception { - T runnable = Reflection.instantiate(command.getRunnableClass()); - List> parameters = command.getParameters(); - List> flags = command.getFlags(); - List> remainingFlags = new ArrayList<>(flags); + private static Context makePlainClassContext(Command command, CommandSender sender, String[] arguments, Optional parentContext) throws ArgumentException { + T runnable = instanciateCommandRunnable(command); + List parameters = command.getParameters(); + List flags = command.getFlags(); + List remainingFlags = new ArrayList<>(flags); int argumentsI = 0; int parametersI = 0; for(; argumentsI < arguments.length; ++argumentsI) { String argument = arguments[argumentsI]; - Flag flag = null; + Flag flag = null; if(argument.startsWith("--")) { argument = argument.substring(2); - Optional> oflag = command.getFlag(argument); - if (!oflag.isPresent()) throw new RuntimeException("unknown flag: " + argument); + Optional oflag = command.getFlag(argument); + if (!oflag.isPresent()) throw new UnknownFlagException(argument, argumentsI); flag = oflag.get(); } else if(argument.startsWith("-")) { argument = argument.substring(1); - Optional> oflag = command.getShortFlag(argument); - if (!oflag.isPresent()) throw new RuntimeException("unknown flag: " + argument); + Optional oflag = command.getShortFlag(argument); + if (!oflag.isPresent()) throw new UnknownFlagException(argument, argumentsI); flag = oflag.get(); } if(flag != null) { - if (!remainingFlags.remove(flag)) throw new RuntimeException("flag already defined: " + argument); + if (!remainingFlags.remove(flag)) throw new FlagAlreadyDefinedException(flag, argument, argumentsI); if (!flag.hasValue()) { - flag.getRunnableField().set(runnable, true); + setRunnableField(runnable, flag, true); } else { - if (argumentsI + 1 >= arguments.length) throw new RuntimeException("Missing value for flag :" + argument); + if (argumentsI + 1 >= arguments.length) throw new FlagMissingValueException(flag, argumentsI); ++argumentsI; - String flagValue = arguments[argumentsI]; + Object runnableValue = convertFieldType(flag, arguments[argumentsI], argumentsI); if (flag.isRequired()) { - flag.getRunnableField().set(runnable, flag.getTypeConverter().fromArgument(flagValue)); + setRunnableField(runnable, flag, runnableValue); } else { - flag.getRunnableField().set(runnable, Optional.of(flag.getTypeConverter().fromArgument(flagValue))); + setRunnableField(runnable, flag, Optional.of(runnableValue)); } } continue; } - if(parametersI >= parameters.size()) throw new RuntimeException("too many arguments"); - Parameter parameter = parameters.get(argumentsI); - parameter.getRunnableField().set(runnable, parameter.getTypeConverter().fromArgument(arguments[argumentsI])); + if(parametersI >= parameters.size()) throw new ExtraArgumentException(arguments[argumentsI], argumentsI); + Parameter parameter = parameters.get(argumentsI); + setRunnableField(runnable, parameter, convertFieldType(parameter, arguments[argumentsI], argumentsI)); ++parametersI; } - if(!remainingFlags.isEmpty()) { - for(Flag f: remainingFlags) { - if(f.isRequired()) throw new RuntimeException("missing required flag : " + f.getName()); - if(f.hasValue()) { - f.getRunnableField().set(runnable, Optional.empty()); - } else { - f.getRunnableField().set(runnable, false); - } + //Look for missing required parameters + for(; parametersI < parameters.size(); ++parametersI) { + Parameter parameter = parameters.get(parametersI); + if(parameter.isRequired()) { + throw new MissingParameterException(parameter); + } + setRunnableField(runnable, parameter, Optional.empty()); + } + + //Look for missing required flags + for(Flag f: remainingFlags) { + if(f.isRequired()) throw new MissingRequiredFlagException(f); + if(f.hasValue()) { + setRunnableField(runnable, f, Optional.empty()); + } else { + setRunnableField(runnable, f, false); } } + return new Context<>(runnable, sender, arguments, command, parentContext, Optional.empty()); } + + static private void setRunnableField(CommandRunnable runnable, Field field, Object value) { + try { + field.getRunnableField().set(runnable, value); + } catch (IllegalAccessException e) { + throw new RuntimeException("Unable to set runnable class field : " + runnable.getClass() + "." + field.getRunnableField().getName(), e); + } + } + + static private T instanciateCommandRunnable(Command command) { + try { + return Reflection.instantiate(command.getRunnableClass()); + } catch(Exception e) { + throw new RuntimeException("Unable to instanciate Command runnable class : " + command.getRunnableClass().getName(), e); + } + } + + static private Object convertFieldType(Field parameter, String argument, int argumentPosition) throws InvalidArgumentException { + try { + return parameter.getTypeConverter().fromArgument(argument); + } catch (ParameterTypeConverterException e) { + throw new InvalidArgumentException(e, argument, argumentPosition); + } + } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Field.java b/src/main/java/fr/zcraft/zlib/components/commands2/Field.java index 6ca72c82..e235c283 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/Field.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Field.java @@ -1,15 +1,15 @@ package fr.zcraft.zlib.components.commands2; -public class Field { - private final Class parameterType; +class Field { + private final Class parameterType; private final java.lang.reflect.Field runnableField; - private final ParameterTypeConverter typeConverter; + private final ParameterTypeConverter typeConverter; private final String name; private final String about; private final boolean isRequired; - Field(Class parameterType, java.lang.reflect.Field runnableField, ParameterTypeConverter typeConverter, String name, String about, boolean isRequired) { + Field(Class parameterType, java.lang.reflect.Field runnableField, ParameterTypeConverter typeConverter, String name, String about, boolean isRequired) { this.parameterType = parameterType; this.runnableField = runnableField; this.typeConverter = typeConverter; @@ -19,11 +19,11 @@ public class Field { this.isRequired = isRequired; } - public Class getParameterType() { + public Class getParameterType() { return parameterType; } - public ParameterTypeConverter getTypeConverter() { + public ParameterTypeConverter getTypeConverter() { return typeConverter; } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Flag.java b/src/main/java/fr/zcraft/zlib/components/commands2/Flag.java index bde60f41..2bfcdcc8 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/Flag.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Flag.java @@ -1,10 +1,10 @@ package fr.zcraft.zlib.components.commands2; -class Flag extends Field { +public class Flag extends Field { private final boolean hasValue; private final String shortName; - Flag(Class parameterType, java.lang.reflect.Field runnableField, ParameterTypeConverter typeConverter, String name, String shortName, String about, boolean isRequired, boolean hasValue) { + Flag(Class parameterType, java.lang.reflect.Field runnableField, ParameterTypeConverter typeConverter, String name, String shortName, String about, boolean isRequired, boolean hasValue) { super(parameterType, runnableField, typeConverter, name, about, isRequired); this.hasValue = hasValue; this.shortName = shortName; diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/Parameter.java b/src/main/java/fr/zcraft/zlib/components/commands2/Parameter.java index cc1a8096..644dd25c 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/Parameter.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/Parameter.java @@ -1,7 +1,7 @@ package fr.zcraft.zlib.components.commands2; -class Parameter extends Field { - Parameter(Class parameterType, java.lang.reflect.Field runnableField, ParameterTypeConverter typeConverter, String name, String about, boolean isRequired) { +public class Parameter extends Field { + Parameter(Class parameterType, java.lang.reflect.Field runnableField, ParameterTypeConverter typeConverter, String name, String about, boolean isRequired) { super(parameterType, runnableField, typeConverter, name, about, isRequired); } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/ParameterTypeConverter.java b/src/main/java/fr/zcraft/zlib/components/commands2/ParameterTypeConverter.java index 78ca9eb7..7ed872d7 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/ParameterTypeConverter.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/ParameterTypeConverter.java @@ -30,13 +30,13 @@ package fr.zcraft.zlib.components.commands2; -import fr.zcraft.zlib.components.commands2.exceptions.InvalidArgumentException; +import fr.zcraft.zlib.components.commands2.exceptions.ParameterTypeConverterException; /** * An interface used to parse types from command arguments. * @param */ public interface ParameterTypeConverter { - public Class getType(); - public T fromArgument(String argument) throws InvalidArgumentException; + Class getType(); + T fromArgument(String argument) throws ParameterTypeConverterException; } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/converters/IntegerTypeConverter.java b/src/main/java/fr/zcraft/zlib/components/commands2/converters/IntegerTypeConverter.java index 4ccb8c45..cb22a386 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/converters/IntegerTypeConverter.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/converters/IntegerTypeConverter.java @@ -1,7 +1,7 @@ package fr.zcraft.zlib.components.commands2.converters; import fr.zcraft.zlib.components.commands2.ParameterTypeConverter; -import fr.zcraft.zlib.components.commands2.exceptions.InvalidArgumentException; +import fr.zcraft.zlib.components.commands2.exceptions.ParameterTypeConverterException; public class IntegerTypeConverter implements ParameterTypeConverter { @Override @@ -10,11 +10,11 @@ public Class getType() { } @Override - public Integer fromArgument(String argument) throws InvalidArgumentException { + public Integer fromArgument(String argument) throws ParameterTypeConverterException { try { return Integer.parseInt(argument); - } catch(NumberFormatException ex) { - throw new InvalidArgumentException("Invalid integer"); + } catch(NumberFormatException e) { + throw new ParameterTypeConverterException("Invalid integer", e); } } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/converters/URITypeConverter.java b/src/main/java/fr/zcraft/zlib/components/commands2/converters/URITypeConverter.java index 8c7d04dd..64cc2710 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/converters/URITypeConverter.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/converters/URITypeConverter.java @@ -1,7 +1,7 @@ package fr.zcraft.zlib.components.commands2.converters; import fr.zcraft.zlib.components.commands2.ParameterTypeConverter; -import fr.zcraft.zlib.components.commands2.exceptions.InvalidArgumentException; +import fr.zcraft.zlib.components.commands2.exceptions.ParameterTypeConverterException; import java.net.URI; import java.net.URISyntaxException; @@ -13,11 +13,11 @@ public Class getType() { } @Override - public URI fromArgument(String argument) throws InvalidArgumentException { + public URI fromArgument(String argument) throws ParameterTypeConverterException { try { return new URI(argument); } catch (URISyntaxException e) { - throw new InvalidArgumentException("Invalid URI"); + throw new ParameterTypeConverterException("Invalid URI", e); } } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ArgumentException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ArgumentException.java new file mode 100644 index 00000000..d5d11760 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ArgumentException.java @@ -0,0 +1,67 @@ +/* + * Copyright or © or Copr. ZLib contributors (2015 - 2016) + * + * This software is governed by the CeCILL-B license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-B + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-B license and that you accept its terms. + */ + +package fr.zcraft.zlib.components.commands2.exceptions; + +import java.util.Optional; + +public abstract class ArgumentException extends CommandException { + private final Optional argument; + private Optional argumentPosition; + + public ArgumentException() { + this(Optional.empty(), Optional.empty()); + } + + public ArgumentException(String argument) { + this(Optional.of(argument), Optional.empty()); + } + + public ArgumentException(String argument, int argumentPosition) { + this(Optional.of(argument), Optional.of(argumentPosition)); + } + + public ArgumentException(Optional argument, Optional argumentPosition) { + this.argument = argument; + this.argumentPosition = argumentPosition; + } + + public Optional getArgument() { + return argument; + } + + public Optional getArgumentPosition() { + return argumentPosition; + } + + public void setArgumentPosition(Optional argumentPosition) { + this.argumentPosition = argumentPosition; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandConfigurationException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandConfigurationException.java new file mode 100644 index 00000000..d76d7dbd --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/CommandConfigurationException.java @@ -0,0 +1,19 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +public class CommandConfigurationException extends RuntimeException { + private final Class runnableClass; + private final String errorMessage; + + public CommandConfigurationException(Class runnableClass, String errorMessage) { + this.runnableClass = runnableClass; + this.errorMessage = errorMessage; + } + + public Class getRunnableClass() { + return runnableClass; + } + + public String getErrorMessage() { + return errorMessage; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ExtraArgumentException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ExtraArgumentException.java new file mode 100644 index 00000000..5a8fd926 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ExtraArgumentException.java @@ -0,0 +1,7 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +public class ExtraArgumentException extends ArgumentException { + public ExtraArgumentException(String argument, int position) { + super(argument, position); + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/FlagAlreadyDefinedException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/FlagAlreadyDefinedException.java new file mode 100644 index 00000000..d71f8340 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/FlagAlreadyDefinedException.java @@ -0,0 +1,16 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +import fr.zcraft.zlib.components.commands2.Flag; + +public class FlagAlreadyDefinedException extends ArgumentException { + private final Flag flag; + + public FlagAlreadyDefinedException(Flag flag, String argument, int position) { + super(argument, position); + this.flag = flag; + } + + public Flag getFlag() { + return flag; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/FlagMissingValueException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/FlagMissingValueException.java new file mode 100644 index 00000000..cf8a5d3d --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/FlagMissingValueException.java @@ -0,0 +1,18 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +import fr.zcraft.zlib.components.commands2.Flag; + +import java.util.Optional; + +public class FlagMissingValueException extends ArgumentException { + private final Flag flag; + + public FlagMissingValueException(Flag flag, int position) { + super(Optional.empty(), Optional.of(position)); + this.flag = flag; + } + + public Flag getFlag() { + return flag; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java index 742b14e1..6b56457e 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/InvalidArgumentException.java @@ -1,47 +1,22 @@ -/* - * Copyright or © or Copr. ZLib contributors (2015 - 2016) - * - * This software is governed by the CeCILL-B license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL-B - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL-B license and that you accept its terms. - */ - package fr.zcraft.zlib.components.commands2.exceptions; -public class InvalidArgumentException extends CommandException { - private final String argument; +import java.util.Optional; + +public class InvalidArgumentException extends ArgumentException { + private final String parseErrorMessage; + private final Optional parseErrorCause; - public InvalidArgumentException() { - this(null); + public InvalidArgumentException(ParameterTypeConverterException sourceException, String argument, int position) { + super(argument, position); + this.parseErrorMessage = sourceException.getParseErrorMessage(); + this.parseErrorCause = sourceException.getParseErrorCause(); } - public InvalidArgumentException(String argument) { - this.argument = argument; + public String getParseErrorMessage() { + return parseErrorMessage; } - public String getArgument() { - return argument; + public Optional getParseErrorCause() { + return parseErrorCause; } } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingArgumentException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingArgumentException.java new file mode 100644 index 00000000..db819108 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingArgumentException.java @@ -0,0 +1,5 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +public class MissingArgumentException extends ArgumentException { + +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingParameterException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingParameterException.java new file mode 100644 index 00000000..4f23cf43 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingParameterException.java @@ -0,0 +1,15 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +import fr.zcraft.zlib.components.commands2.Parameter; + +public class MissingParameterException extends ArgumentException { + private final Parameter flag; + + public MissingParameterException(Parameter flag) { + this.flag = flag; + } + + public Parameter getFlag() { + return flag; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingRequiredFlagException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingRequiredFlagException.java new file mode 100644 index 00000000..ce5c8bb1 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingRequiredFlagException.java @@ -0,0 +1,15 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +import fr.zcraft.zlib.components.commands2.Flag; + +public class MissingRequiredFlagException extends ArgumentException { + private final Flag flag; + + public MissingRequiredFlagException(Flag flag) { + this.flag = flag; + } + + public Flag getFlag() { + return flag; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingSubcommandException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingSubcommandException.java new file mode 100644 index 00000000..23c83b6e --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/MissingSubcommandException.java @@ -0,0 +1,15 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +import fr.zcraft.zlib.components.commands2.Command; + +public class MissingSubcommandException extends ArgumentException { + private final Command parentCommand; + + public MissingSubcommandException(Command parentCommand) { + this.parentCommand = parentCommand; + } + + public Command getParentCommand() { + return parentCommand; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ParameterTypeConverterException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ParameterTypeConverterException.java new file mode 100644 index 00000000..7c6e65ea --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/ParameterTypeConverterException.java @@ -0,0 +1,29 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +import java.util.Optional; + +public class ParameterTypeConverterException extends Exception { + private final String parseErrorMessage; + private final Optional cause; + + public ParameterTypeConverterException(String parseErrorMessage) { + this(parseErrorMessage, Optional.empty()); + } + + public ParameterTypeConverterException(String parseErrorMessage, Throwable cause) { + this(parseErrorMessage, Optional.of(cause)); + } + + public ParameterTypeConverterException(String parseErrorMessage, Optional cause) { + this.parseErrorMessage = parseErrorMessage; + this.cause = cause; + } + + public String getParseErrorMessage() { + return parseErrorMessage; + } + + public Optional getParseErrorCause() { + return cause; + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnhandledParameterType.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnhandledParameterType.java index bb0f2e0b..075dd8ae 100644 --- a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnhandledParameterType.java +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnhandledParameterType.java @@ -2,11 +2,12 @@ import java.lang.reflect.Field; -public class UnhandledParameterType extends CommandException { +public class UnhandledParameterType extends CommandConfigurationException { private final Class type; private final Field field; public UnhandledParameterType(Class type, Field field) { + super(field.getDeclaringClass(), ""); this.type = type; this.field = field; } diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnknownFlagException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnknownFlagException.java new file mode 100644 index 00000000..965acaa5 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnknownFlagException.java @@ -0,0 +1,7 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +public class UnknownFlagException extends ArgumentException { + public UnknownFlagException(String argument, int position) { + super(argument, position); + } +} diff --git a/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnknownSubcommandException.java b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnknownSubcommandException.java new file mode 100644 index 00000000..a7833360 --- /dev/null +++ b/src/main/java/fr/zcraft/zlib/components/commands2/exceptions/UnknownSubcommandException.java @@ -0,0 +1,16 @@ +package fr.zcraft.zlib.components.commands2.exceptions; + +import fr.zcraft.zlib.components.commands2.Command; + +public class UnknownSubcommandException extends ArgumentException { + private final Command parentCommand; + + public UnknownSubcommandException(Command parentCommand, String argument, int position) { + super(argument, position); + this.parentCommand = parentCommand; + } + + public Command getParentCommand() { + return parentCommand; + } +} diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java b/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java index 15c82387..692fb8fa 100644 --- a/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java +++ b/src/test/java/fr/zcraft/zlib/components/commands2/CommandsTest.java @@ -69,5 +69,11 @@ public void iomTest() throws CommandException, URISyntaxException { public void bbTest() throws CommandException { Commands.registerParameterTypeConverter(new BBCommand.BBItemParamConverter()); Commands.register(BBCommand.class, "bb"); + + Context bbContext = Commands.makeContext("bb", sender, new String[]{"saw"}); + Assert.assertTrue(bbContext.getCommandRunnable() instanceof BBCommand); + BBCommand bbRunnable = (BBCommand) bbContext.getCommandRunnable(); + Assert.assertEquals("saw", bbRunnable.item.itemType); + Assert.assertEquals(Optional.empty(), bbRunnable.amount); } } diff --git a/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java b/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java index 44ec8e97..593d3828 100644 --- a/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java +++ b/src/test/java/fr/zcraft/zlib/components/commands2/bb/BBCommand.java @@ -32,7 +32,7 @@ import fr.zcraft.zlib.components.commands2.CommandRunnable; import fr.zcraft.zlib.components.commands2.ParameterTypeConverter; -import fr.zcraft.zlib.components.commands2.exceptions.InvalidArgumentException; +import fr.zcraft.zlib.components.commands2.exceptions.ParameterTypeConverterException; import java.util.Arrays; import java.util.Optional; @@ -43,21 +43,24 @@ public class BBCommand implements CommandRunnable { static public class BBItem { static public final String[] items = {"saw", "stonecutter"}; - public String itemType; + public final String itemType; + + public BBItem(String itemType) { + this.itemType = itemType; + } } static public class BBItemParamConverter implements ParameterTypeConverter { - @Override public Class getType() { return BBItem.class; } @Override - public BBItem fromArgument(String argument) throws InvalidArgumentException { + public BBItem fromArgument(String argument) throws ParameterTypeConverterException { argument = argument.toLowerCase(); - if(!Arrays.asList(BBItem.items).contains(argument)) throw new InvalidArgumentException(argument); - return null; + if(!Arrays.asList(BBItem.items).contains(argument)) throw new ParameterTypeConverterException("Invalid item name"); + return new BBItem(argument); } } }