From 1745c16becfa7e538d3f1aadbffe6c9355d9530f Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:37:38 +0800 Subject: [PATCH] Add support for nested Message within MessageReplacement and fix some checkstyles --- .../commandtools/MVCommandIssuer.java | 3 +- .../commandtools/MVCommandManager.java | 7 ++- .../exceptions/MultiverseException.java | 25 ++++++++ .../utils/message/LocalizedMessage.java | 30 ++++++++-- .../MultiverseCore/utils/message/Message.java | 30 +++++++--- .../utils/message/MessageReplacement.java | 28 +++++++-- .../utils/message/package-info.java | 1 + .../core/commandtools/LocalizationTest.kt | 60 +++++++++++++++++++ 8 files changed, 164 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/onarandombox/MultiverseCore/utils/message/package-info.java diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandIssuer.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandIssuer.java index f45b3a71..a22c9b39 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandIssuer.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandIssuer.java @@ -35,7 +35,8 @@ public class MVCommandIssuer extends OpenBukkitCommandIssuer { private void sendMessage(MessageType messageType, Message message) { if (message instanceof MessageKeyProvider) { - sendMessage(messageType, (MessageKeyProvider) message, message.getReplacements()); + sendMessage(messageType, (MessageKeyProvider) message, + message.getReplacements(getManager().getLocales(), this)); } else { var formatter = getManager().getFormat(messageType); if (formatter != null) { diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandManager.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandManager.java index 2156615d..67abde6a 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandManager.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commandtools/MVCommandManager.java @@ -58,9 +58,12 @@ public class MVCommandManager extends PaperCommandManager { } } + /** + * {@inheritDoc} + */ @Override - public BukkitLocales getLocales() { - return this.locales; + public PluginLocales getLocales() { + return (PluginLocales) this.locales; } /** diff --git a/src/main/java/com/onarandombox/MultiverseCore/exceptions/MultiverseException.java b/src/main/java/com/onarandombox/MultiverseCore/exceptions/MultiverseException.java index 268468fd..ee0642a5 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/exceptions/MultiverseException.java +++ b/src/main/java/com/onarandombox/MultiverseCore/exceptions/MultiverseException.java @@ -14,6 +14,31 @@ public class MultiverseException extends Exception { private final @Nullable Message message; + /** + * Creates a new exception with the given message. + *
+ * If the message is not null, this exception will also contain a {@link Message} which can be accessed via + * {@link #getMVMessage()}. This message will just be the given message wrapped in a {@link Message}. + * + * @param message The message for the exception + */ + public MultiverseException(@Nullable String message) { + this(message != null ? Message.of(message) : null); + } + + /** + * Creates a new exception with the given message. + *
+ * If the message is not null, this exception will also contain a String message which can be accessed via + * {@link #getMessage()}. This message will just be the given message formatted without locale support. + * + * @param message The message for the exception + */ + public MultiverseException(@Nullable Message message) { + super(message != null ? message.formatted() : null); + this.message = message; + } + /** * Creates a new exception with the given message and cause. *
diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/message/LocalizedMessage.java b/src/main/java/com/onarandombox/MultiverseCore/utils/message/LocalizedMessage.java index fcf77f7a..140de226 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/message/LocalizedMessage.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/message/LocalizedMessage.java @@ -17,8 +17,7 @@ final class LocalizedMessage extends Message implements MessageKeyProvider { LocalizedMessage( @NotNull MessageKeyProvider messageKeyProvider, @NotNull String message, - @NotNull MessageReplacement... replacements - ) { + @NotNull MessageReplacement... replacements) { super(message, replacements); this.messageKeyProvider = messageKeyProvider; } @@ -28,13 +27,34 @@ final class LocalizedMessage extends Message implements MessageKeyProvider { return messageKeyProvider.getMessageKey(); } + @Override + public @NotNull String[] getReplacements(@NotNull PluginLocales locales, @Nullable CommandIssuer commandIssuer) { + return toReplacementsArray(locales, commandIssuer, replacements); + } + @Override public @NotNull String formatted(@NotNull PluginLocales locales, @Nullable CommandIssuer commandIssuer) { Objects.requireNonNull(locales, "locales must not be null"); - if (getReplacements().length == 0) { - return raw(); + String[] parsedReplacements = getReplacements(locales, commandIssuer); + if (parsedReplacements.length == 0) { + return locales.getMessage(commandIssuer, getMessageKey()); } - return ACFUtil.replaceStrings(locales.getMessage(commandIssuer, getMessageKey()), getReplacements()); + return ACFUtil.replaceStrings(locales.getMessage(commandIssuer, getMessageKey()), parsedReplacements); + } + + private static String[] toReplacementsArray( + @NotNull PluginLocales locales, + @Nullable CommandIssuer commandIssuer, + @NotNull MessageReplacement... replacements) { + String[] replacementsArray = new String[replacements.length * 2]; + int i = 0; + for (MessageReplacement replacement : replacements) { + replacementsArray[i++] = replacement.getKey(); + replacementsArray[i++] = replacement.getReplacement().fold( + str -> str, + message -> message.formatted(locales, commandIssuer)); + } + return replacementsArray; } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/message/Message.java b/src/main/java/com/onarandombox/MultiverseCore/utils/message/Message.java index 76821066..fe459dd5 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/message/Message.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/message/Message.java @@ -48,8 +48,7 @@ public sealed class Message permits LocalizedMessage { public static Message of( @NotNull MessageKeyProvider messageKeyProvider, @NotNull String nonLocalizedMessage, - @NotNull MessageReplacement... replacements - ) { + @NotNull MessageReplacement... replacements) { Objects.requireNonNull(messageKeyProvider, "messageKeyProvider must not be null"); Objects.requireNonNull(nonLocalizedMessage, "message must not be null"); for (MessageReplacement replacement : replacements) { @@ -60,11 +59,11 @@ public sealed class Message permits LocalizedMessage { } private final @NotNull String message; - private final @NotNull String[] replacements; + protected final @NotNull MessageReplacement[] replacements; protected Message(@NotNull String message, @NotNull MessageReplacement... replacements) { this.message = message; - this.replacements = toReplacementsArray(replacements); + this.replacements = replacements; } /** @@ -76,7 +75,21 @@ public sealed class Message permits LocalizedMessage { * @return The replacements */ public @NotNull String[] getReplacements() { - return replacements; + return toReplacementsArray(replacements); + } + + /** + * Gets the replacements for this message with localization support. + *
+ * This array is guaranteed to be of even length and suitable for use with + * {@link ACFUtil#replaceStrings(String, String...)}. + * + * @param locales The MultiverseCore locales provider + * @param commandIssuer The command issuer the message is for, or null for the console (default locale) + * @return The replacements + */ + public @NotNull String[] getReplacements(@NotNull PluginLocales locales, @Nullable CommandIssuer commandIssuer) { + return getReplacements(); } /** @@ -96,10 +109,11 @@ public sealed class Message permits LocalizedMessage { * @return The formatted message */ public @NotNull String formatted() { - if (replacements.length == 0) { + String[] parsedReplacements = getReplacements(); + if (parsedReplacements.length == 0) { return raw(); } - return ACFUtil.replaceStrings(message, replacements); + return ACFUtil.replaceStrings(message, parsedReplacements); } /** @@ -133,7 +147,7 @@ public sealed class Message permits LocalizedMessage { int i = 0; for (MessageReplacement replacement : replacements) { replacementsArray[i++] = replacement.getKey(); - replacementsArray[i++] = replacement.getReplacement(); + replacementsArray[i++] = replacement.getReplacement().fold(s -> s, Message::formatted); } return replacementsArray; } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/message/MessageReplacement.java b/src/main/java/com/onarandombox/MultiverseCore/utils/message/MessageReplacement.java index ee928f70..010dd943 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/message/MessageReplacement.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/message/MessageReplacement.java @@ -1,5 +1,6 @@ package com.onarandombox.MultiverseCore.utils.message; +import io.vavr.control.Either; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -7,7 +8,7 @@ import org.jetbrains.annotations.Nullable; /** * Captures string replacements for {@link Message}s. */ - public final class MessageReplacement { +public final class MessageReplacement { /** * Creates a replacement key for the given key string. @@ -20,6 +21,9 @@ import org.jetbrains.annotations.Nullable; return new MessageReplacement.Key(key); } + /** + * A replacement key that maps to a value it can be replaced with. + */ public static final class Key { private final @NotNull String key; @@ -28,6 +32,17 @@ import org.jetbrains.annotations.Nullable; this.key = key; } + /** + * Creates a replacement for this key. + * + * @param replacement The replacement message + * @return A new message replacement + */ + @Contract(value = "_ -> new", pure = true) + public MessageReplacement with(@NotNull Message replacement) { + return new MessageReplacement(key, replacement); + } + /** * Creates a replacement for this key. * @@ -41,11 +56,16 @@ import org.jetbrains.annotations.Nullable; } private final @NotNull String key; - private final @NotNull String replacement; + private final @NotNull Either replacement; + + private MessageReplacement(@NotNull String key, @NotNull Message replacement) { + this.key = key; + this.replacement = Either.right(replacement); + } private MessageReplacement(@NotNull String key, @Nullable Object replacement) { this.key = key; - this.replacement = String.valueOf(replacement); + this.replacement = Either.left(String.valueOf(replacement)); } /** @@ -62,7 +82,7 @@ import org.jetbrains.annotations.Nullable; * * @return The replacement */ - public @NotNull String getReplacement() { + public @NotNull Either getReplacement() { return replacement; } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/message/package-info.java b/src/main/java/com/onarandombox/MultiverseCore/utils/message/package-info.java new file mode 100644 index 00000000..2b71f62c --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/message/package-info.java @@ -0,0 +1 @@ +package com.onarandombox.MultiverseCore.utils.message; \ No newline at end of file diff --git a/src/test/java/org/mvplugins/multiverse/core/commandtools/LocalizationTest.kt b/src/test/java/org/mvplugins/multiverse/core/commandtools/LocalizationTest.kt index bfd223eb..df32c5fb 100644 --- a/src/test/java/org/mvplugins/multiverse/core/commandtools/LocalizationTest.kt +++ b/src/test/java/org/mvplugins/multiverse/core/commandtools/LocalizationTest.kt @@ -253,4 +253,64 @@ class LocalizationTest : TestWithMockBukkit() { } } } + + @Nested + inner class WithMessagesAsReplacement { + private val replacementKey = "{world}" + private val replacementValue = Message.of(MVCorei18n.GENERIC_SUCCESS, "success") + private val messageString = "Hello $replacementKey!" + private val replacedMessageString = "Hello success!" + private val replacedMessageStringLocale = "Cloned world 'Success!'!" + + private val message = MVCorei18n.CLONE_SUCCESS + .bundle(messageString, replace(replacementKey).with(replacementValue)) + + @Test + fun `The raw message should be the same as the original`() { + assertEquals(messageString, message.raw()) + } + + @Test + fun `The formatted message should be the replaced original string`() { + assertEquals(replacedMessageString, message.formatted()) + } + + @Test + fun `The formatted message with PluginLocales should be different from the replaced original string`() { + assertEquals(replacedMessageStringLocale, message.formatted(locales)) + } + + @Test + fun `The formatted message with PluginLocales should have performed replacement`() { + assertThat(message.formatted(locales), !containsSubstring(replacementKey)) + assertThat(message.formatted(locales), containsSubstring("Success!")) + } + + @Nested + @DisplayName("And a command sender is provided") + inner class WithCommandSender { + + private lateinit var sender: CommandSender + private lateinit var issuer: MVCommandIssuer + + @BeforeTest + fun setUp() { + sender = spy(Bukkit.getConsoleSender()) + issuer = commandManager.getCommandIssuer(sender) + } + + @Test + fun `Sending the issuer the message should send the formatted message to the sender`() { + issuer.sendInfo(message); + + val sentMessage = argumentCaptor { + verify(sender).sendMessage(capture()) + }.firstValue + + assertNotEquals(replacedMessageString, sentMessage) + assertThat(sentMessage, !containsSubstring(replacementKey)) + assertThat(sentMessage, containsSubstring(replacedMessageStringLocale)) + } + } + } }