Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Allow for case-insensitivity in legacy serializer, closes #1043 #1044

Merged
merged 1 commit into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import org.jetbrains.annotations.Unmodifiable;

/**
* A combination of a {@code character} and a {@link TextFormat}.
* A combination of a {@code character}, a {@link TextFormat}, and if the character is {@link #caseInsensitive()}.
*
* @since 4.14.0
*/
Expand All @@ -46,152 +46,165 @@ public interface CharacterAndFormat extends Examinable {
*
* @since 4.14.0
*/
CharacterAndFormat BLACK = characterAndFormat('0', NamedTextColor.BLACK);
CharacterAndFormat BLACK = characterAndFormat('0', NamedTextColor.BLACK, true);
/**
* Character and format pair representing {@link NamedTextColor#DARK_BLUE}.
*
* @since 4.14.0
*/
CharacterAndFormat DARK_BLUE = characterAndFormat('1', NamedTextColor.DARK_BLUE);
CharacterAndFormat DARK_BLUE = characterAndFormat('1', NamedTextColor.DARK_BLUE, true);
/**
* Character and format pair representing {@link NamedTextColor#DARK_GREEN}.
*
* @since 4.14.0
*/
CharacterAndFormat DARK_GREEN = characterAndFormat('2', NamedTextColor.DARK_GREEN);
CharacterAndFormat DARK_GREEN = characterAndFormat('2', NamedTextColor.DARK_GREEN, true);
/**
* Character and format pair representing {@link NamedTextColor#DARK_AQUA}.
*
* @since 4.14.0
*/
CharacterAndFormat DARK_AQUA = characterAndFormat('3', NamedTextColor.DARK_AQUA);
CharacterAndFormat DARK_AQUA = characterAndFormat('3', NamedTextColor.DARK_AQUA, true);
/**
* Character and format pair representing {@link NamedTextColor#DARK_RED}.
*
* @since 4.14.0
*/
CharacterAndFormat DARK_RED = characterAndFormat('4', NamedTextColor.DARK_RED);
CharacterAndFormat DARK_RED = characterAndFormat('4', NamedTextColor.DARK_RED, true);
/**
* Character and format pair representing {@link NamedTextColor#DARK_PURPLE}.
*
* @since 4.14.0
*/
CharacterAndFormat DARK_PURPLE = characterAndFormat('5', NamedTextColor.DARK_PURPLE);
CharacterAndFormat DARK_PURPLE = characterAndFormat('5', NamedTextColor.DARK_PURPLE, true);
/**
* Character and format pair representing {@link NamedTextColor#GOLD}.
*
* @since 4.14.0
*/
CharacterAndFormat GOLD = characterAndFormat('6', NamedTextColor.GOLD);
CharacterAndFormat GOLD = characterAndFormat('6', NamedTextColor.GOLD, true);
/**
* Character and format pair representing {@link NamedTextColor#GRAY}.
*
* @since 4.14.0
*/
CharacterAndFormat GRAY = characterAndFormat('7', NamedTextColor.GRAY);
CharacterAndFormat GRAY = characterAndFormat('7', NamedTextColor.GRAY, true);
/**
* Character and format pair representing {@link NamedTextColor#DARK_GRAY}.
*
* @since 4.14.0
*/
CharacterAndFormat DARK_GRAY = characterAndFormat('8', NamedTextColor.DARK_GRAY);
CharacterAndFormat DARK_GRAY = characterAndFormat('8', NamedTextColor.DARK_GRAY, true);
/**
* Character and format pair representing {@link NamedTextColor#BLUE}.
*
* @since 4.14.0
*/
CharacterAndFormat BLUE = characterAndFormat('9', NamedTextColor.BLUE);
CharacterAndFormat BLUE = characterAndFormat('9', NamedTextColor.BLUE, true);
/**
* Character and format pair representing {@link NamedTextColor#GREEN}.
*
* @since 4.14.0
*/
CharacterAndFormat GREEN = characterAndFormat('a', NamedTextColor.GREEN);
CharacterAndFormat GREEN = characterAndFormat('a', NamedTextColor.GREEN, true);
/**
* Character and format pair representing {@link NamedTextColor#AQUA}.
*
* @since 4.14.0
*/
CharacterAndFormat AQUA = characterAndFormat('b', NamedTextColor.AQUA);
CharacterAndFormat AQUA = characterAndFormat('b', NamedTextColor.AQUA, true);
/**
* Character and format pair representing {@link NamedTextColor#RED}.
*
* @since 4.14.0
*/
CharacterAndFormat RED = characterAndFormat('c', NamedTextColor.RED);
CharacterAndFormat RED = characterAndFormat('c', NamedTextColor.RED, true);
/**
* Character and format pair representing {@link NamedTextColor#LIGHT_PURPLE}.
*
* @since 4.14.0
*/
CharacterAndFormat LIGHT_PURPLE = characterAndFormat('d', NamedTextColor.LIGHT_PURPLE);
CharacterAndFormat LIGHT_PURPLE = characterAndFormat('d', NamedTextColor.LIGHT_PURPLE, true);
/**
* Character and format pair representing {@link NamedTextColor#YELLOW}.
*
* @since 4.14.0
*/
CharacterAndFormat YELLOW = characterAndFormat('e', NamedTextColor.YELLOW);
CharacterAndFormat YELLOW = characterAndFormat('e', NamedTextColor.YELLOW, true);
/**
* Character and format pair representing {@link NamedTextColor#WHITE}.
*
* @since 4.14.0
*/
CharacterAndFormat WHITE = characterAndFormat('f', NamedTextColor.WHITE);
CharacterAndFormat WHITE = characterAndFormat('f', NamedTextColor.WHITE, true);

/**
* Character and format pair representing {@link TextDecoration#OBFUSCATED}.
*
* @since 4.14.0
*/
CharacterAndFormat OBFUSCATED = characterAndFormat('k', TextDecoration.OBFUSCATED);
CharacterAndFormat OBFUSCATED = characterAndFormat('k', TextDecoration.OBFUSCATED, true);
/**
* Character and format pair representing {@link TextDecoration#BOLD}.
*
* @since 4.14.0
*/
CharacterAndFormat BOLD = characterAndFormat('l', TextDecoration.BOLD);
CharacterAndFormat BOLD = characterAndFormat('l', TextDecoration.BOLD, true);
/**
* Character and format pair representing {@link TextDecoration#STRIKETHROUGH}.
*
* @since 4.14.0
*/
CharacterAndFormat STRIKETHROUGH = characterAndFormat('m', TextDecoration.STRIKETHROUGH);
CharacterAndFormat STRIKETHROUGH = characterAndFormat('m', TextDecoration.STRIKETHROUGH, true);
/**
* Character and format pair representing {@link TextDecoration#UNDERLINED}.
*
* @since 4.14.0
*/
CharacterAndFormat UNDERLINED = characterAndFormat('n', TextDecoration.UNDERLINED);
CharacterAndFormat UNDERLINED = characterAndFormat('n', TextDecoration.UNDERLINED, true);
/**
* Character and format pair representing {@link TextDecoration#ITALIC}.
*
* @since 4.14.0
*/
CharacterAndFormat ITALIC = characterAndFormat('o', TextDecoration.ITALIC);
CharacterAndFormat ITALIC = characterAndFormat('o', TextDecoration.ITALIC, true);

/**
* Character and format pair representing {@link Reset#INSTANCE}.
*
* @since 4.14.0
*/
CharacterAndFormat RESET = characterAndFormat('r', Reset.INSTANCE);
CharacterAndFormat RESET = characterAndFormat('r', Reset.INSTANCE, true);

/**
* Creates a new combination of a {@code character} and a {@link TextFormat}.
* Creates a new combination of a case-sensitive {@code character} and a {@link TextFormat}.
*
* @param character the character
* @param format the format
* @return a new character and format pair.
* @return a new character and format instance.
* @since 4.14.0
*/
static @NotNull CharacterAndFormat characterAndFormat(final char character, final @NotNull TextFormat format) {
return new CharacterAndFormatImpl(character, format);
return characterAndFormat(character, format, false);
}

/**
* Gets an unmodifiable list of character and format pairs containing all default vanilla formats.
* Creates a new combination of a {@code character} and a {@link TextFormat}.
*
* @return am unmodifiable list of character and format pairs containing all default vanilla formats
* @param character the character
* @param format the format
* @param caseInsensitive if the character is case-insensitive
* @return a new character and format instance.
* @since 4.17.0
*/
static @NotNull CharacterAndFormat characterAndFormat(final char character, final @NotNull TextFormat format, final boolean caseInsensitive) {
return new CharacterAndFormatImpl(character, format, caseInsensitive);
}

/**
* Gets an unmodifiable list of character and format instances containing all default vanilla formats.
*
* @return an unmodifiable list of character and format instances containing all default vanilla formats
* @since 4.14.0
*/
@Unmodifiable
Expand All @@ -215,11 +228,20 @@ public interface CharacterAndFormat extends Examinable {
*/
@NotNull TextFormat format();

/**
* If the {@link #character()} is case-insensitive.
*
* @return if the character is case-insensitive
* @since 4.17.0
*/
boolean caseInsensitive();

@Override
default @NotNull Stream<? extends ExaminableProperty> examinableProperties() {
return Stream.of(
ExaminableProperty.of("character", this.character()),
ExaminableProperty.of("format", this.format())
ExaminableProperty.of("format", this.format()),
ExaminableProperty.of("caseInsensitive", this.caseInsensitive())
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@
final class CharacterAndFormatImpl implements CharacterAndFormat {
private final char character;
private final TextFormat format;
private final boolean caseInsensitive;

CharacterAndFormatImpl(final char character, final @NotNull TextFormat format) {
CharacterAndFormatImpl(final char character, final @NotNull TextFormat format, final boolean caseInsensitive) {
this.character = character;
this.format = requireNonNull(format, "format");
this.caseInsensitive = caseInsensitive;
}

@Override
Expand All @@ -52,19 +54,26 @@ public char character() {
return this.format;
}

@Override
public boolean caseInsensitive() {
return this.caseInsensitive;
}

@Override
public boolean equals(final @Nullable Object other) {
if (this == other) return true;
if (!(other instanceof CharacterAndFormatImpl)) return false;
final CharacterAndFormatImpl that = (CharacterAndFormatImpl) other;
return this.character == that.character
&& this.format.equals(that.format);
&& this.format.equals(that.format)
&& this.caseInsensitive == that.caseInsensitive;
}

@Override
public int hashCode() {
int result = this.character;
result = 31 * result + this.format.hashCode();
result = 31 * result + Boolean.hashCode(this.caseInsensitive);
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,36 @@ static CharacterAndFormatSet of(final List<CharacterAndFormat> pairs) {
final StringBuilder characters = new StringBuilder(size);
for (int i = 0; i < size; i++) {
final CharacterAndFormat pair = pairs.get(i);
characters.append(pair.character());
final char character = pair.character();
final TextFormat format = pair.format();
final boolean formatIsTextColor = format instanceof TextColor;

// First, add the "standard" character.
characters.append(character);
formats.add(format);
if (format instanceof TextColor) {
if (formatIsTextColor) {
colors.add((TextColor) format);
}

// If the character is case-insensitive, we need to add the other character too.
if (pair.caseInsensitive()) {
boolean added = false;

if (Character.isUpperCase(character)) {
characters.append(Character.toLowerCase(character));
added = true;
} else if (Character.isLowerCase(character)) {
characters.append(Character.toUpperCase(character));
added = true;
}

if (added) {
formats.add(format);
if (formatIsTextColor) {
colors.add((TextColor) format);
}
}
}
}
if (formats.size() != characters.length()) {
throw new IllegalStateException("formats length differs from characters length");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package net.kyori.adventure.text.serializer.legacy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
Expand Down Expand Up @@ -331,4 +332,27 @@ void testNullTextFormat() {
final String serialized = serializer.serialize(strikethough);
assertEquals(serialized, "Hello World");
}

// https://github.com/KyoriPowered/adventure/issues/1043
@Test
void testCaseInsensitivity() {
final Component expected = Component.text("pop4959", NamedTextColor.YELLOW);
final Component lowercaseActual = LegacyComponentSerializer.legacyAmpersand().deserialize("&epop4959");
assertEquals(expected, lowercaseActual);

final Component uppercaseActual = LegacyComponentSerializer.legacyAmpersand().deserialize("&Epop4959");
assertEquals(expected, uppercaseActual);
}

@Test
void testCaseSensitivity() {
final Component expected = Component.text("&Epop4959");
final Component lowercaseActual = LegacyComponentSerializer
.legacyAmpersand()
.toBuilder()
.formats(Collections.singletonList(CharacterAndFormat.characterAndFormat('e', NamedTextColor.YELLOW)))
.build()
.deserialize("&Epop4959");
assertEquals(expected, lowercaseActual);
}
}