Merge pull request #3010 from Multiverse/ben/mv5/nested-locale-message

Add support for nested Message within MessageReplacement and fix some checkstyles
This commit is contained in:
Jeremy Wood 2023-09-11 08:30:09 -04:00 committed by GitHub
commit 5a474d7efa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 164 additions and 20 deletions

View File

@ -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) {

View File

@ -58,9 +58,12 @@ public class MVCommandManager extends PaperCommandManager {
}
}
/**
* {@inheritDoc}
*/
@Override
public BukkitLocales getLocales() {
return this.locales;
public PluginLocales getLocales() {
return (PluginLocales) this.locales;
}
/**

View File

@ -14,6 +14,31 @@ public class MultiverseException extends Exception {
private final @Nullable Message message;
/**
* Creates a new exception with the given message.
* <br/>
* 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.
* <br/>
* 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.
* <br/>

View File

@ -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;
}
}

View File

@ -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.
* <br/>
* 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;
}

View File

@ -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<String, Message> 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<String, Message> getReplacement() {
return replacement;
}
}

View File

@ -0,0 +1 @@
package com.onarandombox.MultiverseCore.utils.message;

View File

@ -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<String> {
verify(sender).sendMessage(capture())
}.firstValue
assertNotEquals(replacedMessageString, sentMessage)
assertThat(sentMessage, !containsSubstring(replacementKey))
assertThat(sentMessage, containsSubstring(replacedMessageStringLocale))
}
}
}
}