001    package org.bukkit.command;
002    
003    import java.util.ArrayList;
004    import java.util.Collections;
005    import java.util.List;
006    import java.util.Set;
007    
008    import org.apache.commons.lang.Validate;
009    import org.bukkit.Bukkit;
010    import org.bukkit.ChatColor;
011    import org.bukkit.Server;
012    import org.bukkit.entity.Player;
013    import org.bukkit.permissions.Permissible;
014    import org.bukkit.util.StringUtil;
015    
016    import com.google.common.collect.ImmutableList;
017    
018    /**
019     * Represents a Command, which executes various tasks upon user input
020     */
021    public abstract class Command {
022        private final String name;
023        private String nextLabel;
024        private String label;
025        private List<String> aliases;
026        private List<String> activeAliases;
027        private CommandMap commandMap = null;
028        protected String description = "";
029        protected String usageMessage;
030        private String permission;
031        private String permissionMessage;
032    
033        protected Command(String name) {
034            this(name, "", "/" + name, new ArrayList<String>());
035        }
036    
037        protected Command(String name, String description, String usageMessage, List<String> aliases) {
038            this.name = name;
039            this.nextLabel = name;
040            this.label = name;
041            this.description = description;
042            this.usageMessage = usageMessage;
043            this.aliases = aliases;
044            this.activeAliases = new ArrayList<String>(aliases);
045        }
046    
047        /**
048         * Executes the command, returning its success
049         *
050         * @param sender Source object which is executing this command
051         * @param commandLabel The alias of the command used
052         * @param args All arguments passed to the command, split via ' '
053         * @return true if the command was successful, otherwise false
054         */
055        public abstract boolean execute(CommandSender sender, String commandLabel, String[] args);
056    
057        /**
058         * @deprecated This method is not supported and returns null
059         */
060        @Deprecated
061        public List<String> tabComplete(CommandSender sender, String[] args) {
062            return null;
063        }
064    
065        /**
066         * Executed on tab completion for this command, returning a list of options
067         * the player can tab through.
068         *
069         * @param sender Source object which is executing this command
070         * @param alias the alias being used
071         * @param args All arguments passed to the command, split via ' '
072         * @return a list of tab-completions for the specified arguments. This will never be null. List may be immutable.
073         * @throws IllegalArgumentException if sender, alias, or args is null
074         */
075        public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
076            Validate.notNull(sender, "Sender cannot be null");
077            Validate.notNull(args, "Arguments cannot be null");
078            Validate.notNull(alias, "Alias cannot be null");
079    
080            if (!(sender instanceof Player) || args.length == 0) {
081                return ImmutableList.of();
082            }
083    
084            String lastWord = args[args.length - 1];
085    
086            Player senderPlayer = (Player) sender;
087    
088            ArrayList<String> matchedPlayers = new ArrayList<String>();
089            for (Player player : sender.getServer().getOnlinePlayers()) {
090                String name = player.getName();
091                if (senderPlayer.canSee(player) && StringUtil.startsWithIgnoreCase(name, lastWord)) {
092                    matchedPlayers.add(name);
093                }
094            }
095    
096            Collections.sort(matchedPlayers, String.CASE_INSENSITIVE_ORDER);
097            return matchedPlayers;
098        }
099    
100        /**
101         * Returns the name of this command
102         *
103         * @return Name of this command
104         */
105        public String getName() {
106            return name;
107        }
108    
109        /**
110         * Gets the permission required by users to be able to perform this command
111         *
112         * @return Permission name, or null if none
113         */
114        public String getPermission() {
115            return permission;
116        }
117    
118        /**
119         * Sets the permission required by users to be able to perform this command
120         *
121         * @param permission Permission name or null
122         */
123        public void setPermission(String permission) {
124            this.permission = permission;
125        }
126    
127        /**
128         * Tests the given {@link CommandSender} to see if they can perform this command.
129         * <p />
130         * If they do not have permission, they will be informed that they cannot do this.
131         *
132         * @param target User to test
133         * @return true if they can use it, otherwise false
134         */
135        public boolean testPermission(CommandSender target) {
136            if (testPermissionSilent(target)) {
137                return true;
138            }
139    
140            if (permissionMessage == null) {
141                target.sendMessage(ChatColor.RED + "I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error.");
142            } else if (permissionMessage.length() != 0) {
143                for (String line : permissionMessage.replace("<permission>", permission).split("\n")) {
144                    target.sendMessage(line);
145                }
146            }
147    
148            return false;
149        }
150    
151        /**
152         * Tests the given {@link CommandSender} to see if they can perform this command.
153         * <p />
154         * No error is sent to the sender.
155         *
156         * @param target User to test
157         * @return true if they can use it, otherwise false
158         */
159        public boolean testPermissionSilent(CommandSender target) {
160            if ((permission == null) || (permission.length() == 0)) {
161                return true;
162            }
163    
164            for (String p : permission.split(";")) {
165                if (target.hasPermission(p)) {
166                    return true;
167                }
168            }
169    
170            return false;
171        }
172    
173        /**
174         * Returns the current lable for this command
175         *
176         * @return Label of this command or null if not registered
177         */
178        public String getLabel() {
179            return label;
180        }
181    
182        /**
183         * Sets the label of this command
184         * If the command is currently registered the label change will only take effect after
185         * its been reregistered e.g. after a /reload
186         *
187         * @param name The command's name
188         * @return returns true if the name change happened instantly or false if it was scheduled for reregistration
189         */
190        public boolean setLabel(String name) {
191            this.nextLabel = name;
192            if (!isRegistered()) {
193                this.label = name;
194                return true;
195            }
196            return false;
197        }
198    
199        /**
200         * Registers this command to a CommandMap
201         * Once called it only allows changes the registered CommandMap
202         *
203         * @param commandMap the CommandMap to register this command to
204         * @return true if the registration was successful (the current registered CommandMap was the passed CommandMap or null) false otherwise
205         */
206        public boolean register(CommandMap commandMap) {
207            if (allowChangesFrom(commandMap)) {
208                this.commandMap = commandMap;
209                return true;
210            }
211    
212            return false;
213        }
214    
215        /**
216         * Unregisters this command from the passed CommandMap applying any outstanding changes
217         *
218         * @param commandMap the CommandMap to unregister
219         * @return true if the unregistration was successfull (the current registered CommandMap was the passed CommandMap or null) false otherwise
220         */
221        public boolean unregister(CommandMap commandMap) {
222            if (allowChangesFrom(commandMap)) {
223                this.commandMap = null;
224                this.activeAliases = new ArrayList<String>(this.aliases);
225                this.label = this.nextLabel;
226                return true;
227            }
228    
229            return false;
230        }
231    
232        private boolean allowChangesFrom(CommandMap commandMap) {
233            return (null == this.commandMap || this.commandMap == commandMap);
234        }
235    
236        /**
237         * Returns the current registered state of this command
238         *
239         * @return true if this command is currently registered false otherwise
240         */
241        public boolean isRegistered() {
242            return (null != this.commandMap);
243        }
244    
245        /**
246         * Returns a list of active aliases of this command
247         *
248         * @return List of aliases
249         */
250        public List<String> getAliases() {
251            return activeAliases;
252        }
253    
254        /**
255         * Returns a message to be displayed on a failed permission check for this command
256         *
257         * @return Permission check failed message
258         */
259        public String getPermissionMessage() {
260            return permissionMessage;
261        }
262    
263        /**
264         * Gets a brief description of this command
265         *
266         * @return Description of this command
267         */
268        public String getDescription() {
269            return description;
270        }
271    
272        /**
273         * Gets an example usage of this command
274         *
275         * @return One or more example usages
276         */
277        public String getUsage() {
278            return usageMessage;
279        }
280    
281        /**
282         * Sets the list of aliases to request on registration for this command
283         *
284         * @param aliases Aliases to register to this command
285         * @return This command object, for linking
286         */
287        public Command setAliases(List<String> aliases) {
288            this.aliases = aliases;
289            if (!isRegistered()) {
290                this.activeAliases = new ArrayList<String>(aliases);
291            }
292            return this;
293        }
294    
295        /**
296         * Sets a brief description of this command
297         *
298         * @param description New command description
299         * @return This command object, for linking
300         */
301        public Command setDescription(String description) {
302            this.description = description;
303            return this;
304        }
305    
306        /**
307         * Sets the message sent when a permission check fails
308         *
309         * @param permissionMessage New permission message, null to indicate default message, or an empty string to indicate no message
310         * @return This command object, for linking
311         */
312        public Command setPermissionMessage(String permissionMessage) {
313            this.permissionMessage = permissionMessage;
314            return this;
315        }
316    
317        /**
318         * Sets the example usage of this command
319         *
320         * @param usage New example usage
321         * @return This command object, for linking
322         */
323        public Command setUsage(String usage) {
324            this.usageMessage = usage;
325            return this;
326        }
327    
328        public static void broadcastCommandMessage(CommandSender source, String message) {
329            broadcastCommandMessage(source, message, true);
330        }
331    
332        public static void broadcastCommandMessage(CommandSender source, String message, boolean sendToSource) {
333            String result = source.getName() + ": " + message;
334    
335            if (source instanceof BlockCommandSender && ((BlockCommandSender) source).getBlock().getWorld().getGameRuleValue("commandBlockOutput").equalsIgnoreCase("false")) {
336                Bukkit.getConsoleSender().sendMessage(result);
337                return;
338            }
339    
340            Set<Permissible> users = Bukkit.getPluginManager().getPermissionSubscriptions(Server.BROADCAST_CHANNEL_ADMINISTRATIVE);
341            String colored = ChatColor.GRAY + "" + ChatColor.ITALIC + "[" + result + "]";
342    
343            if (sendToSource && !(source instanceof ConsoleCommandSender)) {
344                source.sendMessage(message);
345            }
346    
347            for (Permissible user : users) {
348                if (user instanceof CommandSender) {
349                    CommandSender target = (CommandSender) user;
350    
351                    if (target instanceof ConsoleCommandSender) {
352                        target.sendMessage(result);
353                    } else if (target != source) {
354                        target.sendMessage(colored);
355                    }
356                }
357            }
358        }
359    
360        @Override
361        public String toString() {
362            return getClass().getName() + '(' + name + ')';
363        }
364    }