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 ea5ce0fb..1ba38490 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 @@ -5,8 +5,8 @@ import com.willfp.eco.core.entities.args.EntityArgParser; import com.willfp.eco.core.entities.impl.EmptyTestableEntity; import com.willfp.eco.core.entities.impl.ModifiedTestableEntity; import com.willfp.eco.core.entities.impl.SimpleTestableEntity; +import com.willfp.eco.core.lookup.LookupHelper; import com.willfp.eco.util.NamespacedKeyUtils; -import com.willfp.eco.util.StringUtils; import org.bukkit.Location; import org.bukkit.NamespacedKey; import org.bukkit.entity.Entity; @@ -37,6 +37,11 @@ public final class Entities { */ private static final List ARG_PARSERS = new ArrayList<>(); + /** + * The lookup handler. + */ + private static final EntitiesLookupHandler ENTITIES_LOOKUP_HANDLER = new EntitiesLookupHandler(Entities::doParse); + /** * Register a new custom item. * @@ -87,20 +92,11 @@ public final class Entities { */ @NotNull public static TestableEntity lookup(@NotNull final String key) { - if (key.contains(" ? ")) { - String[] options = StringUtils.splitAround(key, "?"); - for (String option : options) { - TestableEntity lookup = lookup(option); - if (!(lookup instanceof EmptyTestableEntity)) { - return lookup; - } - } - - return new EmptyTestableEntity(); - } - - String[] args = StringUtils.parseTokens(key); + return LookupHelper.parseWith(key, ENTITIES_LOOKUP_HANDLER); + } + @NotNull + private static TestableEntity doParse(@NotNull final String[] args) { if (args.length == 0) { return new EmptyTestableEntity(); } @@ -131,7 +127,6 @@ public final class Entities { entity = part; } - String[] modifierArgs = Arrays.copyOfRange(args, 1, args.length); List parseResults = new ArrayList<>(); @@ -172,7 +167,6 @@ public final class Entities { return entity; } - /** * Get a Testable Entity from an ItemStack. *

diff --git a/eco-api/src/main/java/com/willfp/eco/core/entities/EntitiesLookupHandler.java b/eco-api/src/main/java/com/willfp/eco/core/entities/EntitiesLookupHandler.java new file mode 100644 index 00000000..3a31ff2d --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/entities/EntitiesLookupHandler.java @@ -0,0 +1,48 @@ +package com.willfp.eco.core.entities; + +import com.willfp.eco.core.entities.impl.EmptyTestableEntity; +import com.willfp.eco.core.entities.impl.GroupedTestableEntities; +import com.willfp.eco.core.lookup.LookupHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.function.Function; + +/** + * Handle item lookup strings. + */ +public class EntitiesLookupHandler implements LookupHandler { + /** + * The parser. + */ + private final Function parser; + + /** + * Create new lookup handler. + * + * @param parser The parser. + */ + public EntitiesLookupHandler(@NotNull final Function parser) { + this.parser = parser; + } + + @Override + public @NotNull TestableEntity parse(@NotNull final String[] args) { + return parser.apply(args); + } + + @Override + public boolean validate(@NotNull final TestableEntity object) { + return !(object instanceof EmptyTestableEntity); + } + + @Override + public @NotNull TestableEntity getFailsafe() { + return new EmptyTestableEntity(); + } + + @Override + public @NotNull TestableEntity join(@NotNull final Collection options) { + return new GroupedTestableEntities(options); + } +} diff --git a/eco-api/src/main/java/com/willfp/eco/core/entities/TestableEntity.java b/eco-api/src/main/java/com/willfp/eco/core/entities/TestableEntity.java index f02bdd3f..5685a072 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/entities/TestableEntity.java +++ b/eco-api/src/main/java/com/willfp/eco/core/entities/TestableEntity.java @@ -1,5 +1,6 @@ package com.willfp.eco.core.entities; +import com.willfp.eco.core.lookup.test.Testable; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.jetbrains.annotations.NotNull; @@ -8,13 +9,14 @@ import org.jetbrains.annotations.Nullable; /** * An item with a test to see if any item is that item. */ -public interface TestableEntity { +public interface TestableEntity extends Testable { /** * If an Entity matches the test. * * @param entity The entity to test. * @return If the entity matches. */ + @Override boolean matches(@Nullable Entity entity); /** diff --git a/eco-api/src/main/java/com/willfp/eco/core/entities/impl/GroupedTestableEntities.java b/eco-api/src/main/java/com/willfp/eco/core/entities/impl/GroupedTestableEntities.java new file mode 100644 index 00000000..0eedfca0 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/entities/impl/GroupedTestableEntities.java @@ -0,0 +1,68 @@ +package com.willfp.eco.core.entities.impl; + +import com.willfp.eco.core.entities.TestableEntity; +import com.willfp.eco.util.NumberUtils; +import org.apache.commons.lang.Validate; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * A group of testable entities. + * + * @see com.willfp.eco.core.entities.CustomEntity + */ +public class GroupedTestableEntities implements TestableEntity { + /** + * The children. + */ + private final Collection children; + + /** + * Create a new group of testable entities. + * + * @param children The children. + */ + public GroupedTestableEntities(@NotNull final Collection children) { + Validate.isTrue(!children.isEmpty(), "Group must have at least one child!"); + + this.children = children; + } + + /** + * If the item matches any children. + * + * @param entity The entity to test. + * @return If the entity matches the test of any children. + */ + @Override + public boolean matches(@Nullable final Entity entity) { + for (TestableEntity child : children) { + if (child.matches(entity)) { + return true; + } + } + + return false; + } + + @Override + public Entity spawn(@NotNull final Location location) { + return new ArrayList<>(children) + .get(NumberUtils.randInt(0, children.size() - 1)) + .spawn(location); + } + + /** + * Get the children. + * + * @return The children. + */ + public Collection getChildren() { + return new ArrayList<>(children); + } +} 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 32688bd0..2851ef6b 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 @@ -4,11 +4,11 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.willfp.eco.core.items.args.LookupArgParser; import com.willfp.eco.core.items.provider.ItemProvider; +import com.willfp.eco.core.lookup.LookupHelper; 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 org.bukkit.Material; 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 index aac6fb65..3fd6cf13 100644 --- 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 @@ -1,7 +1,8 @@ package com.willfp.eco.core.items; +import com.willfp.eco.core.lookup.LookupHandler; import com.willfp.eco.core.recipe.parts.EmptyTestableItem; -import com.willfp.eco.lookup.LookupHandler; +import com.willfp.eco.core.recipe.parts.GroupedTestableItems; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -42,6 +43,6 @@ public class ItemsLookupHandler implements LookupHandler { @Override public @NotNull TestableItem join(@NotNull final Collection options) { - throw new UnsupportedOperationException("Joining not supported!"); + return new GroupedTestableItems(options); } } diff --git a/eco-api/src/main/java/com/willfp/eco/core/items/TestableItem.java b/eco-api/src/main/java/com/willfp/eco/core/items/TestableItem.java index 69917c41..0f53259a 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/items/TestableItem.java +++ b/eco-api/src/main/java/com/willfp/eco/core/items/TestableItem.java @@ -1,18 +1,20 @@ package com.willfp.eco.core.items; +import com.willfp.eco.core.lookup.test.Testable; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; /** * An item with a test to see if any item is that item. */ -public interface TestableItem { +public interface TestableItem extends Testable { /** * If an ItemStack matches the recipe part. * * @param itemStack The item to test. * @return If the item matches. */ + @Override boolean matches(@Nullable ItemStack itemStack); /** diff --git a/eco-api/src/main/java/com/willfp/eco/lookup/LookupHandler.java b/eco-api/src/main/java/com/willfp/eco/core/lookup/LookupHandler.java similarity index 86% rename from eco-api/src/main/java/com/willfp/eco/lookup/LookupHandler.java rename to eco-api/src/main/java/com/willfp/eco/core/lookup/LookupHandler.java index d76a0ee9..7b49722b 100644 --- a/eco-api/src/main/java/com/willfp/eco/lookup/LookupHandler.java +++ b/eco-api/src/main/java/com/willfp/eco/core/lookup/LookupHandler.java @@ -1,5 +1,6 @@ -package com.willfp.eco.lookup; +package com.willfp.eco.core.lookup; +import com.willfp.eco.core.lookup.test.Testable; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -9,7 +10,7 @@ import java.util.Collection; * * @param The type of testable object, eg {@link com.willfp.eco.core.items.TestableItem}. */ -public interface LookupHandler { +public interface LookupHandler> { /** * Parse arguments to an object. * @@ -30,7 +31,7 @@ public interface LookupHandler { /** * Get the failsafe object. *

- * A failsafe object should never pass {@link this#validate(Object)}. + * A failsafe object should never pass {@link this#validate(Testable)}. * * @return The failsafe. */ diff --git a/eco-api/src/main/java/com/willfp/eco/lookup/LookupHelper.java b/eco-api/src/main/java/com/willfp/eco/core/lookup/LookupHelper.java similarity index 81% rename from eco-api/src/main/java/com/willfp/eco/lookup/LookupHelper.java rename to eco-api/src/main/java/com/willfp/eco/core/lookup/LookupHelper.java index 372fd0d0..ec0766cd 100644 --- a/eco-api/src/main/java/com/willfp/eco/lookup/LookupHelper.java +++ b/eco-api/src/main/java/com/willfp/eco/core/lookup/LookupHelper.java @@ -1,5 +1,6 @@ -package com.willfp.eco.lookup; +package com.willfp.eco.core.lookup; +import com.willfp.eco.core.lookup.test.Testable; import com.willfp.eco.util.StringUtils; import org.jetbrains.annotations.NotNull; @@ -24,8 +25,8 @@ public final class LookupHelper { * @return The object. */ @NotNull - public static T parseWith(@NotNull final String key, - @NotNull final LookupHandler handler) { + public static > T parseWith(@NotNull final String key, + @NotNull final LookupHandler handler) { for (SegmentParser parser : SEGMENT_PARSERS) { T generated = parser.parse(key, handler); diff --git a/eco-api/src/main/java/com/willfp/eco/lookup/SegmentParser.java b/eco-api/src/main/java/com/willfp/eco/core/lookup/SegmentParser.java similarity index 73% rename from eco-api/src/main/java/com/willfp/eco/lookup/SegmentParser.java rename to eco-api/src/main/java/com/willfp/eco/core/lookup/SegmentParser.java index 53a07ec3..c0de3e0f 100644 --- a/eco-api/src/main/java/com/willfp/eco/lookup/SegmentParser.java +++ b/eco-api/src/main/java/com/willfp/eco/core/lookup/SegmentParser.java @@ -1,5 +1,6 @@ -package com.willfp.eco.lookup; +package com.willfp.eco.core.lookup; +import com.willfp.eco.core.lookup.test.Testable; import com.willfp.eco.util.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,8 +31,8 @@ public abstract class SegmentParser { * @param The object type. * @return The returned object. */ - protected abstract T handleSegments(@NotNull String[] segments, - @NotNull LookupHandler handler); + protected abstract > T handleSegments(@NotNull String[] segments, + @NotNull LookupHandler handler); /** * Try parse segments from key. @@ -42,8 +43,8 @@ public abstract class SegmentParser { * @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) { + public > T parse(@NotNull final String key, + @NotNull final LookupHandler handler) { if (!key.contains(" " + pattern + " ")) { return null; } diff --git a/eco-api/src/main/java/com/willfp/eco/core/lookup/test/Testable.java b/eco-api/src/main/java/com/willfp/eco/core/lookup/test/Testable.java new file mode 100644 index 00000000..67c841bd --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/lookup/test/Testable.java @@ -0,0 +1,18 @@ +package com.willfp.eco.core.lookup.test; + +import org.jetbrains.annotations.Nullable; + +/** + * Interface for testing if any object matches another object. + * + * @param The type of object. + */ +public interface Testable { + /** + * If object matches the test. + * + * @param other The other object. + * @return If matches. + */ + boolean matches(@Nullable T other); +} diff --git a/eco-api/src/main/java/com/willfp/eco/core/recipe/parts/GroupedTestableItems.java b/eco-api/src/main/java/com/willfp/eco/core/recipe/parts/GroupedTestableItems.java new file mode 100644 index 00000000..0cc5efd4 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/recipe/parts/GroupedTestableItems.java @@ -0,0 +1,68 @@ +package com.willfp.eco.core.recipe.parts; + +import com.willfp.eco.core.items.TestableItem; +import org.apache.commons.lang.Validate; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * A group of testable items. + * + * @see com.willfp.eco.core.items.CustomItem + */ +public class GroupedTestableItems implements TestableItem { + /** + * The children. + */ + private final Collection children; + + /** + * Create a new group of testable items. + * + * @param children The children. + */ + public GroupedTestableItems(@NotNull final Collection children) { + Validate.isTrue(!children.isEmpty(), "Group must have at least one child!"); + + this.children = children; + } + + /** + * If the item matches any children. + * + * @param itemStack The item to test. + * @return If the item matches the test of any children.. + */ + @Override + public boolean matches(@Nullable final ItemStack itemStack) { + for (TestableItem child : children) { + if (child.matches(itemStack)) { + return true; + } + } + + return false; + } + + @Override + public ItemStack getItem() { + for (TestableItem child : children) { + return child.getItem(); + } + + throw new IllegalStateException("Empty group of children!"); + } + + /** + * Get the children. + * + * @return The children. + */ + public Collection getChildren() { + return new ArrayList<>(children); + } +} 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 index 62e66c70..a1554872 100644 --- 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 @@ -1,11 +1,12 @@ package com.willfp.eco.internal.lookup -import com.willfp.eco.lookup.LookupHandler -import com.willfp.eco.lookup.LookupHelper -import com.willfp.eco.lookup.SegmentParser +import com.willfp.eco.core.lookup.LookupHandler +import com.willfp.eco.core.lookup.LookupHelper +import com.willfp.eco.core.lookup.SegmentParser +import com.willfp.eco.core.lookup.test.Testable class SegmentParserGroup : SegmentParser("||") { - override fun handleSegments(segments: Array, handler: LookupHandler): T { + override fun > handleSegments(segments: Array, handler: LookupHandler): T { val possibleOptions: MutableList = ArrayList() for (option in segments) { 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 index 5848e048..4b9460eb 100644 --- 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 @@ -1,11 +1,12 @@ package com.willfp.eco.internal.lookup -import com.willfp.eco.lookup.LookupHandler -import com.willfp.eco.lookup.LookupHelper -import com.willfp.eco.lookup.SegmentParser +import com.willfp.eco.core.lookup.LookupHandler +import com.willfp.eco.core.lookup.LookupHelper +import com.willfp.eco.core.lookup.SegmentParser +import com.willfp.eco.core.lookup.test.Testable class SegmentParserUseIfPresent : SegmentParser("?") { - override fun handleSegments(segments: Array, handler: LookupHandler): T { + override fun > handleSegments(segments: Array, handler: LookupHandler): T { for (option in segments) { val lookup = LookupHelper.parseWith(option, handler) if (handler.validate(lookup)) { 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 16d3235e..19e99b3d 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 @@ -114,7 +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.core.lookup.LookupHelper import com.willfp.eco.util.BlockUtils import com.willfp.eco.util.NumberUtils import com.willfp.eco.util.ServerUtils diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/listeners/ComplexInEco.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/listeners/ComplexInEco.kt index 530069be..e51b23b7 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/listeners/ComplexInEco.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/recipes/listeners/ComplexInEco.kt @@ -1,7 +1,9 @@ package com.willfp.eco.internal.spigot.recipes.listeners import com.willfp.eco.core.items.Items +import com.willfp.eco.core.items.TestableItem import com.willfp.eco.core.recipe.Recipes +import com.willfp.eco.core.recipe.parts.GroupedTestableItems import com.willfp.eco.core.recipe.parts.MaterialTestableItem import com.willfp.eco.core.recipe.parts.ModifiedTestableItem import com.willfp.eco.core.recipe.parts.TestableStack @@ -9,6 +11,7 @@ import com.willfp.eco.core.recipe.recipes.ShapedCraftingRecipe import com.willfp.eco.internal.spigot.recipes.GenericCraftEvent import com.willfp.eco.internal.spigot.recipes.RecipeListener import com.willfp.eco.internal.spigot.recipes.ShapedRecipeListener +import org.bukkit.inventory.ItemStack class ComplexInEco : RecipeListener { override fun handle(event: GenericCraftEvent) { @@ -25,27 +28,40 @@ class ComplexInEco : RecipeListener { for (i in 0..8) { val itemStack = event.inventory.matrix[i] val part = craftingRecipe.parts[i] - when (part) { - is MaterialTestableItem -> { - if (Items.isCustomItem(itemStack)) { - event.deny() - } - } - is ModifiedTestableItem -> { - if (part.handle is MaterialTestableItem) { - if (Items.isCustomItem(itemStack)) { - event.deny() - } - } - } - is TestableStack -> { - if (part.handle is MaterialTestableItem) { - if (Items.isCustomItem(itemStack)) { - event.deny() - } - } - } + if (part.isCustomWhenShouldNotBe(itemStack)) { + event.deny() } } } +} + +private fun TestableItem.isCustomWhenShouldNotBe(itemStack: ItemStack): Boolean { + when (this) { + is MaterialTestableItem -> { + if (Items.isCustomItem(itemStack)) { + return true + } + } + is ModifiedTestableItem -> { + if (this.handle is MaterialTestableItem) { + if (Items.isCustomItem(itemStack)) { + return true + } + } + } + is TestableStack -> { + if (this.handle is MaterialTestableItem) { + if (Items.isCustomItem(itemStack)) { + return true + } + } + } + is GroupedTestableItems -> { + if (this.children.any { it.isCustomWhenShouldNotBe(itemStack) }) { + return true + } + } + } + + return false } \ No newline at end of file