diff options
author | Score_Under <seejay.11@gmail.com> | 2012-10-09 14:54:12 -0500 |
---|---|---|
committer | Wesley Wolfe <weswolf@aol.com> | 2012-10-16 00:05:40 -0500 |
commit | 9fd9767a4a55c90bf488e2077123be8a55aaa982 (patch) | |
tree | 5002df639c3787bcef34253fcdac9ec08a858952 /src/main/java | |
parent | 3dc80e1563eab780198b2696e26362bbf057d56b (diff) | |
download | bukkit-9fd9767a4a55c90bf488e2077123be8a55aaa982.tar bukkit-9fd9767a4a55c90bf488e2077123be8a55aaa982.tar.gz bukkit-9fd9767a4a55c90bf488e2077123be8a55aaa982.tar.lz bukkit-9fd9767a4a55c90bf488e2077123be8a55aaa982.tar.xz bukkit-9fd9767a4a55c90bf488e2077123be8a55aaa982.zip |
Add tab-completion API. Fixes BUKKIT-2181. Adds BUKKIT-2602
CommandMap contains a method that will auto-complete commands
appropriately. Before the first space, it searches for commands of which
the sender has permission. After the first space, it delegates to the
individual command.
Vanilla commands contain implementations to mimic vanilla
implementation. Exception would be give, that allows for name matching;
a feature we already allowed as part of the command is now supported for
auto-complete as well.
Plugin commands can get a tab completer set to delegate the completion
for. If no tab completer is set, it can check the executor to see if it
implements the tab completion interface. It will also attempt to chain
calls if null gets returned from these interfaces. Plugins also
implement the new TabCompleter interface, to add ease-of-use for plugin
developers, similar to the onCommand() method.
The default command implementation simply searches for player names.
To help facilitate command completion, a utility class was added with
two functions. One checks two strings, to see if the specified string
starts with (ignoring case) the second. The other method uses the first
to selectively copy elements from one collection to another.
Diffstat (limited to 'src/main/java')
42 files changed, 893 insertions, 45 deletions
diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java index ec502af5..b0b7f21f 100644 --- a/src/main/java/org/bukkit/command/Command.java +++ b/src/main/java/org/bukkit/command/Command.java @@ -1,12 +1,19 @@ package org.bukkit.command; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Server; +import org.bukkit.entity.Player; import org.bukkit.permissions.Permissible; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; /** * Represents a Command, which executes various tasks upon user input @@ -48,19 +55,46 @@ public abstract class Command { public abstract boolean execute(CommandSender sender, String commandLabel, String[] args); /** + * @deprecated This method is not supported and returns null + */ + @Deprecated + public List<String> tabComplete(CommandSender sender, String[] args) { + return null; + } + + /** * Executed on tab completion for this command, returning a list of options * the player can tab through. - * <p /> - * By returning null, you tell Bukkit to generate a list of players to send - * to the sender. - * By returning an empty list, no options will be sent. * * @param sender Source object which is executing this command + * @param alias the alias being used * @param args All arguments passed to the command, split via ' ' - * @return null to generate a Player list, otherwise a list of options + * @return a list of tab-completions for the specified arguments. This will never be null. List may be immutable. + * @throws IllegalArgumentException if sender, alias, or args is null */ - public List<String> tabComplete(CommandSender sender, String[] args) { - return null; + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (!(sender instanceof Player) || args.length == 0) { + return ImmutableList.of(); + } + + String lastWord = args[args.length - 1]; + + Player senderPlayer = (Player) sender; + + ArrayList<String> matchedPlayers = new ArrayList<String>(); + for (Player player : sender.getServer().getOnlinePlayers()) { + String name = player.getName(); + if (senderPlayer.canSee(player) && StringUtil.startsWithIgnoreCase(name, lastWord)) { + matchedPlayers.add(name); + } + } + + Collections.sort(matchedPlayers, String.CASE_INSENSITIVE_ORDER); + return matchedPlayers; } /** diff --git a/src/main/java/org/bukkit/command/CommandMap.java b/src/main/java/org/bukkit/command/CommandMap.java index 5524da27..e7fad287 100644 --- a/src/main/java/org/bukkit/command/CommandMap.java +++ b/src/main/java/org/bukkit/command/CommandMap.java @@ -62,4 +62,16 @@ public interface CommandMap { * @return Command with the specified name or null if a command with that label doesn't exist */ public Command getCommand(String name); + + + /** + * Looks for the requested command and executes an appropriate tab-completer if found. This method will also tab-complete partial commands. + * + * @param sender The command's sender. + * @param cmdLine The entire command string to tab-complete, excluding initial slash. + * @return a list of possible tab-completions. This list may be immutable. Will be null if no matching command of which sender has permission. + * @throws CommandException Thrown when the tab-completer for the given command fails with an unhandled exception + * @throws IllegalArgumentException if either sender or cmdLine are null + */ + public List<String> tabComplete(CommandSender sender, String cmdLine) throws IllegalArgumentException; } diff --git a/src/main/java/org/bukkit/command/MultipleCommandAlias.java b/src/main/java/org/bukkit/command/MultipleCommandAlias.java index b1b83b1f..3a666d16 100644 --- a/src/main/java/org/bukkit/command/MultipleCommandAlias.java +++ b/src/main/java/org/bukkit/command/MultipleCommandAlias.java @@ -10,7 +10,7 @@ public class MultipleCommandAlias extends Command { super(name); this.commands = commands; } - + public Command[] getCommands() { return commands; } diff --git a/src/main/java/org/bukkit/command/PluginCommand.java b/src/main/java/org/bukkit/command/PluginCommand.java index 05975c7f..f82e3ed3 100644 --- a/src/main/java/org/bukkit/command/PluginCommand.java +++ b/src/main/java/org/bukkit/command/PluginCommand.java @@ -1,5 +1,8 @@ package org.bukkit.command; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.plugin.Plugin; /** @@ -8,6 +11,7 @@ import org.bukkit.plugin.Plugin; public final class PluginCommand extends Command implements PluginIdentifiableCommand { private final Plugin owningPlugin; private CommandExecutor executor; + private TabCompleter completer; protected PluginCommand(String name, Plugin owner) { super(name); @@ -70,6 +74,26 @@ public final class PluginCommand extends Command implements PluginIdentifiableCo } /** + * Sets the {@link TabCompleter} to run when tab-completing this command. + * If no TabCompleter is specified, and the command's executor implements + * TabCompleter, then the executor will be used for tab completion. + * + * @param completer New tab completer + */ + public void setTabCompleter(TabCompleter completer) { + this.completer = completer; + } + + /** + * Gets the {@link TabCompleter} associated with this command. + * + * @return TabCompleter object linked to this command + */ + public TabCompleter getTabCompleter() { + return completer; + } + + /** * Gets the owner of this PluginCommand * * @return Plugin that owns this command @@ -77,4 +101,47 @@ public final class PluginCommand extends Command implements PluginIdentifiableCo public Plugin getPlugin() { return owningPlugin; } + + /** + * {@inheritDoc}<br> + * <br> + * Delegates to the tab completer if present.<br> + * If it is not present or returns null, will delegate to the current command + * executor if it implements {@link TabCompleter}. If a non-null list has not + * been found, will default to standard player name completion in + * {@link Command#tabComplete(CommandSender, String, String[])}.<br> + * <br> + * This method does not consider permissions. + * @throws CommandException if the completer or executor throw an exception during the process of tab-completing. + * @throws IllegalArgumentException if sender, alias, or args is null + */ + @Override + public java.util.List<String> tabComplete(CommandSender sender, String alias, String[] args) throws CommandException, IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + List<String> completions = null; + try { + if (completer != null) { + completions = completer.onTabComplete(sender, this, alias, args); + } + if (completions == null && executor instanceof TabCompleter) { + completions = ((TabCompleter) executor).onTabComplete(sender, this, alias, args); + } + } catch (Throwable ex) { + StringBuilder message = new StringBuilder(); + message.append("Unhandled exception during tab completion for command '/").append(alias).append(' '); + for (String arg : args) { + message.append(arg).append(' '); + } + message.deleteCharAt(message.length() - 1).append("' in plugin ").append(owningPlugin.getDescription().getFullName()); + throw new CommandException(message.toString(), ex); + } + + if (completions == null) { + return super.tabComplete(sender, alias, args); + } + return completions; + } } diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java index 384a01ec..a44504ef 100644 --- a/src/main/java/org/bukkit/command/SimpleCommandMap.java +++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java @@ -1,13 +1,25 @@ package org.bukkit.command; -import org.bukkit.command.defaults.*; - -import java.util.*; +import static org.bukkit.util.Java15Compat.Arrays_copyOfRange; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.commons.lang.Validate; import org.bukkit.Server; -import static org.bukkit.util.Java15Compat.Arrays_copyOfRange; +import org.bukkit.command.defaults.*; +import org.bukkit.util.StringUtil; public class SimpleCommandMap implements CommandMap { + private static final Pattern PATTERN_ON_SPACE = Pattern.compile(" ", Pattern.LITERAL); protected final Map<String, Command> knownCommands = new HashMap<String, Command>(); protected final Set<String> aliases = new HashSet<String>(); private final Server server; @@ -81,7 +93,7 @@ public class SimpleCommandMap implements CommandMap { Iterator<String> iterator = command.getAliases().iterator(); while (iterator.hasNext()) { - if (!register((String) iterator.next(), fallbackPrefix, command, true)) { + if (!register(iterator.next(), fallbackPrefix, command, true)) { iterator.remove(); } } @@ -150,7 +162,7 @@ public class SimpleCommandMap implements CommandMap { * {@inheritDoc} */ public boolean dispatch(CommandSender sender, String commandLine) throws CommandException { - String[] args = commandLine.split(" "); + String[] args = PATTERN_ON_SPACE.split(commandLine); if (args.length == 0) { return false; @@ -186,13 +198,82 @@ public class SimpleCommandMap implements CommandMap { } public Command getCommand(String name) { - Command target = knownCommands.get(name.toLowerCase()); + Command target = knownCommands.get(name.toLowerCase()); if (target == null) { target = getFallback(name); } return target; } + public List<String> tabComplete(CommandSender sender, String cmdLine) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(cmdLine, "Command line cannot null"); + + int spaceIndex = cmdLine.indexOf(' '); + + if (spaceIndex == -1) { + ArrayList<String> completions = new ArrayList<String>(); + Map<String, Command> knownCommands = this.knownCommands; + + for (VanillaCommand command : fallbackCommands) { + String name = command.getName(); + + if (!command.testPermissionSilent(sender)) { + continue; + } + if (knownCommands.containsKey(name)) { + // Don't let a vanilla command override a command added below + // This has to do with the way aliases work + continue; + } + if (!StringUtil.startsWithIgnoreCase(name, cmdLine)) { + continue; + } + + completions.add('/' + name); + } + + for (Map.Entry<String, Command> commandEntry : knownCommands.entrySet()) { + Command command = commandEntry.getValue(); + + if (!command.testPermissionSilent(sender)) { + continue; + } + + String name = commandEntry.getKey(); // Use the alias, not command name + + if (StringUtil.startsWithIgnoreCase(name, cmdLine)) { + completions.add('/' + name); + } + } + + Collections.sort(completions, String.CASE_INSENSITIVE_ORDER); + return completions; + } + + String commandName = cmdLine.substring(0, spaceIndex); + Command target = getCommand(commandName); + + if (target == null) { + return null; + } + + if (!target.testPermissionSilent(sender)) { + return null; + } + + String argLine = cmdLine.substring(spaceIndex + 1, cmdLine.length()); + String[] args = PATTERN_ON_SPACE.split(argLine, -1); + + try { + return target.tabComplete(sender, commandName, args); + } catch (CommandException ex) { + throw ex; + } catch (Throwable ex) { + throw new CommandException("Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target, ex); + } + } + public Collection<Command> getCommands() { return knownCommands.values(); } diff --git a/src/main/java/org/bukkit/command/TabCommandExecutor.java b/src/main/java/org/bukkit/command/TabCommandExecutor.java index b2ee58c5..95efbd40 100644 --- a/src/main/java/org/bukkit/command/TabCommandExecutor.java +++ b/src/main/java/org/bukkit/command/TabCommandExecutor.java @@ -4,7 +4,11 @@ import java.util.List; /** * Represents a class which can handle command tab completion and commands + * @deprecated Remains for plugins that would have implemented it even without functionality + * @see TabExecutor */ +@Deprecated public interface TabCommandExecutor extends CommandExecutor { public List<String> onTabComplete(); + } diff --git a/src/main/java/org/bukkit/command/TabCompleter.java b/src/main/java/org/bukkit/command/TabCompleter.java new file mode 100644 index 00000000..3ba64fe8 --- /dev/null +++ b/src/main/java/org/bukkit/command/TabCompleter.java @@ -0,0 +1,20 @@ +package org.bukkit.command; + +import java.util.List; + +/** + * Represents a class which can suggest tab completions for commands. + */ +public interface TabCompleter { + + /** + * Requests a list of possible completions for a command argument. + * + * @param sender Source of the command + * @param command Command which was executed + * @param alias The alias used + * @param args The arguments passed to the command, including final partial argument to be completed and command label + * @return A List of possible completions for the final argument, or null to default to the command executor + */ + public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args); +} diff --git a/src/main/java/org/bukkit/command/TabExecutor.java b/src/main/java/org/bukkit/command/TabExecutor.java new file mode 100644 index 00000000..67b3503d --- /dev/null +++ b/src/main/java/org/bukkit/command/TabExecutor.java @@ -0,0 +1,7 @@ +package org.bukkit.command; + +/** + * This class is provided as a convenience to implement both TabCompleter and CommandExecutor. + */ +public interface TabExecutor extends TabCompleter, CommandExecutor { +} diff --git a/src/main/java/org/bukkit/command/defaults/BanCommand.java b/src/main/java/org/bukkit/command/defaults/BanCommand.java index c4d3a54c..c4366524 100644 --- a/src/main/java/org/bukkit/command/defaults/BanCommand.java +++ b/src/main/java/org/bukkit/command/defaults/BanCommand.java @@ -1,12 +1,16 @@ package org.bukkit.command.defaults; import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class BanCommand extends VanillaCommand { public BanCommand() { super("ban"); @@ -36,12 +40,19 @@ public class BanCommand extends VanillaCommand { } @Override - public List<String> tabComplete(CommandSender sender, String[] args) { - return args.length >= 1 ? null : EMPTY_LIST; + public boolean matches(String input) { + return input.equalsIgnoreCase("ban"); } @Override - public boolean matches(String input) { - return input.equalsIgnoreCase("ban"); + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length >= 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); } } diff --git a/src/main/java/org/bukkit/command/defaults/BanIpCommand.java b/src/main/java/org/bukkit/command/defaults/BanIpCommand.java index b1049739..8ea0c356 100644 --- a/src/main/java/org/bukkit/command/defaults/BanIpCommand.java +++ b/src/main/java/org/bukkit/command/defaults/BanIpCommand.java @@ -2,12 +2,16 @@ package org.bukkit.command.defaults; import java.util.List; import java.util.regex.Pattern; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class BanIpCommand extends VanillaCommand { public static final Pattern ipValidity = Pattern.compile("^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); @@ -43,11 +47,6 @@ public class BanIpCommand extends VanillaCommand { return true; } - @Override - public List<String> tabComplete(CommandSender sender, String[] args) { - return args.length >= 1 ? null : EMPTY_LIST; - } - private void processIPBan(String ip, CommandSender sender) { // TODO: Kick on ban Bukkit.banIP(ip); @@ -59,4 +58,16 @@ public class BanIpCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("ban-ip"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/BanListCommand.java b/src/main/java/org/bukkit/command/defaults/BanListCommand.java index 668a4877..08c5be9a 100644 --- a/src/main/java/org/bukkit/command/defaults/BanListCommand.java +++ b/src/main/java/org/bukkit/command/defaults/BanListCommand.java @@ -1,11 +1,19 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class BanListCommand extends VanillaCommand { + private static final List<String> BANLIST_TYPES = ImmutableList.of("ips", "players"); + public BanListCommand() { super("banlist"); this.description = "View all players banned from this server"; @@ -38,8 +46,15 @@ public class BanListCommand extends VanillaCommand { } @Override - public List<String> tabComplete(CommandSender sender, String[] args) { - return args.length >= 1 ? null : EMPTY_LIST; + public List<String> tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], BANLIST_TYPES, new ArrayList<String>(BANLIST_TYPES.size())); + } + return ImmutableList.of(); } @Override diff --git a/src/main/java/org/bukkit/command/defaults/BukkitCommand.java b/src/main/java/org/bukkit/command/defaults/BukkitCommand.java index d3de7c8b..23c85800 100644 --- a/src/main/java/org/bukkit/command/defaults/BukkitCommand.java +++ b/src/main/java/org/bukkit/command/defaults/BukkitCommand.java @@ -1,10 +1,10 @@ package org.bukkit.command.defaults; -import org.bukkit.command.Command; - import java.util.List; -public abstract class BukkitCommand extends Command{ +import org.bukkit.command.Command; + +public abstract class BukkitCommand extends Command { protected BukkitCommand(String name) { super(name); } diff --git a/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java b/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java index 63390b15..1611d413 100644 --- a/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java +++ b/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java @@ -1,11 +1,20 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class DefaultGameModeCommand extends VanillaCommand { + private static final List<String> GAMEMODE_NAMES = ImmutableList.of("adventure", "creative", "survival"); + public DefaultGameModeCommand() { super("defaultgamemode"); this.description = "Set the default gamemode"; @@ -51,4 +60,17 @@ public class DefaultGameModeCommand extends VanillaCommand { return true; } + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], GAMEMODE_NAMES, new ArrayList<String>(GAMEMODE_NAMES.size())); + } else if (args.length == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/DeopCommand.java b/src/main/java/org/bukkit/command/defaults/DeopCommand.java index 291c0497..1ea6cadb 100644 --- a/src/main/java/org/bukkit/command/defaults/DeopCommand.java +++ b/src/main/java/org/bukkit/command/defaults/DeopCommand.java @@ -1,11 +1,17 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class DeopCommand extends VanillaCommand { public DeopCommand() { @@ -35,12 +41,26 @@ public class DeopCommand extends VanillaCommand { } @Override - public List<String> tabComplete(CommandSender sender, String[] args) { - return args.length >= 1 ? null : EMPTY_LIST; + public boolean matches(String input) { + return input.equalsIgnoreCase("deop"); } @Override - public boolean matches(String input) { - return input.equalsIgnoreCase("deop"); + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + List<String> completions = new ArrayList<String>(); + for (OfflinePlayer player : Bukkit.getOfflinePlayers()) { + String playerName = player.getName(); + if (player.isOp() && StringUtil.startsWithIgnoreCase(playerName, args[0])) { + completions.add(playerName); + } + } + return completions; + } + return ImmutableList.of(); } } diff --git a/src/main/java/org/bukkit/command/defaults/ExpCommand.java b/src/main/java/org/bukkit/command/defaults/ExpCommand.java index 981cfb7b..791d2e14 100644 --- a/src/main/java/org/bukkit/command/defaults/ExpCommand.java +++ b/src/main/java/org/bukkit/command/defaults/ExpCommand.java @@ -1,11 +1,16 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class ExpCommand extends VanillaCommand { public ExpCommand() { super("xp"); @@ -44,4 +49,16 @@ public class ExpCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("xp"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/GameModeCommand.java b/src/main/java/org/bukkit/command/defaults/GameModeCommand.java index ad445160..8721e20b 100644 --- a/src/main/java/org/bukkit/command/defaults/GameModeCommand.java +++ b/src/main/java/org/bukkit/command/defaults/GameModeCommand.java @@ -1,13 +1,22 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; import org.bukkit.GameMode; import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class GameModeCommand extends VanillaCommand { + private static final List<String> GAMEMODE_NAMES = ImmutableList.of("adventure", "creative", "survival"); + public GameModeCommand() { super("gamemode"); this.description = "Changes the player to a specific game mode"; @@ -77,4 +86,18 @@ public class GameModeCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("gamemode"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], GAMEMODE_NAMES, new ArrayList<String>(GAMEMODE_NAMES.size())); + } else if (args.length == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/GiveCommand.java b/src/main/java/org/bukkit/command/defaults/GiveCommand.java index eeac739a..6010ac86 100644 --- a/src/main/java/org/bukkit/command/defaults/GiveCommand.java +++ b/src/main/java/org/bukkit/command/defaults/GiveCommand.java @@ -1,5 +1,10 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -7,8 +12,21 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class GiveCommand extends VanillaCommand { + private static List<String> materials; + static { + ArrayList<String> materialList = new ArrayList<String>(); + for (Material material : Material.values()) { + materialList.add(material.name()); + } + Collections.sort(materialList); + materials = ImmutableList.copyOf(materialList); + } + public GiveCommand() { super("give"); this.description = "Gives the specified player a certain amount of items"; @@ -60,4 +78,45 @@ public class GiveCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("give"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return super.tabComplete(sender, alias, args); + } + if (args.length == 2) { + final String arg = args[1]; + final List<String> materials = GiveCommand.materials; + List<String> completion = null; + + final int size = materials.size(); + int i = Collections.binarySearch(materials, arg, String.CASE_INSENSITIVE_ORDER); + + if (i < 0) { + // Insertion (start) index + i = -1 - i; + } + + for ( ; i < size; i++) { + String material = materials.get(i); + if (StringUtil.startsWithIgnoreCase(material, arg)) { + if (completion == null) { + completion = new ArrayList<String>(); + } + completion.add(material); + } else { + break; + } + } + + if (completion != null) { + return completion; + } + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/HelpCommand.java b/src/main/java/org/bukkit/command/defaults/HelpCommand.java index 4c002eb1..db3d92d0 100644 --- a/src/main/java/org/bukkit/command/defaults/HelpCommand.java +++ b/src/main/java/org/bukkit/command/defaults/HelpCommand.java @@ -1,7 +1,15 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; import org.apache.commons.lang.math.NumberUtils; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -13,7 +21,7 @@ import org.bukkit.help.HelpTopicComparator; import org.bukkit.help.IndexHelpTopic; import org.bukkit.util.ChatPaginator; -import java.util.*; +import com.google.common.collect.ImmutableList; public class HelpCommand extends VanillaCommand { public HelpCommand() { @@ -106,6 +114,27 @@ public class HelpCommand extends VanillaCommand { return input.equalsIgnoreCase("help") || input.equalsIgnoreCase("?"); } + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + List<String> matchedTopics = new ArrayList<String>(); + String searchString = args[0]; + for (HelpTopic topic : Bukkit.getServer().getHelpMap().getHelpTopics()) { + String trimmedTopic = topic.getName().startsWith("/") ? topic.getName().substring(1) : topic.getName(); + + if (trimmedTopic.startsWith(searchString)) { + matchedTopics.add(trimmedTopic); + } + } + return matchedTopics; + } + return ImmutableList.of(); + } + protected HelpTopic findPossibleMatches(String searchString) { int maxDistance = (searchString.length() / 5) + 3; Set<HelpTopic> possibleMatches = new TreeSet<HelpTopic>(HelpTopicComparator.helpTopicComparatorInstance()); diff --git a/src/main/java/org/bukkit/command/defaults/KickCommand.java b/src/main/java/org/bukkit/command/defaults/KickCommand.java index 1f747d8c..7b890b1a 100644 --- a/src/main/java/org/bukkit/command/defaults/KickCommand.java +++ b/src/main/java/org/bukkit/command/defaults/KickCommand.java @@ -1,11 +1,16 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class KickCommand extends VanillaCommand { public KickCommand() { super("kick"); @@ -44,4 +49,16 @@ public class KickCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("kick"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length >= 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/KillCommand.java b/src/main/java/org/bukkit/command/defaults/KillCommand.java index d3049f71..84f783f2 100644 --- a/src/main/java/org/bukkit/command/defaults/KillCommand.java +++ b/src/main/java/org/bukkit/command/defaults/KillCommand.java @@ -1,10 +1,15 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityDamageEvent; +import com.google.common.collect.ImmutableList; + public class KillCommand extends VanillaCommand { public KillCommand() { super("kill"); @@ -38,4 +43,13 @@ public class KillCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("kill"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/ListCommand.java b/src/main/java/org/bukkit/command/defaults/ListCommand.java index 489edd28..e38ae806 100644 --- a/src/main/java/org/bukkit/command/defaults/ListCommand.java +++ b/src/main/java/org/bukkit/command/defaults/ListCommand.java @@ -1,9 +1,14 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class ListCommand extends VanillaCommand { public ListCommand() { super("list"); @@ -41,4 +46,13 @@ public class ListCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("list"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/OpCommand.java b/src/main/java/org/bukkit/command/defaults/OpCommand.java index 4774c7e3..d7874bcf 100644 --- a/src/main/java/org/bukkit/command/defaults/OpCommand.java +++ b/src/main/java/org/bukkit/command/defaults/OpCommand.java @@ -1,10 +1,19 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class OpCommand extends VanillaCommand { public OpCommand() { @@ -33,4 +42,39 @@ public class OpCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("op"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + if (!(sender instanceof Player)) { + return ImmutableList.of(); + } + + String lastWord = args[0]; + if (lastWord.length() == 0) { + return ImmutableList.of(); + } + + Player senderPlayer = (Player) sender; + + ArrayList<String> matchedPlayers = new ArrayList<String>(); + for (Player player : sender.getServer().getOnlinePlayers()) { + String name = player.getName(); + if (!senderPlayer.canSee(player) || player.isOp()) { + continue; + } + if (StringUtil.startsWithIgnoreCase(name, lastWord)) { + matchedPlayers.add(name); + } + } + + Collections.sort(matchedPlayers, String.CASE_INSENSITIVE_ORDER); + return matchedPlayers; + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/PardonCommand.java b/src/main/java/org/bukkit/command/defaults/PardonCommand.java index 29da200c..a737ff09 100644 --- a/src/main/java/org/bukkit/command/defaults/PardonCommand.java +++ b/src/main/java/org/bukkit/command/defaults/PardonCommand.java @@ -1,9 +1,17 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class PardonCommand extends VanillaCommand { public PardonCommand() { @@ -30,4 +38,23 @@ public class PardonCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("pardon"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + List<String> completions = new ArrayList<String>(); + for (OfflinePlayer player : Bukkit.getBannedPlayers()) { + String name = player.getName(); + if (StringUtil.startsWithIgnoreCase(name, args[0])) { + completions.add(name); + } + } + return completions; + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java b/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java index d9742110..4571eeb9 100644 --- a/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java +++ b/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java @@ -1,9 +1,16 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class PardonIpCommand extends VanillaCommand { public PardonIpCommand() { @@ -35,4 +42,16 @@ public class PardonIpCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("pardon-ip"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], Bukkit.getIPBans(), new ArrayList<String>()); + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java index 8cb45cce..b888da13 100644 --- a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java +++ b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java @@ -1,8 +1,9 @@ package org.bukkit.command.defaults; import java.util.Arrays; -import org.bukkit.ChatColor; + import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; diff --git a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java index 9b090edc..7f820b3b 100644 --- a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java +++ b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java @@ -1,8 +1,9 @@ package org.bukkit.command.defaults; import java.util.Arrays; -import org.bukkit.ChatColor; + import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; public class ReloadCommand extends BukkitCommand { diff --git a/src/main/java/org/bukkit/command/defaults/SaveCommand.java b/src/main/java/org/bukkit/command/defaults/SaveCommand.java index 08d8edf9..77839c1c 100644 --- a/src/main/java/org/bukkit/command/defaults/SaveCommand.java +++ b/src/main/java/org/bukkit/command/defaults/SaveCommand.java @@ -1,10 +1,15 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import com.google.common.collect.ImmutableList; + public class SaveCommand extends VanillaCommand { public SaveCommand() { super("save-all"); @@ -34,4 +39,13 @@ public class SaveCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("save-all"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java b/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java index d46cdd36..c081e5e1 100644 --- a/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java +++ b/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java @@ -1,10 +1,15 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import com.google.common.collect.ImmutableList; + public class SaveOffCommand extends VanillaCommand { public SaveOffCommand() { super("save-off"); @@ -29,4 +34,13 @@ public class SaveOffCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("save-off"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java b/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java index 4c1bfec6..d0561c36 100644 --- a/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java +++ b/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java @@ -1,10 +1,15 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import com.google.common.collect.ImmutableList; + public class SaveOnCommand extends VanillaCommand { public SaveOnCommand() { super("save-on"); @@ -29,4 +34,13 @@ public class SaveOnCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("save-on"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/SayCommand.java b/src/main/java/org/bukkit/command/defaults/SayCommand.java index 44dc7ae0..651b60c0 100644 --- a/src/main/java/org/bukkit/command/defaults/SayCommand.java +++ b/src/main/java/org/bukkit/command/defaults/SayCommand.java @@ -1,10 +1,15 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class SayCommand extends VanillaCommand { public SayCommand() { super("say"); @@ -43,4 +48,15 @@ public class SayCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("say"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + + if (args.length >= 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/SeedCommand.java b/src/main/java/org/bukkit/command/defaults/SeedCommand.java index 4fac2678..b64fd405 100644 --- a/src/main/java/org/bukkit/command/defaults/SeedCommand.java +++ b/src/main/java/org/bukkit/command/defaults/SeedCommand.java @@ -1,9 +1,14 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class SeedCommand extends VanillaCommand { public SeedCommand() { super("seed"); @@ -24,4 +29,13 @@ public class SeedCommand extends VanillaCommand { sender.sendMessage("Seed: " + seed); return true; } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/StopCommand.java b/src/main/java/org/bukkit/command/defaults/StopCommand.java index 2b8ee082..c6a470b6 100644 --- a/src/main/java/org/bukkit/command/defaults/StopCommand.java +++ b/src/main/java/org/bukkit/command/defaults/StopCommand.java @@ -1,9 +1,14 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import com.google.common.collect.ImmutableList; + public class StopCommand extends VanillaCommand { public StopCommand() { super("stop"); @@ -26,4 +31,13 @@ public class StopCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("stop"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/TeleportCommand.java b/src/main/java/org/bukkit/command/defaults/TeleportCommand.java index ad2a5527..55be9da3 100644 --- a/src/main/java/org/bukkit/command/defaults/TeleportCommand.java +++ b/src/main/java/org/bukkit/command/defaults/TeleportCommand.java @@ -1,5 +1,8 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; @@ -8,6 +11,8 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import com.google.common.collect.ImmutableList; + public class TeleportCommand extends VanillaCommand { public TeleportCommand() { super("tp"); @@ -66,4 +71,16 @@ public class TeleportCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("tp"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1 || args.length == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/TimeCommand.java b/src/main/java/org/bukkit/command/defaults/TimeCommand.java index 9866877d..011d32db 100644 --- a/src/main/java/org/bukkit/command/defaults/TimeCommand.java +++ b/src/main/java/org/bukkit/command/defaults/TimeCommand.java @@ -1,12 +1,22 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class TimeCommand extends VanillaCommand { + private static final List<String> TABCOMPLETE_ADD_SET = ImmutableList.of("add", "set"); + private static final List<String> TABCOMPLETE_DAY_NIGHT = ImmutableList.of("day", "night"); + public TimeCommand() { super("time"); this.description = "Changes the time on each world"; @@ -66,4 +76,18 @@ public class TimeCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("time"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], TABCOMPLETE_ADD_SET, new ArrayList<String>(TABCOMPLETE_ADD_SET.size())); + } else if (args.length == 2 && args[0].equalsIgnoreCase("set")) { + return StringUtil.copyPartialMatches(args[1], TABCOMPLETE_DAY_NIGHT, new ArrayList<String>(TABCOMPLETE_DAY_NIGHT.size())); + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/TimingsCommand.java b/src/main/java/org/bukkit/command/defaults/TimingsCommand.java index b9c8fc66..29ebbe05 100644 --- a/src/main/java/org/bukkit/command/defaults/TimingsCommand.java +++ b/src/main/java/org/bukkit/command/defaults/TimingsCommand.java @@ -3,18 +3,25 @@ package org.bukkit.command.defaults; import java.io.File; import java.io.IOException; import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.RegisteredListener; import org.bukkit.plugin.TimedRegisteredListener; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class TimingsCommand extends BukkitCommand { + private static final List<String> TIMINGS_SUBCOMMANDS = ImmutableList.of("merged", "reset", "separate"); + public TimingsCommand(String name) { super(name); this.description = "Records timings for all plugin events"; @@ -99,4 +106,16 @@ public class TimingsCommand extends BukkitCommand { } return true; } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS, new ArrayList<String>(TIMINGS_SUBCOMMANDS.size())); + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java b/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java index e81710e1..08ed667f 100644 --- a/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java +++ b/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java @@ -1,5 +1,8 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; @@ -7,6 +10,8 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class ToggleDownfallCommand extends VanillaCommand { public ToggleDownfallCommand() { super("toggledownfall"); @@ -44,4 +49,13 @@ public class ToggleDownfallCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("toggledownfall"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/VanillaCommand.java b/src/main/java/org/bukkit/command/defaults/VanillaCommand.java index bf6df750..1fdcc81e 100644 --- a/src/main/java/org/bukkit/command/defaults/VanillaCommand.java +++ b/src/main/java/org/bukkit/command/defaults/VanillaCommand.java @@ -1,13 +1,11 @@ package org.bukkit.command.defaults; -import java.util.ArrayList; import java.util.List; + import org.bukkit.command.Command; import org.bukkit.command.CommandSender; public abstract class VanillaCommand extends Command { - static final List<String> EMPTY_LIST = new ArrayList(0); - protected VanillaCommand(String name) { super(name); } diff --git a/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/src/main/java/org/bukkit/command/defaults/VersionCommand.java index 4d3ba15b..902f9d15 100644 --- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java +++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java @@ -1,13 +1,18 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.bukkit.ChatColor; +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class VersionCommand extends BukkitCommand { public VersionCommand(String name) { @@ -102,4 +107,23 @@ public class VersionCommand extends BukkitCommand { return result.toString(); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + List<String> completions = new ArrayList<String>(); + String toComplete = args[0].toLowerCase(); + for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { + if (StringUtil.startsWithIgnoreCase(plugin.getName(), toComplete)) { + completions.add(plugin.getName()); + } + } + return completions; + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java b/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java index d0fd8578..70786d11 100644 --- a/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java +++ b/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java @@ -1,12 +1,21 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class WhitelistCommand extends VanillaCommand { + private static final List<String> WHITELIST_SUBCOMMANDS = ImmutableList.of("add", "remove", "on", "off", "list", "reload"); + public WhitelistCommand() { super("whitelist"); this.description = "Prevents the specified player from using this server"; @@ -88,4 +97,36 @@ public class WhitelistCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("whitelist"); } + + @Override + public List<String> tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], WHITELIST_SUBCOMMANDS, new ArrayList<String>(WHITELIST_SUBCOMMANDS.size())); + } else if (args.length == 2) { + if (args[0].equalsIgnoreCase("add")) { + List<String> completions = new ArrayList<String>(); + for (OfflinePlayer player : Bukkit.getOfflinePlayers()) { + String name = player.getName(); + if (StringUtil.startsWithIgnoreCase(name, args[1]) && !player.isWhitelisted()) { + completions.add(name); + } + } + return completions; + } else if (args[0].equalsIgnoreCase("remove")) { + List<String> completions = new ArrayList<String>(); + for (OfflinePlayer player : Bukkit.getWhitelistedPlayers()) { + String name = player.getName(); + if (StringUtil.startsWithIgnoreCase(name, args[1])) { + completions.add(name); + } + } + return completions; + } + } + return ImmutableList.of(); + } } diff --git a/src/main/java/org/bukkit/plugin/Plugin.java b/src/main/java/org/bukkit/plugin/Plugin.java index 6b75c3e7..c266114c 100644 --- a/src/main/java/org/bukkit/plugin/Plugin.java +++ b/src/main/java/org/bukkit/plugin/Plugin.java @@ -5,7 +5,7 @@ import java.io.InputStream; import java.util.logging.Logger; import org.bukkit.Server; -import org.bukkit.command.CommandExecutor; +import org.bukkit.command.TabExecutor; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.generator.ChunkGenerator; @@ -16,7 +16,7 @@ import com.avaje.ebean.EbeanServer; * <p /> * The use of {@link PluginBase} is recommended for actual Implementation */ -public interface Plugin extends CommandExecutor { +public interface Plugin extends TabExecutor { /** * Returns the folder that the plugin data's files are located in. The * folder may not yet exist. diff --git a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java index cdc6e751..d4835547 100644 --- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java +++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java @@ -299,6 +299,13 @@ public abstract class JavaPlugin extends PluginBase { } /** + * {@inheritDoc} + */ + public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + return null; + } + + /** * Gets the command with the given name, specific to this plugin * * @param name Name or alias of the command diff --git a/src/main/java/org/bukkit/util/StringUtil.java b/src/main/java/org/bukkit/util/StringUtil.java new file mode 100644 index 00000000..12bc58d4 --- /dev/null +++ b/src/main/java/org/bukkit/util/StringUtil.java @@ -0,0 +1,49 @@ +package org.bukkit.util; + +import java.util.Collection; +import org.apache.commons.lang.Validate; + +public class StringUtil { + + /** + * Copies all elements from the iterable collection of originals to the collection provided. + * + * @param token String to search for + * @param originals An iterable collection of strings to filter. + * @param collection The collection to add matches to + * @return the collection provided that would have the elements copied into + * @throws UnsupportedOperationException if the collection is immutable and originals contains a string which starts with the specified search string. + * @throws IllegalArgumentException if any parameter is is null + * @throws IllegalArgumentException if originals contains a null element. <b>Note: the collection may be modified before this is thrown</b> + */ + public static <T extends Collection<String>> T copyPartialMatches(final String token, final Iterable<String> originals, final T collection) throws UnsupportedOperationException, IllegalArgumentException { + Validate.notNull(token, "Search token cannot be null"); + Validate.notNull(collection, "Collection cannot be null"); + Validate.notNull(originals, "Originals cannot be null"); + + for (String string : originals) { + if (startsWithIgnoreCase(string, token)) { + collection.add(string); + } + } + + return collection; + } + + /** + * This method uses a substring to check case-insensitive equality. This means the internal array does not need to be copied like a toLowerCase() call would. + * + * @param string String to check + * @param prefix Prefix of string to compare + * @return true if provided string starts with, ignoring case, the prefix provided + * @throws NullPointerException if prefix is null + * @throws IllegalArgumentException if string is null + */ + public static boolean startsWithIgnoreCase(final String string, final String prefix) throws IllegalArgumentException, NullPointerException { + Validate.notNull(string, "Cannot check a null string for a match"); + if (string.length() < prefix.length()) { + return false; + } + return string.substring(0, prefix.length()).equalsIgnoreCase(prefix); + } +} |