/*
* PermissionsEx - Permissions plugin for Bukkit
* Copyright (C) 2011 t3hk0d3 http://www.tehkode.ru
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package pex.permissions.commands;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.minecraft.command.CommandBase;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumChatFormatting;
import pex.permissions.PermissionManager;
import pex.permissions.bukkit.PermissionsEx;
import pex.permissions.commands.exceptions.AutoCompleteChoicesException;
import pex.utils.StringUtils;
/**
*
* @author code
*/
public class CommandsManager {
protected static final Logger logger = Logger.getLogger("Minecraft");
protected Map<String, Map<CommandSyntax, CommandBinding>> listeners = new LinkedHashMap<String, Map<CommandSyntax, CommandBinding>>();
protected Object plugin;
protected List<Object> helpObjects = new LinkedList<Object>();
public CommandsManager(Object plugin) {
this.plugin = plugin;
}
public void register(CommandListener listener) {
for (Method method : listener.getClass().getMethods()) {
if (!method.isAnnotationPresent(Command.class)) {
continue;
}
Command cmdAnnotation = method.getAnnotation(Command.class);
Map<CommandSyntax, CommandBinding> commandListeners = listeners.get(cmdAnnotation.name());
if (commandListeners == null) {
commandListeners = new LinkedHashMap<CommandSyntax, CommandBinding>();
listeners.put(cmdAnnotation.name(), commandListeners);
}
commandListeners.put(new CommandSyntax(cmdAnnotation.syntax()), new CommandBinding(listener, method));
}
listener.onRegistered(this);
}
public boolean execute(ICommandSender sender, CommandBase command, String[] args) {
Map<CommandSyntax, CommandBinding> callMap = listeners.get(command.getCommandName());
if (callMap == null) { // No commands registered
return false;
}
CommandBinding selectedBinding = null;
int argumentsLength = 0;
String arguments = StringUtils.implode(args, " ");
for (Entry<CommandSyntax, CommandBinding> entry : callMap.entrySet()) {
CommandSyntax syntax = entry.getKey();
if (!syntax.isMatch(arguments)) {
continue;
}
if (selectedBinding != null && syntax.getRegexp().length() < argumentsLength) { // match, but there already more fitted variant
continue;
}
CommandBinding binding = entry.getValue();
binding.setParams(syntax.getMatchedArguments(arguments));
selectedBinding = binding;
}
if (selectedBinding == null) { // there is fitting handler
PermissionsEx.sendChatToPlayer(sender, EnumChatFormatting.RED + "Error in command syntax. Check command help.");
// sender.sendChatToPlayer(EnumChatFormatting.RED +
// "Error in command syntax. Check command help.");
return true;
}
// Check permission
if (sender instanceof EntityPlayer) { // this method are not public and
// required permission
if (!selectedBinding.checkPermissions((EntityPlayer) sender)) {
logger.warning("User " + ((EntityPlayer) sender).username + " tried to access chat command \"" + command.getCommandName() + " " + arguments + "\", but doesn't have permission to do this.");
PermissionsEx.sendChatToPlayer(sender, EnumChatFormatting.RED + "Sorry, you don't have enough permissions.");
// sender.sendChatToPlayer(EnumChatFormatting.RED +
// "Sorry, you don't have enough permissions.");
return true;
}
}
try {
selectedBinding.call(plugin, sender, selectedBinding.getParams());
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof AutoCompleteChoicesException) {
AutoCompleteChoicesException autocomplete = (AutoCompleteChoicesException) e.getTargetException();
PermissionsEx.sendChatToPlayer(sender, "Autocomplete for <" + autocomplete.getArgName() + ">:");
PermissionsEx.sendChatToPlayer(sender, " " + StringUtils.implode(autocomplete.getChoices(), " "));
// sender.sendChatToPlayer("Autocomplete for <" +
// autocomplete.getArgName() + ">:");
// sender.sendChatToPlayer(" " +
// StringUtils.implode(autocomplete.getChoices(), " "));
} else {
throw new RuntimeException(e.getTargetException());
}
} catch (Exception e) {
logger.severe("There is bogus command handler for " + command.getCommandName() + " command. (Is appropriate plugin is update?)");
if (e.getCause() != null) {
e.getCause().printStackTrace();
} else {
e.printStackTrace();
}
}
return true;
}
public List<CommandBinding> getCommands() {
List<CommandBinding> commands = new LinkedList<CommandBinding>();
for (Map<CommandSyntax, CommandBinding> map : listeners.values()) {
commands.addAll(map.values());
}
return commands;
}
protected class CommandSyntax {
protected String originalSyntax;
protected String regexp;
protected List<String> arguments = new LinkedList<String>();
public CommandSyntax(String syntax) {
originalSyntax = syntax;
regexp = prepareSyntaxRegexp(syntax);
}
public String getRegexp() {
return regexp;
}
private String prepareSyntaxRegexp(String syntax) {
String expression = syntax;
Matcher argMatcher = Pattern.compile("(?:[\\s]+)((\\<|\\[)([^\\>\\]]+)(?:\\>|\\]))").matcher(expression);
// Matcher argMatcher =
// Pattern.compile("(\\<|\\[)([^\\>\\]]+)(?:\\>|\\])").matcher(expression);
int index = 0;
while (argMatcher.find()) {
if (argMatcher.group(2).equals("[")) {
expression = expression.replace(argMatcher.group(0), "(?:(?:[\\s]+)(\"[^\"]+\"|[^\\s]+))?");
} else {
expression = expression.replace(argMatcher.group(1), "(\"[^\"]+\"|[\\S]+)");
}
arguments.add(index++, argMatcher.group(3));
}
return expression;
}
public boolean isMatch(String str) {
return str.matches(regexp);
}
public Map<String, String> getMatchedArguments(String str) {
Map<String, String> matchedArguments = new HashMap<String, String>(arguments.size());
if (arguments.size() > 0) {
Matcher argMatcher = Pattern.compile(regexp).matcher(str);
if (argMatcher.find()) {
for (int index = 1; index <= argMatcher.groupCount(); index++) {
String argumentValue = argMatcher.group(index);
if (argumentValue == null || argumentValue.isEmpty()) {
continue;
}
if (argumentValue.startsWith("\"") && argumentValue.endsWith("\"")) { // Trim
// boundary
// colons
argumentValue = argumentValue.substring(1, argumentValue.length() - 1);
}
matchedArguments.put(arguments.get(index - 1), argumentValue);
}
}
}
return matchedArguments;
}
}
public class CommandBinding {
protected Object object;
protected Method method;
protected Map<String, String> params = new HashMap<String, String>();
public CommandBinding(Object object, Method method) {
this.object = object;
this.method = method;
}
public Command getMethodAnnotation() {
return method.getAnnotation(Command.class);
}
public Map<String, String> getParams() {
return params;
}
public void setParams(Map<String, String> params) {
this.params = params;
}
public boolean checkPermissions(EntityPlayer player) {
PermissionManager manager = PermissionsEx.getPermissionManager();
String permission = getMethodAnnotation().permission();
if (permission.contains("<")) {
for (Entry<String, String> entry : getParams().entrySet()) {
if (entry.getValue() != null) {
permission = permission.replace("<" + entry.getKey() + ">", entry.getValue().toLowerCase());
}
}
}
return manager.has(player, permission);
}
public void call(Object... args) throws Exception {
method.invoke(object, args);
}
}
}