From 349c365a9d6a95106f54b97d0b1bdfbb7b15a9b1 Mon Sep 17 00:00:00 2001 From: miozune Date: Thu, 6 Feb 2025 18:52:04 +0900 Subject: [PATCH 1/6] Introduce Capability system --- .../gtnhlib/capability/Capability.java | 116 ++++++++++++++++++ .../capability/CapabilityRegistry.java | 69 +++++++++++ .../gtnhlib/capability/CapabilityUtil.java | 111 +++++++++++++++++ .../capability/ICapabilityProvider.java | 51 ++++++++ 4 files changed, 347 insertions(+) create mode 100644 src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java create mode 100644 src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityRegistry.java create mode 100644 src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java create mode 100644 src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java new file mode 100644 index 0000000..4f5674f --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java @@ -0,0 +1,116 @@ +package com.gtnewhorizon.gtnhlib.capability; + +import java.util.Objects; + +import lombok.Getter; + +// spotless:off +/** + * {@link Capability} represents the ability of an object to expose a specific interface, + * allowing for a more flexible and decoupled approach compared to traditional interface implementations. + * It is particularly useful when dealing with complex systems where objects may need to dynamically provide + * different functionalities or when working with composite patterns. + *

+ * Capabilities offer a way to avoid excessive boilerplate code associated with implementing multiple interfaces + * and delegating method calls. Instead of directly implementing interfaces, objects can provide capabilities + * that expose specific functionalities on demand. + *

+ * Here are the key concepts in the capability system: + *

+ *

