Added multiword args and some improvements to value flags.

This commit is contained in:
zml2008 2011-09-13 22:41:19 -07:00
parent c38a6b0677
commit 2f390e9938
4 changed files with 91 additions and 95 deletions

View File

@ -63,15 +63,9 @@ public @interface Command {
* Flags allow special processing for flags such as -h in the command,
* allowing users to easily turn on a flag. This is a string with
* each character being a flag. Use A-Z and a-z as possible flags.
* Appending a flag with a : makes the flag character before a value flag,
* meaning that if it is given it must have a value
*/
String flags() default "";
/**
* Value flags are special flags, that take a value after the flag.
* The semantics are the same as with the flags parameter.
* They aren't automatically documented and thus need to be added
* to the "usage" parameter separately.
*/
String valueFlags() default "";
}

View File

@ -18,11 +18,11 @@
package com.sk89q.minecraft.util.commands;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static com.sk89q.util.ArrayUtil.removePortionOfArray;
public class CommandContext {
protected static final String QUOTE_CHARS = "\'\"";
@ -30,16 +30,30 @@ public class CommandContext {
protected final Set<Character> booleanFlags = new HashSet<Character>();
protected final Map<Character, String> valueFlags = new HashMap<Character, String>();
public CommandContext(String args) {
this(args.split(" "));
public CommandContext(String args) throws CommandException {
this(args.split(" "), null);
}
public CommandContext(String[] args) {
char quotedChar;
for (int i = 1; i < args.length; ++i) {
if (args[i].length() == 0) {
public CommandContext(String[] args) throws CommandException {
this(args, null);
}
public CommandContext(String args, Set<Character> valueFlags) throws CommandException {
this(args.split(" "), valueFlags);
}
/**
* @param args An array with arguments empty strings will be ignored by most things
* @param valueFlags A set containing all value flags. Pass null to disable value flag parsing.
* @throws CommandException This is thrown if a value flag was passed without a value.
*/
public CommandContext(String[] args, Set<Character> valueFlags) throws CommandException {
// Go through empty args and multiword args first
for (int i = 1; i < args.length; i++) {
char quotedChar;
if (args[i].length() < 1) {
args = removePortionOfArray(args, i, i, null);
} else if (QUOTE_CHARS.indexOf(String.valueOf(args[i].charAt(0))) != -1) {
} else if (QUOTE_CHARS.indexOf(args[i].charAt(0)) != -1) {
StringBuilder build = new StringBuilder();
quotedChar = args[i].charAt(0);
int endIndex = i;
@ -55,76 +69,35 @@ public class CommandContext {
}
}
args = removePortionOfArray(args, i, endIndex, build.toString());
} else if (args[i].charAt(0) == '-' && args[i].matches("^-[a-zA-Z]+$")) {
}
}
// Then flags
for (int i = 1; i < args.length; ++i) {
if (args[i].charAt(0) == '-' && args[i].matches("^-[a-zA-Z]+$")) {
for (int k = 1; k < args[i].length(); ++k) {
booleanFlags.add(args[i].charAt(k));
if (valueFlags != null && valueFlags.contains(args[i].charAt(k))) {
int index = i + 1;
if (args.length - 1 < index) {
throw new CommandException("Value flag '" + args[i].charAt(k) + "' specified without value");
}
if (this.valueFlags.containsKey(args[i].charAt(k))) {
throw new CommandException("Value flag '" + args[i].charAt(k) + "' already given");
}
this.valueFlags.put(args[i].charAt(k), args[index]);
args = removePortionOfArray(args, index, index, null);
} else {
booleanFlags.add(args[i].charAt(k));
}
}
args = removePortionOfArray(args, i, i, null);
} else if (args[i].matches("^--$")) {
args = removePortionOfArray(args, i, i, null);
break;
}
}
this.args = args;
}
public CommandContext(String args, Set<Character> isValueFlag) throws CommandException {
this(args.split(" "), isValueFlag);
}
/**
* @param args An array with arguments empty strings will be ignored by most things
* @param isValueFlag A set containing all value flags. Pass null to disable flag parsing altogether.
* @throws CommandException This is thrown if a value flag was passed without a value.
*/
public CommandContext(String[] args, Set<Character> isValueFlag) throws CommandException {
if (isValueFlag == null) {
this.args = args;
return;
}
int nextArg = 1;
while (nextArg < args.length) {
// Fetch argument
String arg = args[nextArg++];
// Empty argument? (multiple consecutive spaces)
if (arg.isEmpty())
continue;
// No more flags?
if (arg.charAt(0) != '-' || arg.length() == 1 || !arg.matches("^-[a-zA-Z]+$")) {
--nextArg;
break;
}
// Handle flag parsing terminator --
if (arg.equals("--"))
break;
// Go through the flags
for (int i = 1; i < arg.length(); ++i) {
char flagName = arg.charAt(i);
if (isValueFlag.contains(flagName)) {
// Skip empty arguments...
while (nextArg < args.length && args[nextArg].isEmpty())
++nextArg;
if (nextArg >= args.length)
throw new CommandException("No value specified for the '-"+flagName+"' flag.");
// If it is a value flag, read another argument and add it
valueFlags.put(flagName, args[nextArg++]);
}
else {
booleanFlags.add(flagName);
}
}
}
this.args = Arrays.copyOfRange(args, nextArg-1, args.length);
this.args[0] = args[0];
}
public String getCommand() {
return args[0];
}
@ -233,13 +206,4 @@ public class CommandContext {
public int argsLength() {
return args.length - 1;
}
public static String[] removePortionOfArray(String[] array, int from, int to, String replace) {
String[] newArray = new String[from + array.length - to - (replace == null ? 1 : 0)];
System.arraycopy(array, 0, newArray, 0, from);
if (replace != null) newArray[from] = replace;
System.arraycopy(array, to + (replace == null ? 0 : 1), newArray, from + (replace == null ? 0 : 1),
array.length - to - 1);
return newArray;
}
}

View File

@ -27,6 +27,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import com.sk89q.util.StringUtil;
import static com.sk89q.util.ArrayUtil.removePortionOfArray;
/**
* <p>Manager for handling commands. This allows you to easily process commands,
@ -228,8 +229,17 @@ public abstract class CommandsManager<T> {
for (int i = 0; i <= level; ++i) {
command.append(args[i] + " ");
}
command.append(cmd.flags().length() > 0 ? "[-" + cmd.flags() + "] " : "");
if (cmd.flags().length() > 0) {
char[] flags = cmd.flags().toCharArray();
for (int i = 0; i < flags.length; ++i) {
if (flags.length > i + 1) {
if (flags[i + 1] == ':') {
flags = removePortionOfArray(flags, i, i + 1, null);
}
}
}
if (flags.length > 0) command.append("[-" + String.valueOf(flags) + "] ");
}
command.append(cmd.usage());
return command.toString();
@ -378,14 +388,19 @@ public abstract class CommandsManager<T> {
String[] newArgs = new String[args.length - level];
System.arraycopy(args, level, newArgs, 0, args.length - level);
final String valueFlags = cmd.valueFlags();
final Set<Character> isValueFlag = new HashSet<Character>();
final Set<Character> valueFlags = new HashSet<Character>();
for (int i = 0; i < valueFlags.length(); ++i) {
isValueFlag.add(valueFlags.charAt(i));
char[] flags = cmd.flags().toCharArray();
for (int i = 0; i < flags.length; ++i) {
if (flags.length > i + 1) {
if (flags[i + 1] == ':') {
valueFlags.add(flags[i]);
flags = removePortionOfArray(flags, i + 1, i + 1, null);
}
}
}
CommandContext context = new CommandContext(newArgs, isValueFlag);
CommandContext context = new CommandContext(newArgs, valueFlags);
if (context.argsLength() < cmd.min())
throw new CommandUsageException("Too few arguments.", getUsage(args, level, cmd));
@ -393,8 +408,9 @@ public abstract class CommandsManager<T> {
if (cmd.max() != -1 && context.argsLength() > cmd.max())
throw new CommandUsageException("Too many arguments.", getUsage(args, level, cmd));
String flagStr = String.valueOf(flags);
for (char flag : context.getFlags()) {
if (cmd.flags().indexOf(String.valueOf(flag)) == -1)
if (flagStr.indexOf(flag) == -1)
throw new CommandUsageException("Unknown flag: " + flag, getUsage(args, level, cmd));
}

View File

@ -0,0 +1,22 @@
package com.sk89q.util;
public class ArrayUtil {
public static String[] removePortionOfArray(String[] array, int from, int to, String replace) {
String[] newArray = new String[from + array.length - to - (replace == null ? 1 : 0)];
System.arraycopy(array, 0, newArray, 0, from);
if (replace != null) newArray[from] = replace;
System.arraycopy(array, to + 1, newArray, from + (replace == null ? 0 : 1),
array.length - to - 1);
return newArray;
}
public static char[] removePortionOfArray(char[] array, int from, int to, Character replace) {
char[] newArray = new char[from + array.length - to - (replace == null ? 1 : 0)];
System.arraycopy(array, 0, newArray, 0, from);
if (replace != null) newArray[from] = replace;
System.arraycopy(array, to + 1, newArray, from + (replace == null ? 0 : 1),
array.length - to - 1);
return newArray;
}
}