From 88714288a7f13221d80a2675caf7c94abde644ee Mon Sep 17 00:00:00 2001 From: Earthcomputer Date: Wed, 26 Sep 2018 15:32:27 +0100 Subject: [PATCH] Add long argument --- .../com/mojang/brigadier/StringReader.java | 17 ++++ .../brigadier/arguments/LongArgumentType.java | 87 +++++++++++++++++++ .../exceptions/BuiltInExceptionProvider.java | 8 ++ .../exceptions/BuiltInExceptions.java | 25 ++++++ .../arguments/LongArgumentTypeTest.java | 81 +++++++++++++++++ 5 files changed, 218 insertions(+) create mode 100644 src/main/java/com/mojang/brigadier/arguments/LongArgumentType.java create mode 100644 src/test/java/com/mojang/brigadier/arguments/LongArgumentTypeTest.java diff --git a/src/main/java/com/mojang/brigadier/StringReader.java b/src/main/java/com/mojang/brigadier/StringReader.java index 11e5cc1..2e23185 100644 --- a/src/main/java/com/mojang/brigadier/StringReader.java +++ b/src/main/java/com/mojang/brigadier/StringReader.java @@ -110,6 +110,23 @@ public class StringReader implements ImmutableStringReader { } } + public long readLong() throws CommandSyntaxException { + final int start = cursor; + while (canRead() && isAllowedNumber(peek())) { + skip(); + } + final String number = string.substring(start, cursor); + if (number.isEmpty()) { + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedLong().createWithContext(this); + } + try { + return Long.parseLong(number); + } catch (final NumberFormatException ex) { + cursor = start; + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidLong().createWithContext(this, number); + } + } + public double readDouble() throws CommandSyntaxException { final int start = cursor; while (canRead() && isAllowedNumber(peek())) { diff --git a/src/main/java/com/mojang/brigadier/arguments/LongArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/LongArgumentType.java new file mode 100644 index 0000000..c04b9f3 --- /dev/null +++ b/src/main/java/com/mojang/brigadier/arguments/LongArgumentType.java @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +package com.mojang.brigadier.arguments; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import java.util.Arrays; +import java.util.Collection; + +public class LongArgumentType implements ArgumentType { + private static final Collection EXAMPLES = Arrays.asList("0", "123", "-123"); + + private final long minimum; + private final long maximum; + + private LongArgumentType(final long minimum, final long maximum) { + this.minimum = minimum; + this.maximum = maximum; + } + + public static LongArgumentType longArg() { + return longArg(Long.MIN_VALUE); + } + + public static LongArgumentType longArg(final long min) { + return longArg(min, Long.MAX_VALUE); + } + + public static LongArgumentType longArg(final long min, final long max) { + return new LongArgumentType(min, max); + } + + public static long getLong(final CommandContext context, final String name) { + return context.getArgument(name, long.class); + } + + public long getMinimum() { + return minimum; + } + + public long getMaximum() { + return maximum; + } + + @Override + public Long parse(final StringReader reader) throws CommandSyntaxException { + final int start = reader.getCursor(); + final long result = reader.readLong(); + if (result < minimum) { + reader.setCursor(start); + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.longTooLow().createWithContext(reader, result, minimum); + } + if (result > maximum) { + reader.setCursor(start); + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.longTooHigh().createWithContext(reader, result, maximum); + } + return result; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof LongArgumentType)) return false; + + final LongArgumentType that = (LongArgumentType) o; + return maximum == that.maximum && minimum == that.minimum; + } + + @Override + public int hashCode() { + return 31 * Long.hashCode(minimum) + Long.hashCode(maximum); + } + + @Override + public String toString() { + if (minimum == Long.MIN_VALUE && maximum == Long.MAX_VALUE) { + return "longArg()"; + } else if (maximum == Long.MAX_VALUE) { + return "longArg(" + minimum + ")"; + } else { + return "longArg(" + minimum + ", " + maximum + ")"; + } + } +} diff --git a/src/main/java/com/mojang/brigadier/exceptions/BuiltInExceptionProvider.java b/src/main/java/com/mojang/brigadier/exceptions/BuiltInExceptionProvider.java index fb844f9..7697fee 100644 --- a/src/main/java/com/mojang/brigadier/exceptions/BuiltInExceptionProvider.java +++ b/src/main/java/com/mojang/brigadier/exceptions/BuiltInExceptionProvider.java @@ -16,6 +16,10 @@ public interface BuiltInExceptionProvider { Dynamic2CommandExceptionType integerTooHigh(); + Dynamic2CommandExceptionType longTooLow(); + + Dynamic2CommandExceptionType longTooHigh(); + DynamicCommandExceptionType literalIncorrect(); SimpleCommandExceptionType readerExpectedStartOfQuote(); @@ -30,6 +34,10 @@ public interface BuiltInExceptionProvider { SimpleCommandExceptionType readerExpectedInt(); + DynamicCommandExceptionType readerInvalidLong(); + + SimpleCommandExceptionType readerExpectedLong(); + DynamicCommandExceptionType readerInvalidDouble(); SimpleCommandExceptionType readerExpectedDouble(); diff --git a/src/main/java/com/mojang/brigadier/exceptions/BuiltInExceptions.java b/src/main/java/com/mojang/brigadier/exceptions/BuiltInExceptions.java index 1379f59..0db6efe 100644 --- a/src/main/java/com/mojang/brigadier/exceptions/BuiltInExceptions.java +++ b/src/main/java/com/mojang/brigadier/exceptions/BuiltInExceptions.java @@ -15,6 +15,9 @@ public class BuiltInExceptions implements BuiltInExceptionProvider { private static final Dynamic2CommandExceptionType INTEGER_TOO_SMALL = new Dynamic2CommandExceptionType((found, min) -> new LiteralMessage("Integer must not be less than " + min + ", found " + found)); private static final Dynamic2CommandExceptionType INTEGER_TOO_BIG = new Dynamic2CommandExceptionType((found, max) -> new LiteralMessage("Integer must not be more than " + max + ", found " + found)); + private static final Dynamic2CommandExceptionType LONG_TOO_SMALL = new Dynamic2CommandExceptionType((found, min) -> new LiteralMessage("Long must not be less than " + min + ", found " + found)); + private static final Dynamic2CommandExceptionType LONG_TOO_BIG = new Dynamic2CommandExceptionType((found, max) -> new LiteralMessage("Long must not be more than " + max + ", found " + found)); + private static final DynamicCommandExceptionType LITERAL_INCORRECT = new DynamicCommandExceptionType(expected -> new LiteralMessage("Expected literal " + expected)); private static final SimpleCommandExceptionType READER_EXPECTED_START_OF_QUOTE = new SimpleCommandExceptionType(new LiteralMessage("Expected quote to start a string")); @@ -23,6 +26,8 @@ public class BuiltInExceptions implements BuiltInExceptionProvider { private static final DynamicCommandExceptionType READER_INVALID_BOOL = new DynamicCommandExceptionType(value -> new LiteralMessage("Invalid bool, expected true or false but found '" + value + "'")); private static final DynamicCommandExceptionType READER_INVALID_INT = new DynamicCommandExceptionType(value -> new LiteralMessage("Invalid integer '" + value + "'")); private static final SimpleCommandExceptionType READER_EXPECTED_INT = new SimpleCommandExceptionType(new LiteralMessage("Expected integer")); + private static final DynamicCommandExceptionType READER_INVALID_LONG = new DynamicCommandExceptionType(value -> new LiteralMessage("Invalid long '" + value + "'")); + private static final SimpleCommandExceptionType READER_EXPECTED_LONG = new SimpleCommandExceptionType((new LiteralMessage("Expected long"))); private static final DynamicCommandExceptionType READER_INVALID_DOUBLE = new DynamicCommandExceptionType(value -> new LiteralMessage("Invalid double '" + value + "'")); private static final SimpleCommandExceptionType READER_EXPECTED_DOUBLE = new SimpleCommandExceptionType(new LiteralMessage("Expected double")); private static final DynamicCommandExceptionType READER_INVALID_FLOAT = new DynamicCommandExceptionType(value -> new LiteralMessage("Invalid float '" + value + "'")); @@ -65,6 +70,16 @@ public class BuiltInExceptions implements BuiltInExceptionProvider { return INTEGER_TOO_BIG; } + @Override + public Dynamic2CommandExceptionType longTooLow() { + return LONG_TOO_SMALL; + } + + @Override + public Dynamic2CommandExceptionType longTooHigh() { + return LONG_TOO_BIG; + } + @Override public DynamicCommandExceptionType literalIncorrect() { return LITERAL_INCORRECT; @@ -100,6 +115,16 @@ public class BuiltInExceptions implements BuiltInExceptionProvider { return READER_EXPECTED_INT; } + @Override + public DynamicCommandExceptionType readerInvalidLong() { + return READER_INVALID_LONG; + } + + @Override + public SimpleCommandExceptionType readerExpectedLong() { + return READER_EXPECTED_LONG; + } + @Override public DynamicCommandExceptionType readerInvalidDouble() { return READER_INVALID_DOUBLE; diff --git a/src/test/java/com/mojang/brigadier/arguments/LongArgumentTypeTest.java b/src/test/java/com/mojang/brigadier/arguments/LongArgumentTypeTest.java new file mode 100644 index 0000000..4d441e5 --- /dev/null +++ b/src/test/java/com/mojang/brigadier/arguments/LongArgumentTypeTest.java @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +package com.mojang.brigadier.arguments; + +import com.google.common.testing.EqualsTester; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.context.CommandContextBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static com.mojang.brigadier.arguments.LongArgumentType.longArg; +import static org.hamcrest.Matchers.hasToString; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +@RunWith(MockitoJUnitRunner.class) +public class LongArgumentTypeTest { + private LongArgumentType type; + @Mock + private CommandContextBuilder context; + + @Before + public void setUp() throws Exception { + type = longArg(-100, 1_000_000_000_000L); + } + + @Test + public void parse() throws Exception { + final StringReader reader = new StringReader("15"); + assertThat(longArg().parse(reader), is(15L)); + assertThat(reader.canRead(), is(false)); + } + + @Test + public void parse_tooSmall() throws Exception { + final StringReader reader = new StringReader("-5"); + try { + longArg(0, 100).parse(reader); + fail(); + } catch (final CommandSyntaxException ex) { + assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.longTooLow())); + assertThat(ex.getCursor(), is(0)); + } + } + + @Test + public void parse_tooBig() throws Exception { + final StringReader reader = new StringReader("5"); + try { + longArg(-100, 0).parse(reader); + fail(); + } catch (final CommandSyntaxException ex) { + assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.longTooHigh())); + assertThat(ex.getCursor(), is(0)); + } + } + + @Test + public void testEquals() throws Exception { + new EqualsTester() + .addEqualityGroup(longArg(), longArg()) + .addEqualityGroup(longArg(-100, 100), longArg(-100, 100)) + .addEqualityGroup(longArg(-100, 50), longArg(-100, 50)) + .addEqualityGroup(longArg(-50, 100), longArg(-50, 100)) + .testEquals(); + } + + @Test + public void testToString() throws Exception { + assertThat(longArg(), hasToString("longArg()")); + assertThat(longArg(-100), hasToString("longArg(-100)")); + assertThat(longArg(-100, 100), hasToString("longArg(-100, 100)")); + assertThat(longArg(Long.MIN_VALUE, 100), hasToString("longArg(-9223372036854775808, 100)")); + } +}