From 92fbb8defc91c611f70602bef6ad38b61da3f1a1 Mon Sep 17 00:00:00 2001 From: Matthias Wijnsma Date: Fri, 26 May 2023 21:15:37 +0200 Subject: [PATCH 1/3] Allow numbers to be specified in any base and add number base argument type --- .../serializers/IntegerSerializer.java | 119 +++++++++++++++++- .../serializers/NumberBaseSerializer.java | 77 ++++++++++++ .../features/commands/BaseConvertFeature.java | 26 ++-- .../commands/BinaryBlockReadFeature.java | 3 +- .../features/commands/ColorCodeFeature.java | 1 - .../features/commands/QuickTpFeature.java | 2 - .../redstonetools/utils/NumberBase.java | 38 ++++++ 7 files changed, 237 insertions(+), 29 deletions(-) create mode 100644 src/main/java/tools/redstone/redstonetools/features/arguments/serializers/NumberBaseSerializer.java create mode 100644 src/main/java/tools/redstone/redstonetools/utils/NumberBase.java diff --git a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java index 0783f48c..fd534988 100644 --- a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java +++ b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java @@ -1,26 +1,124 @@ package tools.redstone.redstonetools.features.arguments.serializers; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.text.Text; +import tools.redstone.redstonetools.utils.NumberBase; -public class IntegerSerializer extends StringBrigadierSerializer { +import java.math.BigInteger; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; - private static final IntegerSerializer INSTANCE = new IntegerSerializer(IntegerArgumentType.integer()); +public class IntegerSerializer extends TypeSerializer { + + private static final IntegerSerializer INSTANCE = new IntegerSerializer(); + + private final int min; + private final int max; public static IntegerSerializer integer() { return INSTANCE; } public static IntegerSerializer integer(int min) { - return new IntegerSerializer(IntegerArgumentType.integer(min)); + return new IntegerSerializer(min); } public static IntegerSerializer integer(int min, int max) { - return new IntegerSerializer(IntegerArgumentType.integer(min, max)); + return new IntegerSerializer(min, max); + } + + private IntegerSerializer(int min, int max) { + super(Integer.class); + + this.min = min; + this.max = max; + } + + private IntegerSerializer(int min) { + this(min, Integer.MAX_VALUE); + } + + private IntegerSerializer() { + this(Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + @Override + public Integer deserialize(StringReader reader) throws CommandSyntaxException { + var input = reader.readUnquotedString(); + + try { + return deserialize(input); + } catch (IllegalArgumentException e) { + throw new CommandSyntaxException(null, Text.of(e.getMessage())); + } + } + + @Override + public Integer deserialize(String serialized) { + var value = deserializeUnchecked(serialized); + + if (value < min) { + throw new IllegalArgumentException(value + " is below the minimum of " + min + "."); + } else if (value > max) { + throw new IllegalArgumentException(value + " is above the maximum of " + max + "."); + } + + return value; + } + + private Integer deserializeUnchecked(String serialized) { + if (serialized.length() == 1) { + return tryParseInteger(serialized); + } + + if (serialized.charAt(0) == '0') { + var prefixedBase = serialized.substring(0, 2); + var number = serialized.substring(2); + + // TODO(Refactor): Write a NumberBase.fromCharacter method instead of this that iterates of the NumberBases (add the char to the NumberBase constructor) + var numberBase = switch (prefixedBase.charAt(1)) { + case 'b' -> NumberBase.BINARY; + case 'o' -> NumberBase.OCTAL; + case 'd' -> NumberBase.DECIMAL; + case 'x' -> NumberBase.HEXADECIMAL; + default -> throw new IllegalArgumentException("Invalid base '" + prefixedBase.charAt(1) + "'."); + }; + + return tryParseInteger(number, numberBase.toInt()); + } + + var parts = serialized.split("_", 2); + if (parts.length == 2) { + var number = parts[0]; + var base = Integer.parseInt(parts[1]); + + return tryParseInteger(number, base); + } + + return tryParseInteger(serialized); } - private IntegerSerializer(ArgumentType argumentType) { - super(Integer.class, argumentType); + private Integer tryParseInteger(String string) { + return tryParseInteger(string, 10); + } + + private Integer tryParseInteger(String string, int radix) { + try { + return Integer.parseInt(string, radix); + } catch (NumberFormatException ignored) { + if (radix != 10) { + throw new IllegalArgumentException("Invalid base " + radix + " number '" + string + "'."); + } + + throw new IllegalArgumentException("Invalid number '" + string + "'."); + } } @Override @@ -28,4 +126,13 @@ public String serialize(Integer value) { return String.valueOf(value); } + @Override + public Collection getExamples() { + return Collections.emptyList(); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return builder.buildFuture(); + } } diff --git a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/NumberBaseSerializer.java b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/NumberBaseSerializer.java new file mode 100644 index 00000000..b407b328 --- /dev/null +++ b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/NumberBaseSerializer.java @@ -0,0 +1,77 @@ +package tools.redstone.redstonetools.features.arguments.serializers; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.text.Text; +import tools.redstone.redstonetools.utils.NumberBase; + +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public class NumberBaseSerializer extends TypeSerializer { + private static final IntegerSerializer INT_SERIALIZER = IntegerSerializer.integer(2, 36); + private static final NumberBaseSerializer INSTANCE = new NumberBaseSerializer(); + + public static NumberBaseSerializer numberBase() { + return INSTANCE; + } + + protected NumberBaseSerializer() { + super(Integer.class); + } + + @Override + public Integer deserialize(StringReader reader) throws CommandSyntaxException { + var input = reader.readUnquotedString(); + + try { + return deserialize(input); + } catch (IllegalArgumentException e) { + throw new CommandSyntaxException(null, Text.of(e.getMessage())); + } + } + + @Override + public Integer deserialize(String serialized) { + try { + return NumberBase.fromString(serialized) + .map(NumberBase::toInt) + .orElseGet(() -> INT_SERIALIZER.deserialize(serialized)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid base '" + serialized + "'.", e); + } + } + + @Override + public String serialize(Integer value) { + return NumberBase.fromInt(value) + .map(NumberBase::toString) + .orElseGet(() -> INT_SERIALIZER.serialize(value)); + } + + public String serialize(NumberBase value) { + return serialize(value.toInt()); + } + + @Override + public Collection getExamples() { + return Arrays.stream(NumberBase.values()) + .map(this::serialize) + .toList(); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + for (var value : NumberBase.values()) { + builder = builder.suggest(serialize(value)); + } + + return builder.buildFuture(); + } +} diff --git a/src/main/java/tools/redstone/redstonetools/features/commands/BaseConvertFeature.java b/src/main/java/tools/redstone/redstonetools/features/commands/BaseConvertFeature.java index 333d8ba6..636ff4fe 100644 --- a/src/main/java/tools/redstone/redstonetools/features/commands/BaseConvertFeature.java +++ b/src/main/java/tools/redstone/redstonetools/features/commands/BaseConvertFeature.java @@ -1,38 +1,26 @@ package tools.redstone.redstonetools.features.commands; +import net.minecraft.server.command.ServerCommandSource; import tools.redstone.redstonetools.features.Feature; import tools.redstone.redstonetools.features.arguments.Argument; import tools.redstone.redstonetools.features.feedback.Feedback; -import net.minecraft.server.command.ServerCommandSource; import static tools.redstone.redstonetools.features.arguments.serializers.IntegerSerializer.integer; -import static tools.redstone.redstonetools.features.arguments.serializers.StringSerializer.word; - - -import java.math.BigInteger; +import static tools.redstone.redstonetools.features.arguments.serializers.NumberBaseSerializer.numberBase; @Feature(name = "Base Convert", description = "Converts a number from one base to another.", command = "base") public class BaseConvertFeature extends CommandFeature { - public static final Argument number = Argument - .ofType(word()); - public static final Argument fromBase = Argument - .ofType(integer(2, 36)); + public static final Argument number = Argument + .ofType(integer()); public static final Argument toBase = Argument - .ofType(integer(2, 36)); - + .ofType(numberBase()) + .withDefault(10); @Override protected Feedback execute(ServerCommandSource source) { - BigInteger input; - try { - input = new BigInteger(number.getValue(), fromBase.getValue()); - } catch (NumberFormatException e) { - return Feedback.invalidUsage("Inputted number does not match the specified base"); - } + var output = Integer.toString(number.getValue(), toBase.getValue()); - var output = input.toString(toBase.getValue()); return Feedback.success(output); } - } diff --git a/src/main/java/tools/redstone/redstonetools/features/commands/BinaryBlockReadFeature.java b/src/main/java/tools/redstone/redstonetools/features/commands/BinaryBlockReadFeature.java index 4d9bae62..2bc984a8 100644 --- a/src/main/java/tools/redstone/redstonetools/features/commands/BinaryBlockReadFeature.java +++ b/src/main/java/tools/redstone/redstonetools/features/commands/BinaryBlockReadFeature.java @@ -14,6 +14,7 @@ import static tools.redstone.redstonetools.features.arguments.serializers.BlockStateArgumentSerializer.blockState; import static tools.redstone.redstonetools.features.arguments.serializers.BoolSerializer.bool; import static tools.redstone.redstonetools.features.arguments.serializers.IntegerSerializer.integer; +import static tools.redstone.redstonetools.features.arguments.serializers.NumberBaseSerializer.numberBase; @Feature(name = "Binary Block Read", description = "Interprets your WorldEdit selection as a binary number.", command = "/read") public class BinaryBlockReadFeature extends CommandFeature { @@ -28,7 +29,7 @@ public class BinaryBlockReadFeature extends CommandFeature { .ofType(blockState()) .withDefault(LIT_LAMP_ARG); public static final Argument toBase = Argument - .ofType(integer(2, 36)) + .ofType(numberBase()) .withDefault(10); public static final Argument reverseBits = Argument .ofType(bool()) diff --git a/src/main/java/tools/redstone/redstonetools/features/commands/ColorCodeFeature.java b/src/main/java/tools/redstone/redstonetools/features/commands/ColorCodeFeature.java index 455c4d82..3c05a54b 100644 --- a/src/main/java/tools/redstone/redstonetools/features/commands/ColorCodeFeature.java +++ b/src/main/java/tools/redstone/redstonetools/features/commands/ColorCodeFeature.java @@ -24,7 +24,6 @@ @Feature(name = "Color Code", description = "Color codes all color-able blocks in your WorldEdit selection.", command = "/colorcode") public class ColorCodeFeature extends CommandFeature { public static final Argument color = Argument - .ofType(blockColor()); public static final Argument onlyColor = Argument .ofType(blockColor()) diff --git a/src/main/java/tools/redstone/redstonetools/features/commands/QuickTpFeature.java b/src/main/java/tools/redstone/redstonetools/features/commands/QuickTpFeature.java index 8dcb4de6..e92ea051 100644 --- a/src/main/java/tools/redstone/redstonetools/features/commands/QuickTpFeature.java +++ b/src/main/java/tools/redstone/redstonetools/features/commands/QuickTpFeature.java @@ -16,9 +16,7 @@ @Feature(name = "Quick TP", description = "Teleports you in the direction you are looking.", command = "quicktp") public class QuickTpFeature extends CommandFeature { - public static final Argument distance = Argument - .ofType(floatArg(1.0f)) .withDefault(50.0f); public static final Argument includeFluids = Argument diff --git a/src/main/java/tools/redstone/redstonetools/utils/NumberBase.java b/src/main/java/tools/redstone/redstonetools/utils/NumberBase.java new file mode 100644 index 00000000..394ebc8c --- /dev/null +++ b/src/main/java/tools/redstone/redstonetools/utils/NumberBase.java @@ -0,0 +1,38 @@ +package tools.redstone.redstonetools.utils; + +import java.util.Arrays; +import java.util.Optional; + +public enum NumberBase { + BINARY(2), + OCTAL(8), + DECIMAL(10), + HEXADECIMAL(16); + + private final int base; + + NumberBase(int base) { + this.base = base; + } + + public int toInt() { + return base; + } + + @Override + public String toString() { + return super.toString().toLowerCase(); + } + + public static Optional fromString(String string) { + return Arrays.stream(values()) + .filter(b -> b.toString().equalsIgnoreCase(string)) + .findFirst(); + } + + public static Optional fromInt(int i) { + return Arrays.stream(values()) + .filter(b -> b.toInt() == i) + .findFirst(); + } +} From aa812e1508f0a54fd8ddcd7a4db6450978c5c9a6 Mon Sep 17 00:00:00 2001 From: Matthias Wijnsma Date: Fri, 26 May 2023 21:22:25 +0200 Subject: [PATCH 2/3] Add todos --- .../features/arguments/serializers/IntegerSerializer.java | 1 + .../features/arguments/serializers/LongSerializer.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java index fd534988..689c7757 100644 --- a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java +++ b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java @@ -94,6 +94,7 @@ private Integer deserializeUnchecked(String serialized) { return tryParseInteger(number, numberBase.toInt()); } + // TODO(Error handling): Add some checks here to make sure the specified base is valid var parts = serialized.split("_", 2); if (parts.length == 2) { var number = parts[0]; diff --git a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/LongSerializer.java b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/LongSerializer.java index c5e20e30..212b2502 100644 --- a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/LongSerializer.java +++ b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/LongSerializer.java @@ -3,6 +3,7 @@ import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.LongArgumentType; +// TODO(Update): Copy the way integer serializer works, maybe make integer serialized abstract so we can reuse code? Also add a BigIntegerSerializer public class LongSerializer extends StringBrigadierSerializer { private static final LongSerializer INSTANCE = new LongSerializer(LongArgumentType.longArg()); From 0ad2ad0a4004efa50bee2174ec8f74cb4239f924 Mon Sep 17 00:00:00 2001 From: Matthias Wijnsma Date: Mon, 29 May 2023 21:03:41 +0200 Subject: [PATCH 3/3] Abstract things away and implement bigint and long serializer --- .../serializers/BigIntegerSerializer.java | 33 +++++ .../serializers/IntLikeSerializer.java | 115 ++++++++++++++++ .../serializers/IntegerSerializer.java | 123 ++---------------- .../arguments/serializers/LongSerializer.java | 26 ++-- .../serializers/NumberBaseSerializer.java | 2 - .../features/commands/BaseConvertFeature.java | 10 +- 6 files changed, 175 insertions(+), 134 deletions(-) create mode 100644 src/main/java/tools/redstone/redstonetools/features/arguments/serializers/BigIntegerSerializer.java create mode 100644 src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntLikeSerializer.java diff --git a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/BigIntegerSerializer.java b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/BigIntegerSerializer.java new file mode 100644 index 00000000..36b94bbb --- /dev/null +++ b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/BigIntegerSerializer.java @@ -0,0 +1,33 @@ +package tools.redstone.redstonetools.features.arguments.serializers; + +import java.math.BigInteger; +import java.util.Optional; + +public class BigIntegerSerializer extends IntLikeSerializer { + private static final BigIntegerSerializer INSTANCE = new BigIntegerSerializer(null, null); + + public static BigIntegerSerializer bigInteger() { + return INSTANCE; + } + + public static BigIntegerSerializer bigInteger(BigInteger min) { + return new BigIntegerSerializer(min, null); + } + + public static BigIntegerSerializer bigInteger(BigInteger min, BigInteger max) { + return new BigIntegerSerializer(min, max); + } + + private BigIntegerSerializer(BigInteger min, BigInteger max) { + super(BigInteger.class, min, max); + } + + @Override + protected Optional tryParseOptional(String string, int radix) { + try { + return Optional.of(new BigInteger(string, radix)); + } catch (NumberFormatException ignored) { + return Optional.empty(); + } + } +} diff --git a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntLikeSerializer.java b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntLikeSerializer.java new file mode 100644 index 00000000..682f5d1c --- /dev/null +++ b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntLikeSerializer.java @@ -0,0 +1,115 @@ +package tools.redstone.redstonetools.features.arguments.serializers; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.text.Text; +import tools.redstone.redstonetools.utils.NumberBase; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +public abstract class IntLikeSerializer> extends TypeSerializer { + private final T min; + private final boolean hasMin; + private final T max; + private final boolean hasMax; + + protected IntLikeSerializer(Class clazz, T min, T max) { + super(clazz); + + this.min = min; + hasMin = min != null; + this.max = max; + hasMax = max != null; + } + + @Override + public T deserialize(StringReader reader) throws CommandSyntaxException { + var input = reader.readUnquotedString(); + + try { + return deserialize(input); + } catch (IllegalArgumentException e) { + throw new CommandSyntaxException(null, Text.of(e.getMessage())); + } + } + + @Override + public T deserialize(String serialized) { + var value = deserializeUnchecked(serialized); + + if (hasMin && value.compareTo(min) < 0) { + throw new IllegalArgumentException(value + " is below the minimum of " + min + "."); + } else if (hasMax && value.compareTo(max) > 0) { + throw new IllegalArgumentException(value + " is above the maximum of " + max + "."); + } + + return value; + } + + private T deserializeUnchecked(String serialized) { + if (serialized.length() == 1) { + return tryParse(serialized); + } + + if (serialized.charAt(0) == '0') { + var prefixedBase = serialized.substring(0, 2); + var number = serialized.substring(2); + + // TODO(Refactor): Write a NumberBase.fromCharacter method instead of this that iterates of the NumberBases (add the char to the NumberBase constructor) + var numberBase = switch (prefixedBase.charAt(1)) { + case 'b' -> NumberBase.BINARY; + case 'o' -> NumberBase.OCTAL; + case 'd' -> NumberBase.DECIMAL; + case 'x' -> NumberBase.HEXADECIMAL; + default -> throw new IllegalArgumentException("Invalid base '" + prefixedBase.charAt(1) + "'."); + }; + + return tryParse(number, numberBase.toInt()); + } + + // TODO(Error handling): Add some checks here to make sure the specified base is valid + var parts = serialized.split("_", 2); + if (parts.length == 2) { + var number = parts[0]; + var base = Integer.parseInt(parts[1]); + + return tryParse(number, base); + } + + return tryParse(serialized); + } + + private T tryParse(String string) { + return tryParse(string, 10); + } + + private T tryParse(String string, int radix) { + return tryParseOptional(string, radix) + .orElseThrow(() -> new IllegalArgumentException(radix == 10 + ? "Invalid number '" + string + "'." + : "Invalid base " + radix + " number '" + string + "'.")); + } + + protected abstract Optional tryParseOptional(String string, int radix); + + @Override + public String serialize(T value) { + return String.valueOf(value); + } + + @Override + public Collection getExamples() { + return Collections.emptyList(); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return builder.buildFuture(); + } +} diff --git a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java index 689c7757..8a53cf08 100644 --- a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java +++ b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/IntegerSerializer.java @@ -1,33 +1,16 @@ package tools.redstone.redstonetools.features.arguments.serializers; -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.text.Text; -import tools.redstone.redstonetools.utils.NumberBase; +import java.util.Optional; -import java.math.BigInteger; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - -public class IntegerSerializer extends TypeSerializer { - - private static final IntegerSerializer INSTANCE = new IntegerSerializer(); - - private final int min; - private final int max; +public class IntegerSerializer extends IntLikeSerializer { + private static final IntegerSerializer INSTANCE = new IntegerSerializer(Integer.MIN_VALUE, Integer.MAX_VALUE); public static IntegerSerializer integer() { return INSTANCE; } public static IntegerSerializer integer(int min) { - return new IntegerSerializer(min); + return new IntegerSerializer(min, Integer.MAX_VALUE); } public static IntegerSerializer integer(int min, int max) { @@ -35,105 +18,15 @@ public static IntegerSerializer integer(int min, int max) { } private IntegerSerializer(int min, int max) { - super(Integer.class); - - this.min = min; - this.max = max; - } - - private IntegerSerializer(int min) { - this(min, Integer.MAX_VALUE); - } - - private IntegerSerializer() { - this(Integer.MIN_VALUE, Integer.MAX_VALUE); + super(Integer.class, min, max); } @Override - public Integer deserialize(StringReader reader) throws CommandSyntaxException { - var input = reader.readUnquotedString(); - - try { - return deserialize(input); - } catch (IllegalArgumentException e) { - throw new CommandSyntaxException(null, Text.of(e.getMessage())); - } - } - - @Override - public Integer deserialize(String serialized) { - var value = deserializeUnchecked(serialized); - - if (value < min) { - throw new IllegalArgumentException(value + " is below the minimum of " + min + "."); - } else if (value > max) { - throw new IllegalArgumentException(value + " is above the maximum of " + max + "."); - } - - return value; - } - - private Integer deserializeUnchecked(String serialized) { - if (serialized.length() == 1) { - return tryParseInteger(serialized); - } - - if (serialized.charAt(0) == '0') { - var prefixedBase = serialized.substring(0, 2); - var number = serialized.substring(2); - - // TODO(Refactor): Write a NumberBase.fromCharacter method instead of this that iterates of the NumberBases (add the char to the NumberBase constructor) - var numberBase = switch (prefixedBase.charAt(1)) { - case 'b' -> NumberBase.BINARY; - case 'o' -> NumberBase.OCTAL; - case 'd' -> NumberBase.DECIMAL; - case 'x' -> NumberBase.HEXADECIMAL; - default -> throw new IllegalArgumentException("Invalid base '" + prefixedBase.charAt(1) + "'."); - }; - - return tryParseInteger(number, numberBase.toInt()); - } - - // TODO(Error handling): Add some checks here to make sure the specified base is valid - var parts = serialized.split("_", 2); - if (parts.length == 2) { - var number = parts[0]; - var base = Integer.parseInt(parts[1]); - - return tryParseInteger(number, base); - } - - return tryParseInteger(serialized); - } - - private Integer tryParseInteger(String string) { - return tryParseInteger(string, 10); - } - - private Integer tryParseInteger(String string, int radix) { + protected Optional tryParseOptional(String string, int radix) { try { - return Integer.parseInt(string, radix); + return Optional.of(Integer.parseInt(string, radix)); } catch (NumberFormatException ignored) { - if (radix != 10) { - throw new IllegalArgumentException("Invalid base " + radix + " number '" + string + "'."); - } - - throw new IllegalArgumentException("Invalid number '" + string + "'."); + return Optional.empty(); } } - - @Override - public String serialize(Integer value) { - return String.valueOf(value); - } - - @Override - public Collection getExamples() { - return Collections.emptyList(); - } - - @Override - public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { - return builder.buildFuture(); - } } diff --git a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/LongSerializer.java b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/LongSerializer.java index 212b2502..43516d84 100644 --- a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/LongSerializer.java +++ b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/LongSerializer.java @@ -1,32 +1,32 @@ package tools.redstone.redstonetools.features.arguments.serializers; -import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.arguments.LongArgumentType; +import java.util.Optional; -// TODO(Update): Copy the way integer serializer works, maybe make integer serialized abstract so we can reuse code? Also add a BigIntegerSerializer -public class LongSerializer extends StringBrigadierSerializer { - - private static final LongSerializer INSTANCE = new LongSerializer(LongArgumentType.longArg()); +public class LongSerializer extends IntLikeSerializer { + private static final LongSerializer INSTANCE = new LongSerializer(Long.MIN_VALUE, Long.MAX_VALUE); public static LongSerializer longArg() { return INSTANCE; } public static LongSerializer longArg(long min) { - return new LongSerializer(LongArgumentType.longArg(min)); + return new LongSerializer(min, Long.MAX_VALUE); } public static LongSerializer longArg(long min, long max) { - return new LongSerializer(LongArgumentType.longArg(min, max)); + return new LongSerializer(min, max); } - private LongSerializer(ArgumentType argumentType) { - super(Long.class, argumentType); + private LongSerializer(long min, long max) { + super(Long.class, min, max); } @Override - public String serialize(Long value) { - return String.valueOf(value); + protected Optional tryParseOptional(String string, int radix) { + try { + return Optional.of(Long.parseLong(string, radix)); + } catch (NumberFormatException ignored) { + return Optional.empty(); + } } - } diff --git a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/NumberBaseSerializer.java b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/NumberBaseSerializer.java index b407b328..87126ee2 100644 --- a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/NumberBaseSerializer.java +++ b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/NumberBaseSerializer.java @@ -10,9 +10,7 @@ import java.util.Arrays; import java.util.Collection; -import java.util.EnumSet; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; public class NumberBaseSerializer extends TypeSerializer { private static final IntegerSerializer INT_SERIALIZER = IntegerSerializer.integer(2, 36); diff --git a/src/main/java/tools/redstone/redstonetools/features/commands/BaseConvertFeature.java b/src/main/java/tools/redstone/redstonetools/features/commands/BaseConvertFeature.java index 636ff4fe..57e8db84 100644 --- a/src/main/java/tools/redstone/redstonetools/features/commands/BaseConvertFeature.java +++ b/src/main/java/tools/redstone/redstonetools/features/commands/BaseConvertFeature.java @@ -5,21 +5,23 @@ import tools.redstone.redstonetools.features.arguments.Argument; import tools.redstone.redstonetools.features.feedback.Feedback; -import static tools.redstone.redstonetools.features.arguments.serializers.IntegerSerializer.integer; +import java.math.BigInteger; + +import static tools.redstone.redstonetools.features.arguments.serializers.BigIntegerSerializer.bigInteger; import static tools.redstone.redstonetools.features.arguments.serializers.NumberBaseSerializer.numberBase; @Feature(name = "Base Convert", description = "Converts a number from one base to another.", command = "base") public class BaseConvertFeature extends CommandFeature { - public static final Argument number = Argument - .ofType(integer()); + public static final Argument number = Argument + .ofType(bigInteger()); public static final Argument toBase = Argument .ofType(numberBase()) .withDefault(10); @Override protected Feedback execute(ServerCommandSource source) { - var output = Integer.toString(number.getValue(), toBase.getValue()); + var output = number.getValue().toString(toBase.getValue()); return Feedback.success(output); }