2011-01-29 10:05:22 +00:00
|
|
|
// $Id$
|
|
|
|
/*
|
|
|
|
* WorldEdit
|
|
|
|
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2011-02-18 06:53:44 +00:00
|
|
|
package com.sk89q.minecraft.util.commands;
|
2011-01-29 10:05:22 +00:00
|
|
|
|
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
|
import java.lang.reflect.Method;
|
2011-06-18 10:56:16 +00:00
|
|
|
import java.lang.reflect.Modifier;
|
2011-12-25 23:36:23 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
2011-06-18 10:56:16 +00:00
|
|
|
import java.util.logging.Level;
|
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
import com.sk89q.util.StringUtil;
|
2011-01-29 10:05:22 +00:00
|
|
|
|
|
|
|
/**
|
2011-06-18 10:56:16 +00:00
|
|
|
* <p>Manager for handling commands. This allows you to easily process commands,
|
|
|
|
* including nested commands, by correctly annotating methods of a class.</p>
|
2011-02-25 00:39:23 +00:00
|
|
|
*
|
|
|
|
* <p>To use this, it is merely a matter of registering classes containing
|
|
|
|
* the commands (as methods with the proper annotations) with the
|
|
|
|
* manager. When you want to process a command, use one of the
|
|
|
|
* <code>execute</code> methods. If something is wrong, such as incorrect
|
|
|
|
* usage, insufficient permissions, or a missing command altogether, an
|
2011-06-18 10:56:16 +00:00
|
|
|
* exception will be raised for upstream handling.</p>
|
|
|
|
*
|
|
|
|
* <p>Methods of a class to be registered can be static, but if an injector
|
|
|
|
* is registered with the class, the instances of the command classes
|
|
|
|
* will be created automatically and methods will be called non-statically.</p>
|
2011-02-25 00:39:23 +00:00
|
|
|
*
|
|
|
|
* <p>To mark a method as a command, use {@link Command}. For nested commands,
|
|
|
|
* see {@link NestedCommand}. To handle permissions, use
|
2011-06-18 10:56:16 +00:00
|
|
|
* {@link CommandPermissions}.</p>
|
2011-02-25 00:39:23 +00:00
|
|
|
*
|
|
|
|
* <p>This uses Java reflection extensively, but to reduce the overhead of
|
|
|
|
* reflection, command lookups are completely cached on registration. This
|
|
|
|
* allows for fast command handling. Method invocation still has to be done
|
2011-06-18 10:56:16 +00:00
|
|
|
* with reflection, but this is quite fast in that of itself.</p>
|
2011-01-29 10:05:22 +00:00
|
|
|
*
|
|
|
|
* @author sk89q
|
2011-02-25 00:39:23 +00:00
|
|
|
* @param <T> command sender class
|
2011-01-29 10:05:22 +00:00
|
|
|
*/
|
2011-02-25 00:39:23 +00:00
|
|
|
public abstract class CommandsManager<T> {
|
2011-06-18 10:56:16 +00:00
|
|
|
/**
|
|
|
|
* Logger for general errors.
|
|
|
|
*/
|
|
|
|
protected static final Logger logger =
|
|
|
|
Logger.getLogger(CommandsManager.class.getCanonicalName());
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-01-29 10:05:22 +00:00
|
|
|
/**
|
2011-02-25 00:39:23 +00:00
|
|
|
* Mapping of commands (including aliases) with a description. Root
|
|
|
|
* commands are stored under a key of null, whereas child commands are
|
2011-06-18 10:56:16 +00:00
|
|
|
* cached under their respective {@link Method}. The child map has
|
|
|
|
* the key of the command name (one for each alias) with the
|
|
|
|
* method.
|
2011-01-29 10:05:22 +00:00
|
|
|
*/
|
2011-11-23 01:29:48 +00:00
|
|
|
protected Map<Method, Map<String, Method>> commands = new HashMap<Method, Map<String, Method>>();
|
2011-06-18 10:56:16 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Used to store the instances associated with a method.
|
|
|
|
*/
|
|
|
|
protected Map<Method, Object> instances = new HashMap<Method, Object>();
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-01-29 10:05:22 +00:00
|
|
|
/**
|
2011-06-18 10:56:16 +00:00
|
|
|
* Mapping of commands (not including aliases) with a description. This
|
|
|
|
* is only for top level commands.
|
2011-01-29 10:05:22 +00:00
|
|
|
*/
|
2011-02-25 00:39:23 +00:00
|
|
|
protected Map<String, String> descs = new HashMap<String, String>();
|
2011-06-18 10:56:16 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Stores the injector used to getInstance.
|
|
|
|
*/
|
|
|
|
protected Injector injector;
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-12-13 08:23:21 +00:00
|
|
|
/**
|
|
|
|
* Mapping of commands (not including aliases) with a description. This
|
|
|
|
* is only for top level commands.
|
|
|
|
*/
|
|
|
|
protected Map<String, String> helpMessages = new HashMap<String, String>();
|
|
|
|
|
2011-01-29 10:05:22 +00:00
|
|
|
/**
|
2011-06-18 10:56:16 +00:00
|
|
|
* Register an class that contains commands (denoted by {@link Command}.
|
|
|
|
* If no dependency injector is specified, then the methods of the
|
|
|
|
* class will be registered to be called statically. Otherwise, new
|
|
|
|
* instances will be created of the command classes and methods will
|
|
|
|
* not be called statically.
|
2011-01-29 10:05:22 +00:00
|
|
|
*
|
|
|
|
* @param cls
|
|
|
|
*/
|
|
|
|
public void register(Class<?> cls) {
|
2011-02-01 10:03:18 +00:00
|
|
|
registerMethods(cls, null);
|
|
|
|
}
|
2011-06-18 10:56:16 +00:00
|
|
|
|
2011-11-11 23:47:50 +00:00
|
|
|
/**
|
|
|
|
* Register an class that contains commands (denoted by {@link Command}.
|
|
|
|
* If no dependency injector is specified, then the methods of the
|
|
|
|
* class will be registered to be called statically. Otherwise, new
|
|
|
|
* instances will be created of the command classes and methods will
|
|
|
|
* not be called statically. A List of {@link Command} annotations from
|
|
|
|
* registered commands is returned.
|
|
|
|
*
|
|
|
|
* @param cls
|
|
|
|
* @return A List of {@link Command} annotations from registered commands,
|
|
|
|
* for use in eg. a dynamic command registration system.
|
|
|
|
*/
|
|
|
|
public List<Command> registerAndReturn(Class<?> cls) {
|
|
|
|
return registerMethods(cls, null);
|
|
|
|
}
|
|
|
|
|
2011-06-18 10:56:16 +00:00
|
|
|
/**
|
|
|
|
* Register the methods of a class. This will automatically construct
|
|
|
|
* instances as necessary.
|
|
|
|
*
|
|
|
|
* @param cls
|
|
|
|
* @param parent
|
2011-11-11 23:47:50 +00:00
|
|
|
* @return Commands Registered
|
2011-06-18 10:56:16 +00:00
|
|
|
*/
|
2011-11-11 23:47:50 +00:00
|
|
|
private List<Command> registerMethods(Class<?> cls, Method parent) {
|
2011-06-18 10:56:16 +00:00
|
|
|
try {
|
|
|
|
if (getInjector() == null) {
|
2011-11-11 23:47:50 +00:00
|
|
|
return registerMethods(cls, parent, null);
|
2011-06-18 10:56:16 +00:00
|
|
|
} else {
|
|
|
|
Object obj = null;
|
|
|
|
obj = getInjector().getInstance(cls);
|
2011-11-11 23:47:50 +00:00
|
|
|
return registerMethods(cls, parent, obj);
|
2011-06-18 10:56:16 +00:00
|
|
|
}
|
|
|
|
} catch (InvocationTargetException e) {
|
|
|
|
logger.log(Level.SEVERE, "Failed to register commands", e);
|
|
|
|
} catch (IllegalAccessException e) {
|
|
|
|
logger.log(Level.SEVERE, "Failed to register commands", e);
|
|
|
|
} catch (InstantiationException e) {
|
|
|
|
logger.log(Level.SEVERE, "Failed to register commands", e);
|
|
|
|
}
|
2011-11-11 23:47:50 +00:00
|
|
|
return null;
|
2011-06-18 10:56:16 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
/**
|
|
|
|
* Register the methods of a class.
|
|
|
|
*
|
|
|
|
* @param cls
|
|
|
|
* @param parent
|
|
|
|
*/
|
2011-11-11 23:47:50 +00:00
|
|
|
private List<Command> registerMethods(Class<?> cls, Method parent, Object obj) {
|
2011-02-01 10:03:18 +00:00
|
|
|
Map<String, Method> map;
|
2011-11-11 23:47:50 +00:00
|
|
|
List<Command> registered = new ArrayList<Command>();
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-18 06:53:44 +00:00
|
|
|
// Make a new hash map to cache the commands for this class
|
|
|
|
// as looking up methods via reflection is fairly slow
|
2011-02-01 10:03:18 +00:00
|
|
|
if (commands.containsKey(parent)) {
|
|
|
|
map = commands.get(parent);
|
|
|
|
} else {
|
|
|
|
map = new HashMap<String, Method>();
|
|
|
|
commands.put(parent, map);
|
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-01-29 10:05:22 +00:00
|
|
|
for (Method method : cls.getMethods()) {
|
|
|
|
if (!method.isAnnotationPresent(Command.class)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-06-18 10:56:16 +00:00
|
|
|
boolean isStatic = Modifier.isStatic(method.getModifiers());
|
|
|
|
|
2011-01-29 10:05:22 +00:00
|
|
|
Command cmd = method.getAnnotation(Command.class);
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-18 06:53:44 +00:00
|
|
|
// Cache the aliases too
|
2011-01-29 10:05:22 +00:00
|
|
|
for (String alias : cmd.aliases()) {
|
2011-02-01 10:03:18 +00:00
|
|
|
map.put(alias, method);
|
2011-01-29 10:05:22 +00:00
|
|
|
}
|
2011-06-18 10:56:16 +00:00
|
|
|
|
|
|
|
// We want to be able invoke with an instance
|
|
|
|
if (!isStatic) {
|
|
|
|
// Can't register this command if we don't have an instance
|
|
|
|
if (obj == null) {
|
|
|
|
continue;
|
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-06-18 10:56:16 +00:00
|
|
|
instances.put(method, obj);
|
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-18 06:53:44 +00:00
|
|
|
// Build a list of commands and their usage details, at least for
|
|
|
|
// root level commands
|
|
|
|
if (parent == null) {
|
2011-12-13 08:23:21 +00:00
|
|
|
final String commandName = cmd.aliases()[0];
|
|
|
|
final String desc = cmd.desc();
|
|
|
|
|
|
|
|
final String usage = cmd.usage();
|
|
|
|
if (usage.length() == 0) {
|
|
|
|
descs.put(commandName, desc);
|
2011-02-18 06:53:44 +00:00
|
|
|
} else {
|
2011-12-13 08:23:21 +00:00
|
|
|
descs.put(commandName, usage + " - " + desc);
|
|
|
|
}
|
|
|
|
|
|
|
|
String help = cmd.help();
|
|
|
|
if (help.length() == 0) {
|
|
|
|
help = desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
final CharSequence arguments = getArguments(cmd);
|
|
|
|
for (String alias : cmd.aliases()) {
|
|
|
|
final String helpMessage = "/"+alias+" "+arguments+"\n\n"+help;
|
|
|
|
final String key = alias.replaceAll("/", "");
|
|
|
|
String previous = helpMessages.put(key, helpMessage);
|
|
|
|
|
|
|
|
if (previous != null && !previous.replaceAll("^/[^ ]+ ", "").equals(helpMessage.replaceAll("^/[^ ]+ ", ""))) {
|
|
|
|
helpMessages.put(key, previous+"\n\n"+helpMessage);
|
|
|
|
}
|
2011-02-18 06:53:44 +00:00
|
|
|
}
|
2011-12-13 08:23:21 +00:00
|
|
|
|
2011-01-29 10:05:22 +00:00
|
|
|
}
|
2011-11-11 23:47:50 +00:00
|
|
|
|
|
|
|
// Add the command to the registered command list for return
|
|
|
|
registered.add(cmd);
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-18 06:53:44 +00:00
|
|
|
// Look for nested commands -- if there are any, those have
|
|
|
|
// to be cached too so that they can be quickly looked
|
|
|
|
// up when processing commands
|
2011-02-01 10:03:18 +00:00
|
|
|
if (method.isAnnotationPresent(NestedCommand.class)) {
|
|
|
|
NestedCommand nestedCmd = method.getAnnotation(NestedCommand.class);
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
for (Class<?> nestedCls : nestedCmd.value()) {
|
|
|
|
registerMethods(nestedCls, method);
|
|
|
|
}
|
|
|
|
}
|
2011-01-29 10:05:22 +00:00
|
|
|
}
|
2011-11-11 23:47:50 +00:00
|
|
|
return registered;
|
2011-01-29 10:05:22 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-01-29 10:05:22 +00:00
|
|
|
/**
|
2011-02-25 00:39:23 +00:00
|
|
|
* Checks to see whether there is a command named such at the root level.
|
|
|
|
* This will check aliases as well.
|
2011-01-29 10:05:22 +00:00
|
|
|
*
|
|
|
|
* @param command
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public boolean hasCommand(String command) {
|
2011-02-01 10:03:18 +00:00
|
|
|
return commands.get(null).containsKey(command.toLowerCase());
|
2011-01-29 10:05:22 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-01-29 10:05:22 +00:00
|
|
|
/**
|
2011-02-25 00:39:23 +00:00
|
|
|
* Get a list of command descriptions. This is only for root commands.
|
2011-01-29 10:05:22 +00:00
|
|
|
*
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public Map<String, String> getCommands() {
|
|
|
|
return descs;
|
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-12-15 12:26:59 +00:00
|
|
|
public Map<Method, Map<String, Method>> getMethods() {
|
|
|
|
return commands;
|
|
|
|
}
|
|
|
|
|
2011-12-13 08:23:21 +00:00
|
|
|
/**
|
|
|
|
* Get a map from command name to help message. This is only for root commands.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public Map<String, String> getHelpMessages() {
|
|
|
|
return helpMessages;
|
|
|
|
}
|
|
|
|
|
2011-01-29 19:36:28 +00:00
|
|
|
/**
|
|
|
|
* Get the usage string for a command.
|
|
|
|
*
|
2011-02-01 10:03:18 +00:00
|
|
|
* @param args
|
|
|
|
* @param level
|
2011-01-29 19:36:28 +00:00
|
|
|
* @param cmd
|
|
|
|
* @return
|
|
|
|
*/
|
2011-02-25 00:39:23 +00:00
|
|
|
protected String getUsage(String[] args, int level, Command cmd) {
|
2011-12-05 09:38:44 +00:00
|
|
|
final StringBuilder command = new StringBuilder();
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-12-05 08:36:15 +00:00
|
|
|
command.append('/');
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-07-15 07:00:48 +00:00
|
|
|
for (int i = 0; i <= level; ++i) {
|
2011-12-05 08:36:15 +00:00
|
|
|
command.append(args[i]);
|
|
|
|
command.append(' ');
|
2011-02-01 10:03:18 +00:00
|
|
|
}
|
2011-12-13 08:23:21 +00:00
|
|
|
command.append(getArguments(cmd));
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-12-05 09:38:44 +00:00
|
|
|
final String help = cmd.help();
|
2011-12-27 00:41:21 +00:00
|
|
|
if (help.length() > 0) {
|
2011-12-05 09:38:44 +00:00
|
|
|
command.append("\n\n");
|
|
|
|
command.append(help);
|
|
|
|
}
|
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
return command.toString();
|
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-12-13 08:23:21 +00:00
|
|
|
protected CharSequence getArguments(Command cmd) {
|
|
|
|
final String flags = cmd.flags();
|
|
|
|
|
|
|
|
final StringBuilder command2 = new StringBuilder();
|
|
|
|
if (flags.length() > 0) {
|
|
|
|
String flagString = flags.replaceAll(".:", "");
|
|
|
|
if (flagString.length() > 0) {
|
|
|
|
command2.append("[-");
|
|
|
|
for (int i = 0; i < flagString.length(); ++i) {
|
|
|
|
command2.append(flagString.charAt(i));
|
|
|
|
}
|
|
|
|
command2.append("] ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
command2.append(cmd.usage());
|
|
|
|
|
|
|
|
return command2;
|
|
|
|
}
|
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
/**
|
|
|
|
* Get the usage string for a nested command.
|
|
|
|
*
|
|
|
|
* @param args
|
|
|
|
* @param level
|
|
|
|
* @param method
|
2011-02-25 00:39:23 +00:00
|
|
|
* @param player
|
2011-02-01 10:03:18 +00:00
|
|
|
* @return
|
2011-02-25 00:39:23 +00:00
|
|
|
* @throws CommandException
|
2011-02-01 10:03:18 +00:00
|
|
|
*/
|
2011-02-25 00:39:23 +00:00
|
|
|
protected String getNestedUsage(String[] args, int level,
|
|
|
|
Method method, T player) throws CommandException {
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
StringBuilder command = new StringBuilder();
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
command.append("/");
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-07-15 07:00:48 +00:00
|
|
|
for (int i = 0; i <= level; ++i) {
|
2011-02-01 10:03:18 +00:00
|
|
|
command.append(args[i] + " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
Map<String, Method> map = commands.get(method);
|
2011-02-25 00:39:23 +00:00
|
|
|
boolean found = false;
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
command.append("<");
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-18 08:09:07 +00:00
|
|
|
Set<String> allowedCommands = new HashSet<String>();
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
for (Map.Entry<String, Method> entry : map.entrySet()) {
|
|
|
|
Method childMethod = entry.getValue();
|
2011-02-25 00:39:23 +00:00
|
|
|
found = true;
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
if (hasPermission(childMethod, player)) {
|
|
|
|
Command childCmd = childMethod.getAnnotation(Command.class);
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
allowedCommands.add(childCmd.aliases()[0]);
|
|
|
|
}
|
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
if (allowedCommands.size() > 0) {
|
|
|
|
command.append(StringUtil.joinString(allowedCommands, "|", 0));
|
|
|
|
} else {
|
2011-02-25 00:39:23 +00:00
|
|
|
if (!found) {
|
|
|
|
command.append("?");
|
|
|
|
} else {
|
|
|
|
//command.append("action");
|
|
|
|
throw new CommandPermissionsException();
|
|
|
|
}
|
2011-02-01 10:03:18 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
command.append(">");
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
return command.toString();
|
2011-01-29 19:36:28 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-25 00:39:23 +00:00
|
|
|
/**
|
|
|
|
* Attempt to execute a command. This version takes a separate command
|
|
|
|
* name (for the root command) and then a list of following arguments.
|
|
|
|
*
|
|
|
|
* @param cmd command to run
|
|
|
|
* @param args arguments
|
|
|
|
* @param player command source
|
|
|
|
* @param methodArgs method arguments
|
|
|
|
* @throws CommandException
|
|
|
|
*/
|
|
|
|
public void execute(String cmd, String[] args, T player,
|
2011-11-23 01:29:48 +00:00
|
|
|
Object... methodArgs) throws CommandException {
|
|
|
|
|
2011-02-25 00:39:23 +00:00
|
|
|
String[] newArgs = new String[args.length + 1];
|
|
|
|
System.arraycopy(args, 0, newArgs, 1, args.length);
|
|
|
|
newArgs[0] = cmd;
|
|
|
|
Object[] newMethodArgs = new Object[methodArgs.length + 1];
|
|
|
|
System.arraycopy(methodArgs, 0, newMethodArgs, 1, methodArgs.length);
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-25 00:39:23 +00:00
|
|
|
executeMethod(null, newArgs, player, newMethodArgs, 0);
|
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-01-29 10:05:22 +00:00
|
|
|
/**
|
|
|
|
* Attempt to execute a command.
|
|
|
|
*
|
|
|
|
* @param args
|
|
|
|
* @param player
|
2011-02-18 06:53:44 +00:00
|
|
|
* @param methodArgs
|
2011-02-25 00:39:23 +00:00
|
|
|
* @throws CommandException
|
2011-01-29 10:05:22 +00:00
|
|
|
*/
|
2011-02-25 00:39:23 +00:00
|
|
|
public void execute(String[] args, T player,
|
2011-11-23 01:29:48 +00:00
|
|
|
Object... methodArgs) throws CommandException {
|
|
|
|
|
2011-02-25 00:39:23 +00:00
|
|
|
Object[] newMethodArgs = new Object[methodArgs.length + 1];
|
|
|
|
System.arraycopy(methodArgs, 0, newMethodArgs, 1, methodArgs.length);
|
|
|
|
executeMethod(null, args, player, newMethodArgs, 0);
|
2011-02-01 10:03:18 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
/**
|
|
|
|
* Attempt to execute a command.
|
|
|
|
*
|
|
|
|
* @param parent
|
|
|
|
* @param args
|
|
|
|
* @param player
|
2011-02-18 06:53:44 +00:00
|
|
|
* @param methodArgs
|
2011-02-01 10:03:18 +00:00
|
|
|
* @param level
|
2011-02-25 00:39:23 +00:00
|
|
|
* @throws CommandException
|
2011-02-01 10:03:18 +00:00
|
|
|
*/
|
2011-02-25 00:39:23 +00:00
|
|
|
public void executeMethod(Method parent, String[] args,
|
|
|
|
T player, Object[] methodArgs, int level) throws CommandException {
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
String cmdName = args[level];
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
Map<String, Method> map = commands.get(parent);
|
|
|
|
Method method = map.get(cmdName.toLowerCase());
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-01-29 10:05:22 +00:00
|
|
|
if (method == null) {
|
2011-02-01 10:03:18 +00:00
|
|
|
if (parent == null) { // Root
|
2011-02-25 00:39:23 +00:00
|
|
|
throw new UnhandledCommandException();
|
2011-02-01 10:03:18 +00:00
|
|
|
} else {
|
2011-02-25 00:39:23 +00:00
|
|
|
throw new MissingNestedCommandException("Unknown command: " + cmdName,
|
|
|
|
getNestedUsage(args, level - 1, parent, player));
|
2011-02-01 10:03:18 +00:00
|
|
|
}
|
2011-01-29 10:05:22 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-12-15 12:26:59 +00:00
|
|
|
checkPermission(player, method);
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
int argsCount = args.length - 1 - level;
|
|
|
|
|
|
|
|
if (method.isAnnotationPresent(NestedCommand.class)) {
|
|
|
|
if (argsCount == 0) {
|
2011-11-23 01:29:48 +00:00
|
|
|
throw new MissingNestedCommandException("Sub-command required.",
|
2011-02-25 00:39:23 +00:00
|
|
|
getNestedUsage(args, level, method, player));
|
2011-02-01 10:03:18 +00:00
|
|
|
} else {
|
2011-02-25 00:39:23 +00:00
|
|
|
executeMethod(method, args, player, methodArgs, level + 1);
|
2011-02-01 10:03:18 +00:00
|
|
|
}
|
2011-08-29 21:15:25 +00:00
|
|
|
} else if (method.isAnnotationPresent(CommandAlias.class)) {
|
|
|
|
CommandAlias aCmd = method.getAnnotation(CommandAlias.class);
|
|
|
|
executeMethod(parent, aCmd.value(), player, methodArgs, level);
|
2011-11-23 01:29:48 +00:00
|
|
|
} else {
|
2011-02-01 10:03:18 +00:00
|
|
|
Command cmd = method.getAnnotation(Command.class);
|
2011-08-29 12:44:14 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
String[] newArgs = new String[args.length - level];
|
|
|
|
System.arraycopy(args, level, newArgs, 0, args.length - level);
|
2011-08-29 12:44:14 +00:00
|
|
|
|
2011-09-14 05:41:19 +00:00
|
|
|
final Set<Character> valueFlags = new HashSet<Character>();
|
2011-08-29 12:44:14 +00:00
|
|
|
|
2011-09-14 05:41:19 +00:00
|
|
|
char[] flags = cmd.flags().toCharArray();
|
2011-11-11 23:47:50 +00:00
|
|
|
Set<Character> newFlags = new HashSet<Character>();
|
2011-09-14 05:41:19 +00:00
|
|
|
for (int i = 0; i < flags.length; ++i) {
|
2011-11-11 23:47:50 +00:00
|
|
|
if (flags.length > i + 1 && flags[i + 1] == ':') {
|
2011-11-23 01:29:48 +00:00
|
|
|
valueFlags.add(flags[i]);
|
|
|
|
++i;
|
2011-09-14 05:41:19 +00:00
|
|
|
}
|
2011-11-11 23:47:50 +00:00
|
|
|
newFlags.add(flags[i]);
|
2011-02-01 10:03:18 +00:00
|
|
|
}
|
2011-08-29 12:44:14 +00:00
|
|
|
|
2011-09-14 05:41:19 +00:00
|
|
|
CommandContext context = new CommandContext(newArgs, valueFlags);
|
2011-08-29 12:44:14 +00:00
|
|
|
|
2011-11-23 01:29:48 +00:00
|
|
|
if (context.argsLength() < cmd.min()) {
|
2011-08-29 12:44:14 +00:00
|
|
|
throw new CommandUsageException("Too few arguments.", getUsage(args, level, cmd));
|
2011-11-23 01:29:48 +00:00
|
|
|
}
|
2011-08-29 12:44:14 +00:00
|
|
|
|
2011-11-23 01:29:48 +00:00
|
|
|
if (cmd.max() != -1 && context.argsLength() > cmd.max()) {
|
2011-08-29 12:44:14 +00:00
|
|
|
throw new CommandUsageException("Too many arguments.", getUsage(args, level, cmd));
|
2011-11-23 01:29:48 +00:00
|
|
|
}
|
2011-08-29 12:44:14 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
for (char flag : context.getFlags()) {
|
2011-11-23 01:29:48 +00:00
|
|
|
if (!newFlags.contains(flag)) {
|
2011-08-29 12:44:14 +00:00
|
|
|
throw new CommandUsageException("Unknown flag: " + flag, getUsage(args, level, cmd));
|
2011-11-23 01:29:48 +00:00
|
|
|
}
|
2011-02-01 10:03:18 +00:00
|
|
|
}
|
2011-08-29 12:44:14 +00:00
|
|
|
|
2011-02-18 06:53:44 +00:00
|
|
|
methodArgs[0] = context;
|
2011-06-18 10:56:16 +00:00
|
|
|
|
|
|
|
Object instance = instances.get(method);
|
2011-08-29 12:44:14 +00:00
|
|
|
|
2011-08-08 08:45:19 +00:00
|
|
|
invokeMethod(parent, args, player, method, instance, methodArgs, argsCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-15 12:26:59 +00:00
|
|
|
protected void checkPermission(T player, Method method) throws CommandException {
|
|
|
|
if (!hasPermission(method, player)) {
|
|
|
|
throw new CommandPermissionsException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-24 19:24:10 +00:00
|
|
|
public void invokeMethod(Method parent, String[] args, T player, Method method,
|
|
|
|
Object instance, Object[] methodArgs, int level) throws CommandException {
|
2011-08-08 08:45:19 +00:00
|
|
|
try {
|
|
|
|
method.invoke(instance, methodArgs);
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
logger.log(Level.SEVERE, "Failed to execute command", e);
|
|
|
|
} catch (IllegalAccessException e) {
|
|
|
|
logger.log(Level.SEVERE, "Failed to execute command", e);
|
|
|
|
} catch (InvocationTargetException e) {
|
|
|
|
if (e.getCause() instanceof CommandException) {
|
|
|
|
throw (CommandException) e.getCause();
|
2011-02-01 10:03:18 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-08-08 08:45:19 +00:00
|
|
|
throw new WrappedCommandException(e.getCause());
|
2011-01-29 10:05:22 +00:00
|
|
|
}
|
2011-02-01 10:03:18 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
/**
|
2011-02-25 00:39:23 +00:00
|
|
|
* Returns whether a player has access to a command.
|
2011-02-01 10:03:18 +00:00
|
|
|
*
|
|
|
|
* @param method
|
|
|
|
* @param player
|
|
|
|
* @return
|
|
|
|
*/
|
2011-02-25 00:39:23 +00:00
|
|
|
protected boolean hasPermission(Method method, T player) {
|
|
|
|
CommandPermissions perms = method.getAnnotation(CommandPermissions.class);
|
|
|
|
if (perms == null) {
|
2011-01-29 10:05:22 +00:00
|
|
|
return true;
|
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
for (String perm : perms.value()) {
|
2011-02-25 00:39:23 +00:00
|
|
|
if (hasPermission(player, perm)) {
|
2011-01-29 19:36:28 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
return false;
|
2011-01-29 10:05:22 +00:00
|
|
|
}
|
2011-11-23 01:29:48 +00:00
|
|
|
|
2011-02-01 10:03:18 +00:00
|
|
|
/**
|
2011-02-25 00:39:23 +00:00
|
|
|
* Returns whether a player permission..
|
2011-02-01 10:03:18 +00:00
|
|
|
*
|
|
|
|
* @param player
|
2011-02-25 00:39:23 +00:00
|
|
|
* @param perm
|
2011-02-01 10:03:18 +00:00
|
|
|
* @return
|
|
|
|
*/
|
2011-02-25 00:39:23 +00:00
|
|
|
public abstract boolean hasPermission(T player, String perm);
|
2011-06-18 10:56:16 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the injector used to create new instances. This can be
|
|
|
|
* null, in which case only classes will be registered statically.
|
|
|
|
*/
|
|
|
|
public Injector getInjector() {
|
|
|
|
return injector;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the injector for creating new instances.
|
|
|
|
*
|
|
|
|
* @param injector injector or null
|
|
|
|
*/
|
|
|
|
public void setInjector(Injector injector) {
|
|
|
|
this.injector = injector;
|
|
|
|
}
|
2011-01-29 10:05:22 +00:00
|
|
|
}
|