Simplify placeholder replacement

This commit is contained in:
filoghost 2021-08-04 19:40:50 +02:00
parent e161dd18f9
commit 2d99a0c36a
6 changed files with 129 additions and 83 deletions

View File

@ -22,7 +22,7 @@ public class PlaceholderAPIHook {
enabled = true; enabled = true;
} }
public static boolean containsPlaceholders(String text) { public static boolean containsPlaceholderPattern(String text) {
if (Strings.isEmpty(text)) { if (Strings.isEmpty(text)) {
return false; return false;
} }

View File

@ -20,35 +20,43 @@ class DisplayText {
private final PlaceholderTracker placeholderTracker; private final PlaceholderTracker placeholderTracker;
private @NotNull StringWithPlaceholders textWithoutReplacements; private @NotNull StringWithPlaceholders textWithoutReplacements;
private @NotNull StringWithPlaceholders textWithGlobalReplacements; private @NotNull StringWithPlaceholders textWithGlobalReplacements;
private boolean containsPlaceholderAPIPattern;
DisplayText(PlaceholderTracker placeholderTracker) { DisplayText(PlaceholderTracker placeholderTracker) {
this.placeholderTracker = placeholderTracker; this.placeholderTracker = placeholderTracker;
this.textWithoutReplacements = StringWithPlaceholders.of(null); this.textWithoutReplacements = StringWithPlaceholders.of(null);
this.textWithGlobalReplacements = StringWithPlaceholders.of(null); this.textWithGlobalReplacements = textWithoutReplacements;
} }
void setWithoutReplacements(@Nullable String textString) { void setWithoutReplacements(@Nullable String textString) {
textWithoutReplacements = StringWithPlaceholders.of(textString); textWithoutReplacements = StringWithPlaceholders.of(textString);
textWithGlobalReplacements = textWithoutReplacements; textWithGlobalReplacements = textWithoutReplacements;
containsPlaceholderAPIPattern = textWithoutReplacements.anyLiteralPartMatch(PlaceholderAPIHook::containsPlaceholderPattern);
} }
String getWithoutReplacements() { String getWithoutReplacements() {
return textWithoutReplacements.getUnreplacedString(); return textWithoutReplacements.getString();
} }
String getWithGlobalReplacements() { String getWithGlobalReplacements() {
return textWithGlobalReplacements.getUnreplacedString(); return textWithGlobalReplacements.getString();
} }
String getWithIndividualReplacements(Player player) { String getWithIndividualReplacements(Player player) {
String textWithIndividualReplacements = textWithGlobalReplacements.replacePlaceholders((PlaceholderOccurrence occurrence) -> return textWithGlobalReplacements.replaceParts(
placeholderTracker.updateAndGetIndividualReplacement(occurrence, player)); (PlaceholderOccurrence placeholderOccurrence) -> {
return placeholderTracker.updateAndGetIndividualReplacement(placeholderOccurrence, player);
if (PlaceholderAPIHook.isEnabled() && PlaceholderAPIHook.containsPlaceholders(textWithIndividualReplacements)) { },
textWithIndividualReplacements = PlaceholderAPIHook.replacePlaceholders(player, textWithIndividualReplacements); (String literalPart) -> {
} if (containsPlaceholderAPIPattern
&& PlaceholderAPIHook.isEnabled()
return textWithIndividualReplacements; && PlaceholderAPIHook.containsPlaceholderPattern(literalPart)) {
return PlaceholderAPIHook.replacePlaceholders(player, literalPart);
} else {
return literalPart;
}
}
);
} }
boolean updateGlobalReplacements() { boolean updateGlobalReplacements() {
@ -68,8 +76,7 @@ class DisplayText {
} }
boolean containsIndividualPlaceholders() { boolean containsIndividualPlaceholders() {
return placeholderTracker.containsIndividualPlaceholders(textWithoutReplacements) return containsPlaceholderAPIPattern || placeholderTracker.containsIndividualPlaceholders(textWithoutReplacements);
|| PlaceholderAPIHook.containsPlaceholders(textWithoutReplacements.getUnreplacedString());
} }
} }

View File

@ -5,9 +5,13 @@
*/ */
package me.filoghost.holographicdisplays.plugin.placeholder.parsing; package me.filoghost.holographicdisplays.plugin.placeholder.parsing;
import org.jetbrains.annotations.Nullable;
@FunctionalInterface @FunctionalInterface
public interface PlaceholderReplaceFunction { public interface PlaceholderReplaceFunction {
String getReplacement(PlaceholderOccurrence placeholderOccurrence); PlaceholderReplaceFunction NO_REPLACEMENTS = placeholderOccurrence -> null;
@Nullable String getReplacement(PlaceholderOccurrence placeholderOccurrence);
} }

View File

@ -11,8 +11,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.UnaryOperator;
public final class StringWithPlaceholders { public final class StringWithPlaceholders {
@ -36,23 +36,39 @@ public final class StringWithPlaceholders {
this.stringParts = stringParts; this.stringParts = stringParts;
} }
public @Nullable String getUnreplacedString() { public @Nullable String getString() {
return string; return string;
} }
public boolean containsPlaceholders() { public boolean containsPlaceholders() {
return stringParts != null; return string != null && stringParts != null;
} }
public boolean anyMatch(Predicate<PlaceholderOccurrence> filter) { public boolean anyPlaceholderMatch(Predicate<PlaceholderOccurrence> filter) {
if (!containsPlaceholders()) { if (!containsPlaceholders()) {
return false; return false;
} }
for (StringPart stringPart : stringParts) { for (StringPart stringPart : stringParts) {
if (stringPart instanceof PlaceholderStringPart) { if (stringPart instanceof PlaceholderPart) {
PlaceholderStringPart placeholderStringPart = (PlaceholderStringPart) stringPart; PlaceholderPart placeholderPart = (PlaceholderPart) stringPart;
if (filter.test(placeholderStringPart.placeholderOccurrence)) { if (filter.test(placeholderPart.getPlaceholderOccurrence())) {
return true;
}
}
}
return false;
}
public boolean anyLiteralPartMatch(Predicate<String> filter) {
if (!containsPlaceholders()) {
return filter.test(string);
}
for (StringPart stringPart : stringParts) {
if (stringPart instanceof LiteralPart) {
if (filter.test(stringPart.getRawValue())) {
return true; return true;
} }
} }
@ -66,75 +82,49 @@ public final class StringWithPlaceholders {
return this; return this;
} }
StringBuilder output = new StringBuilder(); List<StringPart> newStringParts = new ArrayList<>(stringParts.size());
StringBuilder fullOutput = new StringBuilder();
List<StringPart> newStringParts = null; // Lazy initialization
for (StringPart part : stringParts) { for (StringPart part : stringParts) {
if (part instanceof PlaceholderStringPart) { if (part instanceof PlaceholderPart) {
PlaceholderStringPart placeholderStringPart = (PlaceholderStringPart) part; String replacement = replaceFunction.getReplacement(((PlaceholderPart) part).getPlaceholderOccurrence());
String replacement = replaceFunction.getReplacement(placeholderStringPart.placeholderOccurrence);
if (replacement != null) { if (replacement != null) {
output.append(replacement); // Do not use LiteralPart to avoid potentially replacing it again later
fullOutput.append(replacement); newStringParts.add(new ReplacementPart(replacement));
} else { } else {
// Placeholder was not replaced, may be replaced later // Placeholder was not replaced, may be replaced later
if (newStringParts == null) { newStringParts.add(part);
newStringParts = new ArrayList<>();
}
// Append leading literal string, if present
if (output.length() > 0) {
newStringParts.add(new LiteralStringPart(output.toString()));
output.setLength(0);
}
newStringParts.add(placeholderStringPart);
fullOutput.append(placeholderStringPart.unreplacedString);
} }
} else { } else {
LiteralStringPart literalStringPart = (LiteralStringPart) part; newStringParts.add(part);
output.append(literalStringPart.literalString);
fullOutput.append(literalStringPart.literalString);
} }
} }
if (output.length() > 0 && newStringParts != null) { return new StringWithPlaceholders(joinParts(newStringParts), newStringParts);
newStringParts.add(new LiteralStringPart(output.toString()));
}
return new StringWithPlaceholders(fullOutput.toString(), newStringParts);
} }
public @Nullable String replacePlaceholders(PlaceholderReplaceFunction replaceFunction) { public @Nullable String replacePlaceholders(PlaceholderReplaceFunction replaceFunction) {
if (!containsPlaceholders()) { return replaceParts(replaceFunction, UnaryOperator.identity());
return string;
}
StringBuilder output = new StringBuilder();
for (StringPart part : stringParts) {
output.append(part.getValue(replaceFunction));
}
return output.toString();
} }
public String replaceLiteralParts(Function<String, String> transformFunction) { public String replaceLiteralParts(UnaryOperator<String> replaceFunction) {
if (!containsPlaceholders() || string == null) { return replaceParts(PlaceholderReplaceFunction.NO_REPLACEMENTS, replaceFunction);
return transformFunction.apply(string); }
public String replaceParts(PlaceholderReplaceFunction placeholderReplaceFunction, UnaryOperator<String> literalPartReplaceFunction) {
if (!containsPlaceholders()) {
return literalPartReplaceFunction.apply(string);
} }
StringBuilder stringOutput = new StringBuilder(string.length()); StringBuilder output = new StringBuilder(string.length());
for (StringPart part : stringParts) { for (StringPart part : stringParts) {
if (part instanceof LiteralStringPart) { if (part instanceof LiteralPart) {
LiteralStringPart literalStringPart = (LiteralStringPart) part; output.append(literalPartReplaceFunction.apply(part.getRawValue()));
String transformedPart = transformFunction.apply(literalStringPart.literalString);
stringOutput.append(transformedPart);
} else { } else {
stringOutput.append(((PlaceholderStringPart) part).unreplacedString); output.append(part.getValue(placeholderReplaceFunction));
} }
} }
return stringOutput.toString(); return output.toString();
} }
private static @Nullable List<StringPart> splitToParts(@NotNull String string) { private static @Nullable List<StringPart> splitToParts(@NotNull String string) {
@ -163,11 +153,11 @@ public final class StringWithPlaceholders {
// Append leading literal part (if any) // Append leading literal part (if any)
if (placeholderStartIndex != lastAppendIndex) { if (placeholderStartIndex != lastAppendIndex) {
stringParts.add(new LiteralStringPart(string.substring(lastAppendIndex, placeholderStartIndex))); stringParts.add(new LiteralPart(string.substring(lastAppendIndex, placeholderStartIndex)));
} }
// Append placeholder part // Append placeholder part
stringParts.add(new PlaceholderStringPart(content, unparsedString)); stringParts.add(new PlaceholderPart(content, unparsedString));
lastAppendIndex = endIndex; lastAppendIndex = endIndex;
placeholderStartIndex = -1; placeholderStartIndex = -1;
@ -185,12 +175,20 @@ public final class StringWithPlaceholders {
// Append trailing literal part (if any) // Append trailing literal part (if any)
if (lastAppendIndex != string.length() && stringParts != null) { if (lastAppendIndex != string.length() && stringParts != null) {
stringParts.add(new LiteralStringPart(string.substring(lastAppendIndex))); stringParts.add(new LiteralPart(string.substring(lastAppendIndex)));
} }
return stringParts; return stringParts;
} }
private static String joinParts(@NotNull List<StringPart> stringParts) {
StringBuilder stringBuilder = new StringBuilder();
for (StringPart stringPart : stringParts) {
stringBuilder.append(stringPart.getRawValue());
}
return stringBuilder.toString();
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) { if (this == obj) {
@ -214,31 +212,59 @@ public final class StringWithPlaceholders {
String getValue(PlaceholderReplaceFunction placeholderReplaceFunction); String getValue(PlaceholderReplaceFunction placeholderReplaceFunction);
String getRawValue();
} }
private static class LiteralStringPart implements StringPart { private static class LiteralPart implements StringPart {
private final String literalString; private final String value;
LiteralStringPart(String literalString) { LiteralPart(@NotNull String value) {
this.literalString = literalString; this.value = value;
} }
@Override @Override
public String getValue(PlaceholderReplaceFunction placeholderReplaceFunction) { public String getValue(PlaceholderReplaceFunction placeholderReplaceFunction) {
return literalString; return value;
}
@Override
public String getRawValue() {
return value;
} }
} }
private static class PlaceholderStringPart implements StringPart { private static class ReplacementPart implements StringPart {
private final String replacement;
ReplacementPart(@NotNull String replacement) {
this.replacement = replacement;
}
@Override
public String getValue(PlaceholderReplaceFunction placeholderReplaceFunction) {
return replacement;
}
@Override
public String getRawValue() {
return replacement;
}
}
private static class PlaceholderPart implements StringPart {
private final PlaceholderOccurrence placeholderOccurrence; private final PlaceholderOccurrence placeholderOccurrence;
private final String unreplacedString; private final String unreplacedString;
PlaceholderStringPart(PlaceholderOccurrence placeholderOccurrence, String unreplacedString) { PlaceholderPart(@NotNull PlaceholderOccurrence placeholderOccurrence, @NotNull String unreplacedString) {
this.placeholderOccurrence = placeholderOccurrence; this.placeholderOccurrence = placeholderOccurrence;
this.unreplacedString = unreplacedString; this.unreplacedString = unreplacedString;
} }
@ -249,11 +275,20 @@ public final class StringWithPlaceholders {
if (replacement != null) { if (replacement != null) {
return replacement; return replacement;
} else { } else {
// If no replacement is provided, leave the unreplaced placeholder string // If no replacement is provided return the unreplaced placeholder string
return unreplacedString; return unreplacedString;
} }
} }
@Override
public String getRawValue() {
return unreplacedString;
}
public PlaceholderOccurrence getPlaceholderOccurrence() {
return placeholderOccurrence;
}
} }
} }

View File

@ -102,7 +102,7 @@ public class PlaceholderTracker {
} }
public boolean containsIndividualPlaceholders(StringWithPlaceholders textWithPlaceholders) { public boolean containsIndividualPlaceholders(StringWithPlaceholders textWithPlaceholders) {
return textWithPlaceholders.anyMatch(occurrence -> { return textWithPlaceholders.anyPlaceholderMatch(occurrence -> {
PlaceholderExpansion placeholderExpansion = registry.find(occurrence); PlaceholderExpansion placeholderExpansion = registry.find(occurrence);
return placeholderExpansion != null && placeholderExpansion.isIndividual(); return placeholderExpansion != null && placeholderExpansion.isIndividual();
}); });

View File

@ -34,7 +34,7 @@ class StringWithPlaceholdersTest {
boolean expectedContainsPlaceholders = expectedOutput.contains("#"); boolean expectedContainsPlaceholders = expectedOutput.contains("#");
StringWithPlaceholders s = StringWithPlaceholders.of(input); StringWithPlaceholders s = StringWithPlaceholders.of(input);
assertThat(s.partiallyReplacePlaceholders(occurrence -> "#").getUnreplacedString()).isEqualTo(expectedOutput); assertThat(s.partiallyReplacePlaceholders(occurrence -> "#").getString()).isEqualTo(expectedOutput);
assertThat(s.partiallyReplacePlaceholders(occurrence -> null).replacePlaceholders(occurrence -> "#")).isEqualTo(expectedOutput); assertThat(s.partiallyReplacePlaceholders(occurrence -> null).replacePlaceholders(occurrence -> "#")).isEqualTo(expectedOutput);
assertThat(s.containsPlaceholders()).isEqualTo(expectedContainsPlaceholders); assertThat(s.containsPlaceholders()).isEqualTo(expectedContainsPlaceholders);
} }
@ -82,7 +82,7 @@ class StringWithPlaceholdersTest {
List<PlaceholderOccurrence> placeholders = new ArrayList<>(); List<PlaceholderOccurrence> placeholders = new ArrayList<>();
s.replacePlaceholders(occurrence -> { s.replacePlaceholders(occurrence -> {
placeholders.add(occurrence); // Just save occurrences placeholders.add(occurrence); // Just collect occurrences
return null; return null;
}); });
assertThat(placeholders).hasSize(1); assertThat(placeholders).hasSize(1);