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 }