Skip to content

Commit 19ce1ee

Browse files
committed
feat(core): restore pattern replacement on legacy parser
Relates to #232
1 parent 29745a0 commit 19ce1ee

File tree

7 files changed

+130
-40
lines changed

7 files changed

+130
-40
lines changed

core/src/main/java/com/rexcantor64/triton/language/TranslationManager.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.rexcantor64.triton.language.item.LanguageSign;
99
import com.rexcantor64.triton.language.item.LanguageText;
1010
import com.rexcantor64.triton.storage.LocalStorage;
11+
import com.rexcantor64.triton.utils.ComponentUtils;
1112
import lombok.Getter;
1213
import lombok.NonNull;
1314
import lombok.RequiredArgsConstructor;
@@ -151,6 +152,10 @@ public synchronized void setup() {
151152

152153
setupMiniMessage();
153154

155+
if (!this.matches.isEmpty() && !this.triton.getConfig().getParser().equalsIgnoreCase("legacy")) {
156+
this.triton.getLogger().logWarning("Some of your translations have patterns, but you are currently using the Adventure parser, which does not support those. If you need patterns, please use the legacy parser instead.");
157+
}
158+
154159
this.triton.getLogger()
155160
.logInfo(
156161
"Successfully setup the Translation Manager! %1 text translations and %2 sign translations loaded!",
@@ -377,22 +382,16 @@ public Component[] formatLines(@NonNull Language language,
377382
}
378383

379384
public String matchPattern(String input, @NotNull Localized localized) {
380-
return matchPattern(input, localized.getLanguageId());
381-
}
382-
383-
public String matchPattern(String input, String language) {
384385
// TODO this needs to be rethought to work with Adventure
385386
for (Map.Entry<Pattern, LanguageText> entry : matches.entrySet()) {
386-
String replacement = entry.getValue().getMessageRegex(language);
387-
if (replacement == null) {
388-
replacement = entry.getValue().getMessageRegex(triton.getLanguageManager().getMainLanguage().getName());
389-
}
387+
String replacement = getPatternReplacement(entry.getValue(), localized);
390388
if (replacement == null) {
391389
continue;
392390
}
393391
try {
394392
Matcher matcher = entry.getKey().matcher(input);
395-
input = matcher.replaceAll(replacement);
393+
// patterns only support legacy formatting for now
394+
input = matcher.replaceAll(ComponentUtils.translateAlternateColorCodes(replacement));
396395
} catch (IndexOutOfBoundsException e) {
397396
this.triton.getLogger().logError(
398397
"Failed to translate using patterns: translation has more placeholders than regex groups. Translation key: %1",
@@ -402,6 +401,24 @@ public String matchPattern(String input, String language) {
402401
return input;
403402
}
404403

404+
private @Nullable String getPatternReplacement(@NotNull LanguageText languageText, @NotNull Localized localized) {
405+
String replacement = languageText.getMessageRegex(localized.getLanguageId());
406+
if (replacement != null) {
407+
return replacement;
408+
}
409+
for (String fallbackLanguageName : localized.getLanguage().getFallbackLanguages()) {
410+
val fallbackLanguage = triton.getLanguageManager().getLanguageByName(fallbackLanguageName);
411+
if (!fallbackLanguage.isPresent()) {
412+
continue;
413+
}
414+
replacement = languageText.getMessageRegex(fallbackLanguage.get().getLanguageId());
415+
if (replacement != null) {
416+
return replacement;
417+
}
418+
}
419+
return languageText.getMessageRegex(triton.getLanguageManager().getMainLanguage().getName());
420+
}
421+
405422
/**
406423
* @see com.rexcantor64.triton.language.parser.AdventureParser#replaceArguments(Component, List)
407424
*/

core/src/main/java/com/rexcantor64/triton/language/parser/AdventureParser.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.PriorityQueue;
3535
import java.util.Queue;
3636
import java.util.concurrent.atomic.AtomicBoolean;
37+
import java.util.function.Function;
3738
import java.util.stream.Collectors;
3839

3940
/**
@@ -79,7 +80,8 @@ public class AdventureParser extends MessageParser {
7980
syntax,
8081
Triton.get().getConfig().getDisabledLine(),
8182
// TODO properly integrate this
82-
(key, arguments) -> Triton.get().getTranslationManager().getTextComponentOr404(language, key, arguments)
83+
(key, arguments) -> Triton.get().getTranslationManager().getTextComponentOr404(language, key, arguments),
84+
Function.identity()
8385
);
8486

8587
Triton.get().getDumpManager().dump(component, language, syntax);

core/src/main/java/com/rexcantor64/triton/language/parser/LegacyParser.java

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.Arrays;
4040
import java.util.HashMap;
4141
import java.util.List;
42+
import java.util.Objects;
4243
import java.util.Optional;
4344
import java.util.UUID;
4445
import java.util.concurrent.atomic.AtomicBoolean;
@@ -65,12 +66,10 @@ public class LegacyParser extends MessageParser {
6566
private static final char FONT_MID_DELIM = FONT_START_DELIM + 2;
6667
private static final char FONT_END_DELIM = FONT_START_DELIM + 1;
6768

68-
private static final char AMPERSAND_CHAR = '&';
6969
private static final char SECTION_CHAR = '§';
7070
private static final char HEX_PREFIX = '#';
7171
private static final char HEX_CODE = 'x';
7272
private static final char SHADOW_COLOR_CODE = 's';
73-
private static final String VALID_COLOR_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRrXx";
7473
private static final Pattern FORMATTING_STRIP_PATTERN = Pattern.compile(
7574
CLICK_DELIM + "\\d[\\w-]{36}|" + HOVER_DELIM + "[\\w-]{36}|" + CLICK_END_DELIM + "|" + HOVER_END_DELIM
7675
+ "|" + SECTION_CHAR + "[0-9A-Fa-fK-Ok-oRrXx]"
@@ -128,7 +127,8 @@ public class LegacyParser extends MessageParser {
128127
}
129128

130129
return replaceArguments(notFoundComponent, new SerializedComponent(key), argsConcatenationComp);
131-
})
130+
}),
131+
prevText -> Triton.get().getTranslationManager().matchPattern(prevText, language)
132132
);
133133

134134
return translateComponent(component, configuration);
@@ -141,9 +141,21 @@ TranslationResult<SerializedComponent> translateComponent(
141141
@NotNull TranslationConfiguration<SerializedComponent> configuration
142142
) {
143143
String text = component.getText();
144+
String newText = configuration.getMatchSupplier().apply(text);
145+
val changedByPatterns = !Objects.equals(text, newText);
146+
if (changedByPatterns) {
147+
component.setText(newText);
148+
text = newText;
149+
}
144150
val indexes = ParserUtils.getPatternIndexArray(text, configuration.getFeatureSyntax().getLang());
145151

146152
if (indexes.isEmpty()) {
153+
if (changedByPatterns) {
154+
component = handleNonContentText(component, configuration)
155+
.getResult()
156+
.orElse(component);
157+
return TranslationResult.changed(component);
158+
}
147159
return handleNonContentText(component, configuration);
148160
}
149161

@@ -344,31 +356,8 @@ SerializedComponent replaceArguments(@NotNull SerializedComponent comp, @NotNull
344356
} else if (message.startsWith(JSON_TYPE_TAG)) {
345357
return new SerializedComponent(GsonComponentSerializer.gson().deserialize(message.substring(JSON_TYPE_TAG.length())));
346358
} else {
347-
return new SerializedComponent(translateAlternateColorCodes(message));
348-
}
349-
}
350-
351-
/**
352-
* Convert color codes with ampersand (&) into section characters (§).
353-
* The character is only converted if followed by a valid color code.
354-
* Inspired by md5's ChatColor#translateAlternateColorCodes.
355-
*
356-
* @param text The text to convert the color code characters from.
357-
* @return The input text with the color codes replaced.
358-
*/
359-
@Contract("null -> null; !null -> !null")
360-
private @Nullable String translateAlternateColorCodes(@Nullable String text) {
361-
if (text == null) {
362-
return null;
363-
}
364-
365-
char[] chars = text.toCharArray();
366-
for (int i = 0; i < chars.length - 1; i++) {
367-
if (chars[i] == AMPERSAND_CHAR && VALID_COLOR_CODES.indexOf(chars[i + 1]) != -1) {
368-
chars[i] = SECTION_CHAR;
369-
}
359+
return new SerializedComponent(ComponentUtils.translateAlternateColorCodes(message));
370360
}
371-
return new String(chars);
372361
}
373362

374363
/**
@@ -401,7 +390,8 @@ static class SerializedComponent {
401390
@Getter
402391
private final HashMap<UUID, @Nullable HoverEvent<?>> hoverEvents = new HashMap<>();
403392
@Getter
404-
@Setter(AccessLevel.PRIVATE)
393+
@Setter(AccessLevel.PACKAGE)
394+
@VisibleForTesting
405395
private String text;
406396

407397
public SerializedComponent(Component component) {

core/src/main/java/com/rexcantor64/triton/language/parser/TranslationConfiguration.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22

33
import com.rexcantor64.triton.api.config.FeatureSyntax;
44
import lombok.Data;
5+
import org.jetbrains.annotations.NotNull;
56

67
import java.util.function.BiFunction;
8+
import java.util.function.Function;
79

810
@Data
911
public class TranslationConfiguration<T> {
1012
final FeatureSyntax featureSyntax;
1113
final String disabledLine;
1214
final BiFunction<String, T[], T> translationSupplier;
15+
/**
16+
* For legacy parser only.
17+
*/
18+
final Function<@NotNull String, @NotNull String> matchSupplier;
1319
}

core/src/main/java/com/rexcantor64/triton/utils/ComponentUtils.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
import net.kyori.adventure.text.serializer.json.JSONOptions;
1515
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
1616
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
17+
import org.jetbrains.annotations.Contract;
1718
import org.jetbrains.annotations.NotNull;
19+
import org.jetbrains.annotations.Nullable;
1820

1921
import java.util.ArrayList;
2022
import java.util.Collections;
@@ -28,6 +30,8 @@
2830
public class ComponentUtils {
2931

3032
public final static char SECTION_CHAR = '§';
33+
private final static char AMPERSAND_CHAR = '&';
34+
private final static String VALID_COLOR_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRrXx";
3135
private final static GsonComponentSerializer GSON_SERIALIZER;
3236
private final static LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection()
3337
.toBuilder()
@@ -242,4 +246,27 @@ public static boolean isValidClickEvent(ClickEvent clickEvent) {
242246
|| URL_REGEX.matcher(clickEvent.value()).find();
243247
}
244248

249+
/**
250+
* Convert color codes with ampersand (&) into section characters (§).
251+
* The character is only converted if followed by a valid color code.
252+
* Inspired by md5's ChatColor#translateAlternateColorCodes.
253+
*
254+
* @param text The text to convert the color code characters from.
255+
* @return The input text with the color codes replaced.
256+
*/
257+
@Contract("null -> null; !null -> !null")
258+
public static @Nullable String translateAlternateColorCodes(@Nullable String text) {
259+
if (text == null) {
260+
return null;
261+
}
262+
263+
char[] chars = text.toCharArray();
264+
for (int i = 0; i < chars.length - 1; i++) {
265+
if (chars[i] == AMPERSAND_CHAR && VALID_COLOR_CODES.indexOf(chars[i + 1]) != -1) {
266+
chars[i] = SECTION_CHAR;
267+
}
268+
}
269+
return new String(chars);
270+
}
271+
245272
}

core/src/test/java/com/rexcantor64/triton/language/parser/AdventureParserTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ public class AdventureParserTest {
7474
private final TranslationConfiguration<Component> configuration = new TranslationConfiguration<>(
7575
defaultSyntax,
7676
"disabled.line",
77-
(key, args) -> parser.replaceArguments(messageResolver.apply(key), Arrays.asList(args))
77+
(key, args) -> parser.replaceArguments(messageResolver.apply(key), Arrays.asList(args)),
78+
Function.identity()
7879
);
7980

8081
@Test

core/src/test/java/com/rexcantor64/triton/language/parser/LegacyParserTest.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public class LegacyParserTest {
5050
private final TranslationConfiguration<SerializedComponent> configuration = new TranslationConfiguration<>(
5151
defaultSyntax,
5252
"disabled.line",
53-
(key, args) -> parser.replaceArguments(messageResolver.apply(key), args)
53+
(key, args) -> parser.replaceArguments(messageResolver.apply(key), args),
54+
text -> text.replace("abababab", "this has been replaced").replace("babababa", "[lang]without.formatting[/lang]")
5455
);
5556

5657
private final Component ALL_IN_ONE_COMPONENT = Component.text("Lorem ")
@@ -385,4 +386,50 @@ public void testParseComponentWithPlaceholdersInShowEntityHoverAction() {
385386
assertNotNull(result.getResultRaw());
386387
assertEquals(expected.compact(), result.getResultRaw().compact());
387388
}
389+
390+
@Test
391+
public void testParseComponentWithPatternsNoPlaceholders() {
392+
Component comp = Component.text()
393+
.content("test abababab lorem")
394+
.asComponent();
395+
396+
TranslationResult<Component> result = parser.translateComponent(new SerializedComponent(comp), configuration)
397+
.map(SerializedComponent::toComponent);
398+
399+
Component expected = Component.text()
400+
.content("test this has been replaced lorem")
401+
.asComponent();
402+
403+
assertEquals(TranslationResult.ResultState.CHANGED, result.getState());
404+
assertNotNull(result.getResultRaw());
405+
assertEquals(expected.compact(), result.getResultRaw().compact());
406+
}
407+
408+
@Test
409+
public void testParseComponentWithPatterns() {
410+
Component comp = Component.text()
411+
.content("test abababab lorem")
412+
.hoverEvent(
413+
HoverEvent.showText(
414+
Component.text("ipsum babababa")
415+
)
416+
)
417+
.asComponent();
418+
419+
TranslationResult<Component> result = parser.translateComponent(new SerializedComponent(comp), configuration)
420+
.map(SerializedComponent::toComponent);
421+
422+
Component expected = Component.text()
423+
.content("test this has been replaced lorem")
424+
.hoverEvent(
425+
HoverEvent.showText(
426+
Component.text("ipsum This is text without formatting")
427+
)
428+
)
429+
.asComponent();
430+
431+
assertEquals(TranslationResult.ResultState.CHANGED, result.getState());
432+
assertNotNull(result.getResultRaw());
433+
assertEquals(expected.compact(), result.getResultRaw().compact());
434+
}
388435
}

0 commit comments

Comments
 (0)