mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-11-01 08:39:31 +01:00
Placeholder service improvements, or placeholders. Buildscript fixes/improvements related to testing/dependencydownload
This commit is contained in:
parent
b2a970d8c6
commit
f35d8d67ee
@ -30,8 +30,8 @@ import com.discordsrv.api.discord.api.entity.message.DiscordMessageEmbed;
|
||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.discord.api.util.DiscordFormattingUtil;
|
||||
import com.discordsrv.api.placeholder.FormattedText;
|
||||
import com.discordsrv.api.placeholder.PlaceholderResultStringifier;
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
import com.discordsrv.api.placeholder.mapper.ResultMappers;
|
||||
import com.discordsrv.api.placeholder.util.Placeholders;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -259,7 +259,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
|
||||
DiscordMessageEmbed.Builder embedBuilder = embed.toBuilder();
|
||||
|
||||
// TODO: check which parts allow formatting more thoroughly
|
||||
PlaceholderResultStringifier.plainComponents(() -> {
|
||||
ResultMappers.runInPlainComponentContext(() -> {
|
||||
embedBuilder.setAuthor(
|
||||
placeholders.apply(
|
||||
embedBuilder.getAuthorName()),
|
||||
@ -308,7 +308,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
|
||||
builder.addEmbed(embedBuilder.build());
|
||||
}
|
||||
|
||||
PlaceholderResultStringifier.plainComponents(() -> {
|
||||
ResultMappers.runInPlainComponentContext(() -> {
|
||||
builder.setWebhookUsername(placeholders.apply(builder.getWebhookUsername()));
|
||||
builder.setWebhookAvatarUrl(placeholders.apply(builder.getWebhookAvatarUrl()));
|
||||
});
|
||||
|
@ -28,27 +28,37 @@ import com.discordsrv.api.event.events.Processable;
|
||||
import com.discordsrv.api.placeholder.PlaceholderLookupResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class PlaceholderLookupEvent implements Event, Processable {
|
||||
|
||||
private final String placeholder;
|
||||
private final Set<Object> context;
|
||||
private final Set<Object> contexts;
|
||||
|
||||
private boolean processed;
|
||||
private PlaceholderLookupResult result;
|
||||
|
||||
public PlaceholderLookupEvent(String placeholder, Set<Object> context) {
|
||||
public PlaceholderLookupEvent(String placeholder, Set<Object> contexts) {
|
||||
this.placeholder = placeholder;
|
||||
this.context = context;
|
||||
this.contexts = contexts;
|
||||
}
|
||||
|
||||
public String getPlaceholder() {
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
public Set<Object> getContext() {
|
||||
return context;
|
||||
public Set<Object> getContexts() {
|
||||
return contexts;
|
||||
}
|
||||
|
||||
public Optional<Object> getContext(Class<?> type) {
|
||||
for (Object o : contexts) {
|
||||
if (type.isAssignableFrom(o.getClass())) {
|
||||
return Optional.of(o);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,7 +80,7 @@ public class PlaceholderLookupEvent implements Event, Processable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link PlaceholderLookupResult} for the provided {@link #getPlaceholder()} and {@link #getContext()}.
|
||||
* Provides a {@link PlaceholderLookupResult} for the provided {@link #getPlaceholder()} and {@link #getContexts()}.
|
||||
* @param result the result
|
||||
* @throws IllegalStateException if the event is already processed
|
||||
*/
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
package com.discordsrv.api.placeholder;
|
||||
|
||||
import com.discordsrv.api.placeholder.mapper.PlaceholderResultMapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
@ -34,15 +35,15 @@ public interface PlaceholderService {
|
||||
/**
|
||||
* The primary pattern used by DiscordSRV to find placeholders.
|
||||
*/
|
||||
Pattern PATTERN = Pattern.compile("(%)([^%]+)(%)");
|
||||
Pattern PATTERN = Pattern.compile("(%)((?:[^%]|(?<=\\\\)%)+)(%)");
|
||||
|
||||
/**
|
||||
* The pattern DiscordSRV uses to find recursive placeholders.
|
||||
*/
|
||||
Pattern RECURSIVE_PATTERN = Pattern.compile("(\\{)(.+)(})");
|
||||
Pattern RECURSIVE_PATTERN = Pattern.compile("(\\{)((?:[^{}]|(?<=\\\\)[{}])+)(})");
|
||||
|
||||
void addResultStringifier(@NotNull PlaceholderResultStringifier resultConverter);
|
||||
void removeResultStringifier(@NotNull PlaceholderResultStringifier resultConverter);
|
||||
void addResultMapper(@NotNull PlaceholderResultMapper resultMapper);
|
||||
void removeResultMapper(@NotNull PlaceholderResultMapper resultMapper);
|
||||
|
||||
String replacePlaceholders(@NotNull String placeholder, @NotNull Set<Object> context);
|
||||
String replacePlaceholders(@NotNull String placeholder, @NotNull Object... context);
|
||||
|
@ -21,37 +21,18 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.discordsrv.api.placeholder;
|
||||
package com.discordsrv.api.placeholder.mapper;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlaceholderResultStringifier {
|
||||
|
||||
/**
|
||||
* @see #plainComponents(Runnable)
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
ThreadLocal<Boolean> PLAIN_COMPONENT_CONTEXT = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* Utility method to run the provided {@link Runnable} where {@link PlaceholderService}s
|
||||
* will replace {@link com.discordsrv.api.component.MinecraftComponent}s
|
||||
* as plain without formatting (instead of converting to Discord formatting).
|
||||
* @param runnable a task that will be executed immediately
|
||||
*/
|
||||
static void plainComponents(Runnable runnable) {
|
||||
PLAIN_COMPONENT_CONTEXT.set(true);
|
||||
runnable.run();
|
||||
PLAIN_COMPONENT_CONTEXT.set(false);
|
||||
}
|
||||
public interface PlaceholderResultMapper {
|
||||
|
||||
/**
|
||||
* Converts a successful placeholder lookup result into a {@link String}.
|
||||
* @param result the result
|
||||
* @return the result in {@link String} form
|
||||
* @return the result in {@link String} form or {@code null} if this stringifier doesn't know what to do with this result
|
||||
*/
|
||||
String convertPlaceholderResult(@NotNull Object result);
|
||||
Object convertResult(@NotNull Object result);
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* This file is part of the DiscordSRV API, licensed under the MIT License
|
||||
* Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.discordsrv.api.placeholder.mapper;
|
||||
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class ResultMappers {
|
||||
|
||||
private static final ThreadLocal<Boolean> PLAIN_COMPONENTS = new ThreadLocal<>();
|
||||
|
||||
private ResultMappers() {}
|
||||
|
||||
public static boolean isPlainComponentContext() {
|
||||
return PLAIN_COMPONENTS.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to run the provided {@link Runnable} where {@link PlaceholderService}s
|
||||
* will replace {@link com.discordsrv.api.component.MinecraftComponent}s
|
||||
* as plain without formatting (instead of converting to Discord formatting).
|
||||
* @param runnable a task that will be executed immediately
|
||||
*/
|
||||
public static void runInPlainComponentContext(Runnable runnable) {
|
||||
getInPlainComponentContext(() -> {
|
||||
runnable.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to run the provided {@link Runnable} where {@link PlaceholderService}s
|
||||
* will replace {@link com.discordsrv.api.component.MinecraftComponent}s
|
||||
* as plain without formatting (instead of converting to Discord formatting).
|
||||
* @param supplier a supplier that will be executed immediately
|
||||
* @return the output of the supplier provided as parameter
|
||||
*/
|
||||
public static <T> T getInPlainComponentContext(Supplier<T> supplier) {
|
||||
PLAIN_COMPONENTS.set(true);
|
||||
T output = supplier.get();
|
||||
PLAIN_COMPONENTS.set(false);
|
||||
return output;
|
||||
}
|
||||
}
|
@ -1,3 +1,26 @@
|
||||
/*
|
||||
* This file is part of the DiscordSRV API, licensed under the MIT License
|
||||
* Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.discordsrv.api.placeholder.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -46,12 +46,8 @@ allprojects {
|
||||
runtimeDownloadOnly.extendsFrom runtimeDownloadApi
|
||||
}
|
||||
|
||||
ext {
|
||||
dependenciesDirectory = new File(sourceSets.main.output.resourcesDir, 'dependencies')
|
||||
}
|
||||
|
||||
generateRuntimeDownloadResourceForRuntimeDownloadOnly {
|
||||
fileLocation = new File(dependenciesDirectory, 'runtimeDownload-' + project.name + '.txt')
|
||||
file = 'runtimeDownload-' + project.name + '.txt'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -3,23 +3,25 @@ import dev.vankka.dependencydownload.task.GenerateDependencyDownloadResourceTask
|
||||
configurations {
|
||||
h2Driver
|
||||
mysqlDriver
|
||||
testRuntimeOnly.extendsFrom runtimeDownloadOnly
|
||||
}
|
||||
|
||||
task generateResourceForH2Driver(type: GenerateDependencyDownloadResourceTask) {
|
||||
var conf = configurations.h2Driver
|
||||
configuration = conf
|
||||
fileLocation = new File(dependenciesDirectory, conf.name + '.txt')
|
||||
file = conf.name + '.txt'
|
||||
}
|
||||
task generateResourceForMySQLDriver(type: GenerateDependencyDownloadResourceTask) {
|
||||
var conf = configurations.mysqlDriver
|
||||
configuration = conf
|
||||
fileLocation = new File(dependenciesDirectory, conf.name + '.txt')
|
||||
file = conf.name + '.txt'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// API
|
||||
annotationProcessor project(':api')
|
||||
compileOnlyApi project(':api')
|
||||
testImplementation project(':api')
|
||||
|
||||
// DependencyDownload
|
||||
api 'dev.vankka.dependencydownload:runtime:' + rootProject.ddVersion
|
||||
@ -55,7 +57,7 @@ dependencies {
|
||||
compileOnlyApi 'org.slf4j:slf4j-api:1.7.32'
|
||||
}
|
||||
|
||||
jar {
|
||||
processResources {
|
||||
dependsOn(
|
||||
generateRuntimeDownloadResourceForRuntimeDownloadOnly,
|
||||
generateResourceForH2Driver,
|
||||
|
@ -246,7 +246,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
discordConnectionManager.connect().join();
|
||||
|
||||
// Placeholder result stringifiers
|
||||
placeholderService().addResultStringifier(new ComponentResultStringifier(this));
|
||||
placeholderService().addResultMapper(new ComponentResultStringifier(this));
|
||||
|
||||
// Register PlayerProvider listeners
|
||||
playerProvider().subscribe();
|
||||
|
@ -172,7 +172,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
// Map JDA objects to 1st party API objects
|
||||
Set<Object> newContext = new HashSet<>();
|
||||
boolean anyConverted = false;
|
||||
for (Object o : event.getContext()) {
|
||||
for (Object o : event.getContexts()) {
|
||||
Object converted;
|
||||
boolean isConversion = true;
|
||||
if (o instanceof PrivateChannel) {
|
||||
|
@ -70,6 +70,7 @@ public class DiscordChatListener extends AbstractListener {
|
||||
ReceivedDiscordMessage discordMessage = event.getDiscordMessage();
|
||||
DiscordUser author = discordMessage.getAuthor();
|
||||
Optional<DiscordGuildMember> member = discordMessage.getMember();
|
||||
boolean webhookMessage = discordMessage.isWebhookMessage();
|
||||
|
||||
OrDefault<Pair<GameChannel, BaseChannelConfig>> channelPair = discordSRV.channelConfig().orDefault(channel);
|
||||
GameChannel gameChannel = channelPair.get(Pair::getKey);
|
||||
@ -82,7 +83,6 @@ public class DiscordChatListener extends AbstractListener {
|
||||
|
||||
DiscordToMinecraftChatConfig.Ignores ignores = chatConfig.get(cfg -> cfg.ignores);
|
||||
if (ignores != null) {
|
||||
boolean webhookMessage = discordMessage.isWebhookMessage();
|
||||
if (ignores.webhooks && webhookMessage) {
|
||||
return;
|
||||
} else if (ignores.bots && (author.isBot() && !webhookMessage)) {
|
||||
@ -105,7 +105,9 @@ public class DiscordChatListener extends AbstractListener {
|
||||
}
|
||||
}
|
||||
|
||||
String format = chatConfig.get(cfg -> cfg.format.replace("\\n", "\n"));
|
||||
String format = chatConfig.opt(cfg -> webhookMessage ? cfg.format : cfg.webhookFormat)
|
||||
.map(option -> option.replace("\\n", "\n"))
|
||||
.orElse(null);
|
||||
if (format == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -19,14 +19,15 @@
|
||||
package com.discordsrv.common.placeholder;
|
||||
|
||||
import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.api.placeholder.PlaceholderResultStringifier;
|
||||
import com.discordsrv.api.placeholder.mapper.PlaceholderResultMapper;
|
||||
import com.discordsrv.api.placeholder.mapper.ResultMappers;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ComponentResultStringifier implements PlaceholderResultStringifier {
|
||||
public class ComponentResultStringifier implements PlaceholderResultMapper {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
@ -35,13 +36,13 @@ public class ComponentResultStringifier implements PlaceholderResultStringifier
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertPlaceholderResult(@NotNull Object result) {
|
||||
public String convertResult(@NotNull Object result) {
|
||||
if (result instanceof MinecraftComponent) {
|
||||
result = ComponentUtil.fromAPI((MinecraftComponent) result);
|
||||
}
|
||||
if (result instanceof Component) {
|
||||
Component component = (Component) result;
|
||||
if (PLAIN_COMPONENT_CONTEXT.get()) {
|
||||
if (ResultMappers.isPlainComponentContext()) {
|
||||
return PlainTextComponentSerializer.plainText().serialize(component);
|
||||
} else {
|
||||
return discordSRV.componentFactory().discordSerializer().serialize(component);
|
||||
|
@ -21,7 +21,7 @@ package com.discordsrv.common.placeholder;
|
||||
import com.discordsrv.api.event.events.placeholder.PlaceholderLookupEvent;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.PlaceholderLookupResult;
|
||||
import com.discordsrv.api.placeholder.PlaceholderResultStringifier;
|
||||
import com.discordsrv.api.placeholder.mapper.PlaceholderResultMapper;
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
@ -29,6 +29,7 @@ import com.discordsrv.common.placeholder.provider.AnnotationPlaceholderProvider;
|
||||
import com.discordsrv.common.placeholder.provider.PlaceholderProvider;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -47,7 +48,8 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final LoadingCache<Class<?>, Set<PlaceholderProvider>> classProviders;
|
||||
private final Set<PlaceholderResultStringifier> stringifiers = new CopyOnWriteArraySet<>();
|
||||
private final Set<PlaceholderResultMapper> mappers = new CopyOnWriteArraySet<>();
|
||||
private final Set<Object> globalContext = new CopyOnWriteArraySet<>();
|
||||
|
||||
public PlaceholderServiceImpl(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
@ -57,10 +59,18 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
.build(new ClassProviderLoader());
|
||||
}
|
||||
|
||||
public void addGlobalContext(@NotNull Object context) {
|
||||
globalContext.add(context);
|
||||
}
|
||||
|
||||
public void removeGlobalContext(@NotNull Object context) {
|
||||
globalContext.remove(context);
|
||||
}
|
||||
|
||||
private static Set<Object> getArrayAsSet(Object[] array) {
|
||||
return array.length == 0
|
||||
? Collections.emptySet()
|
||||
: new HashSet<>(Arrays.asList(array));
|
||||
? Collections.emptySet()
|
||||
: new HashSet<>(Arrays.asList(array));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -69,34 +79,40 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlaceholderLookupResult lookupPlaceholder(@NotNull String placeholder, @NotNull Set<Object> context) {
|
||||
for (Object o : context) {
|
||||
if (o == null) {
|
||||
public PlaceholderLookupResult lookupPlaceholder(@NotNull String placeholder, @NotNull Set<Object> lookupContexts) {
|
||||
Set<Object> contexts = new HashSet<>(lookupContexts);
|
||||
contexts.addAll(globalContext);
|
||||
for (Object context : contexts) {
|
||||
if (context == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (o instanceof PlaceholderProvider) {
|
||||
PlaceholderLookupResult result = ((PlaceholderProvider) o).lookup(placeholder, context);
|
||||
if (context instanceof PlaceholderProvider) {
|
||||
PlaceholderLookupResult result = ((PlaceholderProvider) context).lookup(placeholder, contexts);
|
||||
if (result.getType() != PlaceholderLookupResult.Type.UNKNOWN_PLACEHOLDER) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Set<PlaceholderProvider> providers = classProviders.get(o instanceof Class ? (Class<?>) o : o.getClass());
|
||||
Set<PlaceholderProvider> providers = classProviders
|
||||
.get(context instanceof Class
|
||||
? (Class<?>) context
|
||||
: context.getClass());
|
||||
if (providers == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (PlaceholderProvider provider : providers) {
|
||||
PlaceholderLookupResult result = provider.lookup(placeholder, context);
|
||||
PlaceholderLookupResult result = provider.lookup(placeholder, contexts);
|
||||
if (result.getType() != PlaceholderLookupResult.Type.UNKNOWN_PLACEHOLDER) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only go through this if a placeholder couldn't be looked up from the context
|
||||
PlaceholderLookupEvent lookupEvent = new PlaceholderLookupEvent(placeholder, context);
|
||||
// Only go through this if a placeholder couldn't be looked up from lookup/global contexts
|
||||
// API users are here as to not interfere with DiscordSRV's own placeholders
|
||||
PlaceholderLookupEvent lookupEvent = new PlaceholderLookupEvent(placeholder, contexts);
|
||||
discordSRV.eventBus().publish(lookupEvent);
|
||||
|
||||
return lookupEvent.isProcessed()
|
||||
@ -110,13 +126,13 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResultStringifier(@NotNull PlaceholderResultStringifier resultStringifier) {
|
||||
stringifiers.add(resultStringifier);
|
||||
public void addResultMapper(@NotNull PlaceholderResultMapper resultMapper) {
|
||||
mappers.add(resultMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResultStringifier(@NotNull PlaceholderResultStringifier resultStringifier) {
|
||||
stringifiers.remove(resultStringifier);
|
||||
public void removeResultMapper(@NotNull PlaceholderResultMapper resultMapper) {
|
||||
mappers.remove(resultMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -129,9 +145,9 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
|
||||
String output = input;
|
||||
while (matcher.find()) {
|
||||
String placeholder = matcher.group(2);
|
||||
PlaceholderLookupResult result = resolve(placeholder, context);
|
||||
output = updateContent(result, placeholder, matcher, output);
|
||||
String placeholder = getPlaceholder(matcher);
|
||||
List<PlaceholderLookupResult> results = resolve(placeholder, context);
|
||||
output = updateContent(results, placeholder, matcher, output);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
@ -141,9 +157,21 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
if (matcher.groupCount() < 3) {
|
||||
throw new IllegalStateException("Matcher must have at least 3 groups");
|
||||
}
|
||||
|
||||
String placeholder = getPlaceholder(matcher);
|
||||
List<PlaceholderLookupResult> results = resolve(placeholder, context);
|
||||
return getResultRepresentation(results, placeholder, matcher);
|
||||
}
|
||||
|
||||
private String getPlaceholder(Matcher matcher) {
|
||||
String placeholder = matcher.group(2);
|
||||
PlaceholderLookupResult result = resolve(matcher, context);
|
||||
return getResultRepresentation(result, placeholder, matcher);
|
||||
Pattern pattern = matcher.pattern();
|
||||
if (PATTERN.equals(pattern)) { // Remove escapes for %
|
||||
placeholder = placeholder.replace("\\%", "%");
|
||||
} else if (RECURSIVE_PATTERN.equals(pattern)) { // Remove escapes for { and }
|
||||
placeholder = placeholder.replaceAll("\\\\([{}])", "$1");
|
||||
}
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -155,45 +183,37 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
private String getResultAsString(Object result) {
|
||||
if (result == null) {
|
||||
return "null";
|
||||
} else if (result instanceof String) {
|
||||
return (String) result;
|
||||
} else if (result instanceof CharSequence) {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
String output = null;
|
||||
for (PlaceholderResultStringifier stringifier : stringifiers) {
|
||||
output = stringifier.convertPlaceholderResult(result);
|
||||
Object output = null;
|
||||
for (PlaceholderResultMapper stringifier : mappers) {
|
||||
output = stringifier.convertResult(result);
|
||||
if (output != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (output == null) {
|
||||
output = String.valueOf(result);
|
||||
}
|
||||
return output;
|
||||
|
||||
return String.valueOf(output != null ? output : result);
|
||||
}
|
||||
|
||||
public PlaceholderLookupResult resolve(Matcher matcher, Set<Object> context) {
|
||||
return resolve(matcher.group(2), context);
|
||||
}
|
||||
|
||||
private PlaceholderLookupResult resolve(String placeholder, Set<Object> context) {
|
||||
private List<PlaceholderLookupResult> resolve(String placeholder, Set<Object> context) {
|
||||
// Recursive
|
||||
placeholder = getReplacement(RECURSIVE_PATTERN, placeholder, context);
|
||||
|
||||
return lookupPlaceholder(placeholder, context);
|
||||
List<PlaceholderLookupResult> results = new ArrayList<>();
|
||||
for (String part : placeholder.split("(?<!\\\\)\\|")) {
|
||||
results.add(lookupPlaceholder(part, context));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private String updateContent(
|
||||
PlaceholderLookupResult result, String placeholder, Matcher matcher, String input) {
|
||||
Object representation = getResultRepresentation(result, placeholder, matcher);
|
||||
private String updateContent(List<PlaceholderLookupResult> results, String placeholder, Matcher matcher, String input) {
|
||||
Object representation = getResultRepresentation(results, placeholder, matcher);
|
||||
|
||||
String output = getResultAsString(representation);
|
||||
for (PlaceholderResultStringifier stringifier : stringifiers) {
|
||||
output = stringifier.convertPlaceholderResult(representation);
|
||||
if (output != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (output == null) {
|
||||
output = String.valueOf(representation);
|
||||
}
|
||||
@ -206,38 +226,47 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
.replaceFirst(output);
|
||||
}
|
||||
|
||||
private Object getResultRepresentation(PlaceholderLookupResult result, String placeholder, Matcher matcher) {
|
||||
while (result != null) {
|
||||
PlaceholderLookupResult.Type type = result.getType();
|
||||
if (type == PlaceholderLookupResult.Type.UNKNOWN_PLACEHOLDER) {
|
||||
break;
|
||||
}
|
||||
private Object getResultRepresentation(List<PlaceholderLookupResult> results, String placeholder, Matcher matcher) {
|
||||
Object best = null;
|
||||
for (PlaceholderLookupResult result : results) {
|
||||
while (result != null) {
|
||||
PlaceholderLookupResult.Type type = result.getType();
|
||||
if (type == PlaceholderLookupResult.Type.UNKNOWN_PLACEHOLDER) {
|
||||
break;
|
||||
}
|
||||
|
||||
boolean newLookup = false;
|
||||
Object replacement = null;
|
||||
switch (type) {
|
||||
case SUCCESS:
|
||||
replacement = result.getValue();
|
||||
boolean newLookup = false;
|
||||
Object replacement = null;
|
||||
switch (type) {
|
||||
case SUCCESS:
|
||||
replacement = result.getValue();
|
||||
if (replacement != null && StringUtils.isNotBlank(getResultAsString(replacement))) {
|
||||
return replacement;
|
||||
}
|
||||
break;
|
||||
case DATA_NOT_AVAILABLE:
|
||||
replacement = "Unavailable";
|
||||
break;
|
||||
case LOOKUP_FAILED:
|
||||
replacement = "Error";
|
||||
break;
|
||||
case NEW_LOOKUP:
|
||||
result = lookupPlaceholder((String) result.getValue(), result.getExtras());
|
||||
newLookup = true;
|
||||
break;
|
||||
}
|
||||
if (replacement != null) {
|
||||
best = replacement;
|
||||
}
|
||||
if (!newLookup) {
|
||||
break;
|
||||
case DATA_NOT_AVAILABLE:
|
||||
replacement = "Unavailable";
|
||||
break;
|
||||
case LOOKUP_FAILED:
|
||||
replacement = "Error";
|
||||
break;
|
||||
case NEW_LOOKUP:
|
||||
result = lookupPlaceholder((String) result.getValue(), result.getExtras());
|
||||
newLookup = true;
|
||||
break;
|
||||
}
|
||||
if (replacement != null) {
|
||||
return replacement;
|
||||
}
|
||||
if (!newLookup) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matcher.group(1) + placeholder + matcher.group(3);
|
||||
|
||||
return best != null
|
||||
? best
|
||||
: matcher.group(1) + placeholder + matcher.group(3);
|
||||
}
|
||||
|
||||
private static class ClassProviderLoader implements CacheLoader<Class<?>, Set<PlaceholderProvider>> {
|
||||
|
@ -92,7 +92,7 @@ public class AnnotationPlaceholderProvider implements PlaceholderProvider {
|
||||
String reLookup = annotation.relookup();
|
||||
if (!reLookup.isEmpty()) {
|
||||
if (result == null) {
|
||||
return PlaceholderLookupResult.success("");
|
||||
return PlaceholderLookupResult.success(null);
|
||||
}
|
||||
|
||||
Set<Object> newContext = new HashSet<>(context);
|
||||
@ -101,6 +101,8 @@ public class AnnotationPlaceholderProvider implements PlaceholderProvider {
|
||||
return PlaceholderLookupResult.newLookup(newPlaceholder, newContext);
|
||||
}
|
||||
|
||||
return PlaceholderLookupResult.success(result);
|
||||
return result instanceof PlaceholderLookupResult
|
||||
? (PlaceholderLookupResult) result
|
||||
: PlaceholderLookupResult.success(result);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,21 @@
|
||||
/*
|
||||
* This file is part of DiscordSRV, licensed under the GPLv3 License
|
||||
* Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common;
|
||||
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
|
@ -1,3 +1,21 @@
|
||||
/*
|
||||
* This file is part of DiscordSRV, licensed under the GPLv3 License
|
||||
* Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.event.bus;
|
||||
|
||||
import com.discordsrv.api.event.bus.EventBus;
|
||||
|
@ -1,3 +1,21 @@
|
||||
/*
|
||||
* This file is part of DiscordSRV, licensed under the GPLv3 License
|
||||
* Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.placeholder;
|
||||
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
@ -9,7 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class PlaceholderServiceTest {
|
||||
|
||||
private PlaceholderService service = MockDiscordSRV.INSTANCE.placeholderService();
|
||||
private final PlaceholderService service = MockDiscordSRV.INSTANCE.placeholderService();
|
||||
|
||||
@Test
|
||||
public void staticFieldTest() {
|
||||
@ -36,6 +54,21 @@ public class PlaceholderServiceTest {
|
||||
assertEquals("e", service.replacePlaceholders("%static_method_with_context%", PlaceholderContext.class, "e"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orPrimaryTest() {
|
||||
assertEquals("a", service.replacePlaceholders("%static_field|static_method%", PlaceholderContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orSecondaryTest() {
|
||||
assertEquals("b", service.replacePlaceholders("%invalid|static_method%", PlaceholderContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orEmptyTest() {
|
||||
assertEquals("b", service.replacePlaceholders("%empty|static_method%", PlaceholderContext.class));
|
||||
}
|
||||
|
||||
public static class PlaceholderContext {
|
||||
|
||||
@Placeholder("static_field")
|
||||
@ -46,6 +79,9 @@ public class PlaceholderServiceTest {
|
||||
return "b";
|
||||
}
|
||||
|
||||
@Placeholder("empty")
|
||||
public static String EMPTY = "";
|
||||
|
||||
@Placeholder("object_field")
|
||||
public String localField = "c";
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user