CommandReader with tests

This commit is contained in:
Noel Németh 2022-07-07 21:35:32 +02:00
parent 079439896f
commit 899a8e02d8
2 changed files with 145 additions and 19 deletions

View File

@ -1,15 +1,17 @@
package net.minestom.server.command;
import org.jetbrains.annotations.VisibleForTesting;
import java.nio.BufferUnderflowException;
import java.util.stream.IntStream;
//TODO IMPLEMENT METHODS
public final class CommandReader {
static final char SPACE = ' ';
static final char QUOTE = '"';
static final char ESCAPE = '\\';
private final CharSequence input;
private int cursor = 0;
private int pendingData = 0;
public CommandReader(CharSequence input) {
this.input = input;
@ -20,19 +22,22 @@ public final class CommandReader {
}
public String getWord() {
final int i = nextIndexOf(SPACE);
return get(i == -1 ? input.length() : i);
final int i = nextIndexOf(SPACE, 0);
final String s = get(i == -1 ? input.length() : i);
pendingData = s.length()+1;
return s;
}
public String getQuotedString() {
if (getNextChar() != QUOTE) throw new RuntimeException("Tried to read an unquoted string as quoted.");
int end;
int end = cursor;
do {
if (!hasRemaining()) throw new RuntimeException("Reached end of input before finding a closing quote");
end = nextIndexOf(QUOTE);
end = nextIndexOf(QUOTE, end-cursor+1);
if (end == -1) throw new RuntimeException("Quoted string doesn't have a closing quote.");
} while (getCharAt(end - 1) != ESCAPE);
return get(end);
} while (getCharAt(end - 1) == ESCAPE);
final String s = get(end+1);
pendingData = s.length()+1;
return s.substring(1, s.length()-1).replaceAll("\\\\\"", "\"");
}
public String getRemaining() {
@ -44,32 +49,59 @@ public final class CommandReader {
}
public char getCharAt(int position) {
pendingData = 1;
return input.charAt(position);
}
public void moveCursor(int amount) {
cursor += amount;
}
public void consume() {
cursor += pendingData;
}
public void consume(int amount) {
cursor += amount;
}
public int getClosingIndexOfJsonObject() {
public int getClosingIndexOfJsonObject(int fromOffset) {
int count = 1;
boolean insideString = false;
boolean lastWasEscape = false;
final int start = nextIndexOf('{', fromOffset);
if (start == -1) return -1;
for (int i = start+1; i < input.length(); i++) {
final char current = getCharAt(i);
if (insideString) {
if (current == '"')
if (lastWasEscape)
lastWasEscape = false;
else
insideString = false;
else if (current == '\\')
lastWasEscape = !lastWasEscape;
else
lastWasEscape = false;
} else {
if (current == '{')
count++;
else if (current == '}' && --count == 0)
return i;
else if (current == '"')
insideString = true;
}
}
return -1;
}
private String get(int end) {
@VisibleForTesting
String get(int exclusiveAbsoluteEnd) {
if (!hasRemaining()) throw new BufferUnderflowException();
return input.subSequence(cursor, end).toString();
final String s = input.subSequence(cursor, exclusiveAbsoluteEnd).toString();
pendingData = s.length();
return s;
}
private int nextIndexOf(char c) {
return IntStream.range(cursor, input.length()).filter(x -> input.charAt(x) == c).findFirst().orElse(-1);
@VisibleForTesting
int nextIndexOf(char c, int offset) {
return IntStream.range(cursor+offset, input.length()).filter(x -> input.charAt(x) == c).findFirst().orElse(-1);
}
public int remaining() {

View File

@ -0,0 +1,94 @@
package net.minestom.server.command;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class CommandReaderTest {
@Test
public void searchTest() {
final CommandReader reader = new CommandReader("0123456789ABCDEF0123456789ABCDEF");
assertEquals(3, reader.nextIndexOf('3', 0));
assertEquals(1, reader.nextIndexOf('1', 0));
assertEquals(12, reader.nextIndexOf('C', 0));
assertEquals(19, reader.nextIndexOf('3', 15));
assertEquals(17, reader.nextIndexOf('1', 15));
assertEquals(28, reader.nextIndexOf('C', 15));
}
@Test
public void readRaw() {
final CommandReader reader = new CommandReader("0123456789ABCDEF");
assertEquals("01", reader.get(2));
reader.consume();
assertEquals("23456", reader.get(7));
reader.consume();
assertEquals("7", reader.get(8));
reader.consume();
assertEquals("89ABCD", reader.get(14));
reader.consume();
assertEquals("EF", reader.get(16));
reader.consume();
assertFalse(reader.hasRemaining());
}
@Test
public void readWords() {
final CommandReader reader = new CommandReader("test 15 foo bar a");
assertEquals("test", reader.getWord());
reader.consume();
assertEquals("15", reader.getWord());
reader.consume();
assertEquals("foo", reader.getWord());
reader.consume();
assertEquals("bar", reader.getWord());
reader.consume();
assertEquals("a", reader.getWord());
reader.consume();
assertFalse(reader.hasRemaining());
}
@Test
public void readQuotedStrings() {
final CommandReader reader = new CommandReader("\"test 15\" \"foo \\\\\"bar\" \"a\"");
assertEquals("test 15", reader.getQuotedString());
reader.consume();
assertEquals("foo \\\"bar", reader.getQuotedString());
reader.consume();
assertEquals("a", reader.getQuotedString());
reader.consume();
assertFalse(reader.hasRemaining());
}
@Test
public void readWordsAndQuotedStringsMixed() {
final CommandReader reader = new CommandReader("\"te\\\"st\" 15 foo \"bar\" \"\\\"a\\\"\"");
assertEquals("te\"st", reader.getQuotedString());
reader.consume();
assertEquals("15", reader.getWord());
reader.consume();
assertEquals("foo", reader.getWord());
reader.consume();
assertEquals("bar", reader.getQuotedString());
reader.consume();
assertEquals("\"a\"", reader.getQuotedString());
reader.consume();
assertFalse(reader.hasRemaining());
}
@Test
public void getJsonObjectClosingIndex() {
assertEquals(-1, jsonTest("no json here"));
assertEquals(-1, jsonTest("no valid {json here"));
assertEquals(1, jsonTest("{}"));
assertEquals(8, jsonTest("0123456{}9A"));
assertEquals(11, jsonTest("012{\"foo\":5}+"));
assertEquals(76, jsonTest("{\"foo\":1,\"bar\":\"S9 M M\",\"baz\":false,\"array\":[],\"obj\":{\"foo\":\"fq h{q}{{{r}\"}}"));
assertEquals(82, jsonTest("012345{\"foo\":1,\"bar\":\"S9 M M\",\"baz\":false,\"array\":[],\"obj\":{\"foo\":\"fq h{q}{{{r}\"}}789"));
}
private int jsonTest(String input) {
return new CommandReader(input).getClosingIndexOfJsonObject(0);
}
}