diff --git a/src/main/java/com/mojang/brigadier/arguments/FloatArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/FloatArgumentType.java new file mode 100644 index 0000000..de1c675 --- /dev/null +++ b/src/main/java/com/mojang/brigadier/arguments/FloatArgumentType.java @@ -0,0 +1,103 @@ +package com.mojang.brigadier.arguments; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.context.CommandContextBuilder; +import com.mojang.brigadier.exceptions.CommandException; +import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType; + +import java.util.Objects; + +public class FloatArgumentType implements ArgumentType { + public static final ParameterizedCommandExceptionType ERROR_WRONG_SUFFIX = new ParameterizedCommandExceptionType("argument.float.wrongsuffix", "Expected suffix '${suffix}'", "suffix"); + public static final ParameterizedCommandExceptionType ERROR_TOO_SMALL = new ParameterizedCommandExceptionType("argument.float.low", "Float must not be less than ${minimum}, found ${found}", "found", "minimum"); + public static final ParameterizedCommandExceptionType ERROR_TOO_BIG = new ParameterizedCommandExceptionType("argument.float.big", "Float must not be more than ${maximum}, found ${found}", "found", "maximum"); + + private final float minimum; + private final float maximum; + private final String suffix; + + private FloatArgumentType(final float minimum, final float maximum, final String suffix) { + this.minimum = minimum; + this.maximum = maximum; + this.suffix = suffix; + } + + public static FloatArgumentType floatArg() { + return floatArg(-Float.MAX_VALUE); + } + + public static FloatArgumentType floatArg(final float min) { + return floatArg(min, Float.MAX_VALUE); + } + + public static FloatArgumentType floatArg(final float min, final float max) { + return floatArg(min, max, ""); + } + + public static FloatArgumentType floatArg(final float min, final float max, final String suffix) { + return new FloatArgumentType(min, max, suffix); + } + + public static int getInteger(final CommandContext context, final String name) { + return context.getArgument(name, int.class); + } + + @Override + public Float parse(final StringReader reader, final CommandContextBuilder contextBuilder) throws CommandException { + final int start = reader.getCursor(); + final float result = (float) reader.readDouble(); + for (int i = 0; i < suffix.length(); i++) { + if (reader.canRead() && reader.peek() == suffix.charAt(i)) { + reader.skip(); + } else { + reader.setCursor(start); + throw ERROR_WRONG_SUFFIX.createWithContext(reader, suffix); + } + } + if (result < minimum) { + reader.setCursor(start); + throw ERROR_TOO_SMALL.createWithContext(reader, result, minimum); + } + if (result > maximum) { + reader.setCursor(start); + throw ERROR_TOO_BIG.createWithContext(reader, result, maximum); + } + return result; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof FloatArgumentType)) return false; + + final FloatArgumentType that = (FloatArgumentType) o; + return maximum == that.maximum && minimum == that.minimum && Objects.equals(suffix, that.suffix); + } + + @Override + public int hashCode() { + return (int) (31 * minimum + maximum); + } + + @Override + public String toString() { + if (minimum == -Float.MAX_VALUE && maximum == Float.MAX_VALUE) { + return "float()"; + } else if (maximum == Float.MAX_VALUE) { + return "float(" + minimum + ")"; + } else { + return "float(" + minimum + ", " + maximum + ")"; + } + } + + @Override + public String getUsageSuffix() { + return suffix.length() == 0 ? null : suffix; + } + + @Override + public String getUsageText() { + return "float"; + } +} diff --git a/src/test/java/com/mojang/brigadier/arguments/FloatArgumentTypeTest.java b/src/test/java/com/mojang/brigadier/arguments/FloatArgumentTypeTest.java new file mode 100644 index 0000000..302f954 --- /dev/null +++ b/src/test/java/com/mojang/brigadier/arguments/FloatArgumentTypeTest.java @@ -0,0 +1,150 @@ +package com.mojang.brigadier.arguments; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import com.google.common.testing.EqualsTester; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.context.CommandContextBuilder; +import com.mojang.brigadier.exceptions.CommandException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.Set; + +import static com.mojang.brigadier.arguments.FloatArgumentType.floatArg; +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; + +@RunWith(MockitoJUnitRunner.class) +public class FloatArgumentTypeTest { + private FloatArgumentType type; + @Mock + private CommandContextBuilder context; + + @Before + public void setUp() throws Exception { + type = floatArg(-100, 100); + } + + @Test + public void parse_noSuffix() throws Exception { + final StringReader reader = new StringReader("15"); + assertThat(floatArg().parse(reader, context), is(15f)); + assertThat(reader.canRead(), is(false)); + } + + @Test + public void parse_suffix() throws Exception { + final StringReader reader = new StringReader("15L"); + assertThat(floatArg(0, 100, "L").parse(reader, context), is(15f)); + assertThat(reader.canRead(), is(false)); + } + + @Test + public void parse_suffix_incorrect() throws Exception { + final StringReader reader = new StringReader("15W"); + try { + floatArg(0, 100, "L").parse(reader, context); + fail(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(FloatArgumentType.ERROR_WRONG_SUFFIX)); + assertThat(ex.getData(), equalTo(ImmutableMap.of("suffix", "L"))); + assertThat(ex.getCursor(), is(0)); + } + } + + @Test + public void parse_suffix_missing() throws Exception { + final StringReader reader = new StringReader("15"); + try { + floatArg(0, 100, "L").parse(reader, context); + fail(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(FloatArgumentType.ERROR_WRONG_SUFFIX)); + assertThat(ex.getData(), equalTo(ImmutableMap.of("suffix", "L"))); + assertThat(ex.getCursor(), is(0)); + } + } + + @Test + public void parse_tooSmall() throws Exception { + final StringReader reader = new StringReader("-5"); + try { + floatArg(0, 100).parse(reader, context); + fail(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(FloatArgumentType.ERROR_TOO_SMALL)); + assertThat(ex.getData(), equalTo(ImmutableMap.of("found", "-5.0", "minimum", "0.0"))); + assertThat(ex.getCursor(), is(0)); + } + } + + @Test + public void parse_tooBig() throws Exception { + final StringReader reader = new StringReader("5"); + try { + floatArg(-100, 0).parse(reader, context); + fail(); + } catch (final CommandException ex) { + assertThat(ex.getType(), is(FloatArgumentType.ERROR_TOO_BIG)); + assertThat(ex.getData(), equalTo(ImmutableMap.of("found", "5.0", "maximum", "0.0"))); + assertThat(ex.getCursor(), is(0)); + } + } + + @Test + public void testSuggestions() throws Exception { + final Set set = Sets.newHashSet(); + @SuppressWarnings("unchecked") final CommandContextBuilder context = Mockito.mock(CommandContextBuilder.class); + type.listSuggestions("", set, context); + assertThat(set, is(empty())); + } + + @Test + public void testEquals() throws Exception { + new EqualsTester() + .addEqualityGroup(floatArg(), floatArg()) + .addEqualityGroup(floatArg(-100, 100), floatArg(-100, 100)) + .addEqualityGroup(floatArg(-100, 50), floatArg(-100, 50)) + .addEqualityGroup(floatArg(-50, 100), floatArg(-50, 100)) + .addEqualityGroup(floatArg(-50, 100, "foo"), floatArg(-50, 100, "foo")) + .addEqualityGroup(floatArg(-50, 100, "bar"), floatArg(-50, 100, "bar")) + .testEquals(); + } + + @Test + public void testToString() throws Exception { + assertThat(floatArg(), hasToString("float()")); + assertThat(floatArg(-100), hasToString("float(-100.0)")); + assertThat(floatArg(-100, 100), hasToString("float(-100.0, 100.0)")); + assertThat(floatArg(Integer.MIN_VALUE, 100), hasToString("float(-2.14748365E9, 100.0)")); + } + + @Test + public void testUsageSuffix() throws Exception { + assertThat(floatArg().getUsageSuffix(), equalTo(null)); + } + + @Test + public void testUsageSuffix_suffix() throws Exception { + assertThat(floatArg(0, 100, "L").getUsageSuffix(), equalTo("L")); + } + + @Test + public void testUsageText() throws Exception { + assertThat(floatArg().getUsageText(), equalTo("float")); + } + + @Test + public void testUsageText_suffix() throws Exception { + assertThat(floatArg(0, 100, "L").getUsageText(), equalTo("float")); + } +} \ No newline at end of file