+ * Providing a Capability: + *

    + *
  1. Implement the {@link ICapabilityProvider} interface in your class.
  2. + *
  3. + * Implement the {@link ICapabilityProvider#getCapability} method. Check the requested {@code capability} + * and return the appropriate capability implementation if available. + *
  4. + *
+ *
{@code
+ *     class MyTileEntity implements ICapabilityProvider {
+ *         private MyInterface delegate = new MyInterfaceImpl();
+ *         public  T getCapability(@Nonnull Capability capability, @Nullable ForgeDirection side) {
+ *             if (capability == MY_CAPABILITY) {
+ *                 return MY_CAPABILITY.cast(delegate);
+ *             }
+ *             return null;
+ *         }
+ *     }
+ * }
+ *

+ * Registering a Capability: + *

+ * Use {@link CapabilityRegistry} to register your capability. + *

{@code
+ *     public static final Capability MY_CAPABILITY = CapabilityRegistry.INSTANCE.create(MyInterface.class);
+ * }
+ *

+ * Retrieving a Capability: + *

+ * Use {@link CapabilityUtil} to retrieve capabilities from objects like TileEntities, ItemStacks, or Entities. + * This utility provides backward compatibility by checking if the object directly implements the interface. + *

{@code
+ *     TileEntity neighbor = getNeighbor();
+ *     MyInterface myInterface = CapabilityUtil.getCapability(neighbor, MY_CAPABILITY);
+ *     if (myInterface != null) {
+ *         // Use the capability
+ *     }
+ * }
+ *

+ * Important Considerations: + *

+ * + * @param The type of the capability interface. + */ +// spotless:on +@Getter +public final class Capability { + + /** + * Casts the given object to the capability interface type {@code T}. This method is typically used when a + * capability provider returns an object through {@link ICapabilityProvider#getCapability}. + * + * @param object The object to cast to the capability interface. + * @param The type to cast the object to, inferred from the context. + * @return The cast object, or null if the object is null. + * @throws ClassCastException if the object cannot be cast to the capability interface. + */ + @SuppressWarnings("unchecked") + public R cast(T object) { + return (R) object; + } + + private final Class typeClass; + + Capability(Class typeClass) { + this.typeClass = typeClass; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Capabilitythat)) return false; + return Objects.equals(typeClass, that.typeClass); + } + + @Override + public int hashCode() { + return Objects.hashCode(typeClass); + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityRegistry.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityRegistry.java new file mode 100644 index 0000000..e252533 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityRegistry.java @@ -0,0 +1,69 @@ +package com.gtnewhorizon.gtnhlib.capability; + +import java.util.HashMap; +import java.util.Map; + +// spotless:off +/** + * Registry for managing {@link Capability} instances. Each capability interface can have only one + * capability instance associated with it. + *

+ * Example usage: + *

{@code
+ *     // Create and register a new capability
+ *     public static final Capability MY_CAPABILITY =
+ *         CapabilityRegistry.INSTANCE.create(MyInterface.class);
+ *
+ *     // Get an existing capability
+ *     Capability capability = CapabilityRegistry.INSTANCE.get(MyInterface.class);
+ * }
+ */ +// spotless:on +public final class CapabilityRegistry { + + /** + * The singleton instance of the {@link CapabilityRegistry}. + */ + public static final CapabilityRegistry INSTANCE = new CapabilityRegistry(); + + /** + * Creates a new {@link Capability} instance for the given capability interface. + * + * @param typeClass The {@link Class} object representing the capability interface. + * @param The type of the capability interface. + * @return The newly created {@link Capability} instance. + * @throws RuntimeException if a capability with the same interface already exists. + */ + public Capability create(Class typeClass) { + if (capabilities.get(typeClass) != null) { + throw new RuntimeException( + String.format("Attempted to create new capability with existing class: %s", typeClass)); + } + Capability capability = new Capability<>(typeClass); + capabilities.put(typeClass, capability); + return capability; + } + + /** + * Retrieves the {@link Capability} instance associated with the given capability interface. + * + * @param typeClass The {@link Class} object representing the capability interface. + * @param The type of the capability interface. + * @return The {@link Capability} instance associated with the given interface. + * @throws RuntimeException if no capability is registered for the given interface. + */ + @SuppressWarnings("unchecked") + public Capability get(Class typeClass) { + if (capabilities.get(typeClass) == null) { + throw new RuntimeException(String.format("Attempted to get capability which doesn't exist: %s", typeClass)); + } + return (Capability) capabilities.get(typeClass); + } + + /** + * A map that stores the registered {@link Capability} instances, using the capability interface as the key. + */ + private final Map, Capability> capabilities = new HashMap<>(); + + private CapabilityRegistry() {} +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java new file mode 100644 index 0000000..a011b3f --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java @@ -0,0 +1,111 @@ +package com.gtnewhorizon.gtnhlib.capability; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.entity.Entity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +// spotless:off +/** + * Utility class for retrieving capabilities from various Minecraft objects. This class provides convenience methods + * that handle both capability-based and direct interface implementations, ensuring backward compatibility with + * traditional interface implementations. + *

+ * Example usage: + *

{@code
+ *     // Retrieve capability from a TileEntity
+ *     MyInterface impl = CapabilityUtil.getCapability(tileEntity, MY_CAPABILITY, ForgeDirection.NORTH);
+ *
+ *     // Retrieve capability from an ItemStack
+ *     MyInterface impl = CapabilityUtil.getCapability(itemStack, MY_CAPABILITY);
+ *
+ *     // Retrieve capability from an Entity
+ *     MyInterface impl = CapabilityUtil.getCapability(entity, MY_CAPABILITY);
+ * }
+ */ +// spotless:on +public final class CapabilityUtil { + + /** + * Retrieves a capability from the given TileEntity. + * + * @param tileEntity The TileEntity to query. + * @param capability The {@link Capability} being requested. + * @param side The side of the TileEntity being queried. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + public static T getCapability(@Nullable TileEntity tileEntity, @Nonnull Capability capability, + @Nullable ForgeDirection side) { + return getCapability((Object) tileEntity, capability, side); + } + + /** + * Retrieves a capability from the given TileEntity without specifying a side. + * + * @param tileEntity The TileEntity to query. + * @param capability The {@link Capability} being requested. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + public static T getCapability(@Nullable TileEntity tileEntity, @Nonnull Capability capability) { + return getCapability((Object) tileEntity, capability, null); + } + + /** + * Retrieves a capability from the given ItemStack's Item. + * + * @param itemStack The ItemStack to query. + * @param capability The {@link Capability} being requested. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + public static T getCapability(@Nullable ItemStack itemStack, @Nonnull Capability capability) { + if (itemStack == null) { + return null; + } + Item item = itemStack.getItem(); + return getCapability(item, capability, null); + } + + /** + * Retrieves a capability from the given Entity. + * + * @param entity The Entity to query. + * @param capability The {@link Capability} being requested. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + public static T getCapability(@Nullable Entity entity, @Nonnull Capability capability) { + return getCapability(entity, capability, null); + } + + /** + * Internal utility method that tries a direct cast if the object matches the capability's interface, then falls + * back to querying {@link ICapabilityProvider} if available. + * + * @param object The object to query. + * @param capability The {@link Capability} being requested. + * @param side The side from which the capability is being requested. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + @SuppressWarnings("unchecked") + private static T getCapability(@Nullable Object object, @Nonnull Capability capability, + @Nullable ForgeDirection side) { + if (object == null) { + return null; + } + if (capability.getTypeClass().isAssignableFrom(object.getClass())) { + return (T) object; + } + if (object instanceof ICapabilityProvider provider) { + return provider.getCapability(capability, side); + } + return null; + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java new file mode 100644 index 0000000..4aad209 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java @@ -0,0 +1,51 @@ +package com.gtnewhorizon.gtnhlib.capability; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraftforge.common.util.ForgeDirection; + +// spotless:off +/** + * Classes implementing this interface indicates that it is able to offer certain {@link Capability}. + * Implementing this interface allows other systems to query and retrieve capabilities without relying on direct + * interface implementations. This can be useful for extending or overriding behavior at runtime. + * Typically implemented by TileEntities, Items or Entities. + *

+ * Example usage: + *

{@code
+ *     public class MyTileEntity implements ICapabilityProvider {
+ *         private MyCapabilityImplementation myCapability = new MyCapabilityImplementation();
+ *
+ *         @Override
+ *         public  T getCapability(@Nonnull Capability capability, @Nullable ForgeDirection side) {
+ *             if (capability == MyCapability.CAPABILITY) {
+ *                 return MyCapability.CAPABILITY.cast(myCapability);
+ *             }
+ *             return null;
+ *         }
+ *     }
+ * }
+ */ +// spotless:on +public interface ICapabilityProvider { + + /** + * Queries this provider for a capability implementation. + *

+ * This method should: + *

    + *
  • Compare the requested capability with known capabilities using {@code ==}.
  • + *
  • If matched, use {@link Capability#cast} to return the implementation.
  • + *
  • Return null if the capability is not supported.
  • + *
+ * + * @param capability The capability instance being requested. + * @param side The {@link ForgeDirection} from which the capability is requested. Can be null if direction is + * not relevant. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + @Nullable + T getCapability(@Nonnull Capability capability, @Nullable ForgeDirection side); +} From 9e06998a9ebc7e20f57b78e2def61e1dfa31eb56 Mon Sep 17 00:00:00 2001 From: miozune Date: Thu, 6 Feb 2025 22:14:38 +0900 Subject: [PATCH 2/6] Make ForgeDirection Nonnull --- .../gtnewhorizon/gtnhlib/capability/Capability.java | 2 +- .../gtnhlib/capability/CapabilityUtil.java | 10 +++++----- .../gtnhlib/capability/ICapabilityProvider.java | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java index 4f5674f..2a49bfd 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java @@ -34,7 +34,7 @@ *
{@code
  *     class MyTileEntity implements ICapabilityProvider {
  *         private MyInterface delegate = new MyInterfaceImpl();
- *         public  T getCapability(@Nonnull Capability capability, @Nullable ForgeDirection side) {
+ *         public  T getCapability(@Nonnull Capability capability, @Nonnull ForgeDirection side) {
  *             if (capability == MY_CAPABILITY) {
  *                 return MY_CAPABILITY.cast(delegate);
  *             }
diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java
index a011b3f..3a653f6 100644
--- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java
+++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java
@@ -40,7 +40,7 @@ public final class CapabilityUtil {
      * @return The capability implementation, or null if not available.
      */
     public static  T getCapability(@Nullable TileEntity tileEntity, @Nonnull Capability capability,
-            @Nullable ForgeDirection side) {
+            @Nonnull ForgeDirection side) {
         return getCapability((Object) tileEntity, capability, side);
     }
 
@@ -53,7 +53,7 @@ public static  T getCapability(@Nullable TileEntity tileEntity, @Nonnull Capa
      * @return The capability implementation, or null if not available.
      */
     public static  T getCapability(@Nullable TileEntity tileEntity, @Nonnull Capability capability) {
-        return getCapability((Object) tileEntity, capability, null);
+        return getCapability((Object) tileEntity, capability, ForgeDirection.UNKNOWN);
     }
 
     /**
@@ -69,7 +69,7 @@ public static  T getCapability(@Nullable ItemStack itemStack, @Nonnull Capabi
             return null;
         }
         Item item = itemStack.getItem();
-        return getCapability(item, capability, null);
+        return getCapability(item, capability, ForgeDirection.UNKNOWN);
     }
 
     /**
@@ -81,7 +81,7 @@ public static  T getCapability(@Nullable ItemStack itemStack, @Nonnull Capabi
      * @return The capability implementation, or null if not available.
      */
     public static  T getCapability(@Nullable Entity entity, @Nonnull Capability capability) {
-        return getCapability(entity, capability, null);
+        return getCapability(entity, capability, ForgeDirection.UNKNOWN);
     }
 
     /**
@@ -96,7 +96,7 @@ public static  T getCapability(@Nullable Entity entity, @Nonnull Capability T getCapability(@Nullable Object object, @Nonnull Capability capability,
-            @Nullable ForgeDirection side) {
+            @Nonnull ForgeDirection side) {
         if (object == null) {
             return null;
         }
diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java
index 4aad209..4bc4387 100644
--- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java
+++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java
@@ -18,7 +18,7 @@
  *         private MyCapabilityImplementation myCapability = new MyCapabilityImplementation();
  *
  *         @Override
- *         public  T getCapability(@Nonnull Capability capability, @Nullable ForgeDirection side) {
+ *         public  T getCapability(@Nonnull Capability capability, @Nonnull ForgeDirection side) {
  *             if (capability == MyCapability.CAPABILITY) {
  *                 return MyCapability.CAPABILITY.cast(myCapability);
  *             }
@@ -41,11 +41,11 @@ public interface ICapabilityProvider {
      * 
      *
      * @param capability The capability instance being requested.
-     * @param side       The {@link ForgeDirection} from which the capability is requested. Can be null if direction is
-     *                   not relevant.
+     * @param side       The {@link ForgeDirection} from which the capability is requested. Can be UNKNOWN if direction
+     *                   is not relevant.
      * @param         The type of the capability interface.
      * @return The capability implementation, or null if not available.
      */
     @Nullable
-     T getCapability(@Nonnull Capability capability, @Nullable ForgeDirection side);
+     T getCapability(@Nonnull Capability capability, @Nonnull ForgeDirection side);
 }

From 3f1ad4eabd792b123b712f4535701e529dd31e99 Mon Sep 17 00:00:00 2001
From: Raven Szewczyk 
Date: Thu, 6 Feb 2025 16:58:40 +0000
Subject: [PATCH 3/6] Some minor docs edits and use jetbrains annotations
 instead of javax

---
 .../gtnhlib/capability/Capability.java        |  6 +++---
 .../gtnhlib/capability/CapabilityUtil.java    | 19 +++++++++----------
 .../capability/ICapabilityProvider.java       | 13 ++++++-------
 3 files changed, 18 insertions(+), 20 deletions(-)

diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java
index 2a49bfd..63511b9 100644
--- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java
+++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java
@@ -6,10 +6,10 @@
 
 // spotless:off
 /**
- * {@link Capability} represents the ability of an object to expose a specific interface,
- * allowing for a more flexible and decoupled approach compared to traditional interface implementations.
+ * {@link Capability} represents the ability of an object to expose a specific interface without directly implementing it,
+ * allowing for a more flexible and decoupled approach compared to the traditional approach.
  * It is particularly useful when dealing with complex systems where objects may need to dynamically provide
- * different functionalities or when working with composite patterns.
+ * different functionalities or when working with complex patterns, like delegating responsibilities to another class.
  * 

* Capabilities offer a way to avoid excessive boilerplate code associated with implementing multiple interfaces * and delegating method calls. Instead of directly implementing interfaces, objects can provide capabilities diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java index 3a653f6..306d691 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java @@ -1,13 +1,12 @@ package com.gtnewhorizon.gtnhlib.capability; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import net.minecraft.entity.Entity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; // spotless:off /** @@ -39,8 +38,8 @@ public final class CapabilityUtil { * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable TileEntity tileEntity, @Nonnull Capability capability, - @Nonnull ForgeDirection side) { + public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Capability capability, + @NotNull ForgeDirection side) { return getCapability((Object) tileEntity, capability, side); } @@ -52,7 +51,7 @@ public static T getCapability(@Nullable TileEntity tileEntity, @Nonnull Capa * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable TileEntity tileEntity, @Nonnull Capability capability) { + public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Capability capability) { return getCapability((Object) tileEntity, capability, ForgeDirection.UNKNOWN); } @@ -64,7 +63,7 @@ public static T getCapability(@Nullable TileEntity tileEntity, @Nonnull Capa * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable ItemStack itemStack, @Nonnull Capability capability) { + public static T getCapability(@Nullable ItemStack itemStack, @NotNull Capability capability) { if (itemStack == null) { return null; } @@ -80,7 +79,7 @@ public static T getCapability(@Nullable ItemStack itemStack, @Nonnull Capabi * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable Entity entity, @Nonnull Capability capability) { + public static T getCapability(@Nullable Entity entity, @NotNull Capability capability) { return getCapability(entity, capability, ForgeDirection.UNKNOWN); } @@ -95,8 +94,8 @@ public static T getCapability(@Nullable Entity entity, @Nonnull Capability T getCapability(@Nullable Object object, @Nonnull Capability capability, - @Nonnull ForgeDirection side) { + private static T getCapability(@Nullable Object object, @NotNull Capability capability, + @NotNull ForgeDirection side) { if (object == null) { return null; } diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java index 4bc4387..04ff044 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java @@ -1,13 +1,12 @@ package com.gtnewhorizon.gtnhlib.capability; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import net.minecraftforge.common.util.ForgeDirection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; // spotless:off /** - * Classes implementing this interface indicates that it is able to offer certain {@link Capability}. + * By implementing this interface, classes indicate that they can offer certain {@link Capability Capabilities}. * Implementing this interface allows other systems to query and retrieve capabilities without relying on direct * interface implementations. This can be useful for extending or overriding behavior at runtime. * Typically implemented by TileEntities, Items or Entities. @@ -35,17 +34,17 @@ public interface ICapabilityProvider { *

* This method should: *

    - *
  • Compare the requested capability with known capabilities using {@code ==}.
  • + *
  • Compare the requested capability with known capabilities using {@code ==} or do HashMap dispatch for a larger quantity of supported capability types.
  • *
  • If matched, use {@link Capability#cast} to return the implementation.
  • *
  • Return null if the capability is not supported.
  • *
* * @param capability The capability instance being requested. - * @param side The {@link ForgeDirection} from which the capability is requested. Can be UNKNOWN if direction + * @param side The {@link ForgeDirection} from which the capability is requested. Can be {@link ForgeDirection#UNKNOWN UNKNOWN} if the direction * is not relevant. * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ @Nullable - T getCapability(@Nonnull Capability capability, @Nonnull ForgeDirection side); + T getCapability(@NotNull Capability capability, @NotNull ForgeDirection side); } From dad19a01a5073855de41efc21bdd1f528e62e37e Mon Sep 17 00:00:00 2001 From: Raven Szewczyk Date: Thu, 6 Feb 2025 17:08:13 +0000 Subject: [PATCH 4/6] Spotless --- .../gtnewhorizon/gtnhlib/capability/CapabilityUtil.java | 1 + .../gtnhlib/capability/ICapabilityProvider.java | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java index 306d691..dbd6359 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java @@ -5,6 +5,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java index 04ff044..a3d037f 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java @@ -1,6 +1,7 @@ package com.gtnewhorizon.gtnhlib.capability; import net.minecraftforge.common.util.ForgeDirection; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,14 +35,15 @@ public interface ICapabilityProvider { *

* This method should: *

    - *
  • Compare the requested capability with known capabilities using {@code ==} or do HashMap dispatch for a larger quantity of supported capability types.
  • + *
  • Compare the requested capability with known capabilities using {@code ==} or do HashMap dispatch for a larger + * quantity of supported capability types.
  • *
  • If matched, use {@link Capability#cast} to return the implementation.
  • *
  • Return null if the capability is not supported.
  • *
* * @param capability The capability instance being requested. - * @param side The {@link ForgeDirection} from which the capability is requested. Can be {@link ForgeDirection#UNKNOWN UNKNOWN} if the direction - * is not relevant. + * @param side The {@link ForgeDirection} from which the capability is requested. Can be + * {@link ForgeDirection#UNKNOWN UNKNOWN} if the direction is not relevant. * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ From 8951d1451f6b18247e5a3af5ba063fd32b53e740 Mon Sep 17 00:00:00 2001 From: miozune Date: Sat, 15 Feb 2025 21:56:33 +0900 Subject: [PATCH 5/6] Rework --- ...{CapabilityUtil.java => Capabilities.java} | 44 +++---- .../gtnhlib/capability/Capability.java | 116 ------------------ .../capability/CapabilityProvider.java | 62 ++++++++++ .../capability/CapabilityRegistry.java | 69 ----------- .../capability/ICapabilityProvider.java | 52 -------- 5 files changed, 84 insertions(+), 259 deletions(-) rename src/main/java/com/gtnewhorizon/gtnhlib/capability/{CapabilityUtil.java => Capabilities.java} (72%) delete mode 100644 src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java create mode 100644 src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java delete mode 100644 src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityRegistry.java delete mode 100644 src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capabilities.java similarity index 72% rename from src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java rename to src/main/java/com/gtnewhorizon/gtnhlib/capability/Capabilities.java index dbd6359..ff4aab9 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityUtil.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capabilities.java @@ -1,7 +1,6 @@ package com.gtnewhorizon.gtnhlib.capability; import net.minecraft.entity.Entity; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; @@ -18,28 +17,31 @@ * Example usage: *
{@code
  *     // Retrieve capability from a TileEntity
- *     MyInterface impl = CapabilityUtil.getCapability(tileEntity, MY_CAPABILITY, ForgeDirection.NORTH);
+ *     MyInterface impl = Capabilities.getCapability(tileEntity, MyCapability.class, ForgeDirection.NORTH);
  *
  *     // Retrieve capability from an ItemStack
- *     MyInterface impl = CapabilityUtil.getCapability(itemStack, MY_CAPABILITY);
+ *     MyInterface impl = Capabilities.getCapability(itemStack, MyCapability.class);
  *
  *     // Retrieve capability from an Entity
- *     MyInterface impl = CapabilityUtil.getCapability(entity, MY_CAPABILITY);
+ *     MyInterface impl = Capabilities.getCapability(entity, MyCapability.class);
  * }
+ * + * @see CapabilityProvider */ // spotless:on -public final class CapabilityUtil { +@SuppressWarnings("unused") +public final class Capabilities { /** * Retrieves a capability from the given TileEntity. * * @param tileEntity The TileEntity to query. - * @param capability The {@link Capability} being requested. + * @param capability The capability being requested. * @param side The side of the TileEntity being queried. * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Capability capability, + public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Class capability, @NotNull ForgeDirection side) { return getCapability((Object) tileEntity, capability, side); } @@ -48,11 +50,11 @@ public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Capa * Retrieves a capability from the given TileEntity without specifying a side. * * @param tileEntity The TileEntity to query. - * @param capability The {@link Capability} being requested. + * @param capability The capability being requested. * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Capability capability) { + public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Class capability) { return getCapability((Object) tileEntity, capability, ForgeDirection.UNKNOWN); } @@ -60,50 +62,48 @@ public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Capa * Retrieves a capability from the given ItemStack's Item. * * @param itemStack The ItemStack to query. - * @param capability The {@link Capability} being requested. + * @param capability The capability being requested. * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable ItemStack itemStack, @NotNull Capability capability) { + public static T getCapability(@Nullable ItemStack itemStack, @NotNull Class capability) { if (itemStack == null) { return null; } - Item item = itemStack.getItem(); - return getCapability(item, capability, ForgeDirection.UNKNOWN); + return getCapability(itemStack.getItem(), capability, ForgeDirection.UNKNOWN); } /** * Retrieves a capability from the given Entity. * * @param entity The Entity to query. - * @param capability The {@link Capability} being requested. + * @param capability The capability being requested. * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable Entity entity, @NotNull Capability capability) { + public static T getCapability(@Nullable Entity entity, @NotNull Class capability) { return getCapability(entity, capability, ForgeDirection.UNKNOWN); } /** * Internal utility method that tries a direct cast if the object matches the capability's interface, then falls - * back to querying {@link ICapabilityProvider} if available. + * back to querying {@link CapabilityProvider} if available. * * @param object The object to query. - * @param capability The {@link Capability} being requested. + * @param capability The capability being requested. * @param side The side from which the capability is being requested. * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - @SuppressWarnings("unchecked") - private static T getCapability(@Nullable Object object, @NotNull Capability capability, + private static T getCapability(@Nullable Object object, @NotNull Class capability, @NotNull ForgeDirection side) { if (object == null) { return null; } - if (capability.getTypeClass().isAssignableFrom(object.getClass())) { - return (T) object; + if (capability.isAssignableFrom(object.getClass())) { + return capability.cast(object); } - if (object instanceof ICapabilityProvider provider) { + if (object instanceof CapabilityProvider provider) { return provider.getCapability(capability, side); } return null; diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java deleted file mode 100644 index 63511b9..0000000 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capability.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.gtnewhorizon.gtnhlib.capability; - -import java.util.Objects; - -import lombok.Getter; - -// spotless:off -/** - * {@link Capability} represents the ability of an object to expose a specific interface without directly implementing it, - * allowing for a more flexible and decoupled approach compared to the traditional approach. - * It is particularly useful when dealing with complex systems where objects may need to dynamically provide - * different functionalities or when working with complex patterns, like delegating responsibilities to another class. - *

- * Capabilities offer a way to avoid excessive boilerplate code associated with implementing multiple interfaces - * and delegating method calls. Instead of directly implementing interfaces, objects can provide capabilities - * that expose specific functionalities on demand. - *

- * Here are the key concepts in the capability system: - *

    - *
  • Capability: Represents a specific ability or functionality that an object can provide.
  • - *
  • Capability Provider: An object that can provide one or more capabilities.
  • - *
  • Capability Interface: The interface that defines the methods exposed by a capability.
  • - *
  • Capability Implementation: A concrete implementation of a capability interface.
  • - *
- *

- * Providing a Capability: - *

    - *
  1. Implement the {@link ICapabilityProvider} interface in your class.
  2. - *
  3. - * Implement the {@link ICapabilityProvider#getCapability} method. Check the requested {@code capability} - * and return the appropriate capability implementation if available. - *
  4. - *
- *
{@code
- *     class MyTileEntity implements ICapabilityProvider {
- *         private MyInterface delegate = new MyInterfaceImpl();
- *         public  T getCapability(@Nonnull Capability capability, @Nonnull ForgeDirection side) {
- *             if (capability == MY_CAPABILITY) {
- *                 return MY_CAPABILITY.cast(delegate);
- *             }
- *             return null;
- *         }
- *     }
- * }
- *

- * Registering a Capability: - *

- * Use {@link CapabilityRegistry} to register your capability. - *

{@code
- *     public static final Capability MY_CAPABILITY = CapabilityRegistry.INSTANCE.create(MyInterface.class);
- * }
- *

- * Retrieving a Capability: - *

- * Use {@link CapabilityUtil} to retrieve capabilities from objects like TileEntities, ItemStacks, or Entities. - * This utility provides backward compatibility by checking if the object directly implements the interface. - *

{@code
- *     TileEntity neighbor = getNeighbor();
- *     MyInterface myInterface = CapabilityUtil.getCapability(neighbor, MY_CAPABILITY);
- *     if (myInterface != null) {
- *         // Use the capability
- *     }
- * }
- *

- * Important Considerations: - *

    - *
  • - * Replace all {@code instanceof} checks with capability checks to ensure proper compatibility. - *
  • - *
  • - * Avoid using capabilities for widely-used interfaces like {@code IFluidHandler} to prevent - * compatibility issues with other mods. - *
  • - *
  • - * Capabilities can return null. Always check for null before using a capability. - *
  • - *
- * - * @param The type of the capability interface. - */ -// spotless:on -@Getter -public final class Capability { - - /** - * Casts the given object to the capability interface type {@code T}. This method is typically used when a - * capability provider returns an object through {@link ICapabilityProvider#getCapability}. - * - * @param object The object to cast to the capability interface. - * @param The type to cast the object to, inferred from the context. - * @return The cast object, or null if the object is null. - * @throws ClassCastException if the object cannot be cast to the capability interface. - */ - @SuppressWarnings("unchecked") - public R cast(T object) { - return (R) object; - } - - private final Class typeClass; - - Capability(Class typeClass) { - this.typeClass = typeClass; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Capabilitythat)) return false; - return Objects.equals(typeClass, that.typeClass); - } - - @Override - public int hashCode() { - return Objects.hashCode(typeClass); - } -} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java new file mode 100644 index 0000000..761da79 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java @@ -0,0 +1,62 @@ +package com.gtnewhorizon.gtnhlib.capability; + +import net.minecraftforge.common.util.ForgeDirection; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +// spotless:off +/** + * Capability represents the ability of an object to expose a specific interface without directly implementing it, + * allowing for a more flexible and decoupled approach compared to the traditional approach. + * It is particularly useful when dealing with complex systems where objects may need to dynamically provide + * different functionalities or when working with complex patterns, like delegating responsibilities to another class. + *

+ * By implementing this interface, other systems are able to query and retrieve capabilities + * without relying on direct interface implementations. + *

+ * Note that it is required to replace all the {@code instanceof} checks with capability checks in order to + * ensure compatibility when migrating from direct interface implementation to capability. + * Avoid using capabilities for widely-used interfaces like {@code IFluidHandler} to prevent compatibility issues + * with other mods. + *

+ * Example usage: + *

{@code
+ *     public class MyTileEntity implements CapabilityProvider {
+ *         private MyCapability myCapability = new MyCapabilityImplementation();
+ *
+ *         @Override
+ *         public  T getCapability(@NotNull Capability capability, @NotNull ForgeDirection side) {
+ *             if (capability == MyCapability.class) {
+ *                 return capability.cast(myCapability);
+ *             }
+ *             return null;
+ *         }
+ *     }
+ * }
+ * + * @see Capabilities + */ +// spotless:on +public interface CapabilityProvider { + + /** + * Queries this provider for a capability implementation. + *

+ * This method should: + *

    + *
  • Compare the requested capability with known capabilities using {@code ==} or do HashMap dispatch for a larger + * quantity of supported capability types.
  • + *
  • If matched, use {@link Class#cast} to return the implementation.
  • + *
  • Return null if the capability is not supported.
  • + *
+ * + * @param capability The capability being requested. + * @param side The {@link ForgeDirection} from which the capability is requested. Can be + * {@link ForgeDirection#UNKNOWN UNKNOWN} if the direction is not relevant. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + @Nullable + T getCapability(@NotNull Class capability, @NotNull ForgeDirection side); +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityRegistry.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityRegistry.java deleted file mode 100644 index e252533..0000000 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityRegistry.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.gtnewhorizon.gtnhlib.capability; - -import java.util.HashMap; -import java.util.Map; - -// spotless:off -/** - * Registry for managing {@link Capability} instances. Each capability interface can have only one - * capability instance associated with it. - *

- * Example usage: - *

{@code
- *     // Create and register a new capability
- *     public static final Capability MY_CAPABILITY =
- *         CapabilityRegistry.INSTANCE.create(MyInterface.class);
- *
- *     // Get an existing capability
- *     Capability capability = CapabilityRegistry.INSTANCE.get(MyInterface.class);
- * }
- */ -// spotless:on -public final class CapabilityRegistry { - - /** - * The singleton instance of the {@link CapabilityRegistry}. - */ - public static final CapabilityRegistry INSTANCE = new CapabilityRegistry(); - - /** - * Creates a new {@link Capability} instance for the given capability interface. - * - * @param typeClass The {@link Class} object representing the capability interface. - * @param The type of the capability interface. - * @return The newly created {@link Capability} instance. - * @throws RuntimeException if a capability with the same interface already exists. - */ - public Capability create(Class typeClass) { - if (capabilities.get(typeClass) != null) { - throw new RuntimeException( - String.format("Attempted to create new capability with existing class: %s", typeClass)); - } - Capability capability = new Capability<>(typeClass); - capabilities.put(typeClass, capability); - return capability; - } - - /** - * Retrieves the {@link Capability} instance associated with the given capability interface. - * - * @param typeClass The {@link Class} object representing the capability interface. - * @param The type of the capability interface. - * @return The {@link Capability} instance associated with the given interface. - * @throws RuntimeException if no capability is registered for the given interface. - */ - @SuppressWarnings("unchecked") - public Capability get(Class typeClass) { - if (capabilities.get(typeClass) == null) { - throw new RuntimeException(String.format("Attempted to get capability which doesn't exist: %s", typeClass)); - } - return (Capability) capabilities.get(typeClass); - } - - /** - * A map that stores the registered {@link Capability} instances, using the capability interface as the key. - */ - private final Map, Capability> capabilities = new HashMap<>(); - - private CapabilityRegistry() {} -} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java deleted file mode 100644 index a3d037f..0000000 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/ICapabilityProvider.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.gtnewhorizon.gtnhlib.capability; - -import net.minecraftforge.common.util.ForgeDirection; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -// spotless:off -/** - * By implementing this interface, classes indicate that they can offer certain {@link Capability Capabilities}. - * Implementing this interface allows other systems to query and retrieve capabilities without relying on direct - * interface implementations. This can be useful for extending or overriding behavior at runtime. - * Typically implemented by TileEntities, Items or Entities. - *

- * Example usage: - *

{@code
- *     public class MyTileEntity implements ICapabilityProvider {
- *         private MyCapabilityImplementation myCapability = new MyCapabilityImplementation();
- *
- *         @Override
- *         public  T getCapability(@Nonnull Capability capability, @Nonnull ForgeDirection side) {
- *             if (capability == MyCapability.CAPABILITY) {
- *                 return MyCapability.CAPABILITY.cast(myCapability);
- *             }
- *             return null;
- *         }
- *     }
- * }
- */ -// spotless:on -public interface ICapabilityProvider { - - /** - * Queries this provider for a capability implementation. - *

- * This method should: - *

    - *
  • Compare the requested capability with known capabilities using {@code ==} or do HashMap dispatch for a larger - * quantity of supported capability types.
  • - *
  • If matched, use {@link Capability#cast} to return the implementation.
  • - *
  • Return null if the capability is not supported.
  • - *
- * - * @param capability The capability instance being requested. - * @param side The {@link ForgeDirection} from which the capability is requested. Can be - * {@link ForgeDirection#UNKNOWN UNKNOWN} if the direction is not relevant. - * @param The type of the capability interface. - * @return The capability implementation, or null if not available. - */ - @Nullable - T getCapability(@NotNull Capability capability, @NotNull ForgeDirection side); -} From 276b821e10735bc5aecd0cf34065f27c9abb23bc Mon Sep 17 00:00:00 2001 From: miozune Date: Sun, 16 Feb 2025 13:43:52 +0900 Subject: [PATCH 6/6] Don't use `? extends T` --- .../gtnewhorizon/gtnhlib/capability/Capabilities.java | 10 +++++----- .../gtnhlib/capability/CapabilityProvider.java | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capabilities.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capabilities.java index ff4aab9..8aa5dd5 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capabilities.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capabilities.java @@ -41,7 +41,7 @@ public final class Capabilities { * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Class capability, + public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Class capability, @NotNull ForgeDirection side) { return getCapability((Object) tileEntity, capability, side); } @@ -54,7 +54,7 @@ public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Clas * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Class capability) { + public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Class capability) { return getCapability((Object) tileEntity, capability, ForgeDirection.UNKNOWN); } @@ -66,7 +66,7 @@ public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Clas * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable ItemStack itemStack, @NotNull Class capability) { + public static T getCapability(@Nullable ItemStack itemStack, @NotNull Class capability) { if (itemStack == null) { return null; } @@ -81,7 +81,7 @@ public static T getCapability(@Nullable ItemStack itemStack, @NotNull Class< * @param The type of the capability interface. * @return The capability implementation, or null if not available. */ - public static T getCapability(@Nullable Entity entity, @NotNull Class capability) { + public static T getCapability(@Nullable Entity entity, @NotNull Class capability) { return getCapability(entity, capability, ForgeDirection.UNKNOWN); } @@ -95,7 +95,7 @@ public static T getCapability(@Nullable Entity entity, @NotNull Class The type of the capability interface. * @return The capability implementation, or null if not available. */ - private static T getCapability(@Nullable Object object, @NotNull Class capability, + private static T getCapability(@Nullable Object object, @NotNull Class capability, @NotNull ForgeDirection side) { if (object == null) { return null; diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java index 761da79..15e6542 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java @@ -26,7 +26,7 @@ * private MyCapability myCapability = new MyCapabilityImplementation(); * * @Override - * public T getCapability(@NotNull Capability capability, @NotNull ForgeDirection side) { + * public T getCapability(@NotNull Class capability, @NotNull ForgeDirection side) { * if (capability == MyCapability.class) { * return capability.cast(myCapability); * } @@ -58,5 +58,5 @@ public interface CapabilityProvider { * @return The capability implementation, or null if not available. */ @Nullable - T getCapability(@NotNull Class capability, @NotNull ForgeDirection side); + T getCapability(@NotNull Class capability, @NotNull ForgeDirection side); }