Skip to content

Commit

Permalink
wip: feat: continue translate methods of legacy parser
Browse files Browse the repository at this point in the history
  • Loading branch information
diogotcorreia committed Aug 27, 2024
1 parent c13963c commit 9e2f59c
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.rexcantor64.triton.api.language.Localized;
import com.rexcantor64.triton.api.language.MessageParser;
import com.rexcantor64.triton.utils.ComponentUtils;
import com.rexcantor64.triton.utils.ParserUtils;
import com.rexcantor64.triton.utils.StringUtils;
import lombok.AccessLevel;
import lombok.Getter;
Expand Down Expand Up @@ -110,7 +111,7 @@ TranslationResult<Component> translateComponent(@NotNull Component component, @N
plainText = componentToString(component);
}

val indexes = this.getPatternIndexArray(plainText, configuration.getFeatureSyntax().getLang());
val indexes = ParserUtils.getPatternIndexArray(plainText, configuration.getFeatureSyntax().getLang());

if (indexes.isEmpty()) {
return handleNonContentText(component, configuration);
Expand Down Expand Up @@ -170,7 +171,7 @@ private Optional<Component> handlePlaceholder(Component placeholder, Translation
placeholder = stripStyleOfFirstCharacter(placeholder);

String placeholderStr = componentToString(placeholder);
val indexes = this.getPatternIndexArray(placeholderStr, configuration.getFeatureSyntax().getArg());
val indexes = ParserUtils.getPatternIndexArray(placeholderStr, configuration.getFeatureSyntax().getArg());
Queue<Integer> indexesToSplitAt = indexes.stream()
.flatMap(Arrays::stream)
.sorted()
Expand All @@ -186,10 +187,7 @@ private Optional<Component> handlePlaceholder(Component placeholder, Translation
Component part = splitComponents.get(i);
if (i == 0) {
key = PlainTextComponentSerializer.plainText().serialize(part);
// The [args] tag is optional since v4.0.0, so strip it if it's present
if (key.endsWith("[" + configuration.getFeatureSyntax().getArgs() + "]")) {
key = key.substring(0, key.length() - configuration.getFeatureSyntax().getArgs().length() - 2);
}
key = ParserUtils.normalizeTranslationKey(key, configuration);
if (!StringUtils.isEmptyOrNull(configuration.getDisabledLine()) && configuration.getDisabledLine()
.equals(key)) {
return Optional.empty();
Expand Down Expand Up @@ -352,7 +350,8 @@ private Style getStyleOfFirstCharacterOrEmpty(Component component) {
*/
@VisibleForTesting
@Contract("_ -> new")
@NotNull Component stripStyleOfFirstCharacter(@NotNull Component component) {
@NotNull
Component stripStyleOfFirstCharacter(@NotNull Component component) {
if (component instanceof TextComponent) {
TextComponent textComponent = (TextComponent) component;
if (!textComponent.content().isEmpty()) {
Expand Down Expand Up @@ -584,51 +583,6 @@ private List<Component> handleChildren(Component parent, List<Component> childre
return accumulator;
}

/**
* Find the indexes of all root "[pattern][/pattern]" tags in the given string.
* <p>
* Only the root tags are included, that is, nested tags are ignored.
* For example, <code>[pattern][pattern][/pattern][/pattern]</code> would only
* return the indexes for the outer tags.
* <p>
* Each array in the returned list corresponds to a different set of opening and closing tags,
* and has size 4.
* Indexes have the following meaning:
* <ul>
* <li>0: the first character of the opening tag</li>
* <li>1: the character after the last character of the closing tag</li>
* <li>2: the character after the last character of the opening tag</li>
* <li>3: the first character of the closing tag</li>
* </ul>
*
* @param input The string to search for opening and closing tags.
* @param pattern The tags to search for (i.e. "lang" will search for "[lang]" and "[/lang]").
* @return A list of indexes of all the found tags, as specified by the method description.
*/
public List<Integer[]> getPatternIndexArray(String input, String pattern) {
List<Integer[]> result = new ArrayList<>();
int start = -1;
int openedAmount = 0;

for (int i = 0; i < input.length(); i++) {
char currentChar = input.charAt(i);
if (currentChar == '[' && input.length() > i + pattern.length() + 1 && input.substring(i + 1,
i + 2 + pattern.length()).equals(pattern + "]")) {
if (start == -1) start = i;
openedAmount++;
i += 1 + pattern.length();
} else if (currentChar == '[' && input.length() > i + pattern.length() + 2 && input.substring(i + 1,
i + 3 + pattern.length()).equals("/" + pattern + "]")) {
openedAmount--;
if (openedAmount == 0) {
result.add(new Integer[]{start, i + 3 + pattern.length(), start + pattern.length() + 2, i});
start = -1;
}
}
}
return result;
}

/**
* Adventure has an issue where some components might not become empty
* components, even though they should be. This is a fix for that, while
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import com.rexcantor64.triton.api.config.FeatureSyntax;
import com.rexcantor64.triton.api.language.Localized;
import com.rexcantor64.triton.api.language.MessageParser;
import com.rexcantor64.triton.api.language.TranslationResult;
import com.rexcantor64.triton.utils.ComponentUtils;
import com.rexcantor64.triton.utils.ParserUtils;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
Expand Down Expand Up @@ -37,6 +37,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -130,8 +131,78 @@ TranslationResult<SerializedComponent> translateComponent(
@NotNull SerializedComponent component,
@NotNull TranslationConfiguration<SerializedComponent> configuration
) {
// TODO!
return null;
String text = component.getText();
val indexes = ParserUtils.getPatternIndexArray(text, configuration.getFeatureSyntax().getLang());

if (indexes.isEmpty()) {
// TODO handle non text components
return TranslationResult.unchanged();
}

val builder = new StringBuilder();
// keep track of last index added to the string builder
int lastCharacter = 0;

for (val index : indexes) {
builder.append(text, lastCharacter, index[0]);
lastCharacter = index[1];

val placeholder = text.substring(index[2], index[3]);

val resultComponent = handlePlaceholder(placeholder, configuration);
if (!resultComponent.isPresent()) {
return TranslationResult.remove();
}

builder.append(resultComponent.get().getText());
component.importFromComponent(resultComponent.get());
}

builder.append(text, lastCharacter, text.length());
component.setText(builder.toString());

// TODO handle non text components

return TranslationResult.changed(component);
}

/**
* An auxiliary method to {@link LegacyParser#translateComponent(SerializedComponent, TranslationConfiguration)}
* that handles translating the component inside the <code>[lang][/lang]</code> tags.
* The <code>[args][/args]</code> tags are optional since Triton v4.0.0.
* <p>
* This method gets the translation for the key and replaces its arguments, if any.
*
* @param placeholder The text inside the <code>[lang][/lang]</code> tags.
* @param configuration The settings to apply to this translation.
* @return The translation of this placeholder. Empty optional if the translation is "disabled line".
* @since 4.0.0
*/
private @NotNull Optional<SerializedComponent> handlePlaceholder(
@NotNull String placeholder,
@NotNull TranslationConfiguration<SerializedComponent> configuration
) {
val indexes = ParserUtils.getPatternIndexArray(placeholder, configuration.getFeatureSyntax().getArg());

SerializedComponent[] arguments = indexes.stream()
.map(index -> placeholder.substring(index[2], index[3]))
.map(SerializedComponent::new)
.toArray(SerializedComponent[]::new);

String key = placeholder;
if (!indexes.isEmpty()) {
key = key.substring(0, indexes.get(0)[0]);
}
key = ParserUtils.normalizeTranslationKey(key, configuration);

val result = configuration.translationSupplier.apply(key, arguments);

TranslationResult<SerializedComponent> translationResult = translateComponent(result, configuration);
if (translationResult.getState() == TranslationResult.ResultState.TO_REMOVE) {
return Optional.empty();
}

return Optional.of(translationResult.getResult().orElse(result));
}

public @NotNull String replaceArguments(@NotNull String text, @Nullable String @NotNull [] arguments) {
Expand Down
81 changes: 81 additions & 0 deletions core/src/main/java/com/rexcantor64/triton/utils/ParserUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.rexcantor64.triton.utils;

import com.rexcantor64.triton.language.parser.TranslationConfiguration;
import lombok.val;

import java.util.ArrayList;
import java.util.List;

/**
* Methods used in implementations of {@link com.rexcantor64.triton.api.language.MessageParser}.
*
* @since 4.0.0
*/
public class ParserUtils {

/**
* Find the indexes of all root "[pattern][/pattern]" tags in the given string.
* <p>
* Only the root tags are included, that is, nested tags are ignored.
* For example, <code>[pattern][pattern][/pattern][/pattern]</code> would only
* return the indexes for the outer tags.
* <p>
* Each array in the returned list corresponds to a different set of opening and closing tags,
* and has size 4.
* Indexes have the following meaning:
* <ul>
* <li>0: the first character of the opening tag</li>
* <li>1: the character after the last character of the closing tag</li>
* <li>2: the character after the last character of the opening tag</li>
* <li>3: the first character of the closing tag</li>
* </ul>
*
* @param input The string to search for opening and closing tags.
* @param pattern The tags to search for (i.e. "lang" will search for "[lang]" and "[/lang]").
* @return A list of indexes of all the found tags, as specified by the method description.
*/
public static List<Integer[]> getPatternIndexArray(String input, String pattern) {
List<Integer[]> result = new ArrayList<>();
int start = -1;
int openedAmount = 0;

for (int i = 0; i < input.length(); i++) {
char currentChar = input.charAt(i);
if (currentChar == '[' && input.length() > i + pattern.length() + 1 && input.substring(i + 1,
i + 2 + pattern.length()).equals(pattern + "]")) {
if (start == -1) start = i;
openedAmount++;
i += 1 + pattern.length();
} else if (currentChar == '[' && input.length() > i + pattern.length() + 2 && input.substring(i + 1,
i + 3 + pattern.length()).equals("/" + pattern + "]")) {
openedAmount--;
if (openedAmount == 0) {
result.add(new Integer[]{start, i + 3 + pattern.length(), start + pattern.length() + 2, i});
start = -1;
}
}
}
return result;
}

/**
* Removes legacy <code>[args][/args]</code> tags from (the end of) translation keys.
* Since v4.0.0, these tags are no longer needed and are therefore deprecated.
* For backwards compatibility, ignore them.
*
* @param key The key, potentially ending in <code>[args]</code>, <code>[/args]</code>, or both.
* @param configuration The settings being applied while translating the placeholder with this key.
* @return The key with the <code>[args][/args]</code> removed.
*/
public static String normalizeTranslationKey(String key, TranslationConfiguration<?> configuration) {
val syntax = configuration.getFeatureSyntax().getArgs();
// The [args] tag is optional since v4.0.0, so strip it if it's present
if (key.endsWith("[/" + syntax + "]")) {
key = key.substring(0, key.length() - syntax.length() - 3);
}
if (key.endsWith("[" + configuration.getFeatureSyntax().getArgs() + "]")) {
key = key.substring(0, key.length() - syntax.length() - 2);
}
return key;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1182,23 +1182,6 @@ public void testSplitComponentWithSingleNonTextComponentWithRepeatedIndexes() {
}
}

@Test
public void testGetPatternIndexArray() {
String input = "Lorem ipsum [tag]dolor [tag]sit[/tag] amet[/tag], [tag2]consectetur[/tag2] [tag]adipiscing elit[/tag]. Nullam posuere.";

List<Integer[]> result = parser.getPatternIndexArray(input, "tag");

List<Integer[]> expected = Arrays.asList(
new Integer[]{12, 48, 17, 42},
new Integer[]{75, 101, 80, 95}
);

assertEquals(expected.size(), result.size());
for (int i = 0; i < expected.size(); i++) {
assertArrayEquals(expected.get(i), result.get(i));
}
}

@Test
public void testGetStyleOfFirstStyle() {
Component comp = Component.text()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.rexcantor64.triton.utils;

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class ParserUtilsTest {

@Test
public void testGetPatternIndexArray() {
String input = "Lorem ipsum [tag]dolor [tag]sit[/tag] amet[/tag], [tag2]consectetur[/tag2] [tag]adipiscing elit[/tag]. Nullam posuere.";

List<Integer[]> result = ParserUtils.getPatternIndexArray(input, "tag");

List<Integer[]> expected = Arrays.asList(
new Integer[]{12, 48, 17, 42},
new Integer[]{75, 101, 80, 95}
);

assertEquals(expected.size(), result.size());
for (int i = 0; i < expected.size(); i++) {
assertArrayEquals(expected.get(i), result.get(i));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.rexcantor64.triton.Triton;
import com.rexcantor64.triton.language.parser.AdventureParser;
import com.rexcantor64.triton.spigot.SpigotTriton;
import com.rexcantor64.triton.utils.ParserUtils;
import lombok.val;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
Expand Down Expand Up @@ -57,7 +58,7 @@ public void onChat(AsyncPlayerChatEvent e) {
}

String msg = e.getMessage();
val indexes = parser().getPatternIndexArray(msg, Triton.get().getConfig().getChatSyntax().getLang());
val indexes = ParserUtils.getPatternIndexArray(msg, Triton.get().getConfig().getChatSyntax().getLang());
for (int i = 0; i < indexes.size(); ++i) {
val index = indexes.get(i);
// add a zero width space to prevent the parser from finding this placeholder
Expand Down

0 comments on commit 9e2f59c

Please sign in to comment.