Added new StringArgumentType

This commit is contained in:
Nathan Adams 2017-06-29 10:31:14 +02:00
parent 60c5770f5e
commit 4337a9645e
3 changed files with 405 additions and 0 deletions

View file

@ -25,6 +25,7 @@ public class CommandDispatcher<S> {
public static final ParameterizedCommandExceptionType ERROR_UNKNOWN_ARGUMENT = new ParameterizedCommandExceptionType("command.unknown.argument", "Incorrect argument for command, couldn't parse: ${argument}", "argument"); public static final ParameterizedCommandExceptionType ERROR_UNKNOWN_ARGUMENT = new ParameterizedCommandExceptionType("command.unknown.argument", "Incorrect argument for command, couldn't parse: ${argument}", "argument");
public static final String ARGUMENT_SEPARATOR = " "; public static final String ARGUMENT_SEPARATOR = " ";
public static final char ARGUMENT_SEPARATOR_CHAR = ' ';
private static final String USAGE_OPTIONAL_OPEN = "["; private static final String USAGE_OPTIONAL_OPEN = "[";
private static final String USAGE_OPTIONAL_CLOSE = "]"; private static final String USAGE_OPTIONAL_CLOSE = "]";
private static final String USAGE_REQUIRED_OPEN = "("; private static final String USAGE_REQUIRED_OPEN = "(";

View file

@ -0,0 +1,134 @@
package com.mojang.brigadier.arguments;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.FixedParsedArgument;
import com.mojang.brigadier.context.ParsedArgument;
import com.mojang.brigadier.exceptions.CommandException;
import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import java.util.Set;
public class StringArgumentType implements CommandArgumentType<String> {
public static final ParameterizedCommandExceptionType ERROR_INVALID_ESCAPE = new ParameterizedCommandExceptionType("argument.string.escape.invalid", "Unknown or invalid escape sequence: ${input}", "input");
public static final SimpleCommandExceptionType ERROR_UNEXPECTED_ESCAPE = new SimpleCommandExceptionType("argument.string.escape.unexpected", "Unexpected escape sequence, please quote the whole argument");
public static final SimpleCommandExceptionType ERROR_UNEXPECTED_START_OF_QUOTE = new SimpleCommandExceptionType("argument.string.quote.unexpected_start", "Unexpected start-of-quote character (\"), please quote the whole argument");
public static final SimpleCommandExceptionType ERROR_UNEXPECTED_END_OF_QUOTE = new SimpleCommandExceptionType("argument.string.quote.unexpected_end", "Unexpected end-of-quote character (\"), it must be at the end or followed by a space (' ') for the next argument");
public static final SimpleCommandExceptionType ERROR_EXPECTED_END_OF_QUOTE = new SimpleCommandExceptionType("argument.string.quote.expected_end", "Expected end-of-quote character (\") but found no more input");
private final StringType type;
private StringArgumentType(StringType type) {
this.type = type;
}
public static StringArgumentType word() {
return new StringArgumentType(StringType.SINGLE_WORLD);
}
public static StringArgumentType string() {
return new StringArgumentType(StringType.QUOTABLE_PHRASE);
}
public static StringArgumentType greedyString() {
return new StringArgumentType(StringType.GREEDY_PHRASE);
}
public static String getString(CommandContext<?> context, String name) {
return context.getArgument(name, String.class);
}
@Override
public ParsedArgument<String> parse(String command) throws CommandException {
if (type == StringType.GREEDY_PHRASE) {
return new FixedParsedArgument<>(command, command);
} else if (type == StringType.SINGLE_WORLD) {
int index = command.indexOf(CommandDispatcher.ARGUMENT_SEPARATOR);
if (index > 0) {
final String word = command.substring(0, index);
return new FixedParsedArgument<>(word, word);
} else {
return new FixedParsedArgument<>(command, command);
}
} else {
StringBuilder result = new StringBuilder();
int i = 0;
boolean escaped = false;
boolean quoted = false;
while (i < command.length()) {
char c = command.charAt(i);
if (escaped) {
if (c == '"' || c == '\\') {
result.append(c);
} else {
throw ERROR_INVALID_ESCAPE.create("\\" + c);
}
escaped = false;
} else if (c == '\\') {
if (quoted) {
escaped = true;
} else {
throw ERROR_UNEXPECTED_ESCAPE.create();
}
} else if (c == '"') {
if (i == 0) {
quoted = true;
} else if (!quoted) {
throw ERROR_UNEXPECTED_START_OF_QUOTE.create();
} else if (i == command.length() - 1 || command.charAt(i + 1) == CommandDispatcher.ARGUMENT_SEPARATOR_CHAR) {
i++;
break;
} else {
throw ERROR_UNEXPECTED_END_OF_QUOTE.create();
}
} else if (!quoted && c == CommandDispatcher.ARGUMENT_SEPARATOR_CHAR) {
break;
} else if (quoted && i == command.length() - 1) {
throw ERROR_EXPECTED_END_OF_QUOTE.create();
} else {
result.append(c);
}
i++;
}
return new FixedParsedArgument<>(command.substring(0, i), result.toString());
}
}
@Override
public void listSuggestions(String command, Set<String> output) {
}
@Override
public String toString() {
return "string()";
}
public static String escapeIfRequired(String input) {
if (input.contains("\\") || input.contains("\"") || input.contains(CommandDispatcher.ARGUMENT_SEPARATOR)) {
return escape(input);
}
return input;
}
private static String escape(String input) {
StringBuilder result = new StringBuilder("\"");
for (int i = 0; i < input.length(); i++) {
final char c = input.charAt(i);
if (c == '\\' || c == '"') {
result.append('\\');
}
result.append(c);
}
result.append("\"");
return result.toString();
}
public enum StringType {
SINGLE_WORLD,
QUOTABLE_PHRASE,
GREEDY_PHRASE,
}
}

View file

@ -0,0 +1,270 @@
package com.mojang.brigadier.arguments;
import com.google.common.collect.Sets;
import com.mojang.brigadier.context.ParsedArgument;
import com.mojang.brigadier.exceptions.CommandException;
import com.sun.xml.internal.ws.api.ComponentEx;
import org.junit.Test;
import java.util.Collections;
import java.util.Set;
import static com.mojang.brigadier.arguments.StringArgumentType.ERROR_EXPECTED_END_OF_QUOTE;
import static com.mojang.brigadier.arguments.StringArgumentType.ERROR_INVALID_ESCAPE;
import static com.mojang.brigadier.arguments.StringArgumentType.ERROR_UNEXPECTED_END_OF_QUOTE;
import static com.mojang.brigadier.arguments.StringArgumentType.ERROR_UNEXPECTED_ESCAPE;
import static com.mojang.brigadier.arguments.StringArgumentType.ERROR_UNEXPECTED_START_OF_QUOTE;
import static com.mojang.brigadier.arguments.StringArgumentType.escapeIfRequired;
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
import static com.mojang.brigadier.arguments.StringArgumentType.string;
import static com.mojang.brigadier.arguments.StringArgumentType.word;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasToString;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
public class StringArgumentTypeTest {
private StringArgumentType type;
@Test
public void testParseWord() throws Exception {
type = word();
ParsedArgument<String> result = type.parse("hello world");
assertThat(result.getRaw(), is("hello"));
assertThat(result.getResult(), is("hello"));
}
@Test
public void testParseWord_empty() throws Exception {
type = word();
ParsedArgument<String> result = type.parse("");
assertThat(result.getRaw(), is(""));
assertThat(result.getResult(), is(""));
}
@Test
public void testParseWord_simple() throws Exception {
type = word();
ParsedArgument<String> result = type.parse("hello");
assertThat(result.getRaw(), is("hello"));
assertThat(result.getResult(), is("hello"));
}
@Test
public void testParseString() throws Exception {
type = string();
ParsedArgument<String> result = type.parse("hello world");
assertThat(result.getRaw(), is("hello"));
assertThat(result.getResult(), is("hello"));
}
@Test
public void testParseGreedyString() throws Exception {
type = greedyString();
ParsedArgument<String> result = type.parse("hello world");
assertThat(result.getRaw(), is("hello world"));
assertThat(result.getResult(), is("hello world"));
}
@Test
public void testParse() throws Exception {
type = string();
ParsedArgument<String> result = type.parse("hello");
assertThat(result.getRaw(), is("hello"));
assertThat(result.getResult(), is("hello"));
}
@Test
public void testParseWordQuoted() throws Exception {
type = word();
ParsedArgument<String> result = type.parse("\"hello \\\" world\"");
assertThat(result.getRaw(), is("\"hello"));
assertThat(result.getResult(), is("\"hello"));
}
@Test
public void testParseQuoted() throws Exception {
type = string();
ParsedArgument<String> result = type.parse("\"hello \\\" world\"");
assertThat(result.getRaw(), is("\"hello \\\" world\""));
assertThat(result.getResult(), is("hello \" world"));
}
@Test
public void testParseQuotedWithRemaining() throws Exception {
type = string();
ParsedArgument<String> result = type.parse("\"hello \\\" world\" with remaining");
assertThat(result.getRaw(), is("\"hello \\\" world\""));
assertThat(result.getResult(), is("hello \" world"));
}
@Test
public void testParseNotQuoted() throws Exception {
type = string();
ParsedArgument<String> result = type.parse("hello world");
assertThat(result.getRaw(), is("hello"));
assertThat(result.getResult(), is("hello"));
}
@Test
public void testParseInvalidQuote_earlyUnquote() throws Exception {
try {
type = string();
type.parse("\"hello \"world");
fail();
} catch (CommandException e) {
assertThat(e.getType(), is(ERROR_UNEXPECTED_END_OF_QUOTE));
assertThat(e.getData(), is(equalTo(Collections.emptyMap())));
}
}
@Test
public void testParseQuote_earlyUnquoteWithRemaining() throws Exception {
type = string();
ParsedArgument<String> result = type.parse("\"hello\" world");
assertThat(result.getRaw(), is("\"hello\""));
assertThat(result.getResult(), is("hello"));
}
@Test
public void testParseInvalidQuote_lateQuote() throws Exception {
try {
type = string();
type.parse("hello\" world\"");
fail();
} catch (CommandException e) {
assertThat(e.getType(), is(ERROR_UNEXPECTED_START_OF_QUOTE));
assertThat(e.getData(), is(equalTo(Collections.emptyMap())));
}
}
@Test
public void testParseQuote_lateQuoteWithRemaining() throws Exception {
type = string();
ParsedArgument<String> result = type.parse("hello \"world\"");
assertThat(result.getRaw(), is("hello"));
assertThat(result.getResult(), is("hello"));
}
@Test
public void testParseInvalidQuote_middleQuote() throws Exception {
try {
type = string();
type.parse("hel\"lo");
fail();
} catch (CommandException e) {
assertThat(e.getType(), is(ERROR_UNEXPECTED_START_OF_QUOTE));
assertThat(e.getData(), is(equalTo(Collections.emptyMap())));
}
}
@Test
public void testParseInvalidQuote_noUnquote() throws Exception {
try {
type = string();
type.parse("\"hello world");
fail();
} catch (CommandException e) {
assertThat(e.getType(), is(ERROR_EXPECTED_END_OF_QUOTE));
assertThat(e.getData(), is(equalTo(Collections.emptyMap())));
}
}
@Test
public void testParseEmpty() throws Exception {
type = string();
ParsedArgument<String> result = type.parse("");
assertThat(result.getRaw(), is(""));
assertThat(result.getResult(), is(""));
}
@Test
public void testParseInvalidEscape_onlyEscape() throws Exception {
try {
type = string();
type.parse("\\");
fail();
} catch (CommandException e) {
assertThat(e.getType(), is(ERROR_UNEXPECTED_ESCAPE));
assertThat(e.getData(), is(equalTo(Collections.emptyMap())));
}
}
@Test
public void testParseInvalidEscape_unknownSequence() throws Exception {
try {
type = string();
type.parse("\"\\n\"");
fail();
} catch (CommandException e) {
assertThat(e.getType(), is(ERROR_INVALID_ESCAPE));
assertThat(e.getData(), is(equalTo(Collections.singletonMap("input", "\\n"))));
}
}
@Test
public void testParseInvalidEscape_notQuoted() throws Exception {
try {
type = string();
type.parse("hel\\\\o");
fail();
} catch (CommandException e) {
assertThat(e.getType(), is(ERROR_UNEXPECTED_ESCAPE));
assertThat(e.getData(), is(equalTo(Collections.emptyMap())));
}
}
@Test
public void testSuggestions() throws Exception {
type = string();
Set<String> set = Sets.newHashSet();
type.listSuggestions("", set);
assertThat(set, is(empty()));
}
@Test
public void testToString() throws Exception {
assertThat(string(), hasToString("string()"));
}
@Test
public void testEscapeIfRequired_notRequired() throws Exception {
assertThat(escapeIfRequired("hello!"), is(equalTo("hello!")));
assertThat(escapeIfRequired(""), is(equalTo("")));
}
@Test
public void testEscapeIfRequired_multipleWords() throws Exception {
assertThat(escapeIfRequired("hello world"), is(equalTo("\"hello world\"")));
}
@Test
public void testEscapeIfRequired_quote() throws Exception {
assertThat(escapeIfRequired("hello \"world\"!"), is(equalTo("\"hello \\\"world\\\"!\"")));
}
@Test
public void testEscapeIfRequired_escapes() throws Exception {
assertThat(escapeIfRequired("\\"), is(equalTo("\"\\\\\"")));
}
@Test
public void testEscapeIfRequired_singleQuote() throws Exception {
assertThat(escapeIfRequired("\""), is(equalTo("\"\\\"\"")));
}
}