Bukkit  1.4.7-R1.0
 All Classes Namespaces Files Functions Variables Enumerator Pages
SimpleCommandMap.java
Go to the documentation of this file.
1 package org.bukkit.command;
2 
3 import static org.bukkit.util.Java15Compat.Arrays_copyOfRange;
4 
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.HashSet;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.regex.Pattern;
15 
16 import org.apache.commons.lang.Validate;
17 import org.bukkit.Server;
18 import org.bukkit.command.defaults.*;
19 import org.bukkit.util.StringUtil;
20 
21 public class SimpleCommandMap implements CommandMap {
22  private static final Pattern PATTERN_ON_SPACE = Pattern.compile(" ", Pattern.LITERAL);
23  protected final Map<String, Command> knownCommands = new HashMap<String, Command>();
24  protected final Set<String> aliases = new HashSet<String>();
25  private final Server server;
26  protected static final Set<VanillaCommand> fallbackCommands = new HashSet<VanillaCommand>();
27 
28  static {
29  fallbackCommands.add(new ListCommand());
30  fallbackCommands.add(new OpCommand());
31  fallbackCommands.add(new DeopCommand());
32  fallbackCommands.add(new BanIpCommand());
33  fallbackCommands.add(new PardonIpCommand());
34  fallbackCommands.add(new BanCommand());
35  fallbackCommands.add(new PardonCommand());
36  fallbackCommands.add(new KickCommand());
37  fallbackCommands.add(new TeleportCommand());
38  fallbackCommands.add(new GiveCommand());
39  fallbackCommands.add(new TimeCommand());
40  fallbackCommands.add(new SayCommand());
41  fallbackCommands.add(new WhitelistCommand());
42  fallbackCommands.add(new TellCommand());
43  fallbackCommands.add(new MeCommand());
44  fallbackCommands.add(new KillCommand());
45  fallbackCommands.add(new GameModeCommand());
46  fallbackCommands.add(new HelpCommand());
47  fallbackCommands.add(new ExpCommand());
48  fallbackCommands.add(new ToggleDownfallCommand());
49  fallbackCommands.add(new BanListCommand());
50  fallbackCommands.add(new DefaultGameModeCommand());
51  fallbackCommands.add(new SeedCommand());
52  fallbackCommands.add(new DifficultyCommand());
53  fallbackCommands.add(new WeatherCommand());
54  fallbackCommands.add(new SpawnpointCommand());
55  fallbackCommands.add(new ClearCommand());
56  fallbackCommands.add(new GameRuleCommand());
57  fallbackCommands.add(new EnchantCommand());
58  }
59 
60  public SimpleCommandMap(final Server server) {
61  this.server = server;
62  setDefaultCommands(server);
63  }
64 
65  private void setDefaultCommands(final Server server) {
66  register("bukkit", new SaveCommand());
67  register("bukkit", new SaveOnCommand());
68  register("bukkit", new SaveOffCommand());
69  register("bukkit", new StopCommand());
70  register("bukkit", new VersionCommand("version"));
71  register("bukkit", new ReloadCommand("reload"));
72  register("bukkit", new PluginsCommand("plugins"));
73  register("bukkit", new TimingsCommand("timings"));
74  }
75 
79  public void registerAll(String fallbackPrefix, List<Command> commands) {
80  if (commands != null) {
81  for (Command c : commands) {
82  register(fallbackPrefix, c);
83  }
84  }
85  }
86 
90  public boolean register(String fallbackPrefix, Command command) {
91  return register(command.getName(), fallbackPrefix, command);
92  }
93 
97  public boolean register(String label, String fallbackPrefix, Command command) {
98  boolean registeredPassedLabel = register(label, fallbackPrefix, command, false);
99 
100  Iterator<String> iterator = command.getAliases().iterator();
101  while (iterator.hasNext()) {
102  if (!register(iterator.next(), fallbackPrefix, command, true)) {
103  iterator.remove();
104  }
105  }
106 
107  // Register to us so further updates of the commands label and aliases are postponed until its reregistered
108  command.register(this);
109 
110  return registeredPassedLabel;
111  }
112 
123  private synchronized boolean register(String label, String fallbackPrefix, Command command, boolean isAlias) {
124  String lowerLabel = label.trim().toLowerCase();
125 
126  if (isAlias && knownCommands.containsKey(lowerLabel)) {
127  // Request is for an alias and it conflicts with a existing command or previous alias ignore it
128  // Note: This will mean it gets removed from the commands list of active aliases
129  return false;
130  }
131 
132  String lowerPrefix = fallbackPrefix.trim().toLowerCase();
133  boolean registerdPassedLabel = true;
134 
135  // If the command exists but is an alias we overwrite it, otherwise we rename it based on the fallbackPrefix
136  while (knownCommands.containsKey(lowerLabel) && !aliases.contains(lowerLabel)) {
137  lowerLabel = lowerPrefix + ":" + lowerLabel;
138  registerdPassedLabel = false;
139  }
140 
141  if (isAlias) {
142  aliases.add(lowerLabel);
143  } else {
144  // Ensure lowerLabel isn't listed as a alias anymore and update the commands registered name
145  aliases.remove(lowerLabel);
146  command.setLabel(lowerLabel);
147  }
148  knownCommands.put(lowerLabel, command);
149 
150  return registerdPassedLabel;
151  }
152 
153  protected Command getFallback(String label) {
154  for (VanillaCommand cmd : fallbackCommands) {
155  if (cmd.matches(label)) {
156  return cmd;
157  }
158  }
159 
160  return null;
161  }
162 
163  public Set<VanillaCommand> getFallbackCommands() {
164  return Collections.unmodifiableSet(fallbackCommands);
165  }
166 
170  public boolean dispatch(CommandSender sender, String commandLine) throws CommandException {
171  String[] args = PATTERN_ON_SPACE.split(commandLine);
172 
173  if (args.length == 0) {
174  return false;
175  }
176 
177  String sentCommandLabel = args[0].toLowerCase();
178  Command target = getCommand(sentCommandLabel);
179 
180  if (target == null) {
181  return false;
182  }
183 
184  try {
185  // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false)
186  target.execute(sender, sentCommandLabel, Arrays_copyOfRange(args, 1, args.length));
187  } catch (CommandException ex) {
188  throw ex;
189  } catch (Throwable ex) {
190  throw new CommandException("Unhandled exception executing '" + commandLine + "' in " + target, ex);
191  }
192 
193  // return true as command was handled
194  return true;
195  }
196 
197  public synchronized void clearCommands() {
198  for (Map.Entry<String, Command> entry : knownCommands.entrySet()) {
199  entry.getValue().unregister(this);
200  }
201  knownCommands.clear();
202  aliases.clear();
203  setDefaultCommands(server);
204  }
205 
206  public Command getCommand(String name) {
207  Command target = knownCommands.get(name.toLowerCase());
208  if (target == null) {
209  target = getFallback(name);
210  }
211  return target;
212  }
213 
214  public List<String> tabComplete(CommandSender sender, String cmdLine) {
215  Validate.notNull(sender, "Sender cannot be null");
216  Validate.notNull(cmdLine, "Command line cannot null");
217 
218  int spaceIndex = cmdLine.indexOf(' ');
219 
220  if (spaceIndex == -1) {
221  ArrayList<String> completions = new ArrayList<String>();
222  Map<String, Command> knownCommands = this.knownCommands;
223 
224  for (VanillaCommand command : fallbackCommands) {
225  String name = command.getName();
226 
227  if (!command.testPermissionSilent(sender)) {
228  continue;
229  }
230  if (knownCommands.containsKey(name)) {
231  // Don't let a vanilla command override a command added below
232  // This has to do with the way aliases work
233  continue;
234  }
235  if (!StringUtil.startsWithIgnoreCase(name, cmdLine)) {
236  continue;
237  }
238 
239  completions.add('/' + name);
240  }
241 
242  for (Map.Entry<String, Command> commandEntry : knownCommands.entrySet()) {
243  Command command = commandEntry.getValue();
244 
245  if (!command.testPermissionSilent(sender)) {
246  continue;
247  }
248 
249  String name = commandEntry.getKey(); // Use the alias, not command name
250 
251  if (StringUtil.startsWithIgnoreCase(name, cmdLine)) {
252  completions.add('/' + name);
253  }
254  }
255 
256  Collections.sort(completions, String.CASE_INSENSITIVE_ORDER);
257  return completions;
258  }
259 
260  String commandName = cmdLine.substring(0, spaceIndex);
261  Command target = getCommand(commandName);
262 
263  if (target == null) {
264  return null;
265  }
266 
267  if (!target.testPermissionSilent(sender)) {
268  return null;
269  }
270 
271  String argLine = cmdLine.substring(spaceIndex + 1, cmdLine.length());
272  String[] args = PATTERN_ON_SPACE.split(argLine, -1);
273 
274  try {
275  return target.tabComplete(sender, commandName, args);
276  } catch (CommandException ex) {
277  throw ex;
278  } catch (Throwable ex) {
279  throw new CommandException("Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target, ex);
280  }
281  }
282 
283  public Collection<Command> getCommands() {
284  return knownCommands.values();
285  }
286 
287  public void registerServerAliases() {
288  Map<String, String[]> values = server.getCommandAliases();
289 
290  for (String alias : values.keySet()) {
291  String[] targetNames = values.get(alias);
292  List<Command> targets = new ArrayList<Command>();
293  StringBuilder bad = new StringBuilder();
294 
295  for (String name : targetNames) {
296  Command command = getCommand(name);
297 
298  if (command == null) {
299  if (bad.length() > 0) {
300  bad.append(", ");
301  }
302  bad.append(name);
303  } else {
304  targets.add(command);
305  }
306  }
307 
308  // We register these as commands so they have absolute priority.
309 
310  if (targets.size() > 0) {
311  knownCommands.put(alias.toLowerCase(), new MultipleCommandAlias(alias.toLowerCase(), targets.toArray(new Command[0])));
312  } else {
313  knownCommands.remove(alias.toLowerCase());
314  }
315 
316  if (bad.length() > 0) {
317  server.getLogger().warning("The following command(s) could not be aliased under '" + alias + "' because they do not exist: " + bad);
318  }
319  }
320  }
321 }