diff --git a/src/main/java/com/mojang/brigadier/StringReader.java b/src/main/java/com/mojang/brigadier/StringReader.java index c1465df..c78de8e 100644 --- a/src/main/java/com/mojang/brigadier/StringReader.java +++ b/src/main/java/com/mojang/brigadier/StringReader.java @@ -174,9 +174,21 @@ public class StringReader implements ImmutableStringReader { || c == '.' || c == '+'; } + public static boolean isAllowedInUnquotedStringGreedy(final char c) { + return c != ' ' && c != SYNTAX_DOUBLE_QUOTE && c != SYNTAX_SINGLE_QUOTE && c != SYNTAX_ESCAPE; + } + public String readUnquotedString() { + return readUnquotedString(true); + } + + public String readUnquotedStringGreedy() { + return readUnquotedString(false); + } + + private String readUnquotedString(boolean asciiOnly) { final int start = cursor; - while (canRead() && isAllowedInUnquotedString(peek())) { + while (canRead() && (asciiOnly ? isAllowedInUnquotedString(peek()) : isAllowedInUnquotedStringGreedy(peek()))) { skip(); } return string.substring(start, cursor); @@ -220,6 +232,14 @@ public class StringReader implements ImmutableStringReader { } public String readString() throws CommandSyntaxException { + return readString(true); + } + + public String readStringGreedy() throws CommandSyntaxException { + return readString(false); + } + + private String readString(boolean asciiOnly) throws CommandSyntaxException { if (!canRead()) { return ""; } @@ -228,7 +248,7 @@ public class StringReader implements ImmutableStringReader { skip(); return readStringUntil(next); } - return readUnquotedString(); + return readUnquotedString(asciiOnly); } public boolean readBoolean() throws CommandSyntaxException { diff --git a/src/main/java/com/mojang/brigadier/arguments/StringArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/StringArgumentType.java index 0e36f34..c61b82a 100644 --- a/src/main/java/com/mojang/brigadier/arguments/StringArgumentType.java +++ b/src/main/java/com/mojang/brigadier/arguments/StringArgumentType.java @@ -11,22 +11,32 @@ import java.util.Arrays; import java.util.Collection; public class StringArgumentType implements ArgumentType { + private final boolean greedyCharset; private final StringType type; - private StringArgumentType(final StringType type) { + private StringArgumentType(final StringType type, final boolean greedyCharset) { this.type = type; + this.greedyCharset = greedyCharset; } public static StringArgumentType word() { - return new StringArgumentType(StringType.SINGLE_WORD); + return word(false); + } + + public static StringArgumentType word(boolean greedyCharset) { + return new StringArgumentType(StringType.SINGLE_WORD, greedyCharset); } public static StringArgumentType string() { - return new StringArgumentType(StringType.QUOTABLE_PHRASE); + return string(false); + } + + public static StringArgumentType string(boolean greedyCharset) { + return new StringArgumentType(StringType.QUOTABLE_PHRASE, greedyCharset); } public static StringArgumentType greedyString() { - return new StringArgumentType(StringType.GREEDY_PHRASE); + return new StringArgumentType(StringType.GREEDY_PHRASE, false); } public static String getString(final CommandContext context, final String name) { @@ -37,6 +47,10 @@ public class StringArgumentType implements ArgumentType { return type; } + public boolean hasGreedyCharset() { + return greedyCharset; + } + @Override public String parse(final StringReader reader) throws CommandSyntaxException { if (type == StringType.GREEDY_PHRASE) { @@ -44,9 +58,17 @@ public class StringArgumentType implements ArgumentType { reader.setCursor(reader.getTotalLength()); return text; } else if (type == StringType.SINGLE_WORD) { - return reader.readUnquotedString(); + if (this.greedyCharset) { + return reader.readUnquotedStringGreedy(); + } else { + return reader.readUnquotedString(); + } } else { - return reader.readString(); + if (this.greedyCharset) { + return reader.readStringGreedy(); + } else { + return reader.readString(); + } } } diff --git a/src/test/java/com/mojang/brigadier/StringReaderTest.java b/src/test/java/com/mojang/brigadier/StringReaderTest.java index 23e2c14..1ffeb66 100644 --- a/src/test/java/com/mojang/brigadier/StringReaderTest.java +++ b/src/test/java/com/mojang/brigadier/StringReaderTest.java @@ -132,6 +132,30 @@ public class StringReaderTest { assertThat(reader.getRemaining(), equalTo(" world")); } + @Test + public void readUnquotedString_strictCharset() throws Exception { + final StringReader reader = new StringReader("1+1=2 2+2=4"); + assertThat(reader.readString(), equalTo("1+1")); + assertThat(reader.getRead(), equalTo("1+1")); + assertThat(reader.getRemaining(), equalTo("=2 2+2=4")); + + // Should not be able to read further -- as invalid character is present + assertThat(reader.readString(), equalTo("")); + } + + @Test + public void readUnquotedString_strictCharsetQuoted() throws Exception { + final StringReader reader = new StringReader("\"1+1=2\" \"2+2=4\""); + assertThat(reader.readString(), equalTo("1+1=2")); + assertThat(reader.getRead(), equalTo("\"1+1=2\"")); + assertThat(reader.getRemaining(), equalTo(" \"2+2=4\"")); + + reader.skipWhitespace(); + + assertThat(reader.readString(), equalTo("2+2=4")); + assertThat(reader.getRead(), equalTo("\"1+1=2\" \"2+2=4\"")); + } + @Test public void readUnquotedString_empty() throws Exception { final StringReader reader = new StringReader(""); @@ -148,6 +172,40 @@ public class StringReaderTest { assertThat(reader.getRemaining(), equalTo(" hello world")); } + @Test + public void readUnquotedStringGreedy() throws Exception { + final StringReader reader = new StringReader("hello world"); + assertThat(reader.readStringGreedy(), equalTo("hello")); + assertThat(reader.getRead(), equalTo("hello")); + assertThat(reader.getRemaining(), equalTo(" world")); + } + + @Test + public void readUnquotedStringGreedy_strictCharset() throws Exception { + final StringReader reader = new StringReader("1+1=2 2+2=4"); + assertThat(reader.readStringGreedy(), equalTo("1+1=2")); + assertThat(reader.getRead(), equalTo("1+1=2")); + assertThat(reader.getRemaining(), equalTo(" 2+2=4")); + reader.skipWhitespace(); + assertThat(reader.readStringGreedy(), equalTo("2+2=4")); + } + + @Test + public void readUnquotedStringGreedy_empty() throws Exception { + final StringReader reader = new StringReader(""); + assertThat(reader.readUnquotedStringGreedy(), equalTo("")); + assertThat(reader.getRead(), equalTo("")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readUnquotedStringGreedy_empty_withRemaining() throws Exception { + final StringReader reader = new StringReader(" hello world"); + assertThat(reader.readUnquotedStringGreedy(), equalTo("")); + assertThat(reader.getRead(), equalTo("")); + assertThat(reader.getRemaining(), equalTo(" hello world")); + } + @Test public void readQuotedString() throws Exception { final StringReader reader = new StringReader("\"hello world\""); diff --git a/src/test/java/com/mojang/brigadier/arguments/StringArgumentTypeTest.java b/src/test/java/com/mojang/brigadier/arguments/StringArgumentTypeTest.java index 68e52e1..5673695 100644 --- a/src/test/java/com/mojang/brigadier/arguments/StringArgumentTypeTest.java +++ b/src/test/java/com/mojang/brigadier/arguments/StringArgumentTypeTest.java @@ -27,6 +27,22 @@ public class StringArgumentTypeTest { @Mock private CommandContextBuilder context; + @Test + public void testParseWord_greedyCharset() throws Exception { + final StringReader reader = mock(StringReader.class); + when(reader.readUnquotedStringGreedy()).thenReturn("1+1=2"); + assertThat(word(true).parse(reader), equalTo("1+1=2")); + verify(reader).readUnquotedStringGreedy(); + } + + @Test + public void testParseString_greedyCharset() throws Exception { + final StringReader reader = mock(StringReader.class); + when(reader.readStringGreedy()).thenReturn("1+1=2 2+2=4"); + assertThat(string(true).parse(reader), equalTo("1+1=2 2+2=4")); + verify(reader).readStringGreedy(); + } + @Test public void testParseWord() throws Exception { final StringReader reader = mock(StringReader.class);