From ebb30e3a70536948d5c96e62cc2a240371e3e26c Mon Sep 17 00:00:00 2001 From: Auxilor Date: Mon, 28 Feb 2022 13:18:58 +0000 Subject: [PATCH] Began rewriting lookups to reusable system --- .../willfp/eco/core/entities/Entities.java | 4 +- .../java/com/willfp/eco/core/items/Items.java | 24 ++++---- .../eco/core/items/ItemsLookupHandler.java | 47 ++++++++++++++++ .../com/willfp/eco/lookup/LookupHandler.java | 48 ++++++++++++++++ .../com/willfp/eco/lookup/LookupHelper.java | 54 ++++++++++++++++++ .../com/willfp/eco/lookup/SegmentParser.java | 55 +++++++++++++++++++ .../java/com/willfp/eco/util/StringUtils.java | 29 ++++++++++ .../kotlin/com/willfp/eco/util/StringUtils.kt | 6 ++ eco-api/src/test/java/StringUtilsTest.java | 29 ++++++++++ .../eco/internal/lookup/SegmentParserGroup.kt | 20 +++++++ .../lookup/SegmentParserUseIfPresent.kt | 18 ++++++ .../eco/internal/spigot/EcoSpigotPlugin.kt | 6 ++ 12 files changed, 324 insertions(+), 16 deletions(-) create mode 100644 eco-api/src/main/java/com/willfp/eco/core/items/ItemsLookupHandler.java create mode 100644 eco-api/src/main/java/com/willfp/eco/lookup/LookupHandler.java create mode 100644 eco-api/src/main/java/com/willfp/eco/lookup/LookupHelper.java create mode 100644 eco-api/src/main/java/com/willfp/eco/lookup/SegmentParser.java create mode 100644 eco-api/src/test/java/StringUtilsTest.java create mode 100644 eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/lookup/SegmentParserGroup.kt create mode 100644 eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/lookup/SegmentParserUseIfPresent.kt diff --git a/eco-api/src/main/java/com/willfp/eco/core/entities/Entities.java b/eco-api/src/main/java/com/willfp/eco/core/entities/Entities.java index 71ba7606..ea5ce0fb 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/entities/Entities.java +++ b/eco-api/src/main/java/com/willfp/eco/core/entities/Entities.java @@ -87,8 +87,8 @@ public final class Entities { */ @NotNull public static TestableEntity lookup(@NotNull final String key) { - if (key.contains("?")) { - String[] options = key.split("\\?"); + if (key.contains(" ? ")) { + String[] options = StringUtils.splitAround(key, "?"); for (String option : options) { TestableEntity lookup = lookup(option); if (!(lookup instanceof EmptyTestableEntity)) { diff --git a/eco-api/src/main/java/com/willfp/eco/core/items/Items.java b/eco-api/src/main/java/com/willfp/eco/core/items/Items.java index 4b2c6285..32688bd0 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/items/Items.java +++ b/eco-api/src/main/java/com/willfp/eco/core/items/Items.java @@ -8,9 +8,9 @@ import com.willfp.eco.core.recipe.parts.EmptyTestableItem; import com.willfp.eco.core.recipe.parts.MaterialTestableItem; import com.willfp.eco.core.recipe.parts.ModifiedTestableItem; import com.willfp.eco.core.recipe.parts.TestableStack; +import com.willfp.eco.lookup.LookupHelper; import com.willfp.eco.util.NamespacedKeyUtils; import com.willfp.eco.util.NumberUtils; -import com.willfp.eco.util.StringUtils; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; @@ -68,6 +68,11 @@ public final class Items { */ private static final List ARG_PARSERS = new ArrayList<>(); + /** + * The handler. + */ + private static final ItemsLookupHandler ITEMS_LOOKUP_HANDLER = new ItemsLookupHandler(Items::doParse); + /** * Register a new custom item. * @@ -131,20 +136,11 @@ public final class Items { */ @NotNull public static TestableItem lookup(@NotNull final String key) { - if (key.contains("?")) { - String[] options = key.split("\\?"); - for (String option : options) { - TestableItem lookup = lookup(option); - if (!(lookup instanceof EmptyTestableItem)) { - return lookup; - } - } - - return new EmptyTestableItem(); - } - - String[] args = StringUtils.parseTokens(key); + return LookupHelper.parseWith(key, ITEMS_LOOKUP_HANDLER); + } + @NotNull + private static TestableItem doParse(@NotNull final String[] args) { if (args.length == 0) { return new EmptyTestableItem(); } diff --git a/eco-api/src/main/java/com/willfp/eco/core/items/ItemsLookupHandler.java b/eco-api/src/main/java/com/willfp/eco/core/items/ItemsLookupHandler.java new file mode 100644 index 00000000..aac6fb65 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/items/ItemsLookupHandler.java @@ -0,0 +1,47 @@ +package com.willfp.eco.core.items; + +import com.willfp.eco.core.recipe.parts.EmptyTestableItem; +import com.willfp.eco.lookup.LookupHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.function.Function; + +/** + * Handle item lookup strings. + */ +public class ItemsLookupHandler implements LookupHandler { + /** + * The parser. + */ + private final Function parser; + + /** + * Create new lookup handler. + * + * @param parser The parser. + */ + public ItemsLookupHandler(@NotNull final Function parser) { + this.parser = parser; + } + + @Override + public @NotNull TestableItem parse(@NotNull final String[] args) { + return parser.apply(args); + } + + @Override + public boolean validate(@NotNull final TestableItem object) { + return !(object instanceof EmptyTestableItem); + } + + @Override + public @NotNull TestableItem getFailsafe() { + return new EmptyTestableItem(); + } + + @Override + public @NotNull TestableItem join(@NotNull final Collection options) { + throw new UnsupportedOperationException("Joining not supported!"); + } +} diff --git a/eco-api/src/main/java/com/willfp/eco/lookup/LookupHandler.java b/eco-api/src/main/java/com/willfp/eco/lookup/LookupHandler.java new file mode 100644 index 00000000..d76a0ee9 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/lookup/LookupHandler.java @@ -0,0 +1,48 @@ +package com.willfp.eco.lookup; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * Handle lookups, used in {@link com.willfp.eco.core.entities.Entities} and {@link com.willfp.eco.core.items.Items}. + * + * @param The type of testable object, eg {@link com.willfp.eco.core.items.TestableItem}. + */ +public interface LookupHandler { + /** + * Parse arguments to an object. + * + * @param args The arguments. + * @return The object. + */ + @NotNull + T parse(@NotNull String[] args); + + /** + * Validate an object. + * + * @param object The object. + * @return If validated. + */ + boolean validate(@NotNull T object); + + /** + * Get the failsafe object. + *

+ * A failsafe object should never pass {@link this#validate(Object)}. + * + * @return The failsafe. + */ + @NotNull + T getFailsafe(); + + /** + * Join several options together. + * + * @param options The options. + * @return The joined object. + */ + @NotNull + T join(@NotNull Collection options); +} diff --git a/eco-api/src/main/java/com/willfp/eco/lookup/LookupHelper.java b/eco-api/src/main/java/com/willfp/eco/lookup/LookupHelper.java new file mode 100644 index 00000000..372fd0d0 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/lookup/LookupHelper.java @@ -0,0 +1,54 @@ +package com.willfp.eco.lookup; + +import com.willfp.eco.util.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * Helper for lookups. + */ +public final class LookupHelper { + /** + * All segment parsers. + */ + private static final List SEGMENT_PARSERS = new ArrayList<>(); + + /** + * Parse key to object. + * + * @param key The key. + * @param handler The handler. + * @param The object type. + * @return The object. + */ + @NotNull + public static T parseWith(@NotNull final String key, + @NotNull final LookupHandler handler) { + for (SegmentParser parser : SEGMENT_PARSERS) { + T generated = parser.parse(key, handler); + + if (generated != null) { + return generated; + } + } + + String[] args = StringUtils.parseTokens(key); + + return handler.parse(args); + } + + /** + * Register segment parser. + * + * @param parser The parser. + */ + public static void registerSegmentParser(@NotNull final SegmentParser parser) { + SEGMENT_PARSERS.add(parser); + } + + private LookupHelper() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } +} diff --git a/eco-api/src/main/java/com/willfp/eco/lookup/SegmentParser.java b/eco-api/src/main/java/com/willfp/eco/lookup/SegmentParser.java new file mode 100644 index 00000000..53a07ec3 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/lookup/SegmentParser.java @@ -0,0 +1,55 @@ +package com.willfp.eco.lookup; + +import com.willfp.eco.util.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Parse a key into segments. + */ +public abstract class SegmentParser { + /** + * The pattern to split keys on. + */ + private final String pattern; + + /** + * Create new lookup segment parser. + * + * @param pattern The pattern. + */ + protected SegmentParser(@NotNull final String pattern) { + this.pattern = pattern; + } + + /** + * Handle segments from key. + * + * @param segments The key segments. + * @param handler The handler. + * @param The object type. + * @return The returned object. + */ + protected abstract T handleSegments(@NotNull String[] segments, + @NotNull LookupHandler handler); + + /** + * Try parse segments from key. + * + * @param key The key. + * @param handler The handler. + * @param The object type. + * @return Null if no segments were found, or the object generated from the segments. + */ + @Nullable + public T parse(@NotNull final String key, + @NotNull final LookupHandler handler) { + if (!key.contains(" " + pattern + " ")) { + return null; + } + + String[] segments = StringUtils.splitAround(key, pattern); + + return handleSegments(segments, handler); + } +} diff --git a/eco-api/src/main/java/com/willfp/eco/util/StringUtils.java b/eco-api/src/main/java/com/willfp/eco/util/StringUtils.java index 7b38ae1b..35e47fa4 100644 --- a/eco-api/src/main/java/com/willfp/eco/util/StringUtils.java +++ b/eco-api/src/main/java/com/willfp/eco/util/StringUtils.java @@ -121,6 +121,14 @@ public final class StringUtils { .put("§k", ChatColor.MAGIC) .build(); + /** + * Regex map for splitting values. + */ + private static final LoadingCache SPACE_AROUND_CHARACTER = Caffeine.newBuilder() + .build( + character -> Pattern.compile("( " + Pattern.quote(character) + " )") + ); + /** * Format a list of strings. *

@@ -560,6 +568,27 @@ public final class StringUtils { return tokens.toArray(new String[0]); } + /** + * Split input string around separator surrounded by spaces. + *

+ * e.g. {@code splitAround("hello ? how are you", "?")} will split, but + * {@code splitAround("hello? how are you", "?")} will not. + * + * @param input Input string. + * @param separator Separator. + * @return The split string. + */ + @NotNull + public static String[] splitAround(@NotNull final String input, + @NotNull final String separator) { + Matcher matcher = SPACE_AROUND_CHARACTER.get(separator).matcher(input); + List groups = new ArrayList<>(); + while (matcher.find()) { + groups.add(matcher.group()); + } + return groups.toArray(new String[0]); + } + /** * Options for formatting. */ diff --git a/eco-api/src/main/kotlin/com/willfp/eco/util/StringUtils.kt b/eco-api/src/main/kotlin/com/willfp/eco/util/StringUtils.kt index 01d802a1..3dab8508 100644 --- a/eco-api/src/main/kotlin/com/willfp/eco/util/StringUtils.kt +++ b/eco-api/src/main/kotlin/com/willfp/eco/util/StringUtils.kt @@ -40,3 +40,9 @@ fun List.formatEco( player, if (formatPlaceholders) StringUtils.FormatOption.WITH_PLACEHOLDERS else StringUtils.FormatOption.WITHOUT_PLACEHOLDERS ) + +/** + * @see StringUtils.splitAround + */ +fun String.splitAround(separator: String): Array = + StringUtils.splitAround(this, separator) diff --git a/eco-api/src/test/java/StringUtilsTest.java b/eco-api/src/test/java/StringUtilsTest.java new file mode 100644 index 00000000..53259358 --- /dev/null +++ b/eco-api/src/test/java/StringUtilsTest.java @@ -0,0 +1,29 @@ +import com.willfp.eco.util.StringUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class StringUtilsTest { + @Test + public void testSplitAround() { + Assertions.assertArrayEquals( + new String[]{"one", "two"}, + StringUtils.splitAround("one ? two", "?") + ); + Assertions.assertArrayEquals( + new String[]{"one? two"}, + StringUtils.splitAround("one? two", "?") + ); + Assertions.assertArrayEquals( + new String[]{"one", "two", "three"}, + StringUtils.splitAround("one ? two ? three", "?") + ); + Assertions.assertArrayEquals( + new String[]{"one", "two"}, + StringUtils.splitAround("one || two", "||") + ); + Assertions.assertArrayEquals( + new String[]{"one|| two"}, + StringUtils.splitAround("one|| two", "||") + ); + } +} diff --git a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/lookup/SegmentParserGroup.kt b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/lookup/SegmentParserGroup.kt new file mode 100644 index 00000000..62e66c70 --- /dev/null +++ b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/lookup/SegmentParserGroup.kt @@ -0,0 +1,20 @@ +package com.willfp.eco.internal.lookup + +import com.willfp.eco.lookup.LookupHandler +import com.willfp.eco.lookup.LookupHelper +import com.willfp.eco.lookup.SegmentParser + +class SegmentParserGroup : SegmentParser("||") { + override fun handleSegments(segments: Array, handler: LookupHandler): T { + val possibleOptions: MutableList = ArrayList() + + for (option in segments) { + val lookup = LookupHelper.parseWith(option, handler) + if (handler.validate(lookup)) { + possibleOptions.add(lookup) + } + } + + return handler.join(possibleOptions) + } +} diff --git a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/lookup/SegmentParserUseIfPresent.kt b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/lookup/SegmentParserUseIfPresent.kt new file mode 100644 index 00000000..5848e048 --- /dev/null +++ b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/lookup/SegmentParserUseIfPresent.kt @@ -0,0 +1,18 @@ +package com.willfp.eco.internal.lookup + +import com.willfp.eco.lookup.LookupHandler +import com.willfp.eco.lookup.LookupHelper +import com.willfp.eco.lookup.SegmentParser + +class SegmentParserUseIfPresent : SegmentParser("?") { + override fun handleSegments(segments: Array, handler: LookupHandler): T { + for (option in segments) { + val lookup = LookupHelper.parseWith(option, handler) + if (handler.validate(lookup)) { + return lookup + } + } + + return handler.failsafe + } +} diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt index 52611123..16d3235e 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt @@ -45,6 +45,8 @@ import com.willfp.eco.internal.items.ArgParserFlag import com.willfp.eco.internal.items.ArgParserName import com.willfp.eco.internal.items.ArgParserTexture import com.willfp.eco.internal.items.ArgParserUnbreakable +import com.willfp.eco.internal.lookup.SegmentParserGroup +import com.willfp.eco.internal.lookup.SegmentParserUseIfPresent import com.willfp.eco.internal.spigot.arrows.ArrowDataListener import com.willfp.eco.internal.spigot.data.DataListener import com.willfp.eco.internal.spigot.data.DataYml @@ -112,6 +114,7 @@ import com.willfp.eco.internal.spigot.recipes.ShapedRecipeListener import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInComplex import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInEco import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInVanilla +import com.willfp.eco.lookup.LookupHelper import com.willfp.eco.util.BlockUtils import com.willfp.eco.util.NumberUtils import com.willfp.eco.util.ServerUtils @@ -155,6 +158,9 @@ abstract class EcoSpigotPlugin : EcoPlugin() { Entities.registerArgParser(EntityArgParserSilent()) Entities.registerArgParser(EntityArgParserEquipment()) + LookupHelper.registerSegmentParser(SegmentParserGroup()) + LookupHelper.registerSegmentParser(SegmentParserUseIfPresent()) + ShapedRecipeListener.registerListener(ComplexInComplex()) ShapedRecipeListener.registerListener(ComplexInEco()) ShapedRecipeListener.registerListener(ComplexInVanilla())