Placeholder service improvements, or placeholders. Buildscript fixes/improvements related to testing/dependencydownload

This commit is contained in:
Vankka 2021-10-21 02:16:06 +03:00
parent b2a970d8c6
commit f35d8d67ee
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
17 changed files with 314 additions and 129 deletions

View File

@ -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()));
});

View File

@ -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
*/

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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,

View File

@ -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();

View File

@ -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) {

View File

@ -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;
}

View File

@ -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);

View File

@ -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,6 +59,14 @@ 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()
@ -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));
}
private String updateContent(
PlaceholderLookupResult result, String placeholder, Matcher matcher, String input) {
Object representation = getResultRepresentation(result, placeholder, matcher);
return results;
}
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,7 +226,9 @@ public class PlaceholderServiceImpl implements PlaceholderService {
.replaceFirst(output);
}
private Object getResultRepresentation(PlaceholderLookupResult result, String placeholder, Matcher matcher) {
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) {
@ -218,6 +240,9 @@ public class PlaceholderServiceImpl implements PlaceholderService {
switch (type) {
case SUCCESS:
replacement = result.getValue();
if (replacement != null && StringUtils.isNotBlank(getResultAsString(replacement))) {
return replacement;
}
break;
case DATA_NOT_AVAILABLE:
replacement = "Unavailable";
@ -231,13 +256,17 @@ public class PlaceholderServiceImpl implements PlaceholderService {
break;
}
if (replacement != null) {
return replacement;
best = 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>> {

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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";