diff --git a/src/main/java/com/mojang/brigadier/StringReader.java b/src/main/java/com/mojang/brigadier/StringReader.java index 2e23185b..c1465df6 100644 --- a/src/main/java/com/mojang/brigadier/StringReader.java +++ b/src/main/java/com/mojang/brigadier/StringReader.java @@ -7,7 +7,8 @@ public class StringReader implements ImmutableStringReader { private static final char SYNTAX_ESCAPE = '\\'; - private static final char SYNTAX_QUOTE = '"'; + private static final char SYNTAX_DOUBLE_QUOTE = '"'; + private static final char SYNTAX_SINGLE_QUOTE = '\''; private final String string; private int cursor; @@ -87,6 +88,10 @@ public static boolean isAllowedNumber(final char c) { return c >= '0' && c <= '9' || c == '.' || c == '-'; } + public static boolean isQuotedStringStart(char c) { + return c == SYNTAX_DOUBLE_QUOTE || c == SYNTAX_SINGLE_QUOTE; + } + public void skipWhitespace() { while (canRead() && Character.isWhitespace(peek())) { skip(); @@ -180,16 +185,22 @@ public String readUnquotedString() { public String readQuotedString() throws CommandSyntaxException { if (!canRead()) { return ""; - } else if (peek() != SYNTAX_QUOTE) { + } + final char next = peek(); + if (!isQuotedStringStart(next)) { throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedStartOfQuote().createWithContext(this); } skip(); + return readStringUntil(next); + } + + public String readStringUntil(char terminator) throws CommandSyntaxException { final StringBuilder result = new StringBuilder(); boolean escaped = false; while (canRead()) { final char c = read(); if (escaped) { - if (c == SYNTAX_QUOTE || c == SYNTAX_ESCAPE) { + if (c == terminator || c == SYNTAX_ESCAPE) { result.append(c); escaped = false; } else { @@ -198,7 +209,7 @@ public String readQuotedString() throws CommandSyntaxException { } } else if (c == SYNTAX_ESCAPE) { escaped = true; - } else if (c == SYNTAX_QUOTE) { + } else if (c == terminator) { return result.toString(); } else { result.append(c); @@ -209,11 +220,15 @@ public String readQuotedString() throws CommandSyntaxException { } public String readString() throws CommandSyntaxException { - if (canRead() && peek() == SYNTAX_QUOTE) { - return readQuotedString(); - } else { - return readUnquotedString(); + if (!canRead()) { + return ""; + } + final char next = peek(); + if (isQuotedStringStart(next)) { + skip(); + return readStringUntil(next); } + return readUnquotedString(); } public boolean readBoolean() throws CommandSyntaxException { diff --git a/src/test/java/com/mojang/brigadier/StringReaderTest.java b/src/test/java/com/mojang/brigadier/StringReaderTest.java index 0bcb7527..23e2c14d 100644 --- a/src/test/java/com/mojang/brigadier/StringReaderTest.java +++ b/src/test/java/com/mojang/brigadier/StringReaderTest.java @@ -156,6 +156,30 @@ public void readQuotedString() throws Exception { assertThat(reader.getRemaining(), equalTo("")); } + @Test + public void readSingleQuotedString() throws Exception { + final StringReader reader = new StringReader("'hello world'"); + assertThat(reader.readQuotedString(), equalTo("hello world")); + assertThat(reader.getRead(), equalTo("'hello world'")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readMixedQuotedString_doubleInsideSingle() throws Exception { + final StringReader reader = new StringReader("'hello \"world\"'"); + assertThat(reader.readQuotedString(), equalTo("hello \"world\"")); + assertThat(reader.getRead(), equalTo("'hello \"world\"'")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readMixedQuotedString_singleInsideDouble() throws Exception { + final StringReader reader = new StringReader("\"hello 'world'\""); + assertThat(reader.readQuotedString(), equalTo("hello 'world'")); + assertThat(reader.getRead(), equalTo("\"hello 'world'\"")); + assertThat(reader.getRemaining(), equalTo("")); + } + @Test public void readQuotedString_empty() throws Exception { final StringReader reader = new StringReader(""); @@ -242,6 +266,40 @@ public void readQuotedString_invalidEscape() throws Exception { } } + @Test + public void readQuotedString_invalidQuoteEscape() throws Exception { + try { + new StringReader("'hello\\\"\'world").readQuotedString(); + } catch (final CommandSyntaxException ex) { + assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidEscape())); + assertThat(ex.getCursor(), is(7)); + } + } + + @Test + public void readString_noQuotes() throws Exception { + final StringReader reader = new StringReader("hello world"); + assertThat(reader.readString(), equalTo("hello")); + assertThat(reader.getRead(), equalTo("hello")); + assertThat(reader.getRemaining(), equalTo(" world")); + } + + @Test + public void readString_singleQuotes() throws Exception { + final StringReader reader = new StringReader("'hello world'"); + assertThat(reader.readString(), equalTo("hello world")); + assertThat(reader.getRead(), equalTo("'hello world'")); + assertThat(reader.getRemaining(), equalTo("")); + } + + @Test + public void readString_doubleQuotes() throws Exception { + final StringReader reader = new StringReader("\"hello world\""); + assertThat(reader.readString(), equalTo("hello world")); + assertThat(reader.getRead(), equalTo("\"hello world\"")); + assertThat(reader.getRemaining(), equalTo("")); + } + @Test public void readInt() throws Exception { final StringReader reader = new StringReader("1234567890");