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