Java tutorial
/* * BasePlugin * Copyright (C) 2014 Viciouss <http://www.doncarnage.de> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ package de.doncarnage.minecraft.common.commandhandler.manager.impl; import de.doncarnage.minecraft.common.commandhandler.manager.*; import de.doncarnage.minecraft.common.commandhandler.tree.Node; import de.doncarnage.minecraft.common.commandhandler.tree.NodeException; import de.doncarnage.minecraft.common.commandhandler.tree.NodeTree; import de.doncarnage.minecraft.common.commandhandler.tree.impl.CommandTree; import de.doncarnage.minecraft.common.commandhandler.tree.impl.NodeType; import net.milkbowl.vault.permission.Permission; import org.apache.commons.lang.StringUtils; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; public class DefaultCommandManager implements CommandManager { private static final String COMMAND_FORMAT = "/e%sr - b%s"; private static final String HELP_AVAILABLE_COMMANDS = "eAvailable commands:"; private static final String NO_PERMISSION_TO_USE_ANY = "eYou do not have permission to use any of these commands."; private static final String PARAMETER_COULD_NOT_BE_INTERPRETED = "eOne of the parameters could not be interpreted. Please have a look at the usage and try again."; private static final String CHECK_SYNTAX = "eAn internal error occured, please check your command and try again. If the command is correct, contact the plugin author."; private static final String COMMAND_DOES_NOT_EXIST = "eThis command does not exist."; private static final String NO_PERMISSION = "eYou do not have permission to use this command."; private static final String ERROR_EXECUTING_COMMAND = "eThere has been an error executing your command. Please report this to the plugin author."; private NodeTree<ExtractedCommandBean> nodeTree; private Permission perms; public DefaultCommandManager(Permission perms) { nodeTree = new CommandTree<>(); this.perms = perms; } public DefaultCommandManager() { this(null); } @Override public void registerCommand(CommandHolder commandHolder) { List<ExtractedCommandBean> commandList = extractAllCommands(commandHolder); registerCommands(commandList); } private void registerCommands(List<ExtractedCommandBean> commandList) { CommandRegistrationException ex = null; for (ExtractedCommandBean command : commandList) { try { nodeTree.addContent(command); } catch (NodeException e) { if (ex == null) { ex = new CommandRegistrationException(); } ex.addNewMessage(e.getMessage()); } } if (ex != null) { throw ex; } } private List<ExtractedCommandBean> extractAllCommands(CommandHolder commandHolder) { List<ExtractedCommandBean> commandList = new ArrayList<>(); for (Method method : commandHolder.getClass().getMethods()) { if (method.isAnnotationPresent(ExecutableCommand.class)) { checkForRightMethodFormat(commandHolder, method); ExtractedCommandBean ecb = new ExtractedCommandBean(); ExecutableCommand ec = method.getAnnotation(ExecutableCommand.class); ecb.setCmd(ec.cmd()); ecb.setDescription(ec.desc()); ecb.setParams(ec.params()); ecb.setPermissions(ec.permission()); ecb.setExecutable(method); ecb.setInstance(commandHolder); if (validate(ecb)) { commandList.add(ecb); } } } return commandList; } private void checkForRightMethodFormat(CommandHolder commandHolder, Method method) { List<Class<?>> params = Arrays.asList(method.getParameterTypes()); if (params.size() != 2 || !params.contains(CommandSender.class) || !params.contains(CommandContext.class)) { String paramString = buildParamString(params); throw new IllegalStateException(String.format( "The method %s in class %s does not have the needed arguments. Current arguments (%d): %s", method.getName(), commandHolder.getClass().getName(), params.size(), paramString)); } } private String buildParamString(List<Class<?>> params) { StringBuilder sb = new StringBuilder(); for (Class<?> param : params) { if (sb.length() > 0) sb.append(", "); sb.append(param.getName()); } return sb.toString(); } private boolean validate(ExtractedCommandBean ecb) { return !StringUtils.isEmpty(ecb.getCmd()); } @Override public boolean executeCommandForString(CommandSender sender, Command command, String label, String[] args) { try { String commandString = buildCommandString(command, args); Node<ExtractedCommandBean> cbNode = nodeTree.resolveContentNode(commandString); ExtractedCommandBean extractedCommandBean = cbNode.getContent(); if (extractedCommandBean == null) { sendHelpToPlayer(sender, cbNode, label); return true; } if (!hasPermissions(sender, extractedCommandBean.getPermissions())) { sender.sendMessage(NO_PERMISSION); return true; } Method executable = extractedCommandBean.getExecutable(); try { CommandContext context = createContext(extractedCommandBean, command, label, args); executable.invoke(extractedCommandBean.getInstance(), sender, context); return true; } catch (IllegalAccessException e) { sendMessageToPlayer(sender, ERROR_EXECUTING_COMMAND); return true; } catch (IllegalArgumentException e) { sendMessageToPlayer(sender, ERROR_EXECUTING_COMMAND); return true; } catch (InvocationTargetException e) { if (e.getTargetException().getClass().equals(ParameterParsingException.class)) { sender.sendMessage(PARAMETER_COULD_NOT_BE_INTERPRETED); sendHelpToPlayer(sender, cbNode, label); } else { sender.getServer().getLogger().warning( String.format("An error occurred while executing the command %s", commandString)); e.printStackTrace(); sendMessageToPlayer(sender, CHECK_SYNTAX); } return true; } } catch (NodeException e) { sendMessageToPlayer(sender, COMMAND_DOES_NOT_EXIST); return true; } } private String buildCommandString(Command command, String[] args) { return String.format("%s %s", command.getName(), StringUtils.join(args, " ")); } private void sendHelpToPlayer(CommandSender sender, Node<ExtractedCommandBean> node, String label) { List<String> helpList = new ArrayList<>(); helpList.add(HELP_AVAILABLE_COMMANDS); recursiveAddHelp(sender, node, helpList); if (helpList.size() == 1) { sender.sendMessage(NO_PERMISSION_TO_USE_ANY); return; } for (String line : helpList) { sender.sendMessage(String.format(line, label)); } } private void recursiveAddHelp(CommandSender sender, Node<ExtractedCommandBean> node, List<String> helpList) { if (node == null) return; ExtractedCommandBean bean = node.getContent(); if (bean != null && !StringUtils.isEmpty(bean.getDescription()) && hasPermissions(sender, bean.getPermissions())) { helpList.add(String.format(COMMAND_FORMAT, beautifyCommand(bean.getPathStringWithCustomCommand("%s")), bean.getDescription())); } for (Node<ExtractedCommandBean> child : node.getAllChildren()) { recursiveAddHelp(sender, child, helpList); } } private String beautifyCommand(String pathString) { String[] splitted = pathString.split(" "); for (int i = 0; i < splitted.length; i++) { if (splitted[i].startsWith(":")) { splitted[i] = splitted[i].replace(":", "<").concat(">"); } else if (splitted[i].startsWith("_")) { splitted[i] = splitted[i].replace("_", "[").concat("]"); } else if (splitted[i].startsWith("*")) { splitted[i] = splitted[i].replace("*", "[").concat("] ..."); } else if (splitted[i].startsWith("+")) { splitted[i] = splitted[i].replace("+", "<").concat("> ..."); } } return StringUtils.join(splitted, " "); } private boolean hasPermissions(CommandSender sender, String[] permissions) { boolean permsPluginActive = perms != null; for (String permission : permissions) { boolean permsByPlugin = permsPluginActive && perms.has(sender, permission); boolean permsByBukkit = sender.hasPermission(permission); if (!StringUtils.isEmpty(permission) && !permsByPlugin && !permsByBukkit) { return false; } } return true; } private void sendMessageToPlayer(CommandSender sender, String string) { sender.sendMessage(string); } CommandContext createContext(ExtractedCommandBean ret, Command command, String label, String[] args) { DefaultCommandContext cc = new DefaultCommandContext(command, label); String[] commandPath = StringUtils.split(ret.getPathString(), " "); for (int i = 0; i < args.length; i++) { // if i is bigger then the command path length it's a repeatable node, add all of them if (i + 1 >= commandPath.length || NodeType.isParamNodeName(commandPath[i + 1])) { cc.addParameter(args[i]); } } return cc; } @Override public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) { Node<ExtractedCommandBean> output = nodeTree.getLastNodeOnPath(buildCommandString(command, args)); int numberOfResolvedArgs = output.getNodeDepth() - 1; String nextCommand; if (args.length < numberOfResolvedArgs) { return Collections.emptyList(); } else if (args.length == numberOfResolvedArgs) { nextCommand = args[numberOfResolvedArgs - 1]; output = output.getParent(); } else { nextCommand = args[numberOfResolvedArgs]; } return getListOfMatchingNodes(output, nextCommand); } private List<String> getListOfMatchingNodes(Node<ExtractedCommandBean> output, String nextCommand) { List<Node<ExtractedCommandBean>> children = output.getAllChildren(); if (children.size() > 0 && !children.get(0).isParam()) { List<String> options = new ArrayList<>(); for (Node<ExtractedCommandBean> child : children) { if (child.getName().length() > nextCommand.length() && child.getName().substring(0, nextCommand.length()).equalsIgnoreCase(nextCommand)) { options.add(child.getName()); } } return options; } return Collections.emptyList(); } }