From 931663693923eb4625a1e42fdb823aaf460c3138 Mon Sep 17 00:00:00 2001 From: Andre_601 <11576465+Andre601@users.noreply.github.com> Date: Sat, 13 Jun 2020 18:16:42 +0200 Subject: [PATCH 01/11] Added "Installed expansions" section --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1798a47..7ffd365 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -38,3 +38,7 @@ Please also make sure that you use the [latest release][Spigot] or the latest [d > What steps did you made, to get this bug? 1. + +### Installed expansions +> Please use all expansions that are listed when running `/papi list` + From 53736888a57470ee58fd922dcbc7b8ce0c0265fc Mon Sep 17 00:00:00 2001 From: Andre_601 <11576465+Andre601@users.noreply.github.com> Date: Tue, 21 Jul 2020 01:26:21 +0200 Subject: [PATCH 02/11] grammar --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7ffd365..7dc4766 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -40,5 +40,5 @@ Please also make sure that you use the [latest release][Spigot] or the latest [d 1. ### Installed expansions -> Please use all expansions that are listed when running `/papi list` +> Please list all expansions that are displayed when running `/papi list` From 65f04ba70e5e7851023bc98e67bc69b26a12f347 Mon Sep 17 00:00:00 2001 From: Sxtanna Date: Sun, 26 Jul 2020 13:39:33 -0400 Subject: [PATCH 03/11] updated cloud expansion list to be sorted via a configuration option of values [name, author, latest]. closes #363 --- .../cloud/CommandECloudExpansionList.java | 4 +-- .../configuration/ExpansionSort.java | 31 +++++++++++++++++++ .../configuration/PlaceholderAPIConfig.java | 18 +++++++++++ src/main/resources/config.yml | 1 + 4 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/main/java/me/clip/placeholderapi/configuration/ExpansionSort.java diff --git a/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudExpansionList.java b/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudExpansionList.java index 480102f..d4e4bec 100644 --- a/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudExpansionList.java +++ b/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudExpansionList.java @@ -7,6 +7,7 @@ import com.google.common.collect.Sets; import com.google.common.primitives.Ints; import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.commands.PlaceholderCommand; +import me.clip.placeholderapi.configuration.ExpansionSort; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.cloud.CloudExpansion; import me.clip.placeholderapi.util.Format; @@ -22,7 +23,6 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -85,7 +85,7 @@ public final class CommandECloudExpansionList extends PlaceholderCommand return; } - expansions.sort(Comparator.comparing(CloudExpansion::getLastUpdate).reversed()); + expansions.sort(plugin.getPlaceholderAPIConfig().getExpansionSort().orElse(ExpansionSort.LATEST)); if (!(sender instanceof Player) && params.size() < 2) { diff --git a/src/main/java/me/clip/placeholderapi/configuration/ExpansionSort.java b/src/main/java/me/clip/placeholderapi/configuration/ExpansionSort.java new file mode 100644 index 0000000..b8306d7 --- /dev/null +++ b/src/main/java/me/clip/placeholderapi/configuration/ExpansionSort.java @@ -0,0 +1,31 @@ +package me.clip.placeholderapi.configuration; + +import me.clip.placeholderapi.expansion.cloud.CloudExpansion; +import org.jetbrains.annotations.NotNull; + +import java.util.Comparator; + +public enum ExpansionSort implements Comparator +{ + + NAME(Comparator.comparing(CloudExpansion::getName)), + AUTHOR(Comparator.comparing(CloudExpansion::getAuthor)), + LATEST(Comparator.comparing(CloudExpansion::getLastUpdate).reversed()); + + + @NotNull + private final Comparator comparator; + + ExpansionSort(@NotNull final Comparator comparator) + { + this.comparator = comparator; + } + + + @Override + public final int compare(final CloudExpansion expansion1, final CloudExpansion expansion2) + { + return comparator.compare(expansion1, expansion2); + } + +} diff --git a/src/main/java/me/clip/placeholderapi/configuration/PlaceholderAPIConfig.java b/src/main/java/me/clip/placeholderapi/configuration/PlaceholderAPIConfig.java index 30635e2..5fd4062 100644 --- a/src/main/java/me/clip/placeholderapi/configuration/PlaceholderAPIConfig.java +++ b/src/main/java/me/clip/placeholderapi/configuration/PlaceholderAPIConfig.java @@ -23,6 +23,8 @@ package me.clip.placeholderapi.configuration; import me.clip.placeholderapi.PlaceholderAPIPlugin; import org.jetbrains.annotations.NotNull; +import java.util.Optional; + public final class PlaceholderAPIConfig { @@ -64,6 +66,22 @@ public final class PlaceholderAPIConfig } + public Optional getExpansionSort() + { + final String option = plugin.getConfig().getString("cloud_sorting", ExpansionSort.LATEST.name()); + + try + { + //noinspection ConstantConditions (bad spigot annotation) + return Optional.of(ExpansionSort.valueOf(option.toUpperCase())); + } + catch (final IllegalArgumentException ignored) + { + return Optional.empty(); + } + } + + @NotNull public String dateFormat() { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 1202dba..6fa0861 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -10,6 +10,7 @@ # Download placeholders: /papi ecloud check_updates: true cloud_enabled: true +cloud_sorting: "name" cloud_allow_unverified_expansions: false boolean: 'true': 'yes' From b4645904913b8d7f71ea4d08f14d8d390dc2d575 Mon Sep 17 00:00:00 2001 From: Sxtanna Date: Sun, 26 Jul 2020 16:26:42 -0400 Subject: [PATCH 04/11] added dump command, closes #366 --- .../commands/PlaceholderCommandRouter.java | 2 + .../commands/impl/local/CommandDump.java | 181 ++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 src/main/java/me/clip/placeholderapi/commands/impl/local/CommandDump.java diff --git a/src/main/java/me/clip/placeholderapi/commands/PlaceholderCommandRouter.java b/src/main/java/me/clip/placeholderapi/commands/PlaceholderCommandRouter.java index cc75744..c8d2a48 100644 --- a/src/main/java/me/clip/placeholderapi/commands/PlaceholderCommandRouter.java +++ b/src/main/java/me/clip/placeholderapi/commands/PlaceholderCommandRouter.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.commands.impl.cloud.CommandECloud; +import me.clip.placeholderapi.commands.impl.local.CommandDump; import me.clip.placeholderapi.commands.impl.local.CommandExpansionRegister; import me.clip.placeholderapi.commands.impl.local.CommandExpansionUnregister; import me.clip.placeholderapi.commands.impl.local.CommandHelp; @@ -35,6 +36,7 @@ public final class PlaceholderCommandRouter implements CommandExecutor, TabCompl private static final List COMMANDS = ImmutableList.of(new CommandHelp(), new CommandInfo(), new CommandList(), + new CommandDump(), new CommandECloud(), new CommandParse(), new CommandReload(), diff --git a/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandDump.java b/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandDump.java new file mode 100644 index 0000000..54012e1 --- /dev/null +++ b/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandDump.java @@ -0,0 +1,181 @@ +package me.clip.placeholderapi.commands.impl.local; + +import com.google.common.io.CharStreams; +import com.google.gson.JsonParser; +import me.clip.placeholderapi.PlaceholderAPIPlugin; +import me.clip.placeholderapi.commands.PlaceholderCommand; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import me.clip.placeholderapi.util.Msg; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.logging.Level; +import java.util.stream.Collectors; + +public final class CommandDump extends PlaceholderCommand +{ + + @NotNull + private static final String URL = "https://paste.helpch.at/"; + + @NotNull + private static final JsonParser JSON_PARSER = new JsonParser(); + @NotNull + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG) + .withLocale(Locale.US) + .withZone(ZoneId.of("UTC")); + + + public CommandDump() + { + super("dump"); + } + + @Override + public void evaluate(@NotNull final PlaceholderAPIPlugin plugin, @NotNull final CommandSender sender, @NotNull final String alias, @NotNull @Unmodifiable final List params) + { + postDump(makeDump(plugin)).whenComplete((key, exception) -> { + if (exception != null) + { + plugin.getLogger().log(Level.WARNING, "failed to post dump details", exception); + + Msg.msg(sender, + "&cFailed to post dump details, check console."); + return; + } + + Msg.msg(sender, + "&aSuccessfully posted dump: " + URL + key); + }); + } + + @NotNull + private CompletableFuture postDump(@NotNull final String dump) + { + return CompletableFuture.supplyAsync(() -> { + try + { + final HttpURLConnection connection = ((HttpURLConnection) new URL(URL + "documents").openConnection()); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "text/plain; charset=utf-8"); + connection.setDoOutput(true); + + connection.connect(); + + try (final OutputStream stream = connection.getOutputStream()) + { + stream.write(dump.getBytes(StandardCharsets.UTF_8)); + } + + try (final InputStream stream = connection.getInputStream()) + { + //noinspection UnstableApiUsage + final String json = CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8)); + return JSON_PARSER.parse(json).getAsJsonObject().get("key").getAsString(); + } + } + catch (final IOException ex) + { + throw new CompletionException(ex); + } + }); + } + + @NotNull + private String makeDump(@NotNull final PlaceholderAPIPlugin plugin) + { + final StringBuilder builder = new StringBuilder(); + + builder.append("Generated: ") + .append(DATE_FORMAT.format(Instant.now())) + .append("\n\n"); + + builder.append("PlaceholderAPI: ") + .append(plugin.getDescription().getVersion()) + .append("\n\n"); + + builder.append("Expansions Registered:") + .append('\n'); + + + final Map> expansions = plugin.getLocalExpansionManager() + .getExpansions() + .stream() + .collect(Collectors.groupingBy(PlaceholderExpansion::getAuthor)); + + for (final Map.Entry> expansionsByAuthor : expansions.entrySet()) + { + builder.append(" ") + .append(expansionsByAuthor.getKey()) + .append(": ") + .append('\n'); + + for (final PlaceholderExpansion expansion : expansionsByAuthor.getValue()) + { + builder.append(" ") + .append(expansion.getName()) + .append(':') + .append(expansion.getVersion()) + .append('\n'); + } + } + + builder.append('\n'); + + builder.append("Expansions Directory:") + .append('\n'); + + final String[] jars = plugin.getLocalExpansionManager() + .getExpansionsFolder() + .list((dir, name) -> name.toLowerCase().endsWith(".jar")); + + for (final String jar : jars) + { + builder.append(" ") + .append(jar) + .append('\n'); + } + + builder.append('\n'); + + + builder.append("Server Info: ") + .append(plugin.getServer().getBukkitVersion()) + .append('/') + .append(plugin.getServer().getVersion()) + .append("\n\n"); + + builder.append("Plugin Info:") + .append('\n'); + + for (final Plugin other : plugin.getServer().getPluginManager().getPlugins()) + { + builder.append(" ") + .append(other.getName()) + .append(": ") + .append(other.getDescription().getVersion()) + .append('\n'); + } + + return builder.toString(); + } + +} From c3e0c1fb64564663155a7d92a495fbb0b1d22b08 Mon Sep 17 00:00:00 2001 From: Sxtanna Date: Sun, 26 Jul 2020 17:32:28 -0400 Subject: [PATCH 05/11] updated gson 2.8.5 -> 2.8.6 --- build.gradle | 2 +- .../clip/placeholderapi/commands/impl/local/CommandDump.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 61506a6..ec2eb6c 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ repositories { } dependencies { - implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.code.gson:gson:2.8.6' implementation 'org.bstats:bstats-bukkit:1.5' implementation 'me.rayzr522:jsonmessage:1.2.1' diff --git a/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandDump.java b/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandDump.java index 54012e1..d66a160 100644 --- a/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandDump.java +++ b/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandDump.java @@ -36,8 +36,6 @@ public final class CommandDump extends PlaceholderCommand @NotNull private static final String URL = "https://paste.helpch.at/"; - @NotNull - private static final JsonParser JSON_PARSER = new JsonParser(); @NotNull private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG) .withLocale(Locale.US) @@ -89,7 +87,7 @@ public final class CommandDump extends PlaceholderCommand { //noinspection UnstableApiUsage final String json = CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8)); - return JSON_PARSER.parse(json).getAsJsonObject().get("key").getAsString(); + return JsonParser.parseString(json).getAsJsonObject().get("key").getAsString(); } } catch (final IOException ex) From 86002f50e690a450c28ba065cb488e061371e9fc Mon Sep 17 00:00:00 2001 From: Sxtanna Date: Sun, 26 Jul 2020 18:03:31 -0400 Subject: [PATCH 06/11] updated to phase out PlaceholderHook --- .../placeholderapi/PlaceholderAPIPlugin.java | 40 ++--- .../replacer/CharsReplacer.java | 7 +- .../replacer/RegexReplacer.java | 11 +- .../placeholderapi/replacer/Replacer.java | 4 +- .../java/me/clip/placeholderapi/Values.java | 153 +++--------------- .../replacer/ReplacerUnitTester.java | 8 +- 6 files changed, 48 insertions(+), 175 deletions(-) diff --git a/src/main/java/me/clip/placeholderapi/PlaceholderAPIPlugin.java b/src/main/java/me/clip/placeholderapi/PlaceholderAPIPlugin.java index fa32fe9..e973fb3 100644 --- a/src/main/java/me/clip/placeholderapi/PlaceholderAPIPlugin.java +++ b/src/main/java/me/clip/placeholderapi/PlaceholderAPIPlugin.java @@ -181,40 +181,20 @@ public final class PlaceholderAPIPlugin extends JavaPlugin private void setupMetrics() { final Metrics metrics = new Metrics(this); - metrics.addCustomChart( - new Metrics.SimplePie( - "using_expansion_cloud", - () -> getPlaceholderAPIConfig().isCloudEnabled() ? "yes" : "no")); + metrics.addCustomChart(new Metrics.SimplePie("using_expansion_cloud", () -> getPlaceholderAPIConfig().isCloudEnabled() ? "yes" : "no")); - metrics.addCustomChart( - new Metrics.SimplePie("using_spigot", () -> getServerVersion().isSpigot() ? "yes" : "no")); + metrics.addCustomChart(new Metrics.SimplePie("using_spigot", () -> getServerVersion().isSpigot() ? "yes" : "no")); - metrics.addCustomChart( - new Metrics.AdvancedPie( - "expansions_used", - () -> { - Map map = new HashMap<>(); - Map hooks = PlaceholderAPI.getPlaceholders(); + metrics.addCustomChart(new Metrics.AdvancedPie("expansions_used", () -> { + final Map values = new HashMap<>(); - if (!hooks.isEmpty()) - { + for (final PlaceholderExpansion expansion : getLocalExpansionManager().getExpansions()) + { + values.put(expansion.getRequiredPlugin() == null ? expansion.getIdentifier() : expansion.getRequiredPlugin(), 1); + } - for (PlaceholderHook hook : hooks.values()) - { - if (hook instanceof PlaceholderExpansion) - { - PlaceholderExpansion expansion = (PlaceholderExpansion) hook; - map.put( - expansion.getRequiredPlugin() == null - ? expansion.getIdentifier() - : expansion.getRequiredPlugin(), - 1); - } - } - } - - return map; - })); + return values; + })); } private void setupExpansions() diff --git a/src/main/java/me/clip/placeholderapi/replacer/CharsReplacer.java b/src/main/java/me/clip/placeholderapi/replacer/CharsReplacer.java index db9f6d8..4de1f4b 100644 --- a/src/main/java/me/clip/placeholderapi/replacer/CharsReplacer.java +++ b/src/main/java/me/clip/placeholderapi/replacer/CharsReplacer.java @@ -1,6 +1,6 @@ package me.clip.placeholderapi.replacer; -import me.clip.placeholderapi.PlaceholderHook; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; @@ -20,8 +20,9 @@ public final class CharsReplacer implements Replacer } + @NotNull @Override - public @NotNull String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function lookup) + public String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function lookup) { final char[] chars = text.toCharArray(); final StringBuilder builder = new StringBuilder(text.length()); @@ -142,7 +143,7 @@ public final class CharsReplacer implements Replacer continue; } - final PlaceholderHook placeholder = lookup.apply(identifierString); + final PlaceholderExpansion placeholder = lookup.apply(identifierString); if (placeholder == null) { builder.append(closure.head).append(identifierString); diff --git a/src/main/java/me/clip/placeholderapi/replacer/RegexReplacer.java b/src/main/java/me/clip/placeholderapi/replacer/RegexReplacer.java index 8218384..dbb3b39 100644 --- a/src/main/java/me/clip/placeholderapi/replacer/RegexReplacer.java +++ b/src/main/java/me/clip/placeholderapi/replacer/RegexReplacer.java @@ -1,6 +1,6 @@ package me.clip.placeholderapi.replacer; -import me.clip.placeholderapi.PlaceholderHook; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; @@ -22,8 +22,9 @@ public final class RegexReplacer implements Replacer } + @NotNull @Override - public @NotNull String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function lookup) + public String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function lookup) { final Matcher matcher = pattern.matcher(text); if (!matcher.find()) @@ -38,13 +39,13 @@ public final class RegexReplacer implements Replacer final String identifier = matcher.group("identifier"); final String parameters = matcher.group("parameters"); - final PlaceholderHook hook = lookup.apply(identifier); - if (hook == null) + final PlaceholderExpansion expansion = lookup.apply(identifier); + if (expansion == null) { continue; } - final String requested = hook.onRequest(player, parameters); + final String requested = expansion.onRequest(player, parameters); matcher.appendReplacement(builder, requested != null ? requested : matcher.group(0)); } while (matcher.find()); diff --git a/src/main/java/me/clip/placeholderapi/replacer/Replacer.java b/src/main/java/me/clip/placeholderapi/replacer/Replacer.java index 005c0f7..31ca757 100644 --- a/src/main/java/me/clip/placeholderapi/replacer/Replacer.java +++ b/src/main/java/me/clip/placeholderapi/replacer/Replacer.java @@ -1,6 +1,6 @@ package me.clip.placeholderapi.replacer; -import me.clip.placeholderapi.PlaceholderHook; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -11,7 +11,7 @@ public interface Replacer { @NotNull - String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function lookup); + String apply(@NotNull final String text, @Nullable final OfflinePlayer player, @NotNull final Function lookup); enum Closure diff --git a/src/test/java/me/clip/placeholderapi/Values.java b/src/test/java/me/clip/placeholderapi/Values.java index 37b393c..f27905b 100644 --- a/src/test/java/me/clip/placeholderapi/Values.java +++ b/src/test/java/me/clip/placeholderapi/Values.java @@ -1,17 +1,11 @@ package me.clip.placeholderapi; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.replacer.CharsReplacer; import me.clip.placeholderapi.replacer.RegexReplacer; import me.clip.placeholderapi.replacer.Replacer; -import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Set; -import java.util.function.Function; public interface Values { @@ -19,137 +13,16 @@ public interface Values String SMALL_TEXT = "My name is %player_name%"; String LARGE_TEXT = "My name is %player_name% and my location is (%player_x%, %player_y%, %player_z%), this placeholder is invalid %server_name%"; - ImmutableMap PLACEHOLDERS = ImmutableMap.builder() - .put("player", new MockPlayerPlaceholderHook()) + ImmutableMap PLACEHOLDERS = ImmutableMap.builder() + .put("player", new MockPlayerPlaceholderExpansion()) .build(); Replacer CHARS_REPLACER = new CharsReplacer(Replacer.Closure.PERCENT); Replacer REGEX_REPLACER = new RegexReplacer(Replacer.Closure.PERCENT); - Replacer TESTS_REPLACER = new Replacer() - { - private final Set COLOR_CODES = ImmutableSet.of('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f', 'k', 'l', 'm', 'o', 'r', 'x'); - - private final boolean colorize = true; - private final Replacer.Closure closure = Replacer.Closure.PERCENT; - - @Override - public @NotNull String apply(final @NotNull String text, final @Nullable OfflinePlayer player, final @NotNull Function lookup) - { - char[] chars = text.toCharArray(); - StringBuilder builder = new StringBuilder(chars.length); - - // This won't cause memory leaks. It's inside a method. And we want to use setLength instead of - // creating a new string builder to use the maximum capacity and avoid initializing new objects. - StringBuilder identifier = new StringBuilder(50); - PlaceholderHook handler = null; - - // Stages: - // Stage -1: Look for the color code in the next character. - // Stage 0: No closures detected, or the detected identifier is invalid. We're going forward while appending the characters normally. - // Stage 1: The closure has been detected, looking for the placeholder identifier... - // Stage 2: Detected the identifier and the parameter. Translating the placeholder... - int stage = 0; - - for (char ch : chars) - { - if (stage == -1 && COLOR_CODES.contains(ch)) - { - builder.append(ChatColor.COLOR_CHAR).append(ch); - stage = 0; - continue; - } - - // Check if the placeholder starts or ends. - if (ch == closure.head || ch == closure.tail) - { - // If the placeholder ends. - if (stage == 2) - { - String parameter = identifier.toString(); - String translated = handler.onRequest(player, parameter); - - if (translated == null) - { - String name = ""; - builder.append(closure.head).append(name).append('_').append(parameter).append(closure.tail); - } - else - { - builder.append(translated); - } - - identifier.setLength(0); - stage = 0; - continue; - } - else if (stage == 1) - { // If it just started | Double closures | If it's still hasn't detected the indentifier, reset. - builder.append(closure.head).append(identifier); - } - - identifier.setLength(0); - stage = 1; - continue; - } - - // Placeholder identifier started. - if (stage == 1) - { - // Compare the current character with the idenfitier's. - // We reached the end of our identifier. - if (ch == '_') - { - handler = lookup.apply(identifier.toString()); - if (handler == null) - { - builder.append(closure.head).append(identifier).append('_'); - stage = 0; - } - else - { - identifier.setLength(0); - stage = 2; - } - continue; - } - - // Keep building the identifier name. - identifier.append(ch); - continue; - } - - // Building the placeholder parameter. - if (stage == 2) - { - identifier.append(ch); - continue; - } - - // Nothing placeholder related was found. - if (colorize && ch == '&') - { - stage = -1; - continue; - } - builder.append(ch); - } - - if (identifier != null) - { - if (stage > 0) - { - builder.append(closure.tail); - } - builder.append(identifier); - } - return builder.toString(); - } - }; - final class MockPlayerPlaceholderHook extends PlaceholderHook + final class MockPlayerPlaceholderExpansion extends PlaceholderExpansion { public static final String PLAYER_X = String.valueOf(10); @@ -158,6 +31,24 @@ public interface Values public static final String PLAYER_NAME = "Sxtanna"; + @Override + public String getIdentifier() + { + return "player"; + } + + @Override + public String getAuthor() + { + return "Sxtanna"; + } + + @Override + public String getVersion() + { + return "1.0"; + } + @Override public String onRequest(final OfflinePlayer player, final String params) { diff --git a/src/test/java/me/clip/placeholderapi/replacer/ReplacerUnitTester.java b/src/test/java/me/clip/placeholderapi/replacer/ReplacerUnitTester.java index 02ab33a..c20da41 100644 --- a/src/test/java/me/clip/placeholderapi/replacer/ReplacerUnitTester.java +++ b/src/test/java/me/clip/placeholderapi/replacer/ReplacerUnitTester.java @@ -3,10 +3,10 @@ package me.clip.placeholderapi.replacer; import me.clip.placeholderapi.Values; import org.junit.jupiter.api.Test; -import static me.clip.placeholderapi.Values.MockPlayerPlaceholderHook.PLAYER_NAME; -import static me.clip.placeholderapi.Values.MockPlayerPlaceholderHook.PLAYER_X; -import static me.clip.placeholderapi.Values.MockPlayerPlaceholderHook.PLAYER_Y; -import static me.clip.placeholderapi.Values.MockPlayerPlaceholderHook.PLAYER_Z; +import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_NAME; +import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_X; +import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_Y; +import static me.clip.placeholderapi.Values.MockPlayerPlaceholderExpansion.PLAYER_Z; import static org.junit.jupiter.api.Assertions.assertEquals; public final class ReplacerUnitTester From b63f10f749f260dea2eee96c920c9cff8f5a3f07 Mon Sep 17 00:00:00 2001 From: Sxtanna Date: Sun, 26 Jul 2020 18:36:50 -0400 Subject: [PATCH 07/11] lets pretend no one saw this. --- src/test/java/me/clip/placeholderapi/Values.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/me/clip/placeholderapi/Values.java b/src/test/java/me/clip/placeholderapi/Values.java index f27905b..89e3cad 100644 --- a/src/test/java/me/clip/placeholderapi/Values.java +++ b/src/test/java/me/clip/placeholderapi/Values.java @@ -25,9 +25,9 @@ public interface Values final class MockPlayerPlaceholderExpansion extends PlaceholderExpansion { - public static final String PLAYER_X = String.valueOf(10); - public static final String PLAYER_Y = String.valueOf(20); - public static final String PLAYER_Z = String.valueOf(30); + public static final String PLAYER_X = "10"; + public static final String PLAYER_Y = "20"; + public static final String PLAYER_Z = "30"; public static final String PLAYER_NAME = "Sxtanna"; From a160f3abc95c39dfd29cb44c9ec82621f50c32ad Mon Sep 17 00:00:00 2001 From: Sxtanna Date: Sun, 26 Jul 2020 18:46:00 -0400 Subject: [PATCH 08/11] updated to further phase out PlaceholderHook, added contracts to expansion methods --- .../clip/placeholderapi/PlaceholderHook.java | 8 + .../expansion/PlaceholderExpansion.java | 376 ++++++++++-------- .../java/me/clip/placeholderapi/Values.java | 7 +- 3 files changed, 230 insertions(+), 161 deletions(-) diff --git a/src/main/java/me/clip/placeholderapi/PlaceholderHook.java b/src/main/java/me/clip/placeholderapi/PlaceholderHook.java index 629143c..4378c32 100644 --- a/src/main/java/me/clip/placeholderapi/PlaceholderHook.java +++ b/src/main/java/me/clip/placeholderapi/PlaceholderHook.java @@ -25,6 +25,10 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * @deprecated This class will be completely removed in the next release, please use {@link me.clip.placeholderapi.expansion.PlaceholderExpansion} + */ +@Deprecated public abstract class PlaceholderHook { @@ -35,8 +39,10 @@ public abstract class PlaceholderHook * player * @param params String passed to the hook to determine what value to return * @return value for the requested player and params + * @deprecated This method will be completely removed, please use {@link me.clip.placeholderapi.expansion.PlaceholderExpansion#onRequest(OfflinePlayer, String)} */ @Nullable + @Deprecated public String onRequest(@Nullable final OfflinePlayer player, @NotNull final String params) { if (player != null && player.isOnline()) @@ -53,6 +59,8 @@ public abstract class PlaceholderHook * @param player {@link Player} to request the placeholder value for, null if not needed for a player * @param params String passed to the hook to determine what value to return * @return value for the requested player and params + * + * @deprecated This method will be completely removed, please use {@link me.clip.placeholderapi.expansion.PlaceholderExpansion#onRequest(OfflinePlayer, String)} */ @Nullable @Deprecated diff --git a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java index 8cad404..d406331 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java +++ b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java @@ -23,177 +23,233 @@ package me.clip.placeholderapi.expansion; import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderHook; -import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.List; -public abstract class PlaceholderExpansion extends PlaceholderHook { +public abstract class PlaceholderExpansion extends PlaceholderHook +{ - /** - * The name of this expansion - * - * @return {@link #getIdentifier()} by default, name of this expansion if specified - */ - public String getName() { - return getIdentifier(); - } + /** + * The placeholder identifier of this expansion + * + * @return placeholder identifier that is associated with this expansion + */ + @NotNull + public abstract String getIdentifier(); - /** - * The placeholder identifier of this expansion - * - * @return placeholder identifier that is associated with this expansion - */ - public abstract String getIdentifier(); + /** + * The author of this expansion + * + * @return name of the author for this expansion + */ + @NotNull + public abstract String getAuthor(); - /** - * The author of this expansion - * - * @return name of the author for this expansion - */ - public abstract String getAuthor(); + /** + * The version of this expansion + * + * @return current version of this expansion + */ + @NotNull + public abstract String getVersion(); - /** - * The version of this expansion - * - * @return current version of this expansion - */ - public abstract String getVersion(); - - /** - * The name of the plugin that this expansion hooks into. by default will return the deprecated - * {@link #getPlugin()} method - * - * @return plugin name that this expansion requires to function - */ - public String getRequiredPlugin() { - return getPlugin(); - } - - /** - * The placeholders associated with this expansion - * - * @return placeholder list that this expansion provides - */ - public List getPlaceholders() { - return null; - } - - /** - * Expansions that do not use the ecloud and instead register from the dependency should set this - * to true to ensure that your placeholder expansion is not unregistered when the papi reload - * command is used - * - * @return if this expansion should persist through placeholder reloads - */ - public boolean persist() { - return false; - } - - /** - * Check if this placeholder identifier has already been registered - * - * @return true if the identifier for this expansion is already registered - */ - public boolean isRegistered() { - Validate.notNull(getIdentifier(), "Placeholder identifier can not be null!"); - return PlaceholderAPI.isRegistered(getIdentifier()); - } - - /** - * If any requirements need to be checked before this expansion should register, you can check - * them here - * - * @return true if this hook meets all the requirements to register - */ - public boolean canRegister() { - return getRequiredPlugin() == null || Bukkit.getPluginManager().getPlugin(getRequiredPlugin()) != null; - } - - /** - * Attempt to register this PlaceholderExpansion - * - * @return true if this expansion is now registered with PlaceholderAPI - */ - public boolean register() { - Validate.notNull(getIdentifier(), "Placeholder identifier can not be null!"); - return canRegister() && getPlaceholderAPI().getLocalExpansionManager().register(this); - } - - /** - * Quick getter for the {@link PlaceholderAPIPlugin} instance - * - * @return {@link PlaceholderAPIPlugin} instance - */ - public PlaceholderAPIPlugin getPlaceholderAPI() { - return PlaceholderAPIPlugin.getInstance(); - } - - public String getString(String path, String def) { - return getPlaceholderAPI().getConfig() - .getString("expansions." + getIdentifier() + "." + path, def); - } - - public int getInt(String path, int def) { - return getPlaceholderAPI().getConfig() - .getInt("expansions." + getIdentifier() + "." + path, def); - } - - public long getLong(String path, long def) { - return getPlaceholderAPI().getConfig() - .getLong("expansions." + getIdentifier() + "." + path, def); - } - - public double getDouble(String path, double def) { - return getPlaceholderAPI().getConfig() - .getDouble("expansions." + getIdentifier() + "." + path, def); - } - - public List getStringList(String path) { - return getPlaceholderAPI().getConfig() - .getStringList("expansions." + getIdentifier() + "." + path); - } - - public Object get(String path, Object def) { - return getPlaceholderAPI().getConfig().get("expansions." + getIdentifier() + "." + path, def); - } - - public ConfigurationSection getConfigSection(String path) { - return getPlaceholderAPI().getConfig() - .getConfigurationSection("expansions." + getIdentifier() + "." + path); - } - - public ConfigurationSection getConfigSection() { - return getPlaceholderAPI().getConfig().getConfigurationSection("expansions." + getIdentifier()); - } - - public boolean configurationContains(String path) { - return getPlaceholderAPI().getConfig().contains("expansions." + getIdentifier() + "." + path); - } + @Nullable + @Override /* override for now >:) */ + public String onRequest(@Nullable final OfflinePlayer player, @NotNull final String params) + { + return super.onRequest(player, params); + } - /** - * @deprecated As of versions greater than 2.8.7, use {@link #getRequiredPlugin()} - */ - @Deprecated - public String getPlugin() { - return null; - } + /** + * The name of this expansion + * + * @return {@link #getIdentifier()} by default, name of this expansion if specified + */ + @NotNull + public String getName() + { + return getIdentifier(); + } - /** - * @deprecated As of versions greater than 2.8.7, use the expansion cloud to show a description - */ - @Deprecated - public String getDescription() { - return null; - } + /** + * The name of the plugin that this expansion hooks into. by default will null + * + * @return plugin name that this expansion requires to function + */ + @Nullable + public String getRequiredPlugin() + { + return null; + } + + /** + * The placeholders associated with this expansion + * + * @return placeholder list that this expansion provides + */ + @NotNull + public List getPlaceholders() + { + return Collections.emptyList(); + } + + + /** + * Expansions that do not use the ecloud and instead register from the dependency should set this + * to true to ensure that your placeholder expansion is not unregistered when the papi reload + * command is used + * + * @return if this expansion should persist through placeholder reloads + */ + public boolean persist() + { + return false; + } + + + /** + * Check if this placeholder identifier has already been registered + * + * @return true if the identifier for this expansion is already registered + */ + public final boolean isRegistered() + { + return PlaceholderAPI.isRegistered(getIdentifier()); + } + + + /** + * If any requirements need to be checked before this expansion should register, you can check + * them here + * + * @return true if this hook meets all the requirements to register + */ + public boolean canRegister() + { + return getRequiredPlugin() == null || Bukkit.getPluginManager().getPlugin(getRequiredPlugin()) != null; + } + + /** + * Attempt to register this PlaceholderExpansion + * + * @return true if this expansion is now registered with PlaceholderAPI + */ + public boolean register() + { + return canRegister() && getPlaceholderAPI().getLocalExpansionManager().register(this); + } + + + /** + * Quick getter for the {@link PlaceholderAPIPlugin} instance + * + * @return {@link PlaceholderAPIPlugin} instance + */ + @NotNull + public final PlaceholderAPIPlugin getPlaceholderAPI() + { + return PlaceholderAPIPlugin.getInstance(); + } + + + // === Configuration === + + @Nullable + public final ConfigurationSection getConfigSection() + { + return getPlaceholderAPI().getConfig().getConfigurationSection("expansions." + getIdentifier()); + } + + @Nullable + public final ConfigurationSection getConfigSection(@NotNull final String path) + { + final ConfigurationSection section = getConfigSection(); + return section == null ? null : section.getConfigurationSection(path); + } + + @Nullable + @Contract("_, !null -> !null") + public final Object get(@NotNull final String path, final Object def) + { + final ConfigurationSection section = getConfigSection(); + return section == null ? def : section.get(path, def); + } + + public final int getInt(@NotNull final String path, final int def) + { + final ConfigurationSection section = getConfigSection(); + return section == null ? def : section.getInt(path, def); + } + + public final long getLong(@NotNull final String path, final long def) + { + final ConfigurationSection section = getConfigSection(); + return section == null ? def : section.getLong(path, def); + } + + public final double getDouble(@NotNull final String path, final double def) + { + final ConfigurationSection section = getConfigSection(); + return section == null ? def : section.getDouble(path, def); + } + + @Nullable + @Contract("_, !null -> !null") + public final String getString(@NotNull final String path, @Nullable final String def) + { + final ConfigurationSection section = getConfigSection(); + return section == null ? def : section.getString(path, def); + } + + @NotNull + public final List getStringList(@NotNull final String path) + { + final ConfigurationSection section = getConfigSection(); + return section == null ? Collections.emptyList() : section.getStringList(path); + } + + public final boolean configurationContains(@NotNull final String path) + { + final ConfigurationSection section = getConfigSection(); + return section != null && section.contains(path); + } + + + // === Deprecated API === + + /** + * @deprecated As of versions greater than 2.8.7, use {@link #getRequiredPlugin()} + */ + @Deprecated + public final String getPlugin() + { + return null; + } + + /** + * @deprecated As of versions greater than 2.8.7, use the expansion cloud to show a description + */ + @Deprecated + public final String getDescription() + { + return null; + } + + /** + * @deprecated As of versions greater than 2.8.7, use the expansion cloud to display a link + */ + @Deprecated + public final String getLink() + { + return null; + } - /** - * @deprecated As of versions greater than 2.8.7, use the expansion cloud to display a link - */ - @Deprecated - public String getLink() { - return null; - } } diff --git a/src/test/java/me/clip/placeholderapi/Values.java b/src/test/java/me/clip/placeholderapi/Values.java index 89e3cad..bf8b0ca 100644 --- a/src/test/java/me/clip/placeholderapi/Values.java +++ b/src/test/java/me/clip/placeholderapi/Values.java @@ -6,6 +6,8 @@ import me.clip.placeholderapi.replacer.CharsReplacer; import me.clip.placeholderapi.replacer.RegexReplacer; import me.clip.placeholderapi.replacer.Replacer; import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public interface Values { @@ -31,18 +33,21 @@ public interface Values public static final String PLAYER_NAME = "Sxtanna"; + @NotNull @Override public String getIdentifier() { return "player"; } + @NotNull @Override public String getAuthor() { return "Sxtanna"; } + @NotNull @Override public String getVersion() { @@ -50,7 +55,7 @@ public interface Values } @Override - public String onRequest(final OfflinePlayer player, final String params) + public String onRequest(@Nullable final OfflinePlayer player, @NotNull final String params) { final String[] parts = params.split("_"); if (parts.length == 0) From ee33de5ec8b8d931224e674edb62fae78b9352bf Mon Sep 17 00:00:00 2001 From: Sxtanna Date: Sun, 26 Jul 2020 21:01:12 -0400 Subject: [PATCH 09/11] removed final from deprecated methods, restored fallback for getRequiredPlugin --- .../placeholderapi/expansion/PlaceholderExpansion.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java index d406331..4b313ee 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java +++ b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java @@ -87,7 +87,7 @@ public abstract class PlaceholderExpansion extends PlaceholderHook @Nullable public String getRequiredPlugin() { - return null; + return getPlugin(); } /** @@ -229,7 +229,7 @@ public abstract class PlaceholderExpansion extends PlaceholderHook * @deprecated As of versions greater than 2.8.7, use {@link #getRequiredPlugin()} */ @Deprecated - public final String getPlugin() + public String getPlugin() { return null; } @@ -238,7 +238,7 @@ public abstract class PlaceholderExpansion extends PlaceholderHook * @deprecated As of versions greater than 2.8.7, use the expansion cloud to show a description */ @Deprecated - public final String getDescription() + public String getDescription() { return null; } @@ -247,7 +247,7 @@ public abstract class PlaceholderExpansion extends PlaceholderHook * @deprecated As of versions greater than 2.8.7, use the expansion cloud to display a link */ @Deprecated - public final String getLink() + public String getLink() { return null; } From 8360511c50d00e5a6eec3568c28531b47fda99b5 Mon Sep 17 00:00:00 2001 From: Sxtanna Date: Sun, 26 Jul 2020 21:02:55 -0400 Subject: [PATCH 10/11] rewrote discovery and registration code to be composable and higher level --- .../impl/cloud/CommandECloudUpdate.java | 72 ++++++++----------- .../impl/local/CommandExpansionRegister.java | 26 +++---- .../manager/LocalExpansionManager.java | 50 +++++++------ .../me/clip/placeholderapi/util/FileUtil.java | 40 +++-------- .../me/clip/placeholderapi/util/Futures.java | 63 ++++++++++++++++ 5 files changed, 139 insertions(+), 112 deletions(-) create mode 100644 src/main/java/me/clip/placeholderapi/util/Futures.java diff --git a/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudUpdate.java b/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudUpdate.java index 9d9399c..91498e5 100644 --- a/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudUpdate.java +++ b/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudUpdate.java @@ -5,16 +5,16 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.cloud.CloudExpansion; +import me.clip.placeholderapi.util.Futures; import me.clip.placeholderapi.util.Msg; -import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; -import java.io.File; import java.util.ArrayList; -import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -67,31 +67,31 @@ public final class CommandECloudUpdate extends PlaceholderCommand Msg.msg(sender, "&aUpdating expansions: " + expansions.stream().map(CloudExpansion::getName).collect(Collectors.joining("&7, &6", "&8[&6", "&8]&r"))); - downloadExpansions(plugin, expansions) - .thenCompose(files -> discoverExpansions(plugin, files)) - .whenComplete((classes, exception) -> { - if (exception != null) - { - Msg.msg(sender, - "&cFailed to update expansions: &e" + exception.getMessage()); - return; - } - Msg.msg(sender, - "&aSuccessfully downloaded updates, registering new versions."); + Futures.onMainThread(plugin, downloadAndDiscover(expansions, plugin), (classes, exception) -> { + if (exception != null) + { + Msg.msg(sender, + "&cFailed to update expansions: &e" + exception.getMessage()); + return; + } - Bukkit.getScheduler().runTask(plugin, () -> { - final String message = classes.stream() - .map(plugin.getLocalExpansionManager()::register) - .filter(Optional::isPresent) - .map(Optional::get) - .map(expansion -> " &a" + expansion.getName() + " &f" + expansion.getVersion()) - .collect(Collectors.joining("\n")); - Msg.msg(sender, - "&7Registered expansions:", - message); - }); - }); + Msg.msg(sender, + "&aSuccessfully downloaded updates, registering new versions."); + + + final String message = classes.stream() + .filter(Objects::nonNull) + .map(plugin.getLocalExpansionManager()::register) + .filter(Optional::isPresent) + .map(Optional::get) + .map(expansion -> " &a" + expansion.getName() + " &f" + expansion.getVersion()) + .collect(Collectors.joining("\n")); + + Msg.msg(sender, + "&7Registered expansions:", message); + + }); } @Override @@ -114,22 +114,12 @@ public final class CommandECloudUpdate extends PlaceholderCommand } - public static CompletableFuture> downloadExpansions(@NotNull final PlaceholderAPIPlugin plugin, @NotNull final List expansions) + private static CompletableFuture>> downloadAndDiscover(@NotNull final List expansions, @NotNull final PlaceholderAPIPlugin plugin) { - final List> futures = expansions.stream() - .map(expansion -> plugin.getCloudExpansionManager().downloadExpansion(expansion, expansion.getVersion())) - .collect(Collectors.toList()); - - return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApplyAsync(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList())); - } - - public static CompletableFuture>> discoverExpansions(@NotNull final PlaceholderAPIPlugin plugin, @NotNull final List files) - { - final List>>> futures = files.stream() - .map(file -> plugin.getLocalExpansionManager().findExpansionsInFile(file)) - .collect(Collectors.toList()); - - return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApplyAsync(v -> futures.stream().map(CompletableFuture::join).flatMap(Collection::stream).collect(Collectors.toList())); + return expansions.stream() + .map(expansion -> plugin.getCloudExpansionManager().downloadExpansion(expansion, expansion.getVersion())) + .map(future -> future.thenCompose(plugin.getLocalExpansionManager()::findExpansionInFile)) + .collect(Futures.collector()); } } diff --git a/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandExpansionRegister.java b/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandExpansionRegister.java index d1c504b..348b76b 100644 --- a/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandExpansionRegister.java +++ b/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandExpansionRegister.java @@ -4,8 +4,8 @@ import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.manager.LocalExpansionManager; +import me.clip.placeholderapi.util.Futures; import me.clip.placeholderapi.util.Msg; -import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Unmodifiable; @@ -45,7 +45,7 @@ public final class CommandExpansionRegister extends PlaceholderCommand return; } - manager.findExpansionsInFile(file).whenComplete((classes, exception) -> { + Futures.onMainThread(plugin, manager.findExpansionInFile(file), (clazz, exception) -> { if (exception != null) { Msg.msg(sender, @@ -55,25 +55,25 @@ public final class CommandExpansionRegister extends PlaceholderCommand return; } - if (classes.isEmpty()) + if (clazz == null) { Msg.msg(sender, "&cNo expansion class found in file: &f" + file); return; } - Bukkit.getScheduler().runTask(plugin, () -> { - final Optional expansion = manager.register(classes.get(0)); - if (!expansion.isPresent()) - { - Msg.msg(sender, - "&cFailed to register expansion from &f" + params.get(0)); - return; - } + final Optional expansion = manager.register(clazz); + if (!expansion.isPresent()) + { Msg.msg(sender, - "&aSuccessfully registered expansion: &f" + expansion.get().getName()); - }); + "&cFailed to register expansion from &f" + params.get(0)); + return; + } + + Msg.msg(sender, + "&aSuccessfully registered expansion: &f" + expansion.get().getName()); + }); } diff --git a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java index a9088b9..f5689cc 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java +++ b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java @@ -13,6 +13,7 @@ import me.clip.placeholderapi.expansion.Taskable; import me.clip.placeholderapi.expansion.VersionSpecific; import me.clip.placeholderapi.expansion.cloud.CloudExpansion; import me.clip.placeholderapi.util.FileUtil; +import me.clip.placeholderapi.util.Futures; import me.clip.placeholderapi.util.Msg; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; @@ -28,7 +29,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import java.io.File; -import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -290,18 +291,17 @@ public final class LocalExpansionManager implements Listener { plugin.getLogger().info("Placeholder expansion registration initializing..."); - findExpansionsOnDisk().whenCompleteAsync((classes, exception) -> { + Futures.onMainThread(plugin, findExpansionsOnDisk(), (classes, exception) -> { if (exception != null) { plugin.getLogger().log(Level.SEVERE, "failed to load class files of expansions", exception); return; } - Bukkit.getScheduler().runTask(plugin, () -> { - final long registered = classes.stream().map(this::register).filter(Optional::isPresent).count(); - Msg.msg(sender, - registered == 0 ? "&6No expansions were registered!" : registered + "&a placeholder hooks successfully registered!"); - }); + final long registered = classes.stream().map(this::register).filter(Optional::isPresent).count(); + + Msg.msg(sender, + registered == 0 ? "&6No expansions were registered!" : registered + "&a placeholder hooks successfully registered!"); }); } @@ -320,33 +320,26 @@ public final class LocalExpansionManager implements Listener @NotNull - public CompletableFuture>> findExpansionsOnDisk() + public CompletableFuture<@NotNull List<@NotNull Class>> findExpansionsOnDisk() { - return CompletableFuture.supplyAsync(() -> { - try - { - return FileUtil.getClasses(getExpansionsFolder(), PlaceholderExpansion.class); - } - catch (final IOException | ClassNotFoundException ex) - { - throw new CompletionException(ex); - } - }); + return Arrays.stream(folder.listFiles((dir, name) -> name.endsWith(".jar"))) + .map(this::findExpansionInFile) + .collect(Futures.collector()); } @NotNull - public CompletableFuture>> findExpansionsInFile(@NotNull final File file) + public CompletableFuture<@Nullable Class> findExpansionInFile(@NotNull final File file) { return CompletableFuture.supplyAsync(() -> { try { - final List<@NotNull Class> classes = FileUtil.getClasses(getExpansionsFolder(), PlaceholderExpansion.class, file.getName()); - if (classes.size() > 1) - { - throw new IllegalStateException("multiple expansion classes in one file!"); - } - - return classes; + return FileUtil.findClass(file, PlaceholderExpansion.class); + } + catch (final VerifyError ex) + { + plugin.getLogger().severe("expansion file " + file.getName() + " is outdated: \n" + + "Failed to load due to a [" + ex.getClass().getSimpleName() + "], attempted to use" + ex.getMessage().substring(ex.getMessage().lastIndexOf(' '))); + return null; } catch (final Exception ex) { @@ -365,6 +358,11 @@ public final class LocalExpansionManager implements Listener } catch (final Exception ex) { + if (ex.getCause() instanceof LinkageError) + { + throw ((LinkageError) ex.getCause()); + } + plugin.getLogger().log(Level.SEVERE, "Failed to load placeholder expansion from class: " + clazz.getName(), ex); return null; } diff --git a/src/main/java/me/clip/placeholderapi/util/FileUtil.java b/src/main/java/me/clip/placeholderapi/util/FileUtil.java index 3510715..0ed981e 100644 --- a/src/main/java/me/clip/placeholderapi/util/FileUtil.java +++ b/src/main/java/me/clip/placeholderapi/util/FileUtil.java @@ -24,51 +24,25 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; -import java.io.FilenameFilter; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; public class FileUtil { - @NotNull - public static List<@NotNull Class> getClasses(@NotNull final File folder, @NotNull final Class clazz) throws IOException, ClassNotFoundException + @Nullable + public static Class findClass(@NotNull final File file, @NotNull final Class clazz) throws IOException, ClassNotFoundException { - return getClasses(folder, clazz, null); - } - - @NotNull - public static List<@NotNull Class> getClasses(@NotNull final File folder, @NotNull final Class clazz, @Nullable final String target) throws IOException, ClassNotFoundException - { - if (!folder.exists()) + if (!file.exists()) { - return Collections.emptyList(); + return null; } - final File[] jars = folder.listFiles((dir, name) -> name.endsWith(".jar") && (target == null || name.replace(".jar", "").equalsIgnoreCase(target.replace(".jar", "")))); - if (jars == null) - { - return Collections.emptyList(); - } + final URL jar = file.toURI().toURL(); - final List<@NotNull Class> list = new ArrayList<>(); - - for (final File file : jars) - { - gather(file.toURI().toURL(), clazz, list); - } - - return list; - } - - private static void gather(@NotNull final URL jar, @NotNull final Class clazz, @NotNull final List<@NotNull Class> list) throws IOException, ClassNotFoundException - { try (final URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader()); final JarInputStream stream = new JarInputStream(jar.openStream())) { JarEntry entry; @@ -85,7 +59,7 @@ public class FileUtil final Class loaded = loader.loadClass(name.substring(0, name.lastIndexOf('.')).replace('/', '.')); if (clazz.isAssignableFrom(loaded)) { - list.add(loaded.asSubclass(clazz)); + return loaded.asSubclass(clazz); } } catch (final NoClassDefFoundError ignored) @@ -93,6 +67,8 @@ public class FileUtil } } } + + return null; } } diff --git a/src/main/java/me/clip/placeholderapi/util/Futures.java b/src/main/java/me/clip/placeholderapi/util/Futures.java new file mode 100644 index 0000000..d689b56 --- /dev/null +++ b/src/main/java/me/clip/placeholderapi/util/Futures.java @@ -0,0 +1,63 @@ +package me.clip.placeholderapi.util; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public final class Futures +{ + + private Futures() + {} + + + public static void onMainThread(@NotNull final Plugin plugin, @NotNull final CompletableFuture future, @NotNull final BiConsumer consumer) + { + future.whenComplete((value, exception) -> { + if (Bukkit.isPrimaryThread()) + { + consumer.accept(value, exception); + } + else + { + Bukkit.getScheduler().runTask(plugin, () -> consumer.accept(value, exception)); + } + }); + } + + + @NotNull + public static Collector, ?, CompletableFuture>> collector() + { + return Collectors.collectingAndThen(Collectors.toList(), Futures::of); + } + + + @NotNull + public static CompletableFuture> of(@NotNull final Stream> futures) + { + return of(futures.collect(Collectors.toList())); + } + + @NotNull + public static CompletableFuture> of(@NotNull final Collection> futures) + { + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .thenApplyAsync($ -> awaitCompletion(futures)); + } + + @NotNull + private static List awaitCompletion(@NotNull final Collection> futures) + { + return futures.stream().map(CompletableFuture::join).collect(Collectors.toList()); + } + +} From f61b6acfa875ddce40a2d4fe6c300510254144c3 Mon Sep 17 00:00:00 2001 From: Sxtanna Date: Sun, 26 Jul 2020 23:01:12 -0400 Subject: [PATCH 11/11] updated placeholderexpansion to define its own unregister method --- .../local/CommandExpansionUnregister.java | 2 +- .../expansion/PlaceholderExpansion.java | 46 ++++++++++++++++- .../manager/LocalExpansionManager.java | 51 ++++++++++--------- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandExpansionUnregister.java b/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandExpansionUnregister.java index ccc0d2f..42936c2 100644 --- a/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandExpansionUnregister.java +++ b/src/main/java/me/clip/placeholderapi/commands/impl/local/CommandExpansionUnregister.java @@ -39,7 +39,7 @@ public final class CommandExpansionUnregister extends PlaceholderCommand } - final String message = !plugin.getLocalExpansionManager().unregister(expansion.get()) ? + final String message = !expansion.get().unregister() ? "&cFailed to unregister expansion: &f" : "&aSuccessfully unregistered expansion: &f"; diff --git a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java index 4b313ee..6eab658 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java +++ b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java @@ -20,7 +20,6 @@ */ package me.clip.placeholderapi.expansion; -import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderHook; import org.bukkit.Bukkit; @@ -32,6 +31,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; +import java.util.Objects; public abstract class PlaceholderExpansion extends PlaceholderHook { @@ -122,7 +122,7 @@ public abstract class PlaceholderExpansion extends PlaceholderHook */ public final boolean isRegistered() { - return PlaceholderAPI.isRegistered(getIdentifier()); + return getPlaceholderAPI().getLocalExpansionManager().findExpansionByIdentifier(getIdentifier()).map(it -> it.equals(this)).orElse(false); } @@ -141,12 +141,24 @@ public abstract class PlaceholderExpansion extends PlaceholderHook * Attempt to register this PlaceholderExpansion * * @return true if this expansion is now registered with PlaceholderAPI + * @deprecated This is going to be final in the future, startup and shutdown logic will have their own methods soon. */ + @Deprecated public boolean register() { return canRegister() && getPlaceholderAPI().getLocalExpansionManager().register(this); } + /** + * Attempt to unregister this PlaceholderExpansion + * + * @return true if this expansion is now unregistered with PlaceholderAPI + */ + public final boolean unregister() + { + return getPlaceholderAPI().getLocalExpansionManager().unregister(this); + } + /** * Quick getter for the {@link PlaceholderAPIPlugin} instance @@ -222,6 +234,36 @@ public abstract class PlaceholderExpansion extends PlaceholderHook return section != null && section.contains(path); } + @Override + public final boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (!(o instanceof PlaceholderExpansion)) + { + return false; + } + + final PlaceholderExpansion expansion = (PlaceholderExpansion) o; + + return getIdentifier().equals(expansion.getIdentifier()) && + getAuthor().equals(expansion.getAuthor()) && + getVersion().equals(expansion.getVersion()); + } + + @Override + public final int hashCode() + { + return Objects.hash(getIdentifier(), getAuthor(), getVersion()); + } + + @Override + public final String toString() + { + return String.format("PlaceholderExpansion[name: '%s', author: '%s', version: '%s']", getName(), getAuthor(), getVersion()); + } // === Deprecated API === diff --git a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java index f5689cc..efd1e8e 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java +++ b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java @@ -124,6 +124,27 @@ public final class LocalExpansionManager implements Listener } + public Optional register(@NotNull final Class clazz) + { + try + { + final PlaceholderExpansion expansion = createExpansionInstance(clazz); + if (expansion == null || !expansion.register()) + { + return Optional.empty(); + } + + return Optional.of(expansion); + } + catch (final LinkageError ex) + { + plugin.getLogger().severe("expansion class " + clazz.getSimpleName() + " is outdated: \n" + + "Failed to load due to a [" + ex.getClass().getSimpleName() + "], attempted to use " + ex.getMessage()); + } + + return Optional.empty(); + } + /** * Do not call this method yourself, use {@link PlaceholderExpansion#register()} */ @@ -188,7 +209,7 @@ public final class LocalExpansionManager implements Listener } final PlaceholderExpansion removed = expansions.get(expansion.getIdentifier()); - if (removed != null && !unregister(removed)) + if (removed != null && !removed.unregister()) { return false; } @@ -228,27 +249,9 @@ public final class LocalExpansionManager implements Listener return true; } - public Optional register(@NotNull final Class clazz) - { - try - { - final PlaceholderExpansion expansion = createExpansionInstance(clazz); - if (expansion == null || !expansion.register()) - { - return Optional.empty(); - } - - return Optional.of(expansion); - } - catch (final LinkageError ex) - { - plugin.getLogger().severe("expansion class " + clazz.getSimpleName() + " is outdated: \n" + - "Failed to load due to a [" + ex.getClass().getSimpleName() + "], attempted to use " + ex.getMessage()); - } - - return Optional.empty(); - } - + /** + * Do not call this method yourself, use {@link PlaceholderExpansion#unregister()} + */ public boolean unregister(@NotNull final PlaceholderExpansion expansion) { if (expansions.remove(expansion.getIdentifier()) == null) @@ -314,7 +317,7 @@ public final class LocalExpansionManager implements Listener continue; } - unregister(expansion); + expansion.unregister(); } } @@ -399,7 +402,7 @@ public final class LocalExpansionManager implements Listener continue; } - unregister(expansion); + expansion.unregister(); plugin.getLogger().info("Unregistered placeholder expansion: " + expansion.getName()); } }