├── .gitignore ├── Bukkit ├── ebcm-bukkit.iml ├── pom.xml └── src │ └── main │ └── java │ └── me │ └── fixeddev │ └── ebcm │ └── bukkit │ ├── BukkitAuthorizer.java │ ├── BukkitCommandManager.java │ ├── BukkitCommandWrapper.java │ ├── BukkitMessager.java │ └── parameter │ └── provider │ ├── BukkitModule.java │ ├── CommandSenderProvider.java │ ├── OfflinePlayerProvider.java │ ├── PlayerProvider.java │ ├── PlayerSenderProvider.java │ └── annotation │ └── Sender.java ├── Bungee ├── Bungee.iml ├── pom.xml └── src │ └── main │ └── java │ └── me │ └── fixeddev │ └── ebcm │ └── bungee │ ├── BungeeAuthorizer.java │ ├── BungeeCommandManager.java │ ├── BungeeCommandWrapper.java │ ├── BungeeMessager.java │ └── parameter │ └── provider │ ├── BungeeModule.java │ ├── CommandSenderProvider.java │ ├── ProxiedPlayerProvider.java │ └── ProxiedPlayerSenderProvider.java ├── LICENSE ├── README.md ├── Universal ├── ebcm-universal.iml ├── pom.xml └── src │ └── main │ └── java │ └── me │ └── fixeddev │ └── ebcm │ ├── ArgumentStack.java │ ├── Authorizer.java │ ├── Command.java │ ├── CommandAction.java │ ├── CommandContext.java │ ├── CommandData.java │ ├── CommandManager.java │ ├── ImmutableCommand.java │ ├── Messager.java │ ├── MutableCommand.java │ ├── NamespaceAccesor.java │ ├── ParseResult.java │ ├── SimpleCommandManager.java │ ├── SuggestionProvider.java │ ├── exception │ ├── CommandException.java │ ├── CommandNotFound.java │ ├── CommandParseException.java │ ├── CommandUsageException.java │ ├── NoMoreArgumentsException.java │ └── NoPermissionException.java │ ├── internal │ ├── CommandLineParser.java │ ├── Messages.java │ └── namespace │ │ ├── Namespace.java │ │ ├── NamespaceAccessorDelegate.java │ │ └── SimpleCommandContext.java │ ├── parameter │ └── provider │ │ ├── InjectedProvider.java │ │ ├── Key.java │ │ ├── ParameterProvider.java │ │ ├── ParameterProviderRegistry.java │ │ ├── ParameterProviderRegistryImpl.java │ │ ├── ProvidersModule.java │ │ ├── SimpleResult.java │ │ ├── SingleArgumentProvider.java │ │ └── defaults │ │ ├── BooleanProvider.java │ │ ├── DefaultsModule.java │ │ ├── DoubleProvider.java │ │ ├── IntProvider.java │ │ └── StringProvider.java │ ├── parametric │ ├── CommandClass.java │ ├── ParametricCommandBuilder.java │ ├── ReflectionParametricCommandBuilder.java │ └── annotation │ │ ├── ACommand.java │ │ ├── ConsumedArgs.java │ │ ├── Default.java │ │ ├── Flag.java │ │ ├── Injected.java │ │ ├── ModifierAnnotation.java │ │ ├── Named.java │ │ ├── Optional.java │ │ ├── ParentArg.java │ │ ├── Required.java │ │ ├── SubCommandClasses.java │ │ └── Usage.java │ ├── part │ ├── ArgumentConsumingPart.java │ ├── ArgumentPart.java │ ├── CommandPart.java │ ├── FlagPart.java │ ├── InjectedValuePart.java │ ├── LineConsumingPart.java │ ├── SubCommandPart.java │ └── package-info.java │ └── util │ ├── ListAppender.java │ └── UsageBuilder.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /EBCM.iml 3 | /Universal/target/ 4 | -------------------------------------------------------------------------------- /Bukkit/ebcm-bukkit.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Bukkit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | ebcm 7 | me.fixeddev 8 | 1.10 9 | 10 | 4.0.0 11 | 12 | ${bukkit-version} 13 | 14 | ebcm-bukkit 15 | 16 | 17 | 18 | org.apache.maven.plugins 19 | maven-compiler-plugin 20 | 21 | 8 22 | 8 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | me.fixeddev 31 | ebcm-universal 32 | ${universal-version} 33 | compile 34 | 35 | 36 | org.spigotmc 37 | spigot-api 38 | 1.8.8-R0.1-SNAPSHOT 39 | provided 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Bukkit/src/main/java/me/fixeddev/ebcm/bukkit/BukkitAuthorizer.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bukkit; 2 | 3 | import me.fixeddev.ebcm.Authorizer; 4 | import me.fixeddev.ebcm.NamespaceAccesor; 5 | import org.bukkit.command.CommandSender; 6 | 7 | public class BukkitAuthorizer implements Authorizer { 8 | @Override 9 | public boolean isAuthorized(NamespaceAccesor namespace, String permission) { 10 | if(permission.isEmpty()){ 11 | return true; 12 | } 13 | 14 | CommandSender sender = namespace.getObject(CommandSender.class, BukkitCommandManager.SENDER_NAMESPACE); 15 | 16 | return sender.hasPermission(permission); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Bukkit/src/main/java/me/fixeddev/ebcm/bukkit/BukkitCommandManager.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bukkit; 2 | 3 | import me.fixeddev.ebcm.Authorizer; 4 | import me.fixeddev.ebcm.Command; 5 | import me.fixeddev.ebcm.CommandManager; 6 | import me.fixeddev.ebcm.Messager; 7 | import me.fixeddev.ebcm.NamespaceAccesor; 8 | import me.fixeddev.ebcm.ParseResult; 9 | import me.fixeddev.ebcm.exception.CommandException; 10 | import me.fixeddev.ebcm.exception.CommandNotFound; 11 | import me.fixeddev.ebcm.exception.CommandParseException; 12 | import me.fixeddev.ebcm.exception.NoPermissionException; 13 | import me.fixeddev.ebcm.parameter.provider.ParameterProviderRegistry; 14 | import org.bukkit.Bukkit; 15 | import org.bukkit.command.CommandMap; 16 | 17 | import java.lang.reflect.Field; 18 | import java.util.List; 19 | import java.util.Optional; 20 | import java.util.logging.Level; 21 | 22 | public class BukkitCommandManager implements CommandManager { 23 | public static final String SENDER_NAMESPACE = "SENDER"; 24 | private CommandManager delegate; 25 | private CommandMap bukkitCommandMap; 26 | private String fallbackPrefix; 27 | 28 | 29 | public BukkitCommandManager(CommandManager delegate, String fallbackPrefix) { 30 | this.delegate = delegate; 31 | this.fallbackPrefix = fallbackPrefix; 32 | 33 | try { 34 | Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap"); 35 | commandMapField.setAccessible(true); 36 | 37 | bukkitCommandMap = (CommandMap) commandMapField.get(Bukkit.getServer()); 38 | } catch (NoSuchFieldException | IllegalAccessException ex) { 39 | Bukkit.getLogger().log(Level.SEVERE, "Failed to get command map: ", ex); 40 | } 41 | } 42 | 43 | public void registerCommand(Command command) { 44 | delegate.registerCommand(command); 45 | 46 | org.bukkit.command.Command bukkitCommand = new BukkitCommandWrapper(command, this); 47 | bukkitCommandMap.register(fallbackPrefix, bukkitCommand); 48 | } 49 | 50 | public void registerCommands(List commandList) { 51 | for (Command command : commandList) { 52 | registerCommand(command); 53 | } 54 | } 55 | 56 | public boolean exists(String commandName) { 57 | return delegate.exists(commandName); 58 | } 59 | 60 | public ParameterProviderRegistry getProviderRegistry() { 61 | return delegate.getProviderRegistry(); 62 | } 63 | 64 | @Override 65 | public Authorizer getAuthorizer() { 66 | return delegate.getAuthorizer(); 67 | } 68 | 69 | @Override 70 | public Messager getMessager() { 71 | return delegate.getMessager(); 72 | } 73 | 74 | public Optional getCommand(String commandName) { 75 | return delegate.getCommand(commandName); 76 | } 77 | 78 | public boolean execute(NamespaceAccesor accessor, List arguments) throws CommandParseException, CommandException { 79 | return delegate.execute(accessor, arguments); 80 | } 81 | 82 | @Override 83 | public List getSuggestions(NamespaceAccesor accessor, List arguments) throws NoPermissionException { 84 | return delegate.getSuggestions(accessor, arguments); 85 | } 86 | 87 | public ParseResult parse(NamespaceAccesor accessor, List arguments) throws CommandParseException, CommandNotFound { 88 | return delegate.parse(accessor, arguments); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /Bukkit/src/main/java/me/fixeddev/ebcm/bukkit/BukkitCommandWrapper.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bukkit; 2 | 3 | import me.fixeddev.ebcm.Authorizer; 4 | import me.fixeddev.ebcm.CommandManager; 5 | import me.fixeddev.ebcm.exception.CommandException; 6 | import me.fixeddev.ebcm.exception.CommandParseException; 7 | import me.fixeddev.ebcm.exception.CommandUsageException; 8 | import me.fixeddev.ebcm.exception.NoPermissionException; 9 | import me.fixeddev.ebcm.internal.namespace.Namespace; 10 | import me.fixeddev.ebcm.util.UsageBuilder; 11 | import org.bukkit.ChatColor; 12 | import org.bukkit.command.Command; 13 | import org.bukkit.command.CommandSender; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.Collections; 18 | import java.util.List; 19 | 20 | public class BukkitCommandWrapper extends Command { 21 | private CommandManager commandManager; 22 | 23 | public BukkitCommandWrapper(me.fixeddev.ebcm.Command command, CommandManager dispatcher) { 24 | super(command.getData().getName()); 25 | 26 | this.setAliases(command.getData().getAliases()); 27 | 28 | this.setDescription(command.getData().getDescription()); 29 | this.setUsage(UsageBuilder.getUsageForCommand(null, command, "")); 30 | 31 | this.setPermission(command.getPermission()); 32 | this.setPermissionMessage(command.getPermissionMessage()); 33 | 34 | this.commandManager = dispatcher; 35 | } 36 | 37 | 38 | @Override 39 | public boolean execute(CommandSender commandSender, String label, String[] args) { 40 | List argumentLine = new ArrayList<>(); 41 | 42 | argumentLine.add(label); 43 | argumentLine.addAll(Arrays.asList(args)); 44 | 45 | Namespace namespace = new Namespace(); 46 | namespace.setObject(CommandSender.class, BukkitCommandManager.SENDER_NAMESPACE, commandSender); 47 | 48 | try { 49 | if (commandManager.execute(namespace, argumentLine)) { 50 | return true; 51 | } 52 | } catch (CommandUsageException ex) { 53 | String[] usage = ChatColor.translateAlternateColorCodes('&', ex.getMessage()).replace("", label) 54 | .split("\n"); 55 | 56 | for (String s : usage) { 57 | commandSender.sendMessage(ChatColor.RED +s); 58 | } 59 | } catch (CommandParseException e) { 60 | throw new org.bukkit.command.CommandException("An internal parse exception occurred while executing the command " + label, e); 61 | } catch (CommandException e) { 62 | throw new org.bukkit.command.CommandException("An unexpected exception occurred while executing the command " + label, e); 63 | } 64 | 65 | return false; 66 | } 67 | 68 | @Override 69 | public List tabComplete(CommandSender sender, String alias, String[] args) { 70 | List argumentLine = new ArrayList<>(Arrays.asList(args)); 71 | argumentLine.add(0, alias); 72 | 73 | Namespace namespace = new Namespace(); 74 | namespace.setObject(CommandSender.class, BukkitCommandManager.SENDER_NAMESPACE, sender); 75 | 76 | try { 77 | return commandManager.getSuggestions(namespace, argumentLine); 78 | } catch (NoPermissionException e) { 79 | sender.sendMessage(ChatColor.RED + e.getMessage()); 80 | } 81 | 82 | return Collections.emptyList(); 83 | } 84 | 85 | @Override 86 | // Ignored value, the command manager authorizer manages this 87 | public boolean testPermissionSilent(CommandSender target) { 88 | Authorizer authorizer = commandManager.getAuthorizer(); 89 | 90 | Namespace namespace = new Namespace(); 91 | namespace.setObject(CommandSender.class, BukkitCommandManager.SENDER_NAMESPACE, target); 92 | 93 | return authorizer.isAuthorized(namespace, getPermission()); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /Bukkit/src/main/java/me/fixeddev/ebcm/bukkit/BukkitMessager.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bukkit; 2 | 3 | import me.fixeddev.ebcm.Messager; 4 | import me.fixeddev.ebcm.NamespaceAccesor; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.command.CommandSender; 7 | 8 | public class BukkitMessager implements Messager { 9 | @Override 10 | public void sendMessage(NamespaceAccesor namespace, String messageId, String message, String... parameters) { 11 | CommandSender sender = namespace.getObject(CommandSender.class, BukkitCommandManager.SENDER_NAMESPACE); 12 | 13 | message = String.format(message, (Object[]) parameters); 14 | 15 | sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Bukkit/src/main/java/me/fixeddev/ebcm/bukkit/parameter/provider/BukkitModule.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bukkit.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.parameter.provider.Key; 4 | import me.fixeddev.ebcm.parameter.provider.ParameterProviderRegistry; 5 | import me.fixeddev.ebcm.parameter.provider.ProvidersModule; 6 | import org.bukkit.OfflinePlayer; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | 10 | public class BukkitModule implements ProvidersModule { 11 | @Override 12 | public void configure(ParameterProviderRegistry registry) { 13 | registry.registerParameterProvider(CommandSender.class, new CommandSenderProvider()); 14 | registry.registerParameterProvider(OfflinePlayer.class, new OfflinePlayerProvider()); 15 | registry.registerParameterProvider(Player.class, new PlayerProvider()); 16 | registry.registerParameterProvider(new Key<>(PlayerSenderProvider.SENDER_MODIFIER, Player.class), new PlayerSenderProvider()); 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Bukkit/src/main/java/me/fixeddev/ebcm/bukkit/parameter/provider/CommandSenderProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bukkit.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.bukkit.BukkitCommandManager; 5 | import me.fixeddev.ebcm.exception.CommandException; 6 | import me.fixeddev.ebcm.parameter.provider.InjectedProvider; 7 | import me.fixeddev.ebcm.part.CommandPart; 8 | import org.bukkit.command.CommandSender; 9 | 10 | public class CommandSenderProvider implements InjectedProvider { 11 | 12 | @Override 13 | public Result transform(NamespaceAccesor namespaceAccesor, CommandPart part) { 14 | CommandSender sender = namespaceAccesor.getObject(CommandSender.class, BukkitCommandManager.SENDER_NAMESPACE); 15 | 16 | if (sender == null) { 17 | return Result.createResult("Failed to get command sender!", new CommandException("Failed to get CommandSender, maybe the namespace wasn't provided with the command sender when executing the command?")); 18 | } 19 | 20 | return Result.createResult(sender); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Bukkit/src/main/java/me/fixeddev/ebcm/bukkit/parameter/provider/OfflinePlayerProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bukkit.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.bukkit.BukkitCommandManager; 5 | import me.fixeddev.ebcm.exception.CommandException; 6 | import me.fixeddev.ebcm.parameter.provider.ParameterProvider; 7 | import me.fixeddev.ebcm.part.CommandPart; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.OfflinePlayer; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class OfflinePlayerProvider implements ParameterProvider { 17 | @Override 18 | public Result transform(List arguments, NamespaceAccesor namespaceAccesor, CommandPart part) { 19 | CommandSender sender = namespaceAccesor.getObject(CommandSender.class, BukkitCommandManager.SENDER_NAMESPACE); 20 | 21 | if (part.getModifiers().contains(PlayerSenderProvider.SENDER_MODIFIER)) { 22 | if (sender == null) { 23 | return Result.createResult("Failed to get command sender!", new CommandException("Failed to get CommandSender, maybe the namespace wasn't provided with the command sender when executing the command?")); 24 | } 25 | 26 | if (!(sender instanceof Player)) { 27 | return Result.createResultOfMessage("Only players can execute this command!"); 28 | } 29 | 30 | return Result.createResult((Player) sender); 31 | } 32 | 33 | String argument = arguments.get(0); 34 | 35 | OfflinePlayer player = Bukkit.getOfflinePlayer(argument); 36 | 37 | return Result.createResult(player); 38 | } 39 | 40 | @Override 41 | public List getSuggestions(String startsWith) { 42 | List suggestions = new ArrayList<>(); 43 | 44 | for (Player player : Bukkit.getOnlinePlayers()) { 45 | if(player.getName().startsWith(startsWith)){ 46 | suggestions.add(player.getName()); 47 | } 48 | } 49 | 50 | return suggestions; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Bukkit/src/main/java/me/fixeddev/ebcm/bukkit/parameter/provider/PlayerProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bukkit.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.parameter.provider.ParameterProvider; 5 | import me.fixeddev.ebcm.part.CommandPart; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.entity.Player; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class PlayerProvider implements ParameterProvider { 13 | 14 | @Override 15 | public Result transform(List arguments, NamespaceAccesor namespaceAccesor, CommandPart part) { 16 | String argument = arguments.get(0); 17 | 18 | Player player = Bukkit.getPlayer(argument); 19 | 20 | if (player == null) { 21 | return Result.createResultOfMessage("The player " + argument + " is not online!"); 22 | } 23 | 24 | return Result.createResult(player); 25 | } 26 | 27 | @Override 28 | public List getSuggestions(String startsWith) { 29 | List suggestions = new ArrayList<>(); 30 | 31 | for (Player player : Bukkit.getOnlinePlayers()) { 32 | if(player.getName().startsWith(startsWith)){ 33 | suggestions.add(player.getName()); 34 | } 35 | } 36 | 37 | return suggestions; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Bukkit/src/main/java/me/fixeddev/ebcm/bukkit/parameter/provider/PlayerSenderProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bukkit.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.bukkit.BukkitCommandManager; 5 | import me.fixeddev.ebcm.exception.CommandException; 6 | import me.fixeddev.ebcm.parameter.provider.InjectedProvider; 7 | import me.fixeddev.ebcm.part.CommandPart; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | 11 | public class PlayerSenderProvider implements InjectedProvider { 12 | public static final String SENDER_MODIFIER = "PLAYER_IS_SENDER"; 13 | 14 | @Override 15 | public Result transform(NamespaceAccesor namespaceAccesor, CommandPart part) { 16 | CommandSender sender = namespaceAccesor.getObject(CommandSender.class, BukkitCommandManager.SENDER_NAMESPACE); 17 | 18 | if (part.getModifiers().contains(SENDER_MODIFIER)) { 19 | if (sender == null) { 20 | return Result.createResult("Failed to get command sender!", new CommandException("Failed to get CommandSender, maybe the namespace wasn't provided with the command sender when executing the command?")); 21 | } 22 | 23 | if (!(sender instanceof Player)) { 24 | return Result.createResultOfMessage("Only players can execute this command!"); 25 | } 26 | 27 | return Result.createResult((Player) sender); 28 | } 29 | 30 | return Result.createResultOfMessage("Internal error."); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Bukkit/src/main/java/me/fixeddev/ebcm/bukkit/parameter/provider/annotation/Sender.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bukkit.parameter.provider.annotation; 2 | 3 | import me.fixeddev.ebcm.bukkit.parameter.provider.PlayerSenderProvider; 4 | import me.fixeddev.ebcm.parametric.annotation.ModifierAnnotation; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @ModifierAnnotation(PlayerSenderProvider.SENDER_MODIFIER) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target(ElementType.PARAMETER) 14 | public @interface Sender { 15 | } 16 | -------------------------------------------------------------------------------- /Bungee/Bungee.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BUNGEECORD 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Bungee/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | ebcm 7 | me.fixeddev 8 | 1.10 9 | 10 | 4.0.0 11 | 12 | ${bungee-version} 13 | ebcm-bungee 14 | 15 | 16 | 17 | 18 | org.apache.maven.plugins 19 | maven-compiler-plugin 20 | 21 | 8 22 | 8 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | bungeecord-repo 31 | https://oss.sonatype.org/content/repositories/snapshots 32 | 33 | 34 | 35 | 36 | 37 | me.fixeddev 38 | ebcm-universal 39 | ${universal-version} 40 | compile 41 | 42 | 43 | net.md-5 44 | bungeecord-api 45 | 1.8-SNAPSHOT 46 | provided 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Bungee/src/main/java/me/fixeddev/ebcm/bungee/BungeeAuthorizer.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bungee; 2 | 3 | import me.fixeddev.ebcm.Authorizer; 4 | import me.fixeddev.ebcm.NamespaceAccesor; 5 | import net.md_5.bungee.api.CommandSender; 6 | 7 | public class BungeeAuthorizer implements Authorizer { 8 | 9 | @Override 10 | public boolean isAuthorized(NamespaceAccesor namespace, String permission) { 11 | if(permission.isEmpty()){ 12 | return true; 13 | } 14 | 15 | CommandSender sender = namespace.getObject(CommandSender.class, BungeeCommandManager.SENDER_NAMESPACE); 16 | return sender.hasPermission(permission); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Bungee/src/main/java/me/fixeddev/ebcm/bungee/BungeeCommandManager.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bungee; 2 | 3 | import me.fixeddev.ebcm.bungee.parameter.provider.BungeeModule; 4 | import me.fixeddev.ebcm.*; 5 | import me.fixeddev.ebcm.exception.CommandException; 6 | import me.fixeddev.ebcm.exception.CommandNotFound; 7 | import me.fixeddev.ebcm.exception.CommandParseException; 8 | import me.fixeddev.ebcm.exception.NoPermissionException; 9 | import me.fixeddev.ebcm.parameter.provider.ParameterProviderRegistry; 10 | import net.md_5.bungee.api.ProxyServer; 11 | import net.md_5.bungee.api.plugin.Plugin; 12 | 13 | import java.util.List; 14 | import java.util.Optional; 15 | 16 | public class BungeeCommandManager implements CommandManager { 17 | 18 | public static final String SENDER_NAMESPACE = "SENDER"; 19 | private CommandManager parent; 20 | private Plugin plugin; 21 | 22 | public BungeeCommandManager(CommandManager parent, Plugin plugin) { 23 | this.parent = parent; 24 | this.plugin = plugin; 25 | 26 | parent.getProviderRegistry().installModule(new BungeeModule()); 27 | } 28 | 29 | @Override 30 | public void registerCommand(Command command) { 31 | parent.registerCommand(command); 32 | net.md_5.bungee.api.plugin.Command bungeeCommand = new BungeeCommandWrapper(command, this); 33 | ProxyServer.getInstance().getPluginManager().registerCommand(plugin, bungeeCommand); 34 | } 35 | 36 | @Override 37 | public void registerCommands(List commandList) { 38 | for(Command command : commandList) { 39 | registerCommand(command); 40 | } 41 | } 42 | 43 | @Override 44 | public boolean exists(String commandName) { 45 | return parent.exists(commandName); 46 | } 47 | 48 | @Override 49 | public ParameterProviderRegistry getProviderRegistry() { 50 | return parent.getProviderRegistry(); 51 | } 52 | 53 | @Override 54 | public Authorizer getAuthorizer() { 55 | return parent.getAuthorizer(); 56 | } 57 | 58 | @Override 59 | public Messager getMessager() { 60 | return parent.getMessager(); 61 | } 62 | 63 | @Override 64 | public Optional getCommand(String commandName) { 65 | return parent.getCommand(commandName); 66 | } 67 | 68 | @Override 69 | public boolean execute(NamespaceAccesor accessor, List arguments) throws CommandParseException, CommandException { 70 | return parent.execute(accessor, arguments); 71 | } 72 | 73 | @Override 74 | public List getSuggestions(NamespaceAccesor accessor, List arguments) throws NoPermissionException { 75 | return parent.getSuggestions(accessor, arguments); 76 | } 77 | 78 | @Override 79 | public ParseResult parse(NamespaceAccesor accessor, List arguments) throws CommandParseException, CommandNotFound { 80 | return parent.parse(accessor, arguments); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Bungee/src/main/java/me/fixeddev/ebcm/bungee/BungeeCommandWrapper.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bungee; 2 | 3 | import me.fixeddev.ebcm.CommandManager; 4 | import me.fixeddev.ebcm.exception.CommandException; 5 | import me.fixeddev.ebcm.exception.CommandParseException; 6 | import me.fixeddev.ebcm.exception.CommandUsageException; 7 | import me.fixeddev.ebcm.exception.NoPermissionException; 8 | import me.fixeddev.ebcm.internal.namespace.Namespace; 9 | import net.md_5.bungee.api.ChatColor; 10 | import net.md_5.bungee.api.CommandSender; 11 | import net.md_5.bungee.api.chat.ComponentBuilder; 12 | import net.md_5.bungee.api.chat.TextComponent; 13 | import net.md_5.bungee.api.plugin.Command; 14 | import net.md_5.bungee.api.plugin.TabExecutor; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | import java.util.Collections; 19 | import java.util.List; 20 | 21 | public class BungeeCommandWrapper extends Command implements TabExecutor { 22 | 23 | private CommandManager commandManager; 24 | private String[] aliases; 25 | private String permission; 26 | 27 | public BungeeCommandWrapper(me.fixeddev.ebcm.Command command, CommandManager commandManager) { 28 | super(command.getData().getName()); 29 | 30 | this.aliases = command.getData().getAliases().toArray(new String[0]); 31 | this.permission = command.getPermission(); 32 | 33 | this.commandManager = commandManager; 34 | } 35 | 36 | @Override 37 | public void execute(CommandSender sender, String[] args) { 38 | List argumentList = new ArrayList<>(Arrays.asList(args)); 39 | argumentList.add(0, getName()); 40 | 41 | Namespace namespace = new Namespace(); 42 | namespace.setObject(CommandSender.class, BungeeCommandManager.SENDER_NAMESPACE, sender); 43 | 44 | try { 45 | commandManager.execute(namespace, argumentList); 46 | } catch (CommandUsageException exception) { 47 | String[] usageExamples = ChatColor.translateAlternateColorCodes( 48 | '&', 49 | exception.getMessage() 50 | ).split("\n"); 51 | 52 | for (String usage : usageExamples) { 53 | sender.sendMessage(TextComponent.fromLegacyText(usage)); 54 | } 55 | } catch (CommandParseException exception) { 56 | sender.sendMessage(new ComponentBuilder("An internal parse exception occurred while executing the command.").color(ChatColor.RED).create()); 57 | 58 | throw new RuntimeException("An internal parse exception occurred while executing the command " + getName(), exception); 59 | } catch (CommandException exception) { 60 | sender.sendMessage(new ComponentBuilder("An unexpected exception occurred while executing the command.").color(ChatColor.RED).create()); 61 | throw new RuntimeException("An unexpected exception occurred while executing the command " + getName(), exception); 62 | } 63 | } 64 | 65 | @Override 66 | public String[] getAliases() { 67 | return this.aliases; 68 | } 69 | 70 | @Override 71 | public String getPermission() { 72 | return permission; 73 | } 74 | 75 | @Override 76 | public Iterable onTabComplete(CommandSender commandSender, String[] strings) { 77 | List argumentList = Arrays.asList(strings); 78 | 79 | Namespace namespace = new Namespace(); 80 | namespace.setObject(CommandSender.class, BungeeCommandManager.SENDER_NAMESPACE, commandSender); 81 | 82 | try { 83 | return commandManager.getSuggestions(namespace, argumentList); 84 | } catch (NoPermissionException e) { 85 | commandSender.sendMessage(ChatColor.RED + e.getMessage()); 86 | } 87 | 88 | return Collections.emptyList(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Bungee/src/main/java/me/fixeddev/ebcm/bungee/BungeeMessager.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bungee; 2 | 3 | import me.fixeddev.ebcm.Messager; 4 | import me.fixeddev.ebcm.NamespaceAccesor; 5 | import net.md_5.bungee.api.ChatColor; 6 | import net.md_5.bungee.api.CommandSender; 7 | import net.md_5.bungee.api.chat.TextComponent; 8 | 9 | public class BungeeMessager implements Messager { 10 | @Override 11 | public void sendMessage(NamespaceAccesor namespace, String messageId, String message, String... parameters) { 12 | CommandSender sender = namespace.getObject(CommandSender.class, BungeeCommandManager.SENDER_NAMESPACE); 13 | 14 | message = String.format(message, (Object[]) parameters); 15 | 16 | message = ChatColor.translateAlternateColorCodes('&',message); 17 | sender.sendMessage(TextComponent.fromLegacyText(message)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Bungee/src/main/java/me/fixeddev/ebcm/bungee/parameter/provider/BungeeModule.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bungee.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.parameter.provider.Key; 4 | import me.fixeddev.ebcm.parameter.provider.ParameterProviderRegistry; 5 | import me.fixeddev.ebcm.parameter.provider.ProvidersModule; 6 | import net.md_5.bungee.api.CommandSender; 7 | import net.md_5.bungee.api.connection.ProxiedPlayer; 8 | 9 | public class BungeeModule implements ProvidersModule { 10 | 11 | @Override 12 | public void configure(ParameterProviderRegistry registry) { 13 | registry.registerParameterProvider(CommandSender.class, new CommandSenderProvider()); 14 | registry.registerParameterProvider(ProxiedPlayer.class, new ProxiedPlayerProvider()); 15 | registry.registerParameterProvider(new Key<>(ProxiedPlayerSenderProvider.SENDER_MODIFIER, ProxiedPlayer.class), new ProxiedPlayerSenderProvider()); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Bungee/src/main/java/me/fixeddev/ebcm/bungee/parameter/provider/CommandSenderProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bungee.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.bungee.BungeeCommandManager; 4 | import me.fixeddev.ebcm.NamespaceAccesor; 5 | import me.fixeddev.ebcm.exception.CommandException; 6 | import me.fixeddev.ebcm.parameter.provider.InjectedProvider; 7 | import me.fixeddev.ebcm.part.CommandPart; 8 | import net.md_5.bungee.api.CommandSender; 9 | 10 | public class CommandSenderProvider implements InjectedProvider { 11 | 12 | @Override 13 | public Result transform(NamespaceAccesor namespaceAccesor, CommandPart part) { 14 | CommandSender sender = namespaceAccesor.getObject(CommandSender.class, BungeeCommandManager.SENDER_NAMESPACE); 15 | 16 | if(sender == null) { 17 | return Result.createResult( 18 | "Failed to get command sender!", 19 | new CommandException("Failed to get CommandSender, maybe the namespace wasn't " + 20 | "provided with the command sender when executing the command?") 21 | ); 22 | } 23 | 24 | return Result.createResult(sender); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Bungee/src/main/java/me/fixeddev/ebcm/bungee/parameter/provider/ProxiedPlayerProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bungee.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.bungee.BungeeCommandManager; 4 | import me.fixeddev.ebcm.NamespaceAccesor; 5 | import me.fixeddev.ebcm.exception.CommandException; 6 | import me.fixeddev.ebcm.parameter.provider.ParameterProvider; 7 | import me.fixeddev.ebcm.part.CommandPart; 8 | import net.md_5.bungee.api.ProxyServer; 9 | import net.md_5.bungee.api.connection.ProxiedPlayer; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class ProxiedPlayerProvider implements ParameterProvider { 15 | 16 | @Override 17 | public Result transform(List arguments, NamespaceAccesor namespaceAccesor, CommandPart part) { 18 | String playerName = arguments.get(0); 19 | ProxiedPlayer player = ProxyServer.getInstance().getPlayer(playerName); 20 | 21 | if(player == null) { 22 | return Result.createResultOfMessage("The player " + playerName + " is not online!"); 23 | } 24 | 25 | return Result.createResult(player); 26 | } 27 | 28 | @Override 29 | public List getSuggestions(String startsWith) { 30 | List suggestions = new ArrayList<>(); 31 | 32 | for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { 33 | if(player.getName().startsWith(startsWith)){ 34 | suggestions.add(player.getName()); 35 | } 36 | } 37 | 38 | return suggestions; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Bungee/src/main/java/me/fixeddev/ebcm/bungee/parameter/provider/ProxiedPlayerSenderProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.bungee.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.bungee.BungeeCommandManager; 5 | import me.fixeddev.ebcm.exception.CommandException; 6 | import me.fixeddev.ebcm.parameter.provider.InjectedProvider; 7 | import me.fixeddev.ebcm.part.CommandPart; 8 | import net.md_5.bungee.api.CommandSender; 9 | import net.md_5.bungee.api.ProxyServer; 10 | import net.md_5.bungee.api.connection.ProxiedPlayer; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class ProxiedPlayerSenderProvider implements InjectedProvider { 16 | 17 | public static final String SENDER_MODIFIER = "PLAYER_IS_SENDER"; 18 | 19 | @Override 20 | public Result transform(NamespaceAccesor namespaceAccesor, CommandPart part) { 21 | CommandSender sender = namespaceAccesor.getObject(CommandSender.class, BungeeCommandManager.SENDER_NAMESPACE); 22 | 23 | if(part.getModifiers().contains(SENDER_MODIFIER)) { 24 | if(sender == null) { 25 | return Result.createResult( 26 | "Failed to get command sender!", 27 | new CommandException("Failed to get CommandSender, maybe the namespace wasn't " + 28 | "provided with the command sender when executing the command?") 29 | ); 30 | } 31 | 32 | if(!(sender instanceof ProxiedPlayer)) { 33 | return Result.createResultOfMessage("Only players can execute this command!"); 34 | } 35 | 36 | return Result.createResult((ProxiedPlayer) sender); 37 | } 38 | 39 | return Result.createResultOfMessage("Internal error."); 40 | } 41 | 42 | @Override 43 | public List getSuggestions(String startsWith) { 44 | List suggestions = new ArrayList<>(); 45 | 46 | for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { 47 | if(player.getName().startsWith(startsWith)){ 48 | suggestions.add(player.getName()); 49 | } 50 | } 51 | 52 | return suggestions; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Gilberto Garcia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EBCM [![Codacy Badge](https://api.codacy.com/project/badge/Grade/78392a0229da4390a2069ab0efc24534)](https://www.codacy.com/manual/FixedDev/EBCM?utm_source=github.com&utm_medium=referral&utm_content=FixedDev/EBCM&utm_campaign=Badge_Grade) [![Version](https://jitpack.io/v/FixedDev/EBCM.svg)](https://jitpack.io/#FixedDev/EBCM) 2 | My new command manager slightly inspired by EngineHub/Piston 3 | ## Purpose 4 | The purpose of the EBCM is: 5 | * Fix some problems of my previous command manager 6 | * Allow things that weren't possible on the previous version 7 | ## Current State 8 | While the command manager is working, it's not complete yet, there are a lot of features missing from the old command manager, 9 | and still has some bugs that will be solved with the time, but, since it's relatively functional I released it 10 | ## TODO 11 | * [ ] ~Fix the bug that's related to subcommands and optional arguments~(not actually a bug) 12 | * [x] Develop the parametric commands 13 | * [x] Allow subcommands in the parametric commands 14 | * [x] Allow more than 1 depth of subcommands in the parametric commands 15 | * [x] Allow parent commands parameters to be used on the subcommands 16 | * [x] Modify the parser so it's more maintainable and has better performance 17 | * [ ] Refactor some things(command structure, implementations of interfaces) to make the above task easier to complete 18 | * [x] Allow optional subcommands 19 | * [x] Create a bukkit wrapper 20 | * [x] Create a bungee wrapper 21 | 22 | ## Usage 23 | The command manager actually has 2 types of commands. 24 | The basic commands and the parametric commands, the basic commands are commands created using a CommandBuilder in which you build every argument and later you specify an action for that command. The Parametric Commands are created by annotations on methods which specify certain needed things. 25 | ### Repository 26 | At the moment I don't have my own repository, so I'm using jitpack. 27 | 28 | Add this to your repository list 29 | ```xml 30 | 31 | jitpack.io 32 | https://jitpack.io 33 | 34 | ``` 35 | 36 | And this to your dependencies 37 | ```xml 38 | 39 | com.github.FixedDev.EBCM 40 | ebcm 41 | 1.10-SNAPSHOT 42 | 43 | ``` 44 | ### Common 45 | There's a common thing that you need to do for the 2 types of commands. 46 | Initialize a CommandManager instance! 47 | Here's how you do it 48 | ```java 49 | CommandManager manager = new SimpleCommandManager(); // There are more constructors for this but, they're for other things 50 | ``` 51 | This automatically creates all the needed objects and gives you a ready to work instance, but, for more complex scenarios like using the wrapper for bukkit, or registering ParameterProvider's you need to create the objects manually, like this. 52 | ```java 53 | Authorizer authorizer = new BukkitAuthorizer(); 54 | ParameterProviderRegistry providerRegistry = ParameterProviderRegistry.createRegistry(); 55 | Messager messager = new BukkitMessager(); 56 | 57 | CommandManager commandManager = new SimpleCommandManager(authorizer, messager, providerRegistry); 58 | 59 | ``` 60 | ### Basic Commands 61 | To create a basic command you need to use the ImmutableCommand.Builder class or the MutableCommand.Builder class, here an example 62 | ```java 63 | Command command = ImmutableCommand.builder(CommandData.builder("yourcommand")) 64 | .addPart(ArgumentPart.builder("argument1", String.class).build()) 65 | .setAction(context -> { 66 | List parts = context.getParts("argument1"); 67 | Optional value = context.getValue(parts.get(0)); 68 | 69 | System.out.println(value.orElse("Missing value!")); 70 | }).build(); 71 | ``` 72 | And you register it using the `registerCommand(Command)` method of the CommandManager 73 | ```java 74 | commandManager.registerCommand(command); 75 | ``` 76 | ### ParametricCommands 77 | Creating a ParametricCommand is easy but, you need to create a new object to create them 78 | ```java 79 | ParametricCommandBuilder builder = new ReflectionParametricCommandBuilder(); 80 | ``` 81 | It only has 2 methods: `fromClass` and `fromMethod`. You will need the method `fromClass` almost everytime that you use this command manager. 82 | The first method accepts an instance of an instance of an interface called `CommandClass` which doesn't has any method, it's just a marking interface, there you can add your commands and they will be converted to normal commands when the method is run. 83 | Here an example of how to create a command with the `CommandClass` 84 | ```java 85 | class TestCommand implements CommandClass { 86 | @ACommand(names = "yourcommand") 87 | public boolean test(@Default("Missing Value!") @Named("argument1") String argument1) { 88 | System.out.println(argument1); 89 | } 90 | } 91 | ``` 92 | When you have your instance of CommandClass created, you can use the `ParametricCommandBuilder` to convert it 93 | ```java 94 | List commands = builder.fromClass(new TestCommand()); 95 | ``` 96 | Wait, you may ask. Why does the method return a list of commands? 97 | Well, that's because on the same CommandClass you can have multiple commands and even subcommands 98 | ```java 99 | class TestCommands implements CommandClass { 100 | @ACommand(names = "command1") 101 | public boolean test(@Default("Missing Value!") @Named("argument1") String argument1) { 102 | System.out.println("Command1"); 103 | System.out.println(argument1); 104 | } 105 | 106 | @ACommand(names = "command2") 107 | public boolean test(@Default("Missing Value!") @Named("argument1") String argument1) { 108 | System.out.println("Command2"); 109 | System.out.println(argument1); 110 | } 111 | } 112 | ``` 113 | Now, how you register that List of commands returned? Well, like any other command, they're not special instances so you can register them easily 114 | ```java 115 | commandManager.registerCommands(commands); 116 | ``` 117 | Also, you see that ugly `@Named` annotation? Well, using java8 `-parameters` compiler flag you can get rid of it! 118 | ### Finally 119 | There are lots of things that you can do, since the CommandManager was created to be really flexible and it will be updated to be better. At the moment it doesn't has a wiki but, soon it will be created, to make the experience of using it easier 120 | -------------------------------------------------------------------------------- /Universal/ebcm-universal.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Universal/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | ebcm 7 | me.fixeddev 8 | 1.10 9 | 10 | 4.0.0 11 | 12 | ${universal-version} 13 | 14 | ebcm-universal 15 | 16 | 17 | 18 | org.apache.maven.plugins 19 | maven-compiler-plugin 20 | 3.6.1 21 | 22 | 8 23 | 8 24 | 25 | 26 | com.google.auto.value 27 | auto-value 28 | 1.6.6 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | com.google.auto.value 40 | auto-value-annotations 41 | 1.6.6 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/ArgumentStack.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import me.fixeddev.ebcm.exception.CommandParseException; 4 | import me.fixeddev.ebcm.exception.NoMoreArgumentsException; 5 | 6 | import java.util.List; 7 | 8 | public class ArgumentStack implements Cloneable { 9 | protected List originalArguments; 10 | 11 | private int position = -1; 12 | 13 | public ArgumentStack(List originalArguments) { 14 | this.originalArguments = originalArguments; 15 | } 16 | 17 | protected ArgumentStack(List originalArguments, int position) { 18 | this.originalArguments = originalArguments; 19 | 20 | this.position = position; 21 | } 22 | 23 | public boolean hasNext() { 24 | return (originalArguments.size() - 1) > position; 25 | } 26 | 27 | public String next() throws NoMoreArgumentsException { 28 | if (!hasNext()) { 29 | throw new NoMoreArgumentsException(originalArguments.size(), position); 30 | } 31 | 32 | position++; 33 | 34 | return originalArguments.get(position); 35 | } 36 | 37 | public String peek() throws NoMoreArgumentsException { 38 | int nextPosition = position + 1; 39 | 40 | if (!hasNext()) { 41 | throw new NoMoreArgumentsException(originalArguments.size(), position); 42 | } 43 | 44 | return originalArguments.get(nextPosition); 45 | } 46 | 47 | public String current() { 48 | if(position == -1){ 49 | throw new IllegalStateException("You must advance the stack at least once before using the current() method!"); 50 | } 51 | 52 | return originalArguments.get(position); 53 | } 54 | 55 | public int getPosition() { 56 | return position; 57 | } 58 | 59 | public int getSize() { 60 | return originalArguments.size(); 61 | } 62 | 63 | public int nextInt() throws CommandParseException { 64 | String next = next(); 65 | try { 66 | return Integer.parseInt(next); 67 | } catch (NumberFormatException e) { 68 | throw new CommandParseException("Failed to parse the string " + next + " as int!"); 69 | } 70 | } 71 | 72 | public float nextFloat() throws CommandParseException { 73 | String next = next(); 74 | 75 | try { 76 | return Float.parseFloat(next); 77 | } catch (NumberFormatException e) { 78 | throw new CommandParseException("Failed to parse the string " + next + " as float!"); 79 | } 80 | } 81 | 82 | public double nextDouble() throws CommandParseException { 83 | String next = next(); 84 | 85 | try { 86 | return Double.parseDouble(next); 87 | } catch (NumberFormatException e) { 88 | throw new CommandParseException("Failed to parse the string " + next + " as double!"); 89 | } 90 | } 91 | 92 | public byte nextByte() throws CommandParseException { 93 | String next = next(); 94 | 95 | try { 96 | return Byte.parseByte(next); 97 | } catch (NumberFormatException e) { 98 | throw new CommandParseException("Failed to parse the string " + next + " as byte!"); 99 | } 100 | } 101 | 102 | public boolean nextBoolean() throws CommandParseException { 103 | String next = next(); 104 | 105 | if (!next.equalsIgnoreCase("true") && !next.equalsIgnoreCase("false")) { 106 | throw new CommandParseException("Failed to parse the string " + next + " as boolean!"); 107 | } 108 | 109 | return Boolean.parseBoolean(next); 110 | } 111 | 112 | public void markAsConsumed() { 113 | this.position = originalArguments.size(); 114 | } 115 | 116 | public List getBacking() { 117 | return originalArguments; 118 | } 119 | 120 | @Override 121 | public ArgumentStack clone() throws CloneNotSupportedException { 122 | return (ArgumentStack) super.clone(); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/Authorizer.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | public interface Authorizer { 4 | boolean isAuthorized(NamespaceAccesor namespace, String permission); 5 | } 6 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/Command.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import me.fixeddev.ebcm.part.CommandPart; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public interface Command { 9 | CommandData getData(); 10 | 11 | String getPermission(); 12 | 13 | String getPermissionMessage(); 14 | 15 | String getUsage(); 16 | 17 | CommandAction getAction(); 18 | 19 | List getParts(); 20 | 21 | default List getPartWithName(String name) { 22 | List matchingParts = new ArrayList<>(); 23 | 24 | for (CommandPart part : getParts()) { 25 | if (part.getName().equals(name)) { 26 | matchingParts.add(part); 27 | } 28 | } 29 | 30 | return matchingParts; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/CommandAction.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import me.fixeddev.ebcm.exception.CommandException; 4 | 5 | public interface CommandAction { 6 | boolean execute(CommandContext parameters) throws CommandException; 7 | } 8 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/CommandContext.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import me.fixeddev.ebcm.part.CommandPart; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | public interface CommandContext extends NamespaceAccesor { 9 | /** 10 | * @return The executed command, that means that if you execute a subcommand 11 | * this command will return that subcommand instead of the main command 12 | */ 13 | Command getCommand(); 14 | 15 | /** 16 | * @return The raw arguments passed to the command manager 17 | */ 18 | List getArguments(); 19 | 20 | String getLabel(); 21 | 22 | boolean has(CommandPart part); 23 | 24 | default List getParts(String name) { 25 | return getCommand().getPartWithName(name); 26 | } 27 | 28 | Optional> getRaw(CommandPart part); 29 | 30 | List getBoundParts(); 31 | 32 | Optional getValue(CommandPart part); 33 | 34 | /** 35 | * The difference between this and the {@link CommandContext#getValue(CommandPart)} method is that this method allows null values(being valid) to be returned 36 | * In certain cases it can be useful, since the provider can return a null, but still be a valid value 37 | * 38 | * @param part The part to retrieve the value from 39 | * @param The returned object type 40 | * @return The nullable object associated as the value for the specified part 41 | * @throws IllegalArgumentException If the specified part doesn't has a value associated with 42 | */ 43 | V getRawValue(CommandPart part); 44 | 45 | boolean hasValue(CommandPart part); 46 | } 47 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/CommandData.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | @AutoValue 9 | public abstract class CommandData { 10 | public static Builder builder(String name) { 11 | return new AutoValue_CommandData.Builder() 12 | .named(name) 13 | .setAliases(Collections.emptyList()) 14 | .setDescription(""); 15 | } 16 | 17 | public abstract String getName(); 18 | 19 | public abstract List getAliases(); 20 | 21 | public abstract String getDescription(); 22 | 23 | @AutoValue.Builder 24 | public abstract static class Builder { 25 | protected final Builder named(String name) { 26 | return setName(name); 27 | } 28 | 29 | protected abstract Builder setName(String name); 30 | 31 | public abstract Builder setAliases(List aliases); 32 | 33 | public abstract Builder setDescription(String description); 34 | 35 | public abstract CommandData build(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/CommandManager.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import me.fixeddev.ebcm.exception.CommandException; 4 | import me.fixeddev.ebcm.exception.CommandNotFound; 5 | import me.fixeddev.ebcm.exception.CommandParseException; 6 | import me.fixeddev.ebcm.exception.NoPermissionException; 7 | import me.fixeddev.ebcm.parameter.provider.ParameterProviderRegistry; 8 | 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | public interface CommandManager { 13 | 14 | void registerCommand(Command command); 15 | 16 | void registerCommands(List commandList); 17 | 18 | boolean exists(String commandName); 19 | 20 | ParameterProviderRegistry getProviderRegistry(); 21 | 22 | Authorizer getAuthorizer(); 23 | 24 | Messager getMessager(); 25 | 26 | Optional getCommand(String commandName); 27 | 28 | boolean execute(NamespaceAccesor accessor, List arguments) throws CommandParseException, CommandException; 29 | 30 | List getSuggestions(NamespaceAccesor accessor, List arguments) throws NoPermissionException; 31 | 32 | ParseResult parse(NamespaceAccesor accessor, List arguments) throws CommandParseException, CommandNotFound; 33 | } 34 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/ImmutableCommand.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import com.google.auto.value.AutoValue; 4 | import me.fixeddev.ebcm.part.CommandPart; 5 | import me.fixeddev.ebcm.util.ListAppender; 6 | 7 | import java.util.List; 8 | 9 | @AutoValue 10 | public abstract class ImmutableCommand implements Command { 11 | 12 | public static Builder builder(CommandData.Builder dataBuilder) { 13 | return new AutoValue_ImmutableCommand.Builder() 14 | .withData(dataBuilder) 15 | .setPermission("") 16 | .setUsage("_!!_NOT_OVERRIDE_!!_") 17 | .setPermissionMessage("No permission.") 18 | .setAction(params -> false); 19 | } 20 | 21 | @AutoValue.Builder 22 | public abstract static class Builder { 23 | 24 | private ListAppender partListAppender = new ListAppender<>(); 25 | 26 | protected final Builder withData(CommandData.Builder dataBuilder) { 27 | return setData(dataBuilder.build()); 28 | } 29 | 30 | protected abstract Builder setData(CommandData newData); 31 | 32 | public abstract Builder setPermission(String permission); 33 | 34 | public abstract Builder setPermissionMessage(String message); 35 | 36 | public abstract Builder setUsage(String usage); 37 | 38 | public abstract Builder setAction(CommandAction newAction); 39 | 40 | protected abstract Builder setParts(List newParts); 41 | 42 | public abstract ImmutableCommand autoBuild(); 43 | 44 | public Builder setCommandParts(List newParts) { 45 | partListAppender.set(newParts); 46 | 47 | return this; 48 | } 49 | 50 | public Builder addPart(CommandPart part) { 51 | if (part == null) { 52 | throw new IllegalArgumentException("The provided part is null"); 53 | } 54 | 55 | partListAppender.add(part); 56 | 57 | return this; 58 | } 59 | 60 | public ImmutableCommand build() { 61 | setParts(partListAppender.toList()); 62 | 63 | return autoBuild(); 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/Messager.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | public interface Messager { 4 | default void sendMessage(NamespaceAccesor namespaceAccesor, String message) { 5 | sendMessage(namespaceAccesor, "UNKNOWN", message); 6 | } 7 | 8 | void sendMessage(NamespaceAccesor namespaceAccesor, String messageId, String message, String... parameters); 9 | } 10 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/MutableCommand.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import me.fixeddev.ebcm.part.CommandPart; 4 | import me.fixeddev.ebcm.util.ListAppender; 5 | 6 | import java.util.List; 7 | import java.util.Objects; 8 | 9 | public class MutableCommand implements Command { 10 | 11 | private final CommandData data; 12 | private String usage; 13 | private String permission; 14 | private String permissionMessage; 15 | private CommandAction action; 16 | private List parts; 17 | 18 | private MutableCommand(CommandData data, String usage, String permission, String permissionMessage, CommandAction action, List parts) { 19 | this.data = data; 20 | this.usage = usage; 21 | this.permission = permission; 22 | this.permissionMessage = permissionMessage; 23 | this.action = action; 24 | this.parts = parts; 25 | } 26 | 27 | public static Builder builder(CommandData.Builder dataBuilder) { 28 | return new MutableCommand.Builder(dataBuilder.build()) 29 | .setPermission("") 30 | .setPermissionMessage("No permission.") 31 | .setAction(params -> false); 32 | } 33 | 34 | @Override 35 | public CommandData getData() { 36 | return data; 37 | } 38 | 39 | @Override 40 | public String getPermission() { 41 | return permission; 42 | } 43 | 44 | public void setPermission(String permission) { 45 | this.permission = permission; 46 | } 47 | 48 | @Override 49 | public String getPermissionMessage() { 50 | return permissionMessage; 51 | } 52 | 53 | @Override 54 | public String getUsage() { 55 | return usage; 56 | } 57 | 58 | public void setPermissionMessage(String permissionMessage) { 59 | this.permissionMessage = permissionMessage; 60 | } 61 | 62 | @Override 63 | public CommandAction getAction() { 64 | return action; 65 | } 66 | 67 | public void setAction(CommandAction action) { 68 | this.action = action; 69 | } 70 | 71 | @Override 72 | public List getParts() { 73 | return parts; 74 | } 75 | 76 | public void setParts(List parts) { 77 | this.parts = parts; 78 | } 79 | 80 | public static class Builder { 81 | private CommandData data; 82 | private String usage; 83 | private String permission; 84 | private String permissionMessage; 85 | private CommandAction action; 86 | 87 | private ListAppender partListAppender = new ListAppender<>(); 88 | 89 | protected Builder(CommandData data) { 90 | Objects.requireNonNull(data); 91 | 92 | this.data = data; 93 | } 94 | 95 | public Builder setPermission(String permission) { 96 | Objects.requireNonNull(permission); 97 | 98 | this.permission = permission; 99 | return this; 100 | } 101 | 102 | public Builder setPermissionMessage(String permissionMessage) { 103 | Objects.requireNonNull(permissionMessage); 104 | 105 | this.permissionMessage = permissionMessage; 106 | return this; 107 | } 108 | 109 | public Builder setUsage(String usage) { 110 | this.usage = usage; 111 | return this; 112 | } 113 | 114 | public Builder setAction(CommandAction action) { 115 | Objects.requireNonNull(action); 116 | 117 | this.action = action; 118 | return this; 119 | } 120 | 121 | public Builder setCommandParts(List newParts) { 122 | partListAppender.set(newParts); 123 | 124 | return this; 125 | } 126 | 127 | public Builder addPart(CommandPart part) { 128 | Objects.requireNonNull(part); 129 | 130 | partListAppender.add(part); 131 | 132 | return this; 133 | } 134 | 135 | public MutableCommand build() { 136 | String missing = ""; 137 | if (this.data == null) { 138 | missing += " data"; 139 | } 140 | if (this.permission == null) { 141 | missing += " permission"; 142 | } 143 | if (this.permissionMessage == null) { 144 | missing += " permissionMessage"; 145 | } 146 | if (this.action == null) { 147 | missing += " action"; 148 | } 149 | if (!missing.isEmpty()) { 150 | throw new IllegalStateException("Missing required properties:" + missing); 151 | } 152 | return new MutableCommand( 153 | this.data, 154 | this.usage, 155 | this.permission, 156 | this.permissionMessage, 157 | this.action, 158 | this.partListAppender.toList()); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/NamespaceAccesor.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | public interface NamespaceAccesor { 4 | @SuppressWarnings("unchecked") 5 | T getObject(Class clazz, String name); 6 | } 7 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/ParseResult.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import me.fixeddev.ebcm.part.CommandPart; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public interface ParseResult { 9 | String getLabel(); 10 | 11 | List getCommandLine(); 12 | 13 | Command getMainCommand(); 14 | 15 | Command getCommandToExecute(); 16 | 17 | List getBindings(); 18 | 19 | Map getValueBindings(); 20 | 21 | interface ParameterBinding { 22 | List getRaw(); 23 | 24 | CommandPart getBind(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/SimpleCommandManager.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import me.fixeddev.ebcm.exception.CommandException; 4 | import me.fixeddev.ebcm.exception.CommandNotFound; 5 | import me.fixeddev.ebcm.exception.CommandParseException; 6 | import me.fixeddev.ebcm.internal.CommandLineParser; 7 | import me.fixeddev.ebcm.internal.Messages; 8 | import me.fixeddev.ebcm.internal.namespace.SimpleCommandContext; 9 | import me.fixeddev.ebcm.parameter.provider.ParameterProviderRegistry; 10 | import me.fixeddev.ebcm.part.ArgumentPart; 11 | import me.fixeddev.ebcm.part.CommandPart; 12 | import me.fixeddev.ebcm.part.FlagPart; 13 | import me.fixeddev.ebcm.part.SubCommandPart; 14 | import me.fixeddev.ebcm.util.UsageBuilder; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Collections; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.ListIterator; 21 | import java.util.Map; 22 | import java.util.Optional; 23 | import java.util.function.Function; 24 | import java.util.stream.Collectors; 25 | 26 | public class SimpleCommandManager implements CommandManager { 27 | private Map commandMap; 28 | 29 | private Authorizer authorizer; 30 | private Messager messager; 31 | private ParameterProviderRegistry registry; 32 | 33 | public SimpleCommandManager(Authorizer authorizer, Messager messager, ParameterProviderRegistry registry) { 34 | this.authorizer = authorizer; 35 | this.messager = messager; 36 | this.registry = registry; 37 | 38 | 39 | commandMap = new HashMap<>(); 40 | } 41 | 42 | public SimpleCommandManager(ParameterProviderRegistry registry) { 43 | this((namespace, permission) -> true, (namespaceAccesor, messageId, message, parameters) -> { 44 | System.out.println(String.format(message, (Object[]) parameters)); 45 | }, registry); 46 | } 47 | 48 | public SimpleCommandManager() { 49 | this(ParameterProviderRegistry.createRegistry()); 50 | } 51 | 52 | @Override 53 | public void registerCommand(Command command) { 54 | CommandData data = command.getData(); 55 | 56 | if (commandMap.containsKey(data.getName())) { 57 | throw new IllegalArgumentException("A command with the name " + data.getName() + " is already registered!"); 58 | } 59 | 60 | commandMap.put(data.getName().toLowerCase(), command); 61 | 62 | data.getAliases().forEach(alias -> { 63 | commandMap.putIfAbsent(alias.toLowerCase(), command); 64 | }); 65 | } 66 | 67 | @Override 68 | public void registerCommands(List commandList) { 69 | for (Command command : commandList) { 70 | registerCommand(command); 71 | } 72 | } 73 | 74 | @Override 75 | public boolean exists(String commandName) { 76 | return commandMap.containsKey(commandName.toLowerCase()); 77 | } 78 | 79 | @Override 80 | public ParameterProviderRegistry getProviderRegistry() { 81 | return registry; 82 | } 83 | 84 | @Override 85 | public Authorizer getAuthorizer() { 86 | return authorizer; 87 | } 88 | 89 | @Override 90 | public Messager getMessager() { 91 | return messager; 92 | } 93 | 94 | @Override 95 | public Optional getCommand(String commandName) { 96 | return Optional.ofNullable(commandMap.get(commandName.toLowerCase())); 97 | } 98 | 99 | @Override 100 | public boolean execute(NamespaceAccesor accessor, List arguments) throws CommandParseException, CommandException { 101 | ParseResult result; 102 | 103 | try { 104 | result = parse(accessor, arguments); 105 | } catch (CommandNotFound ex) { 106 | return false; 107 | } 108 | 109 | CommandContext context = new SimpleCommandContext(accessor, result); 110 | 111 | Command toExecute = result.getCommandToExecute(); 112 | 113 | if (!authorizer.isAuthorized(accessor, toExecute.getPermission())) { 114 | messager.sendMessage(accessor, Messages.COMMAND_NO_PERMISSIONS.getId(), toExecute.getPermissionMessage()); 115 | 116 | return true; 117 | } 118 | 119 | CommandAction action = toExecute.getAction(); 120 | 121 | boolean usage = false; 122 | 123 | try { 124 | if (!action.execute(context)) { 125 | usage = true; 126 | } 127 | } catch (CommandException ex) { 128 | throw ex; 129 | } catch (Exception ex) { 130 | throw new CommandException("An exception occurred while executing the command", ex); 131 | } 132 | 133 | if (usage) { 134 | messager.sendMessage(accessor, Messages.COMMAND_USAGE.getId(), "Usage: %1$s", UsageBuilder.getUsageForCommand(result.getMainCommand(), toExecute, result.getLabel())); 135 | } 136 | 137 | return true; 138 | } 139 | 140 | @Override 141 | public List getSuggestions(NamespaceAccesor accessor, List arguments) { 142 | if (arguments == null || arguments.isEmpty()) { 143 | return Collections.emptyList(); 144 | } 145 | 146 | Optional optionalCommand = getCommand(arguments.get(0)); 147 | 148 | if (!optionalCommand.isPresent()) { 149 | return Collections.emptyList(); 150 | } 151 | 152 | arguments.remove(0); 153 | 154 | Command command = optionalCommand.get(); 155 | 156 | if (!authorizer.isAuthorized(accessor, command.getPermission())) { 157 | return Collections.emptyList(); 158 | } 159 | 160 | List parts = command.getParts(); 161 | arguments = parseFlags(arguments, parts); 162 | 163 | int argumentsLeft = arguments.size(); 164 | int lastArgumentsLeft; 165 | 166 | CommandPart partToComplete = null; 167 | String startsWith = ""; 168 | 169 | List commandParts = command.getParts(); 170 | 171 | ListIterator partsIterator = commandParts.listIterator(); 172 | while (partsIterator.hasNext()) { 173 | CommandPart part = partsIterator.next(); 174 | 175 | if (part instanceof ArgumentPart) { 176 | ArgumentPart argumentPart = (ArgumentPart) part; 177 | lastArgumentsLeft = argumentsLeft; 178 | argumentsLeft -= argumentPart.getConsumedArguments(); 179 | 180 | if (argumentsLeft <= 0) { 181 | if (lastArgumentsLeft > 0) { 182 | int i = argumentPart.getConsumedArguments() + argumentsLeft; 183 | 184 | if (i == 0) { 185 | startsWith = arguments.get(arguments.size() - 1); 186 | } else { 187 | startsWith = String.join(" ", arguments.subList(arguments.size() - i, arguments.size())); 188 | } 189 | } 190 | 191 | partToComplete = part; 192 | break; 193 | } 194 | } 195 | 196 | if (part instanceof SubCommandPart) { 197 | lastArgumentsLeft = argumentsLeft; 198 | argumentsLeft--; 199 | 200 | if (argumentsLeft <= 0) { 201 | partToComplete = part; 202 | startsWith = arguments.get(arguments.size() - 1); 203 | 204 | break; 205 | } 206 | 207 | if (!partsIterator.hasNext()) { 208 | Map availableValues = new HashMap<>(); 209 | 210 | for (Command subCommand : ((SubCommandPart) part).getCommandsToCall()) { 211 | availableValues.put(subCommand.getData().getName(), subCommand); 212 | 213 | for (String value : command.getData().getAliases()) { 214 | availableValues.put(value.toLowerCase(), subCommand); 215 | } 216 | } 217 | 218 | String lastArgument = arguments.get(arguments.size() - argumentsLeft - 1); 219 | 220 | Command subCommand = availableValues.get(lastArgument.toLowerCase()); 221 | 222 | if (subCommand == null) { 223 | continue; 224 | } 225 | 226 | parts = subCommand.getParts(); 227 | partsIterator = parts.listIterator(); 228 | } 229 | } 230 | } 231 | 232 | if (partToComplete == null) { 233 | return Collections.emptyList(); 234 | } 235 | 236 | if (partToComplete instanceof ArgumentPart) { 237 | ArgumentPart part = (ArgumentPart) partToComplete; 238 | SuggestionProvider provider = part.getSuggestionProvider().orElse(getProviderRegistry().getParameterProvider(part.getArgumentType())); 239 | 240 | if (provider == null) { 241 | return Collections.emptyList(); 242 | } 243 | 244 | return provider.getSuggestions(startsWith); 245 | } 246 | 247 | SubCommandPart part = (SubCommandPart) partToComplete; 248 | 249 | return getSubCommands(accessor, part, startsWith); 250 | } 251 | 252 | private List getSubCommands(NamespaceAccesor accesor, SubCommandPart part, String startsWith) { 253 | List availableValues = new ArrayList<>(); 254 | 255 | for (Command command : part.getCommandsToCall()) { 256 | if (!authorizer.isAuthorized(accesor, command.getPermission())) { 257 | continue; 258 | } 259 | 260 | String name = command.getData().getName(); 261 | 262 | if (name.startsWith(startsWith)) { 263 | availableValues.add(name.toLowerCase()); 264 | } 265 | 266 | for (String alias : command.getData().getAliases()) { 267 | if (alias.startsWith(startsWith)) { 268 | availableValues.add(alias.toLowerCase()); 269 | } 270 | } 271 | } 272 | 273 | return availableValues; 274 | } 275 | 276 | private List parseFlags(List arguments, List parts) { 277 | Map flagParts = parts.stream() 278 | .filter(part -> part instanceof FlagPart) 279 | .map(part -> (FlagPart) part).collect(Collectors.toMap(FlagPart::getFlagChar, Function.identity())); 280 | 281 | List newArguments = new ArrayList<>(); 282 | 283 | boolean ignore = false; 284 | 285 | for (String argument : arguments) { 286 | if (argument.startsWith("-") && argument.length() == 2 && !ignore) { 287 | char flagChar = argument.charAt(1); 288 | FlagPart part = flagParts.get(flagChar); 289 | 290 | if (part != null && !ignore) { 291 | continue; 292 | } 293 | } 294 | 295 | // Disable the parsing of the next flags 296 | if ("--".equals(argument)) { 297 | ignore = true; 298 | break; 299 | } 300 | 301 | newArguments.add(argument); 302 | } 303 | 304 | parts.removeAll(flagParts.values()); 305 | 306 | return newArguments; 307 | } 308 | 309 | 310 | @Override 311 | public ParseResult parse(NamespaceAccesor accessor, List arguments) throws CommandParseException, CommandNotFound { 312 | CommandLineParser parser = new CommandLineParser(arguments, accessor, this); 313 | 314 | return parser.parse(); 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/SuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm; 2 | 3 | import java.util.List; 4 | 5 | public interface SuggestionProvider { 6 | List getSuggestions(String startsWith); 7 | } 8 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/exception/CommandException.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.exception; 2 | 3 | public class CommandException extends Exception { 4 | 5 | public CommandException() { 6 | } 7 | 8 | public CommandException(String message) { 9 | super(message); 10 | } 11 | 12 | public CommandException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public CommandException(Throwable cause) { 17 | super(cause); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/exception/CommandNotFound.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.exception; 2 | 3 | public class CommandNotFound extends Exception { 4 | public CommandNotFound() { 5 | super(); 6 | } 7 | 8 | public CommandNotFound(String message) { 9 | super(message); 10 | } 11 | 12 | public CommandNotFound(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public CommandNotFound(Throwable cause) { 17 | super(cause); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/exception/CommandParseException.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.exception; 2 | 3 | public class CommandParseException extends Exception { 4 | public CommandParseException() { 5 | super(); 6 | } 7 | 8 | public CommandParseException(String message) { 9 | super(message); 10 | } 11 | 12 | public CommandParseException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public CommandParseException(Throwable cause) { 17 | super(cause); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/exception/CommandUsageException.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.exception; 2 | 3 | public class CommandUsageException extends CommandParseException { 4 | public CommandUsageException() { 5 | super(); 6 | } 7 | 8 | public CommandUsageException(String message) { 9 | super(message); 10 | } 11 | 12 | public CommandUsageException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public CommandUsageException(Throwable cause) { 17 | super(cause); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/exception/NoMoreArgumentsException.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.exception; 2 | 3 | public class NoMoreArgumentsException extends CommandParseException { 4 | public NoMoreArgumentsException(int size, int position) { 5 | super("No more arguments were found, size: " + size + " position: " + position); 6 | } 7 | 8 | public NoMoreArgumentsException() { 9 | } 10 | 11 | public NoMoreArgumentsException(String message) { 12 | super(message); 13 | } 14 | 15 | public NoMoreArgumentsException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | public NoMoreArgumentsException(Throwable cause) { 20 | super(cause); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/exception/NoPermissionException.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.exception; 2 | 3 | public class NoPermissionException extends CommandUsageException { 4 | public NoPermissionException() { 5 | this("No permission."); 6 | } 7 | 8 | public NoPermissionException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/internal/CommandLineParser.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.internal; 2 | 3 | import me.fixeddev.ebcm.ArgumentStack; 4 | import me.fixeddev.ebcm.Command; 5 | import me.fixeddev.ebcm.CommandManager; 6 | import me.fixeddev.ebcm.NamespaceAccesor; 7 | import me.fixeddev.ebcm.ParseResult; 8 | import me.fixeddev.ebcm.exception.CommandNotFound; 9 | import me.fixeddev.ebcm.exception.CommandParseException; 10 | import me.fixeddev.ebcm.exception.CommandUsageException; 11 | import me.fixeddev.ebcm.exception.NoMoreArgumentsException; 12 | import me.fixeddev.ebcm.parameter.provider.Key; 13 | import me.fixeddev.ebcm.parameter.provider.ParameterProvider; 14 | import me.fixeddev.ebcm.parameter.provider.ParameterProviderRegistry; 15 | import me.fixeddev.ebcm.part.ArgumentPart; 16 | import me.fixeddev.ebcm.part.CommandPart; 17 | import me.fixeddev.ebcm.part.FlagPart; 18 | import me.fixeddev.ebcm.part.InjectedValuePart; 19 | import me.fixeddev.ebcm.part.SubCommandPart; 20 | import me.fixeddev.ebcm.util.UsageBuilder; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Collections; 24 | import java.util.HashMap; 25 | import java.util.LinkedHashMap; 26 | import java.util.List; 27 | import java.util.ListIterator; 28 | import java.util.Map; 29 | import java.util.Optional; 30 | import java.util.function.Function; 31 | import java.util.stream.Collectors; 32 | 33 | public class CommandLineParser { 34 | 35 | private Command rootCommand; 36 | private String commandLabel; 37 | 38 | // Per command fields start 39 | private List currentCommandParts; 40 | private Command currentCommand; 41 | 42 | private int partsLeft; 43 | private ListIterator partsIterator; 44 | private CommandPart currentPart; 45 | // Per command fields end 46 | 47 | private ArgumentStack argumentStack; 48 | 49 | private int argumentsLeft; 50 | 51 | private List bindings; 52 | private Map valueBindings; 53 | 54 | private NamespaceAccesor namespaceAccesor; 55 | 56 | private CommandManager commandManager; 57 | 58 | private ParameterProviderRegistry providerRegistry; 59 | 60 | public CommandLineParser(List argumentsLine, NamespaceAccesor namespaceAccesor, CommandManager commandManager) { 61 | commandLabel = ""; 62 | 63 | argumentStack = new ArgumentStack(argumentsLine); 64 | 65 | argumentsLeft = argumentsLine.size(); 66 | 67 | bindings = new ArrayList<>(); 68 | valueBindings = new LinkedHashMap<>(); 69 | 70 | this.commandManager = commandManager; 71 | this.namespaceAccesor = namespaceAccesor; 72 | providerRegistry = commandManager.getProviderRegistry(); 73 | } 74 | 75 | public String nextArgument() throws CommandParseException { 76 | argumentsLeft--; 77 | return argumentStack.next(); 78 | } 79 | 80 | public boolean hasNextArgument() { 81 | return argumentStack.hasNext(); 82 | } 83 | 84 | public CommandPart nextUnboundPart() throws CommandParseException { 85 | if (!partsIterator.hasNext()) { 86 | throw new CommandParseException("There're is not a next part!"); 87 | } 88 | 89 | return (currentPart = partsIterator.next()); 90 | } 91 | 92 | public boolean hasNextUnboundPart() { 93 | return partsIterator.hasNext(); 94 | } 95 | 96 | public void bindPart(CommandPart part, List arguments) { 97 | ParseResult.ParameterBinding binding = new ParameterBindingData(arguments, part); 98 | 99 | bindings.add(binding); 100 | } 101 | 102 | public Command currentAsRootCommand() throws CommandNotFound, CommandParseException { 103 | String currentArgument = argumentStack.current(); 104 | 105 | if (rootCommand != null) { 106 | throw new CommandParseException("The command was already found, no need to execute this method again!"); 107 | } 108 | 109 | rootCommand = commandManager.getCommand(currentArgument) 110 | .orElseThrow(() -> new CommandNotFound("The current argument(" + currentArgument + ") can't be found as command!")); 111 | 112 | useCommand(rootCommand); 113 | 114 | return rootCommand; 115 | } 116 | 117 | public void useCommand(Command command) { 118 | currentCommand = command; 119 | 120 | commandLabel += " " + argumentStack.current(); 121 | currentCommandParts = new ArrayList<>(command.getParts()); 122 | partsIterator = currentCommandParts.listIterator(); 123 | partsLeft = currentCommandParts.size(); 124 | } 125 | 126 | private void parseFlags() throws NoMoreArgumentsException { 127 | Map flagParts = currentCommandParts.stream() 128 | .filter(part -> part instanceof FlagPart) 129 | .map(part -> (FlagPart) part).collect(Collectors.toMap(FlagPart::getFlagChar, Function.identity())); 130 | 131 | currentCommandParts.removeAll(flagParts.values()); 132 | partsIterator = currentCommandParts.listIterator(); 133 | partsLeft = currentCommandParts.size(); 134 | 135 | List newArguments = argumentStack.getBacking().subList(0, argumentStack.getPosition() + 1); 136 | // We have the arguments already used, now, use them to create a new list 137 | newArguments = new ArrayList<>(newArguments); 138 | 139 | while (argumentStack.hasNext()) { 140 | String argument = argumentStack.next(); 141 | 142 | if (!argument.startsWith("-") || argument.length() != 2) { 143 | newArguments.add(argument); 144 | continue; 145 | } 146 | 147 | // Disable the parsing of the next flags 148 | if ("--".equals(argument)) { 149 | argumentsLeft--; 150 | break; 151 | } 152 | 153 | char flagChar = argument.charAt(1); 154 | FlagPart part = flagParts.get(flagChar); 155 | 156 | // The flag is valid, but it doesn't has a part to be bound 157 | if (part == null) { 158 | newArguments.add(argument); 159 | continue; 160 | } 161 | 162 | bindings.add(new ParameterBindingData(argument, part)); 163 | valueBindings.put(part, true); 164 | 165 | // We remove the part, at the end create a value binding for every flag that wasn't 166 | // in the command line with value false 167 | flagParts.remove(flagChar); 168 | 169 | argumentsLeft--; 170 | } 171 | 172 | this.argumentStack = new ArgumentStack(newArguments); 173 | flagParts.values().forEach(part -> valueBindings.put(part, false)); 174 | 175 | int newSize = argumentStack.getSize() - argumentsLeft; 176 | 177 | for (int i = 0; i < newSize; i++) { 178 | // Move the cursor to the last position, to prevent that an argument that was already used being used again 179 | argumentStack.next(); 180 | } 181 | } 182 | 183 | private boolean hasSubCommand; 184 | private int neededArguments; 185 | private int allNeededArguments; 186 | // Ok, the name of this is not clear 187 | // But, this variable it's supposed to mean the quantity of optional parts 188 | // that can be bound before only the required parts can be bound 189 | private int optionalArgumentsToBound; 190 | 191 | public ParseResult parse() throws CommandParseException, CommandNotFound { 192 | nextArgument(); 193 | currentAsRootCommand(); 194 | parseFlags(); 195 | 196 | hasSubCommand = currentCommandParts.subList(partsIterator.nextIndex(), currentCommandParts.size()).stream().anyMatch(part -> part instanceof SubCommandPart && part.isRequired()); 197 | neededArguments = calculateNeededArgs(); 198 | allNeededArguments = calculateAllNeededArgs(); 199 | optionalArgumentsToBound = argumentsLeft - neededArguments; 200 | 201 | if (partsLeft <= 0) { 202 | return new ParseResultData(commandLabel, argumentStack.getBacking(), currentCommand, currentCommand, bindings, valueBindings); 203 | } 204 | 205 | checkForInvalidInfiniteParts(); 206 | 207 | while (hasNextUnboundPart()) { 208 | CommandPart partToBind = nextUnboundPart(); 209 | 210 | if (partToBind instanceof SubCommandPart) { 211 | parseSubCommand(partToBind); 212 | } else if (partToBind instanceof ArgumentPart) { 213 | parseArgument(partToBind); 214 | } else if (partToBind instanceof InjectedValuePart) { 215 | parseInjectedPart(partToBind); 216 | } else { 217 | throw new CommandParseException("Invalid part type provided! Type: " + partToBind.getClass().getSimpleName()); 218 | } 219 | 220 | } 221 | 222 | return new ParseResultData(commandLabel.trim(), argumentStack.getBacking(), rootCommand, currentCommand, bindings, valueBindings); 223 | } 224 | 225 | private void parseInjectedPart(CommandPart partToBind) throws CommandParseException { 226 | InjectedValuePart part = (InjectedValuePart) partToBind; 227 | 228 | String modifier = part.getModifiers().isEmpty() ? null : part.getModifiers().get(0); 229 | ParameterProvider provider = providerRegistry.getParameterProvider(new Key<>(modifier, part.getType())); 230 | 231 | if (provider == null) { 232 | provider = providerRegistry.getParameterProvider(part.getType()); 233 | } 234 | 235 | if (provider == null || !provider.isInjected()) { 236 | throw new CommandParseException("Failed to get a provider for the part " + part.getName()); 237 | } 238 | 239 | ParameterProvider.Result result = provider.transform(Collections.emptyList(), namespaceAccesor, part); 240 | 241 | Optional optionalObject = unwrapObject(result, part); 242 | 243 | if (!optionalObject.isPresent() && part.isRequired()) { 244 | throw new CommandParseException("Failed to get the injected value for the part with name " + part.getName() + 245 | "\n injected name: " + part.getInjectedName() + 246 | "\n type: " + part.getType()); 247 | } 248 | 249 | 250 | valueBindings.put(partToBind, optionalObject.orElse(null)); 251 | } 252 | 253 | private void parseArgument(CommandPart partToBind) throws CommandParseException { 254 | ArgumentPart part = (ArgumentPart) partToBind; 255 | 256 | String modifier = part.getModifiers().isEmpty() ? null : part.getModifiers().get(0); 257 | ParameterProvider provider = providerRegistry.getParameterProvider(new Key<>(modifier, part.getArgumentType())); 258 | 259 | if (provider == null) { 260 | provider = providerRegistry.getParameterProvider(part.getArgumentType()); 261 | } 262 | 263 | if (provider == null || provider.isInjected()) { 264 | throw new CommandParseException("Failed to get a provider for the part " + part.getName()); 265 | } 266 | 267 | List argumentsToUse = new ArrayList<>(); 268 | boolean usingDefaults = false; 269 | 270 | if (!part.isRequired()) { 271 | if ((optionalArgumentsToBound <= 0 || argumentsLeft < neededArguments + part.getConsumedArguments()) || 272 | (allNeededArguments == -1 || part.getConsumedArguments() == -1 || hasSubCommand)) { 273 | if (part.getDefaultValues().isEmpty()) { 274 | return; 275 | } 276 | 277 | usingDefaults = true; 278 | argumentsToUse = part.getDefaultValues(); 279 | allNeededArguments = allNeededArguments - part.getConsumedArguments(); 280 | } 281 | } 282 | 283 | if (!usingDefaults) { 284 | argumentsToUse = getArgumentsToConsume(part); 285 | 286 | bindPart(part, argumentsToUse); 287 | } 288 | 289 | ParameterProvider.Result object = provider.transform(argumentsToUse, namespaceAccesor, part); 290 | 291 | valueBindings.put(part, unwrapObject(object, part).orElse(null)); 292 | } 293 | 294 | 295 | private Optional unwrapObject(ParameterProvider.Result result, CommandPart part) throws CommandParseException { 296 | Optional providedObject = result.getResultObject(); 297 | Optional message = result.getMessage(); 298 | Optional lastError = result.lastError(); 299 | 300 | if (!providedObject.isPresent()) { 301 | if (lastError.isPresent()) { 302 | throw new CommandParseException("An exception occurred while parsing the part " + part.getName() + " argument!", lastError.get()); 303 | } 304 | 305 | message.ifPresent(s -> commandManager.getMessager().sendMessage(namespaceAccesor, s)); 306 | 307 | return Optional.empty(); 308 | } 309 | 310 | return providedObject; 311 | } 312 | 313 | private List getArgumentsToConsume(ArgumentPart part) throws CommandParseException { 314 | List argumentsToUse = new ArrayList<>(); 315 | 316 | int i = 0; 317 | 318 | while (i != part.getConsumedArguments()) { 319 | if (!hasNextArgument()) { 320 | if (part.getConsumedArguments() != -1) { 321 | commandManager.getMessager().sendMessage(namespaceAccesor, Messages.MISSING_ARGUMENT.getId(), 322 | "Missing arguments for required part %s minimum arguments required: %s", part.getName(), neededArguments + ""); 323 | 324 | throw new CommandUsageException(UsageBuilder.getUsageForCommand(rootCommand, currentCommand, commandLabel)); 325 | } 326 | 327 | break; 328 | } 329 | 330 | String argument = argumentStack.next(); 331 | argumentsLeft--; 332 | 333 | argumentsToUse.add(argument); 334 | 335 | /* 336 | * Don't subtract an argument from needed arguments if this part is not required, because it's not counted there 337 | */ 338 | if (part.isRequired()) { 339 | neededArguments--; 340 | } else { 341 | optionalArgumentsToBound--; 342 | } 343 | allNeededArguments--; 344 | 345 | i++; 346 | } 347 | 348 | return argumentsToUse; 349 | } 350 | 351 | private void parseSubCommand(CommandPart partToBind) throws CommandParseException { 352 | if (hasNextUnboundPart()) { 353 | throw new CommandParseException("The sub-command should be the last part of the command!"); 354 | } 355 | 356 | SubCommandPart subCommandPart = (SubCommandPart) partToBind; 357 | Map availableValues = new HashMap<>(); 358 | 359 | for (Command command : subCommandPart.getCommandsToCall()) { 360 | availableValues.put(command.getData().getName(), command); 361 | 362 | for (String value : command.getData().getAliases()) { 363 | availableValues.put(value.toLowerCase(), command); 364 | } 365 | } 366 | 367 | String availableValuesString = String.join(", ", availableValues.keySet()); 368 | 369 | if (!hasNextArgument()) { 370 | if (partToBind.isRequired()) { 371 | commandManager.getMessager().sendMessage(namespaceAccesor, Messages.MISSING_SUBCOMMAND.getId(), 372 | "Missing argument for required part %s, available values: %s", partToBind.getName(), availableValuesString); 373 | 374 | throw new CommandUsageException(UsageBuilder.getUsageForCommand(rootCommand, currentCommand, commandLabel)); 375 | } 376 | 377 | return; 378 | } 379 | 380 | String argument = argumentStack.next(); 381 | argumentsLeft--; 382 | 383 | Command command = availableValues.get(argument.toLowerCase()); 384 | 385 | if (command == null) { 386 | commandManager.getMessager().sendMessage(namespaceAccesor, Messages.INVALID_SUBCOMMAND.getId(), 387 | "Invalid sub-command, valid values: %s", availableValuesString); 388 | 389 | 390 | throw new CommandUsageException(UsageBuilder.getUsageForCommand(rootCommand, currentCommand, commandLabel)); 391 | } 392 | 393 | useCommand(command); 394 | parseFlags(); 395 | 396 | hasSubCommand = currentCommandParts.subList(partsIterator.nextIndex(), currentCommandParts.size()).stream().anyMatch(part -> part instanceof SubCommandPart); 397 | neededArguments = calculateNeededArgs(); 398 | allNeededArguments = calculateAllNeededArgs(); 399 | 400 | ParseResult.ParameterBinding parameterBinding = new ParameterBindingData(argumentStack.current(), currentPart); 401 | bindings.add(parameterBinding); 402 | } 403 | 404 | 405 | private void checkForInvalidInfiniteParts() throws CommandParseException { 406 | boolean infinitePartFound = false; 407 | 408 | for (CommandPart part : currentCommandParts) { 409 | if (!(part instanceof ArgumentPart)) { 410 | continue; 411 | } 412 | 413 | ArgumentPart argumentPart = (ArgumentPart) part; 414 | 415 | if (argumentPart.getConsumedArguments() == -1) { 416 | infinitePartFound = true; 417 | continue; 418 | } 419 | 420 | if (argumentPart.isRequired() && infinitePartFound) { 421 | throw new CommandParseException("A required part was found after an infinite part!"); 422 | } 423 | } 424 | } 425 | 426 | private int calculateNeededArgs() { 427 | int sum = 0; 428 | 429 | for (CommandPart part : currentCommandParts) { 430 | if (!(part instanceof ArgumentPart) || !part.isRequired()) { 431 | continue; 432 | } 433 | 434 | int consumedArgs = ((ArgumentPart) part).getConsumedArguments(); 435 | 436 | if (consumedArgs == -1) { 437 | consumedArgs = 1; 438 | } 439 | 440 | sum += consumedArgs; 441 | } 442 | 443 | return sum; 444 | } 445 | 446 | private int calculateAllNeededArgs() { 447 | int sum = 0; 448 | 449 | for (CommandPart part : currentCommandParts) { 450 | if (!(part instanceof ArgumentPart)) { 451 | continue; 452 | } 453 | 454 | int consumedArgs = ((ArgumentPart) part).getConsumedArguments(); 455 | 456 | if (consumedArgs == -1) { 457 | return -1; 458 | } 459 | 460 | sum += consumedArgs; 461 | } 462 | 463 | return sum; 464 | } 465 | 466 | static class ParameterBindingData implements ParseResult.ParameterBinding { 467 | 468 | private List raw; 469 | private CommandPart boundPart; 470 | 471 | public ParameterBindingData(String raw, CommandPart boundPart) { 472 | this.raw = new ArrayList<>(1); 473 | this.raw.add(raw); 474 | 475 | this.boundPart = boundPart; 476 | } 477 | 478 | public ParameterBindingData(List raw, CommandPart boundPart) { 479 | this.raw = raw; 480 | this.boundPart = boundPart; 481 | } 482 | 483 | @Override 484 | public List getRaw() { 485 | return raw; 486 | } 487 | 488 | @Override 489 | public CommandPart getBind() { 490 | return boundPart; 491 | } 492 | } 493 | 494 | static class ParseResultData implements ParseResult { 495 | 496 | private String label; 497 | private List commandLine; 498 | private Command command; 499 | private Command commandToExecute; 500 | private List parameterBindings; 501 | private Map valueBindings; 502 | 503 | public ParseResultData(String label, 504 | List commandLine, 505 | Command command, 506 | Command commandToExecute, 507 | List parameterBindings, 508 | Map valueBindings) { 509 | this.label = label; 510 | this.commandLine = commandLine; 511 | this.command = command; 512 | this.commandToExecute = commandToExecute; 513 | this.parameterBindings = parameterBindings; 514 | this.valueBindings = valueBindings; 515 | } 516 | 517 | @Override 518 | public String getLabel() { 519 | return label; 520 | } 521 | 522 | @Override 523 | public List getCommandLine() { 524 | return commandLine; 525 | } 526 | 527 | @Override 528 | public Command getMainCommand() { 529 | return command; 530 | } 531 | 532 | @Override 533 | public Command getCommandToExecute() { 534 | return commandToExecute; 535 | } 536 | 537 | public List getBindings() { 538 | return parameterBindings; 539 | } 540 | 541 | @Override 542 | public Map getValueBindings() { 543 | return valueBindings; 544 | } 545 | } 546 | } 547 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/internal/Messages.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.internal; 2 | 3 | import java.util.StringJoiner; 4 | 5 | public enum Messages { 6 | COMMAND_USAGE("command.usage") { 7 | @Override 8 | public String getId(String... commandName) { 9 | StringJoiner joiner = new StringJoiner("."); 10 | joiner.add(getId()); 11 | 12 | for (String s : commandName) { 13 | joiner.add(s); 14 | } 15 | 16 | return joiner.toString(); 17 | } 18 | }, 19 | COMMAND_NO_PERMISSIONS("command.no.permissions") { 20 | @Override 21 | public String getId(String... commandName) { 22 | StringJoiner joiner = new StringJoiner("."); 23 | joiner.add(getId()); 24 | 25 | for (String s : commandName) { 26 | joiner.add(s); 27 | } 28 | 29 | return joiner.toString(); 30 | } 31 | }, 32 | MISSING_ARGUMENT("missing.argument"), 33 | MISSING_SUBCOMMAND("missing.subcommand"), 34 | INVALID_SUBCOMMAND("invalid.subcommand"); 35 | 36 | private String id; 37 | 38 | Messages(String id) { 39 | this.id = id; 40 | } 41 | 42 | public String getId() { 43 | return id; 44 | } 45 | 46 | public String getId(String... commandName) { 47 | return id; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return getId(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/internal/namespace/Namespace.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.internal.namespace; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | public class Namespace implements NamespaceAccesor { 9 | private Map, Map> backing; 10 | 11 | public Namespace() { 12 | backing = new ConcurrentHashMap<>(); 13 | } 14 | 15 | @Override 16 | @SuppressWarnings("unchecked") 17 | public T getObject(Class clazz, String name) { 18 | return (T) backing.getOrDefault(clazz, new ConcurrentHashMap<>()).get(name); 19 | } 20 | 21 | public void setObject(Class clazz, String name, T object) { 22 | Map map = backing.getOrDefault(clazz, new ConcurrentHashMap<>()); 23 | map.put(name, object); 24 | 25 | backing.put(clazz, map); 26 | } 27 | } -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/internal/namespace/NamespaceAccessorDelegate.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.internal.namespace; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | 5 | 6 | public class NamespaceAccessorDelegate implements NamespaceAccesor { 7 | private NamespaceAccesor delegate; 8 | 9 | public NamespaceAccessorDelegate(NamespaceAccesor delegate) { 10 | this.delegate = delegate; 11 | } 12 | 13 | @Override 14 | public T getObject(Class clazz, String name) { 15 | return delegate.getObject(clazz, name); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/internal/namespace/SimpleCommandContext.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.internal.namespace; 2 | 3 | import me.fixeddev.ebcm.Command; 4 | import me.fixeddev.ebcm.CommandContext; 5 | import me.fixeddev.ebcm.NamespaceAccesor; 6 | import me.fixeddev.ebcm.ParseResult; 7 | import me.fixeddev.ebcm.part.CommandPart; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Optional; 14 | 15 | public class SimpleCommandContext extends NamespaceAccessorDelegate implements CommandContext { 16 | private final Command executedCommand; 17 | private final List rawArguments; 18 | private final String label; 19 | 20 | private final Map> allParts; 21 | private final Map> rawBindings; 22 | private final Map valueBindings; 23 | 24 | private List boundParts; 25 | 26 | public SimpleCommandContext(NamespaceAccesor namespace, ParseResult result) { 27 | super(namespace); 28 | 29 | allParts = new HashMap<>(); 30 | 31 | for (CommandPart part : result.getCommandToExecute().getParts()) { 32 | allParts.computeIfAbsent(part.getName(), k -> new ArrayList<>()).add(part); 33 | } 34 | 35 | for (CommandPart part : result.getMainCommand().getParts()) { 36 | allParts.computeIfAbsent(part.getName(), k -> new ArrayList<>()).add(part); 37 | } 38 | 39 | rawBindings = new HashMap<>(); 40 | 41 | for (ParseResult.ParameterBinding binding : result.getBindings()) { 42 | rawBindings.put(binding.getBind(), binding.getRaw()); 43 | } 44 | 45 | valueBindings = result.getValueBindings(); 46 | 47 | label = result.getLabel(); 48 | rawArguments = result.getCommandLine(); 49 | executedCommand = result.getCommandToExecute(); 50 | } 51 | 52 | @Override 53 | public Command getCommand() { 54 | return executedCommand; 55 | } 56 | 57 | @Override 58 | public List getArguments() { 59 | return rawArguments; 60 | } 61 | 62 | @Override 63 | public String getLabel() { 64 | return label; 65 | } 66 | 67 | @Override 68 | public boolean has(CommandPart part) { 69 | return rawBindings.containsKey(part); 70 | } 71 | 72 | @Override 73 | public List getParts(String name) { 74 | return allParts.computeIfAbsent(name, k -> new ArrayList<>()); 75 | } 76 | 77 | @Override 78 | public Optional> getRaw(CommandPart part) { 79 | return Optional.ofNullable(rawBindings.get(part)); 80 | } 81 | 82 | @Override 83 | public List getBoundParts() { 84 | if (boundParts == null) { 85 | boundParts = new ArrayList<>(valueBindings.keySet()); 86 | } 87 | 88 | return boundParts; 89 | } 90 | 91 | @Override 92 | public Optional getValue(CommandPart part) { 93 | return (Optional) Optional.ofNullable(valueBindings.get(part)); 94 | } 95 | 96 | @Override 97 | public V getRawValue(CommandPart part) { 98 | if(!hasValue(part)){ 99 | throw new IllegalArgumentException("The part " + part.getName() + " doesn't has a value associated with!"); 100 | } 101 | 102 | return (V) valueBindings.get(part); 103 | } 104 | 105 | @Override 106 | public boolean hasValue(CommandPart part) { 107 | return valueBindings.containsKey(part); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/InjectedProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.part.CommandPart; 5 | 6 | import java.util.List; 7 | 8 | public interface InjectedProvider extends ParameterProvider { 9 | @Override 10 | default Result transform(List arguments, NamespaceAccesor namespaceAccesor, CommandPart part) { 11 | if (!arguments.isEmpty()) { 12 | throw new IllegalStateException("An injected provider shouldn't have any arguments!"); 13 | } 14 | 15 | return transform(namespaceAccesor, part); 16 | } 17 | 18 | @Override 19 | default boolean isInjected() { 20 | return true; 21 | } 22 | 23 | Result transform(NamespaceAccesor namespaceAccesor, CommandPart part); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/Key.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider; 2 | 3 | import java.util.Objects; 4 | 5 | public class Key { 6 | private String modifier; 7 | private Class clazz; 8 | 9 | public Key(String modifier, Class clazz) { 10 | this.modifier = modifier; 11 | this.clazz = clazz; 12 | } 13 | 14 | public Key(Class clazz) { 15 | this.clazz = clazz; 16 | } 17 | 18 | public String getModifier() { 19 | return modifier; 20 | } 21 | 22 | public Class getClazz() { 23 | return clazz; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | if (this == o) return true; 29 | if (!(o instanceof Key)) return false; 30 | Key key = (Key) o; 31 | return Objects.equals(modifier, key.modifier) && 32 | Objects.equals(clazz, key.clazz); 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | return Objects.hash(modifier, clazz); 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "Key{" + 43 | "'" + modifier + '\'' + 44 | ": " + clazz + 45 | '}'; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/ParameterProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.SuggestionProvider; 5 | import me.fixeddev.ebcm.part.CommandPart; 6 | 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | public interface ParameterProvider extends SuggestionProvider { 13 | 14 | Result transform(List arguments, NamespaceAccesor namespaceAccesor, CommandPart part); 15 | 16 | default List getSuggestions(String startsWith) { 17 | return Collections.emptyList(); 18 | } 19 | 20 | default boolean isInjected() { 21 | return false; 22 | } 23 | 24 | interface Result { 25 | static Result createResult(T object) { 26 | return new SimpleResult<>(object); 27 | } 28 | 29 | static Result createResultOfMessage(String message) { 30 | return new SimpleResult<>(message); 31 | } 32 | 33 | static Result createResult(String message, List errors) { 34 | return new SimpleResult<>(message, errors); 35 | } 36 | 37 | static Result createResult(String message, Exception... errors) { 38 | return new SimpleResult<>(message, Arrays.asList(errors)); 39 | } 40 | 41 | static Result createResult(T object, String message, List errors) { 42 | return new SimpleResult<>(object, message, errors); 43 | } 44 | 45 | Optional getResultObject(); 46 | 47 | Optional getMessage(); 48 | 49 | List getErrors(); 50 | 51 | Optional lastError(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/ParameterProviderRegistry.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider; 2 | 3 | import java.util.Map; 4 | 5 | public interface ParameterProviderRegistry { 6 | static ParameterProviderRegistry createRegistry() { 7 | return new ParameterProviderRegistryImpl(); 8 | } 9 | 10 | Map, ParameterProvider> getRegisteredProviders(); 11 | 12 | void registerParameterProvider(Key key, ParameterProvider parameterProvider); 13 | 14 | default void registerParameterProvider(Class clazz, ParameterProvider parameterProvider) { 15 | registerParameterProvider(new Key<>(clazz), parameterProvider); 16 | } 17 | 18 | default ParameterProvider getParameterProvider(Class clazz) { 19 | return getParameterProvider(new Key<>(clazz)); 20 | } 21 | 22 | ParameterProvider getParameterProvider(Key key); 23 | 24 | default boolean hasRegisteredProvider(Class clazz){ 25 | return hasRegisteredProvider(new Key<>(clazz)); 26 | } 27 | 28 | boolean hasRegisteredProvider(Key key); 29 | 30 | default void installModule(ProvidersModule module) { 31 | module.configure(this); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/ParameterProviderRegistryImpl.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.parameter.provider.defaults.DefaultsModule; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | class ParameterProviderRegistryImpl implements ParameterProviderRegistry { 9 | private Map, ParameterProvider> parameterProviders; 10 | 11 | ParameterProviderRegistryImpl() { 12 | parameterProviders = new HashMap<>(); 13 | 14 | installModule(new DefaultsModule()); 15 | } 16 | 17 | @Override 18 | public boolean hasRegisteredProvider(Key key) { 19 | return parameterProviders.containsKey(key); 20 | } 21 | 22 | @Override 23 | public Map, ParameterProvider> getRegisteredProviders() { 24 | return parameterProviders; 25 | } 26 | 27 | @Override 28 | public void registerParameterProvider(Key key, ParameterProvider parameterProvider) { 29 | if (hasRegisteredProvider(key)) { 30 | throw new IllegalStateException("Failed to register parameter provider for key " + key.toString() + ", there's already a registered parameter provider for that class!"); 31 | } 32 | 33 | parameterProviders.put(key, parameterProvider); 34 | } 35 | 36 | @Override 37 | @SuppressWarnings("unchecked") 38 | public ParameterProvider getParameterProvider(Key key) { 39 | return (ParameterProvider) parameterProviders.get(key); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/ProvidersModule.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider; 2 | 3 | public interface ProvidersModule { 4 | void configure(ParameterProviderRegistry registry); 5 | } 6 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/SimpleResult.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import java.util.Optional; 6 | 7 | class SimpleResult implements ParameterProvider.Result { 8 | private T object; 9 | private String message; 10 | private List errors; 11 | 12 | private Exception lastError; 13 | 14 | SimpleResult(T object) { 15 | this.object = object; 16 | errors = Collections.emptyList(); 17 | } 18 | 19 | SimpleResult(String message) { 20 | this.message = message; 21 | errors = Collections.emptyList(); 22 | } 23 | 24 | SimpleResult(String message, List errors) { 25 | this.message = message; 26 | this.errors = errors; 27 | } 28 | 29 | SimpleResult(T object, String message, List errors) { 30 | this.object = object; 31 | this.message = message; 32 | this.errors = errors; 33 | } 34 | 35 | @Override 36 | public Optional getResultObject() { 37 | return Optional.ofNullable(object); 38 | } 39 | 40 | @Override 41 | public Optional getMessage() { 42 | return Optional.ofNullable(message); 43 | } 44 | 45 | @Override 46 | public List getErrors() { 47 | return errors; 48 | } 49 | 50 | @Override 51 | public Optional lastError() { 52 | if (lastError == null && !errors.isEmpty()) { 53 | lastError = errors.get(errors.size() - 1); 54 | } 55 | 56 | return Optional.ofNullable(lastError); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/SingleArgumentProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.part.CommandPart; 5 | 6 | import java.util.List; 7 | 8 | public interface SingleArgumentProvider extends ParameterProvider { 9 | @Override 10 | default Result transform(List arguments, NamespaceAccesor namespaceAccesor, CommandPart part) { 11 | if (arguments.size() != 1) { 12 | return Result.createResult("Internal error!", 13 | new IllegalArgumentException("A single argument provider should receive only one argument!")); 14 | } 15 | 16 | return transform(arguments.get(0), namespaceAccesor, part); 17 | } 18 | 19 | Result transform(String arguments, NamespaceAccesor namespaceAccesor, CommandPart part); 20 | } 21 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/defaults/BooleanProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider.defaults; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.parameter.provider.SingleArgumentProvider; 5 | import me.fixeddev.ebcm.part.CommandPart; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | public class BooleanProvider implements SingleArgumentProvider { 12 | private static List suggestions = Arrays.asList("true", "false"); 13 | 14 | @Override 15 | public Result transform(String argument, NamespaceAccesor namespaceAccesor, CommandPart part) { 16 | if (!argument.equalsIgnoreCase("true") && !argument.equalsIgnoreCase("false")) { 17 | return Result.createResultOfMessage("The provided argument(" + argument + ") is not a valid boolean!"); 18 | } 19 | 20 | return Result.createResult(Boolean.parseBoolean(argument)); 21 | } 22 | 23 | @Override 24 | public List getSuggestions(String startsWith) { 25 | List newSuggestions = new ArrayList<>(); 26 | 27 | for (String suggestion : suggestions) { 28 | if(suggestion.startsWith(startsWith)){ 29 | newSuggestions.add(suggestion); 30 | } 31 | } 32 | 33 | return newSuggestions; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/defaults/DefaultsModule.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider.defaults; 2 | 3 | import me.fixeddev.ebcm.parameter.provider.ParameterProviderRegistry; 4 | import me.fixeddev.ebcm.parameter.provider.ProvidersModule; 5 | 6 | public class DefaultsModule implements ProvidersModule { 7 | @Override 8 | public void configure(ParameterProviderRegistry registry) { 9 | registry.registerParameterProvider(Boolean.class, new BooleanProvider()); 10 | registry.registerParameterProvider(Double.class, new DoubleProvider()); 11 | registry.registerParameterProvider(Integer.class, new IntProvider()); 12 | registry.registerParameterProvider(String.class, new StringProvider()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/defaults/DoubleProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider.defaults; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.parameter.provider.SingleArgumentProvider; 5 | import me.fixeddev.ebcm.part.CommandPart; 6 | 7 | public class DoubleProvider implements SingleArgumentProvider { 8 | @Override 9 | public Result transform(String argument, NamespaceAccesor namespaceAccesor, CommandPart part) { 10 | try { 11 | return Result.createResult(Double.parseDouble(argument)); 12 | } catch (NumberFormatException ex) { 13 | return Result.createResultOfMessage("The argument " + argument + " is not a valid number!"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/defaults/IntProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider.defaults; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.parameter.provider.SingleArgumentProvider; 5 | import me.fixeddev.ebcm.part.CommandPart; 6 | 7 | public class IntProvider implements SingleArgumentProvider { 8 | @Override 9 | public Result transform(String argument, NamespaceAccesor namespaceAccesor, CommandPart part) { 10 | try { 11 | return Result.createResult(Integer.parseInt(argument)); 12 | } catch (NumberFormatException ex) { 13 | return Result.createResultOfMessage("The argument " + argument + " is not a valid number!"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parameter/provider/defaults/StringProvider.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parameter.provider.defaults; 2 | 3 | import me.fixeddev.ebcm.NamespaceAccesor; 4 | import me.fixeddev.ebcm.parameter.provider.ParameterProvider; 5 | import me.fixeddev.ebcm.part.CommandPart; 6 | 7 | import java.util.List; 8 | 9 | /* 10 | Ok, if you're reading this, you may ask why this class is not a SingleArgumentProvider 11 | Well, that's because a string can be a joined string, that means that a list of string arguments 12 | can be joined to form a single string in this form "arg arg2 arg3" 13 | */ 14 | public class StringProvider implements ParameterProvider { 15 | 16 | @Override 17 | public Result transform(List arguments, NamespaceAccesor namespaceAccesor, CommandPart part) { 18 | return Result.createResult(String.join(" ", arguments)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/CommandClass.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric; 2 | 3 | public interface CommandClass { 4 | } 5 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/ParametricCommandBuilder.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric; 2 | 3 | import me.fixeddev.ebcm.Command; 4 | 5 | import java.lang.reflect.Method; 6 | import java.util.List; 7 | 8 | public interface ParametricCommandBuilder { 9 | Command fromMethod(CommandClass commandClass, Method method); 10 | 11 | List fromClass(CommandClass commandClass); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/ReflectionParametricCommandBuilder.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric; 2 | 3 | import me.fixeddev.ebcm.Command; 4 | import me.fixeddev.ebcm.CommandAction; 5 | import me.fixeddev.ebcm.CommandContext; 6 | import me.fixeddev.ebcm.CommandData; 7 | import me.fixeddev.ebcm.ImmutableCommand; 8 | import me.fixeddev.ebcm.exception.CommandException; 9 | import me.fixeddev.ebcm.parametric.annotation.ACommand; 10 | import me.fixeddev.ebcm.parametric.annotation.ConsumedArgs; 11 | import me.fixeddev.ebcm.parametric.annotation.Default; 12 | import me.fixeddev.ebcm.parametric.annotation.Flag; 13 | import me.fixeddev.ebcm.parametric.annotation.Injected; 14 | import me.fixeddev.ebcm.parametric.annotation.ModifierAnnotation; 15 | import me.fixeddev.ebcm.parametric.annotation.Named; 16 | import me.fixeddev.ebcm.parametric.annotation.ParentArg; 17 | import me.fixeddev.ebcm.parametric.annotation.Required; 18 | import me.fixeddev.ebcm.parametric.annotation.SubCommandClasses; 19 | import me.fixeddev.ebcm.parametric.annotation.Usage; 20 | import me.fixeddev.ebcm.part.ArgumentPart; 21 | import me.fixeddev.ebcm.part.CommandPart; 22 | import me.fixeddev.ebcm.part.FlagPart; 23 | import me.fixeddev.ebcm.part.InjectedValuePart; 24 | import me.fixeddev.ebcm.part.SubCommandPart; 25 | 26 | import java.lang.annotation.Annotation; 27 | import java.lang.reflect.Constructor; 28 | import java.lang.reflect.InvocationTargetException; 29 | import java.lang.reflect.Method; 30 | import java.lang.reflect.Modifier; 31 | import java.lang.reflect.Parameter; 32 | import java.util.ArrayList; 33 | import java.util.Arrays; 34 | import java.util.Collections; 35 | import java.util.HashMap; 36 | import java.util.List; 37 | import java.util.Map; 38 | import java.util.Optional; 39 | 40 | public class ReflectionParametricCommandBuilder implements ParametricCommandBuilder { 41 | 42 | @Override 43 | public Command fromMethod(CommandClass commandClass, Method method) { 44 | ACommand commandAnnotation = method.getAnnotation(ACommand.class); 45 | 46 | if (commandAnnotation == null) { 47 | throw new IllegalArgumentException("The provided method isn't annotated with an ACommand annotation"); 48 | } 49 | 50 | if (!Modifier.isPublic(method.getModifiers())) { 51 | throw new IllegalArgumentException("The provided method doesn't has public visibility!"); 52 | } 53 | 54 | if (method.getReturnType() != boolean.class && method.getReturnType() != Boolean.class) { 55 | throw new IllegalArgumentException("The provided method doesn't return a boolean value!"); 56 | } 57 | 58 | String[] names = commandAnnotation.names(); 59 | 60 | ImmutableCommand.Builder commandBuilder = ImmutableCommand.builder( 61 | // 62 | CommandData.builder(names[0]) 63 | .setDescription(commandAnnotation.desc()) 64 | .setAliases(Arrays.asList(Arrays.copyOfRange(names, 1, names.length))) 65 | // 66 | ).setPermission(commandAnnotation.permission()) 67 | .setPermissionMessage(commandAnnotation.permissionMessage()); 68 | 69 | for (Parameter parameter : method.getParameters()) { 70 | CommandPart part = fromParameter(parameter); 71 | 72 | if (part == null) { 73 | continue; 74 | } 75 | 76 | commandBuilder.addPart(part); 77 | } 78 | 79 | commandBuilder.setAction(actionOfMethod(commandClass, method)); 80 | 81 | Usage usage = method.getAnnotation(Usage.class); 82 | if (usage != null) { 83 | if (!usage.usage()[0].equals("_!!_NOT_OVERRIDE_!!_")) { 84 | commandBuilder.setUsage(String.join("\n", usage.usage())); 85 | } 86 | } 87 | 88 | 89 | return commandBuilder.build(); 90 | } 91 | 92 | @Override 93 | public List fromClass(CommandClass commandClass) { 94 | Class clazz = commandClass.getClass(); 95 | 96 | ACommand commandAnnotation = clazz.getAnnotation(ACommand.class); 97 | 98 | Map commands = new HashMap<>(); 99 | List commandList = new ArrayList<>(); 100 | for (Method method : clazz.getDeclaredMethods()) { 101 | if (Modifier.isStatic(method.getModifiers()) || !Modifier.isPublic(method.getModifiers())) { 102 | continue; 103 | } 104 | 105 | if (method.getReturnType() != boolean.class && method.getReturnType() != Boolean.class) { 106 | continue; 107 | } 108 | 109 | if (!method.isAnnotationPresent(ACommand.class)) { 110 | continue; 111 | } 112 | 113 | Command command = fromMethod(commandClass, method); 114 | 115 | commands.put(command.getData().getName(), command); 116 | commandList.add(command); 117 | } 118 | 119 | if (commandAnnotation != null) { 120 | Command command = commands.get(""); 121 | 122 | String[] names = commandAnnotation.names(); 123 | 124 | CommandData.Builder dataBuilder = CommandData.builder(commandAnnotation.names()[0]) 125 | .setAliases(Arrays.asList(Arrays.copyOfRange(names, 1, names.length))) 126 | .setDescription(commandAnnotation.desc()); 127 | 128 | ImmutableCommand.Builder builder = ImmutableCommand.builder(dataBuilder); 129 | 130 | builder.setPermission(commandAnnotation.permission()) 131 | .setPermissionMessage(commandAnnotation.permissionMessage()); 132 | 133 | if (command != null) { 134 | commands.remove(""); 135 | commandList.remove(command); 136 | 137 | builder.setUsage(command.getUsage()) 138 | .setAction(command.getAction()) 139 | .setCommandParts(command.getParts()); 140 | } 141 | 142 | SubCommandPart.Builder subCommandBuilder = SubCommandPart.builder("subcommand"); 143 | 144 | if (clazz.isAnnotationPresent(Required.class)) { 145 | subCommandBuilder.setRequired(true); 146 | } 147 | 148 | SubCommandClasses classesAnnotation = clazz.getAnnotation(SubCommandClasses.class); 149 | 150 | if (classesAnnotation != null) { 151 | for (Class subCommandClass : classesAnnotation.value()) { 152 | if (subCommandClass == clazz) { 153 | continue; 154 | } 155 | 156 | try { 157 | List subCommands = fromClass(createSubCommandInstance(subCommandClass, clazz, commandClass)); 158 | commandList.addAll(subCommands); 159 | } catch (RuntimeException e) { 160 | continue; 161 | } 162 | } 163 | } 164 | 165 | Usage usage = clazz.getAnnotation(Usage.class); 166 | if (usage != null) { 167 | if (!usage.usage()[0].equals("_!!_NOT_OVERRIDE_!!_")) { 168 | builder.setUsage(String.join("\n", usage.usage())); 169 | } 170 | } 171 | 172 | builder.addPart(subCommandBuilder 173 | .setCommands(commandList) 174 | .build()); 175 | 176 | return Collections.singletonList(builder.build()); 177 | } 178 | 179 | return commandList; 180 | } 181 | 182 | private CommandClass createSubCommandInstance(Class clazz, Class upperCommandClass, CommandClass upperCommand) { 183 | try { 184 | Constructor constructor; 185 | boolean useUpperClass = true; 186 | try { 187 | constructor = clazz.getConstructor(upperCommandClass); 188 | } catch (NoSuchMethodException e) { 189 | constructor = clazz.getConstructor(); 190 | useUpperClass = false; 191 | } 192 | 193 | boolean accessible = constructor.isAccessible(); 194 | 195 | constructor.setAccessible(true); 196 | 197 | CommandClass instance; 198 | 199 | if (useUpperClass) { 200 | instance = (CommandClass) constructor.newInstance(upperCommand); 201 | } else { 202 | instance = (CommandClass) constructor.newInstance(); 203 | } 204 | constructor.setAccessible(accessible); 205 | 206 | return instance; 207 | } catch (Exception ex) { 208 | throw new RuntimeException(ex); 209 | } 210 | } 211 | 212 | private CommandPart fromParameter(Parameter parameter) { 213 | if (parameter.isAnnotationPresent(ParentArg.class)) { 214 | return null; 215 | } 216 | 217 | Class type = parameter.getType(); 218 | String name = getName(parameter); 219 | int consumedArgs = getConsumedArgs(parameter); 220 | Optional defaultValues = getDefault(parameter); 221 | 222 | Flag flag = parameter.getAnnotation(Flag.class); 223 | Injected injected = parameter.getAnnotation(Injected.class); 224 | 225 | List modifiers = new ArrayList<>(); 226 | 227 | for (Annotation annotation : parameter.getAnnotations()) { 228 | Class annotationType = annotation.annotationType(); 229 | 230 | ModifierAnnotation modifierAnnotation = annotationType.getAnnotation(ModifierAnnotation.class); 231 | 232 | // This is not a modifier annotation, ignore it 233 | if (modifierAnnotation == null) { 234 | continue; 235 | } 236 | 237 | modifiers.add(modifierAnnotation.value()); 238 | } 239 | 240 | if (injected != null && flag != null) { 241 | throw new IllegalArgumentException("The provided parameter has a Flag annotation and a Injected annotation, it should have only one of the two!"); 242 | } 243 | 244 | if (flag != null) { 245 | if (type != boolean.class && type != Boolean.class) { 246 | throw new IllegalArgumentException("The provided parameter has a Flag annotation but it doesn't a boolean!"); 247 | } 248 | 249 | return FlagPart.builder(name, flag.value()) 250 | .setAllModifiers(modifiers) 251 | .build(); 252 | } 253 | 254 | if (injected != null) { 255 | return InjectedValuePart.builder(name, type) 256 | .setRequired(injected.value()) 257 | .setAllModifiers(modifiers) 258 | .build(); 259 | } 260 | 261 | return ArgumentPart.builder(name, type) 262 | .setConsumedArguments(consumedArgs) 263 | .setRequired(!defaultValues.isPresent()) 264 | .setDefaultValues(Arrays.asList(defaultValues.orElse(new String[0]))) 265 | .setAllModifiers(modifiers) 266 | .build(); 267 | } 268 | 269 | private CommandAction actionOfMethod(CommandClass commandClass, Method method) { 270 | class ParametricCommandAction implements CommandAction { 271 | 272 | List commandParts = new ArrayList<>(); 273 | 274 | @Override 275 | public boolean execute(CommandContext parameters) throws CommandException { 276 | if (commandParts.isEmpty()) { 277 | computeParts(parameters); 278 | } 279 | 280 | List params = new ArrayList<>(); 281 | 282 | for (CommandPart part : commandParts) { 283 | if (part instanceof FlagPart || part instanceof ArgumentPart || part instanceof InjectedValuePart) { 284 | if (parameters.hasValue(part)) { 285 | params.add(parameters.getRawValue(part)); 286 | } else { 287 | if (part.isRequired()) { 288 | throw new CommandException("The value for the required part" + part.getName() + " is missing!"); 289 | } 290 | params.add(null); 291 | } 292 | } 293 | } 294 | 295 | boolean accessible = method.isAccessible(); 296 | 297 | try { 298 | method.setAccessible(true); 299 | 300 | boolean result = (boolean) method.invoke(commandClass, params.toArray()); 301 | method.setAccessible(accessible); 302 | 303 | return result; 304 | } catch (IllegalAccessException | InvocationTargetException e) { 305 | throw new CommandException("An exception occurred while executing the command", e); 306 | } 307 | } 308 | 309 | private void computeParts(CommandContext parameters) { 310 | for (Parameter parameter : method.getParameters()) { 311 | String name = getName(parameter); 312 | int indexOf = 0; 313 | 314 | ParentArg parentArg = parameter.getAnnotation(ParentArg.class); 315 | if (parentArg != null) { 316 | indexOf = parentArg.value(); 317 | } 318 | 319 | commandParts.add(parameters.getParts(name).get(indexOf)); 320 | } 321 | } 322 | 323 | } 324 | 325 | return new ParametricCommandAction(); 326 | } 327 | 328 | private String getName(Parameter parameter) { 329 | Named named = parameter.getAnnotation(Named.class); 330 | 331 | return named != null ? named.value() : parameter.getName(); 332 | } 333 | 334 | private int getConsumedArgs(Parameter parameter) { 335 | ConsumedArgs consumedArgs = parameter.getAnnotation(ConsumedArgs.class); 336 | 337 | return consumedArgs != null ? consumedArgs.value() : 1; 338 | } 339 | 340 | private Optional getDefault(Parameter parameter) { 341 | Default defaultA = parameter.getAnnotation(Default.class); 342 | 343 | return defaultA == null ? Optional.empty() : Optional.of(defaultA.value()); 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/ACommand.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.METHOD, ElementType.TYPE}) 10 | public @interface ACommand { 11 | 12 | String[] names(); 13 | 14 | String desc() default ""; 15 | 16 | String permission() default ""; 17 | 18 | String permissionMessage() default "No permission."; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/ConsumedArgs.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface ConsumedArgs { 11 | int value(); 12 | } 13 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/Default.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import me.fixeddev.ebcm.part.CommandPart; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * The purpose of this annotation is to define the default value of a parameter 12 | * in an {@link ACommand}. 13 | *

14 | * When you set this annotation to any parameter in an {@link ACommand} you set 15 | * the default value of that parameter and also you set that parameter {@link CommandPart#isRequired()} 16 | * value to false 17 | */ 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.PARAMETER) 20 | public @interface Default { 21 | String[] value() default {}; 22 | } 23 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/Flag.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Flag { 11 | char value(); 12 | } 13 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/Injected.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Injected { 11 | /** 12 | * @return a boolean value that indicates if this injected value is required or not 13 | */ 14 | boolean value() default false; 15 | } 16 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/ModifierAnnotation.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.ANNOTATION_TYPE) 10 | public @interface ModifierAnnotation { 11 | String value(); 12 | } 13 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/Named.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Named { 11 | String value(); 12 | } 13 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/Optional.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import me.fixeddev.ebcm.part.CommandPart; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * The purpose of this annotation is to define the default value of a parameter 12 | * in an {@link ACommand}. 13 | *

14 | * When you set this annotation to any parameter in an {@link ACommand} you set 15 | * the default value of that parameter and also you set that parameter {@link CommandPart#isRequired()} 16 | * value to false 17 | */ 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.PARAMETER) 20 | public @interface Optional { 21 | String[] value() default {}; 22 | } 23 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/ParentArg.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface ParentArg { 11 | /** 12 | * The index used to get this argument CommandPart value, 0 by default 13 | * but can be changed if more than 1 part with the same name is present 14 | * @return An integer representing the index of this argument's CommandPart 15 | */ 16 | int value() default 0; 17 | } 18 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/Required.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Indicates that a command class that contains subcommands 11 | * Requires a subcommand to be executed 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.TYPE) 15 | public @interface Required { 16 | } 17 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/SubCommandClasses.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import me.fixeddev.ebcm.parametric.CommandClass; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * This annotation has the purpose to allow multiple layers of subcommands 12 | * on parametric commands. This annotation will register the specified command classes 13 | * as subcommands for the annotated command class 14 | */ 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Target(ElementType.TYPE) 17 | public @interface SubCommandClasses { 18 | Class[] value(); 19 | } 20 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/parametric/annotation/Usage.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.parametric.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.METHOD, ElementType.TYPE}) 10 | public @interface Usage { 11 | String[] usage() default "_!!_NOT_OVERRIDE_!!_"; 12 | } 13 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/part/ArgumentConsumingPart.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.part; 2 | 3 | import java.util.List; 4 | 5 | public interface ArgumentConsumingPart extends LineConsumingPart { 6 | Class getArgumentType(); 7 | 8 | /** 9 | * This default values are supposed to replace the command line arguments 10 | * in case that we need the default value 11 | * 12 | * @return A list of arguments that are used as default value 13 | */ 14 | List getDefaultValues(); 15 | } 16 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/part/ArgumentPart.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.part; 2 | 3 | import com.google.auto.value.AutoValue; 4 | import com.google.auto.value.extension.memoized.Memoized; 5 | import me.fixeddev.ebcm.SuggestionProvider; 6 | import me.fixeddev.ebcm.util.ListAppender; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | @AutoValue 13 | public abstract class ArgumentPart implements ArgumentConsumingPart { 14 | 15 | public static Builder builder(String name, Class argumentType) { 16 | return new AutoValue_ArgumentPart.Builder() 17 | .named(name) 18 | .argumentType(argumentType) 19 | .setConsumedArguments(1) 20 | .setRequired(false) 21 | .setDescription("") 22 | .setDefaultValues(new ArrayList<>()); 23 | } 24 | 25 | public abstract Optional getSuggestionProvider(); 26 | 27 | public abstract int getConsumedArguments(); 28 | 29 | @Override 30 | @Memoized 31 | public String getLineRepresentation() { 32 | 33 | return (isRequired() ? "<" : "[") + 34 | getName() + 35 | (isRequired() ? ">" : "]"); 36 | } 37 | 38 | @AutoValue.Builder 39 | public abstract static class Builder { 40 | private ListAppender modifiersAppender = new ListAppender<>(); 41 | 42 | protected final Builder named(String name) { 43 | return setName(name); 44 | } 45 | 46 | protected abstract Builder setName(String newName); 47 | 48 | protected final Builder argumentType(Class argumentType) { 49 | return setArgumentType(argumentType); 50 | } 51 | 52 | protected abstract Builder setArgumentType(Class newArgumentType); 53 | 54 | public abstract Builder setDefaultValues(List defaultValues); 55 | 56 | public abstract Builder setDescription(String newDescription); 57 | 58 | public abstract Builder setSuggestionProvider(SuggestionProvider suggestionProvider); 59 | 60 | public Builder setAllModifiers(List modifiers) { 61 | this.modifiersAppender.set(modifiers); 62 | 63 | return this; 64 | } 65 | 66 | public Builder addModifier(String modifier) { 67 | this.modifiersAppender.add(modifier); 68 | 69 | return this; 70 | } 71 | 72 | protected abstract Builder setModifiers(List modifiers); 73 | 74 | protected abstract boolean isRequired(); 75 | 76 | public abstract Builder setRequired(boolean newRequired); 77 | 78 | protected abstract int getConsumedArguments(); 79 | 80 | public abstract Builder setConsumedArguments(int consumedArguments); 81 | 82 | protected abstract ArgumentPart autoBuild(); 83 | 84 | public ArgumentPart build() { 85 | setModifiers(modifiersAppender.toList()); 86 | 87 | return autoBuild(); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/part/CommandPart.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.part; 2 | 3 | import java.util.List; 4 | 5 | public interface CommandPart { 6 | String getName(); 7 | 8 | String getDescription(); 9 | 10 | boolean isRequired(); 11 | 12 | /** 13 | * You may ask, what are the modifiers? 14 | * The modifiers are properties set at the moment of the part creation 15 | * Those are used on the parameter providers to do things like joining strings with 16 | * specific character instead of the generic space 17 | * 18 | * @return The list of modifiers for this part 19 | */ 20 | List getModifiers(); 21 | } 22 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/part/FlagPart.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.part; 2 | 3 | import com.google.auto.value.AutoValue; 4 | import com.google.auto.value.extension.memoized.Memoized; 5 | import me.fixeddev.ebcm.util.ListAppender; 6 | 7 | import java.util.List; 8 | 9 | @AutoValue 10 | public abstract class FlagPart implements LineConsumingPart { 11 | 12 | public static Builder builder(String named, char flagChar) { 13 | return new AutoValue_FlagPart.Builder().named(named).flagChar(flagChar) 14 | .setDescription(""); 15 | } 16 | 17 | @Override 18 | @Memoized 19 | public String getLineRepresentation() { 20 | return "[-" + 21 | getFlagChar() + 22 | "]"; 23 | } 24 | 25 | @Override 26 | public boolean isRequired() { 27 | return false; 28 | } 29 | 30 | public abstract char getFlagChar(); 31 | 32 | @AutoValue.Builder 33 | public abstract static class Builder { 34 | private ListAppender modifiersAppender = new ListAppender<>(); 35 | 36 | protected final Builder named(String name) { 37 | return setName(name); 38 | } 39 | 40 | protected final Builder flagChar(char flagChar) { 41 | return setFlagChar(flagChar); 42 | } 43 | 44 | protected abstract Builder setName(String newName); 45 | 46 | protected abstract Builder setFlagChar(char newFlagChar); 47 | 48 | public abstract Builder setDescription(String newDescription); 49 | 50 | public Builder setAllModifiers(List modifiers) { 51 | this.modifiersAppender.set(modifiers); 52 | 53 | return this; 54 | } 55 | 56 | public Builder addModifier(String modifier) { 57 | this.modifiersAppender.add(modifier); 58 | 59 | return this; 60 | } 61 | 62 | protected abstract Builder setModifiers(List modifiers); 63 | 64 | protected abstract FlagPart autoBuild(); 65 | 66 | public final FlagPart build() { 67 | setModifiers(modifiersAppender.toList()); 68 | 69 | return autoBuild(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/part/InjectedValuePart.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.part; 2 | 3 | import com.google.auto.value.AutoValue; 4 | import me.fixeddev.ebcm.util.ListAppender; 5 | 6 | import java.util.List; 7 | 8 | @AutoValue 9 | public abstract class InjectedValuePart implements CommandPart { 10 | public static Builder builder(String name, Class type) { 11 | return new AutoValue_InjectedValuePart.Builder() 12 | .setName(name) 13 | .setInjectedName(name) 14 | .setType(type) 15 | .setDescription("") 16 | .setRequired(true); 17 | } 18 | 19 | public abstract Class getType(); 20 | 21 | public abstract String getInjectedName(); 22 | 23 | @AutoValue.Builder 24 | public abstract static class Builder { 25 | private ListAppender modifiersAppender = new ListAppender<>(); 26 | 27 | abstract Builder setName(String newName); 28 | 29 | public abstract Builder setDescription(String newDescription); 30 | 31 | public abstract Builder setRequired(boolean newRequired); 32 | 33 | public abstract Builder setType(Class type); 34 | 35 | public abstract Builder setInjectedName(String newInjectedName); 36 | 37 | public Builder setAllModifiers(List modifiers) { 38 | this.modifiersAppender.set(modifiers); 39 | 40 | return this; 41 | } 42 | 43 | public Builder addModifier(String modifier) { 44 | this.modifiersAppender.add(modifier); 45 | 46 | return this; 47 | } 48 | 49 | protected abstract Builder setModifiers(List modifiers); 50 | 51 | protected abstract InjectedValuePart autoBuild(); 52 | 53 | public InjectedValuePart build() { 54 | setModifiers(modifiersAppender.toList()); 55 | 56 | return autoBuild(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/part/LineConsumingPart.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.part; 2 | 3 | /** 4 | * Represents a CommandPart that consumes an argument/part of the command 5 | * line 6 | */ 7 | public interface LineConsumingPart extends CommandPart { 8 | String getLineRepresentation(); 9 | } 10 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/part/SubCommandPart.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.part; 2 | 3 | import com.google.auto.value.AutoValue; 4 | import com.google.auto.value.extension.memoized.Memoized; 5 | import me.fixeddev.ebcm.Command; 6 | import me.fixeddev.ebcm.util.ListAppender; 7 | 8 | import java.util.List; 9 | 10 | @AutoValue 11 | public abstract class SubCommandPart implements LineConsumingPart { 12 | 13 | public static Builder builder(String name) { 14 | return new AutoValue_SubCommandPart.Builder() 15 | .named(name) 16 | .setRequired(false) 17 | .setDescription(""); 18 | } 19 | 20 | public abstract List getCommandsToCall(); 21 | 22 | @Override 23 | @Memoized 24 | public String getLineRepresentation() { 25 | return (isRequired() ? "<" : "[") + 26 | getName() + 27 | (isRequired() ? ">" : "]"); 28 | } 29 | 30 | @AutoValue.Builder 31 | public abstract static class Builder { 32 | 33 | private ListAppender commandsToCallAppender = new ListAppender<>(); 34 | private ListAppender modifiersAppender = new ListAppender<>(); 35 | 36 | protected final Builder named(String name) { 37 | return setName(name); 38 | } 39 | 40 | protected abstract Builder setName(String newName); 41 | 42 | public abstract Builder setRequired(boolean newRequired); 43 | 44 | public abstract Builder setDescription(String newDescription); 45 | 46 | public Builder addCommand(Command command) { 47 | commandsToCallAppender.add(command); 48 | 49 | return this; 50 | } 51 | 52 | public Builder setCommands(List commands) { 53 | commandsToCallAppender.set(commands); 54 | 55 | return this; 56 | } 57 | 58 | abstract Builder setCommandsToCall(List commands); 59 | 60 | public Builder setAllModifiers(List modifiers) { 61 | this.modifiersAppender.set(modifiers); 62 | 63 | return this; 64 | } 65 | 66 | public Builder addModifier(String modifier) { 67 | this.modifiersAppender.add(modifier); 68 | 69 | return this; 70 | } 71 | 72 | abstract Builder setModifiers(List modifiers); 73 | 74 | public SubCommandPart build() { 75 | setCommandsToCall(commandsToCallAppender.toList()); 76 | setModifiers(modifiersAppender.toList()); 77 | 78 | return autoBuild(); 79 | } 80 | 81 | protected abstract SubCommandPart autoBuild(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/part/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This part of the command manager is inspired by the EngineHub command manager(EngineHub/Piston) 3 | * But, I'm not trying to copy Piston command manager, I just liked how the CommandParts work 4 | * So I decided to include it on my own command manager 5 | */ 6 | package me.fixeddev.ebcm.part; 7 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/util/ListAppender.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Collection; 6 | import java.util.List; 7 | 8 | public class ListAppender { 9 | private static final int DEFAULT_SIZE = 10; 10 | 11 | private Object[] content; 12 | private int size; 13 | 14 | public ListAppender(int size) { 15 | this.content = new Object[size]; 16 | } 17 | 18 | public ListAppender() { 19 | this(DEFAULT_SIZE); 20 | } 21 | 22 | /** 23 | * Grows the appender content backing to the actual size + {@param size} 24 | * 25 | * @param size The new size to add 26 | */ 27 | private void grow(int size) { 28 | if (size == 0) return; 29 | 30 | content = Arrays.copyOf(content, content.length + size); 31 | } 32 | 33 | public ListAppender set(Collection object) { 34 | content = object.toArray(); 35 | size = content.length; 36 | 37 | return this; 38 | } 39 | 40 | public ListAppender add(T object) { 41 | if (object == null) { 42 | throw new IllegalArgumentException("The provided object is null"); 43 | } 44 | 45 | if (size >= content.length - 1) 46 | grow(1); 47 | content[size++] = object; 48 | 49 | return this; 50 | } 51 | 52 | public List toList() { 53 | content = Arrays.copyOf(content, size); 54 | 55 | return new ArrayList(Arrays.asList(content)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Universal/src/main/java/me/fixeddev/ebcm/util/UsageBuilder.java: -------------------------------------------------------------------------------- 1 | package me.fixeddev.ebcm.util; 2 | 3 | import me.fixeddev.ebcm.Command; 4 | import me.fixeddev.ebcm.part.LineConsumingPart; 5 | import me.fixeddev.ebcm.part.SubCommandPart; 6 | 7 | import java.util.stream.Collectors; 8 | 9 | public class UsageBuilder { 10 | 11 | public static String getUsageForCommand(Command parent, Command command, String label) { 12 | if(!command.getUsage().equals("_!!_NOT_OVERRIDE_!!_")){ 13 | return command.getUsage(); 14 | } 15 | 16 | String parentUsage = null; 17 | 18 | if (command.equals(parent)) { 19 | return getUsageForCommand(null, command, label); 20 | } 21 | 22 | if (parent != null && !parent.getParts().isEmpty()) { 23 | if(parent.getParts().size() > 1 || !(parent.getParts().get(0) instanceof SubCommandPart)){ 24 | parentUsage = getUsageForCommand(null, parent, label); 25 | } 26 | } 27 | 28 | String usage = command.getParts().stream() 29 | .filter(part -> part instanceof LineConsumingPart) 30 | .map(part -> (LineConsumingPart) part) 31 | .map(LineConsumingPart::getLineRepresentation) 32 | .collect(Collectors.joining(" ")) 33 | .replace("", label); 34 | 35 | if (parentUsage != null && !parentUsage.isEmpty()) { 36 | return parentUsage + " " + usage; 37 | } 38 | 39 | return !usage.isEmpty() ? label + " " + usage : label; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.fixeddev 8 | ebcm 9 | 1.10 10 | 11 | Universal 12 | Bukkit 13 | Bungee 14 | 15 | 16 | pom 17 | 18 | 19 | 20 | spigot-repo 21 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 22 | 23 | 24 | bungeecord-repo 25 | https://oss.sonatype.org/content/repositories/snapshots 26 | 27 | 28 | 29 | 30 | 1.10.3-SNAPSHOT 31 | 1.10.0-SNAPSHOT 32 | 1.10.0-SNAPSHOT 33 | ${project.version} 34 | 35 | 36 | --------------------------------------------------------------------------------