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))
+ }
+ }
+ }
}