Added nested command support.

Dieser Commit ist enthalten in:
sk89q 2011-02-01 02:03:18 -08:00
Ursprung 78c78fee1f
Commit 01f7be3ba3
5 geänderte Dateien mit 262 neuen und 51 gelöschten Zeilen

Datei anzeigen

@ -18,6 +18,8 @@
package com.sk89q.util; package com.sk89q.util;
import java.util.List;
/** /**
* String utilities. * String utilities.
* *
@ -133,4 +135,24 @@ public class StringUtil {
} }
return buffer.toString(); return buffer.toString();
} }
/**
* Join an list of strings into a string.
*
* @param str
* @param delimiter
* @param initialIndex
* @return
*/
public static String joinString(List<String> str, String delimiter,
int initialIndex) {
if (str.size() == 0) {
return "";
}
StringBuilder buffer = new StringBuilder(str.get(initialIndex).toString());
for (int i = initialIndex + 1; i < str.size(); i++) {
buffer.append(delimiter).append(str.get(i).toString());
}
return buffer.toString();
}
} }

Datei anzeigen

@ -24,9 +24,9 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Command { public @interface Command {
String[] aliases(); String[] aliases();
String usage(); String usage() default "";
String desc(); String desc();
int min(); int min() default 0;
int max(); int max() default -1;
String flags() default ""; String flags() default "";
} }

Datei anzeigen

@ -0,0 +1,28 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 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/>.
*/
package com.sk89q.util.commands;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface NestedCommand {
Class<?>[] value();
}

Datei anzeigen

@ -30,7 +30,6 @@ import java.util.logging.Logger;
import java.io.*; import java.io.*;
import javax.script.ScriptException; import javax.script.ScriptException;
import com.sk89q.util.StringUtil; import com.sk89q.util.StringUtil;
import com.sk89q.util.commands.CommandContext;
import com.sk89q.worldedit.LocalSession.CompassMode; import com.sk89q.worldedit.LocalSession.CompassMode;
import com.sk89q.worldedit.bags.BlockBag; import com.sk89q.worldedit.bags.BlockBag;
import com.sk89q.worldedit.blocks.*; import com.sk89q.worldedit.blocks.*;
@ -841,7 +840,7 @@ public class WorldEdit {
+ StringUtil.joinString(split, " ")); + StringUtil.joinString(split, " "));
} }
return commands.execute(new CommandContext(split), this, return commands.execute(split, this,
session, player, editSession); session, player, editSession);
} finally { } finally {
session.remember(editSession); session.remember(editSession);

Datei anzeigen

@ -21,10 +21,14 @@ package com.sk89q.worldedit.commands;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import com.sk89q.util.StringUtil;
import com.sk89q.util.commands.Command; import com.sk89q.util.commands.Command;
import com.sk89q.util.commands.CommandContext; import com.sk89q.util.commands.CommandContext;
import com.sk89q.util.commands.NestedCommand;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.LocalPlayer;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalSession;
@ -38,9 +42,10 @@ import com.sk89q.worldedit.WorldEditException;
*/ */
public class CommandsManager { public class CommandsManager {
/** /**
* Mapping of commands (including aliases) with their method. * Mapping of nested commands (including aliases) with a description.
*/ */
public Map<String, Method> commands = new HashMap<String, Method>(); public Map<Method, Map<String, Method>> commands
= new HashMap<Method, Map<String, Method>>();
/** /**
* Mapping of commands (not including aliases) with a description. * Mapping of commands (not including aliases) with a description.
*/ */
@ -55,6 +60,25 @@ public class CommandsManager {
* @param cls * @param cls
*/ */
public void register(Class<?> cls) { public void register(Class<?> cls) {
registerMethods(cls, null);
}
/**
* Register the methods of a class.
*
* @param cls
* @param parent
*/
private void registerMethods(Class<?> cls, Method parent) {
Map<String, Method> map;
if (commands.containsKey(parent)) {
map = commands.get(parent);
} else {
map = new HashMap<String, Method>();
commands.put(parent, map);
}
for (Method method : cls.getMethods()) { for (Method method : cls.getMethods()) {
if (!method.isAnnotationPresent(Command.class)) { if (!method.isAnnotationPresent(Command.class)) {
continue; continue;
@ -64,7 +88,7 @@ public class CommandsManager {
// Cache the commands // Cache the commands
for (String alias : cmd.aliases()) { for (String alias : cmd.aliases()) {
commands.put(alias, method); map.put(alias, method);
} }
// Build a list of commands and their usage details // Build a list of commands and their usage details
@ -73,6 +97,14 @@ public class CommandsManager {
} else { } else {
descs.put(cmd.aliases()[0], cmd.usage() + " - " + cmd.desc()); descs.put(cmd.aliases()[0], cmd.usage() + " - " + cmd.desc());
} }
if (method.isAnnotationPresent(NestedCommand.class)) {
NestedCommand nestedCmd = method.getAnnotation(NestedCommand.class);
for (Class<?> nestedCls : nestedCmd.value()) {
registerMethods(nestedCls, method);
}
}
} }
} }
@ -83,7 +115,7 @@ public class CommandsManager {
* @return * @return
*/ */
public boolean hasCommand(String command) { public boolean hasCommand(String command) {
return commands.containsKey(command.toLowerCase()); return commands.get(null).containsKey(command.toLowerCase());
} }
/** /**
@ -98,14 +130,71 @@ public class CommandsManager {
/** /**
* Get the usage string for a command. * Get the usage string for a command.
* *
* @param command * @param args
* @param level
* @param cmd * @param cmd
* @return * @return
*/ */
private String getUsage(String command, Command cmd) { private String getUsage(String[] args, int level, Command cmd) {
return command StringBuilder command = new StringBuilder();
+ (cmd.flags().length() > 0 ? " [-" + cmd.flags() + "] " : " ")
+ cmd.usage(); command.append("/");
for (int i = 0; i <= level; i++) {
command.append(args[i] + " ");
}
command.append(cmd.flags().length() > 0 ? "[-" + cmd.flags() + "] " : "");
command.append(cmd.usage());
return command.toString();
}
/**
* Get the usage string for a nested command.
*
* @param args
* @param level
* @param method
* @param palyer
* @return
*/
private String getNestedUsage(String[] args, int level, Method method,
LocalPlayer player) {
StringBuilder command = new StringBuilder();
command.append("/");
for (int i = 0; i <= level; i++) {
command.append(args[i] + " ");
}
Map<String, Method> map = commands.get(method);
command.append("<");
List<String> allowedCommands = new ArrayList<String>();
for (Map.Entry<String, Method> entry : map.entrySet()) {
Method childMethod = entry.getValue();
if (hasPermission(childMethod, player)) {
Command childCmd = childMethod.getAnnotation(Command.class);
allowedCommands.add(childCmd.aliases()[0]);
}
}
if (allowedCommands.size() > 0) {
command.append(StringUtil.joinString(allowedCommands, "|", 0));
} else {
command.append("action");
}
command.append(">");
return command.toString();
} }
/** /**
@ -118,61 +207,135 @@ public class CommandsManager {
* @param editSession * @param editSession
* @return * @return
*/ */
public boolean execute(CommandContext args, WorldEdit we, public boolean execute(String[] args, WorldEdit we,
LocalSession session, LocalPlayer player, EditSession editSession) LocalSession session, LocalPlayer player, EditSession editSession)
throws WorldEditException, Throwable { throws WorldEditException, Throwable {
Method method = commands.get(args.getCommand().toLowerCase()); return executeMethod(null, args, we, session, player, editSession, 0);
}
/**
* Attempt to execute a command.
*
* @param parent
* @param args
* @param we
* @param session
* @param player
* @param editSession
* @param level
* @return
*/
public boolean executeMethod(Method parent, String[] args, WorldEdit we,
LocalSession session, LocalPlayer player, EditSession editSession,
int level) throws WorldEditException, Throwable {
String cmdName = args[level];
Map<String, Method> map = commands.get(parent);
Method method = map.get(cmdName.toLowerCase());
if (method == null) { if (method == null) {
return false; // No command if (parent == null) { // Root
return false;
} else {
player.printError(getNestedUsage(args, level - 1, method, player));
return true;
}
} }
if (!checkPermissions(method, player)) { if (!checkPermissions(method, player)) {
return true; return true;
} }
int argsCount = args.length - 1 - level;
Command cmd = method.getAnnotation(Command.class); if (method.isAnnotationPresent(NestedCommand.class)) {
if (argsCount == 0) {
if (args.argsLength() < cmd.min()) { player.printError(getNestedUsage(args, level, method, player));
player.printError("Too few arguments."); return true;
player.printError(getUsage(args.getCommand(), cmd)); } else {
return true; return executeMethod(method, args, we, session, player,
} editSession, level + 1);
}
if (cmd.max() != -1 && args.argsLength() > cmd.max()) { } else {
player.printError("Too many arguments."); Command cmd = method.getAnnotation(Command.class);
player.printError(getUsage(args.getCommand(), cmd));
return true; String[] newArgs = new String[args.length - level];
} System.arraycopy(args, level, newArgs, 0, args.length - level);
for (char flag : args.getFlags()) { CommandContext context = new CommandContext(newArgs);
if (cmd.flags().indexOf(String.valueOf(flag)) == -1) {
player.printError("Unknown flag: " + flag); if (context.argsLength() < cmd.min()) {
player.printError(getUsage(args.getCommand(), cmd)); player.printError("Too few arguments.");
player.printError(getUsage(args, level, cmd));
return true; return true;
} }
}
if (cmd.max() != -1 && context.argsLength() > cmd.max()) {
try { player.printError("Too many arguments.");
method.invoke(null, args, we, session, player, editSession); player.printError(getUsage(args, level, cmd));
} catch (IllegalArgumentException e) { return true;
e.printStackTrace(); }
} catch (IllegalAccessException e) {
e.printStackTrace(); for (char flag : context.getFlags()) {
} catch (InvocationTargetException e) { if (cmd.flags().indexOf(String.valueOf(flag)) == -1) {
if (e.getCause() instanceof WorldEditException) { player.printError("Unknown flag: " + flag);
throw (WorldEditException)e.getCause(); player.printError(getUsage(args, level, cmd));
} else if (e.getCause() instanceof NumberFormatException) { return true;
throw (NumberFormatException)e.getCause(); }
} else { }
throw e.getCause();
try {
method.invoke(null, context, we, session, player, editSession);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
if (e.getCause() instanceof WorldEditException) {
throw (WorldEditException)e.getCause();
} else if (e.getCause() instanceof NumberFormatException) {
throw (NumberFormatException)e.getCause();
} else {
throw e.getCause();
}
} }
} }
return true; return true;
} }
/**
* Checks permissions, prints an error if needed.
*
* @param method
* @param player
* @return
*/
private boolean checkPermissions(Method method, LocalPlayer player) { private boolean checkPermissions(Method method, LocalPlayer player) {
if (!method.isAnnotationPresent(CommandPermissions.class)) {
return true;
}
CommandPermissions perms = method.getAnnotation(CommandPermissions.class);
for (String perm : perms.value()) {
if (player.hasPermission(perm)) {
return true;
}
}
player.printError("You don't have permission for this command.");
return false;
}
/**
* Returns whether a player has access to a command.
*
* @param method
* @param player
* @return
*/
private boolean hasPermission(Method method, LocalPlayer player) {
CommandPermissions perms = method.getAnnotation(CommandPermissions.class); CommandPermissions perms = method.getAnnotation(CommandPermissions.class);
if (perms == null) { if (perms == null) {
return true; return true;
@ -184,7 +347,6 @@ public class CommandsManager {
} }
} }
player.printError("You don't have permission for this command.");
return false; return false;
} }
} }