diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/DataExtension.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/DataExtension.java index b51420d10..e9838f561 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/DataExtension.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/DataExtension.java @@ -21,6 +21,7 @@ package com.djrapitops.plan.extension; *

* The class implementing this interface should be annotated with {@link com.djrapitops.plan.extension.annotation.PluginInfo}. * If the extension is given to Plan API without the annotation it will be rejected. + *


*

* Public methods in the class should be annotated with appropriate Provider annotations. * Provider annotations: @@ -29,12 +30,14 @@ package com.djrapitops.plan.extension; * {@link com.djrapitops.plan.extension.annotation.DoubleProvider} for {@code double} values. * {@link com.djrapitops.plan.extension.annotation.PercentageProvider} for {@code double} values that represent a percentage. * {@link com.djrapitops.plan.extension.annotation.StringProvider} for {@link String} values. + *


*

* Methods can have one of the following as method parameters: * {@code UUID playerUUID} - UUID of the player the data is about * {@code String playerName} - Name of the player the data is about * {@link Group group} - Provided group the data is about (In case a group needs additional information) * nothing - The data is interpreted to be about the server. + *


*

* The name of the method will be used as an identifier in the database, so that a single provider does not duplicate entries. * Only first 50 characters of the method name are stored. @@ -44,7 +47,21 @@ package com.djrapitops.plan.extension; * {@link com.djrapitops.plan.extension.annotation.Conditional} A {@code boolean} returned by {@link com.djrapitops.plan.extension.annotation.BooleanProvider} has to be {@code true} for this method to be called. * {@link com.djrapitops.plan.extension.annotation.Tab} The value of this provider should be placed on a tab with a specific name * {@link com.djrapitops.plan.extension.annotation.TabInfo} Optional Structure information about a tab - * {@link com.djrapitops.plan.extension.annotation.TabOrder} Optional information about preferred tab order + * {@link com.djrapitops.plan.extension.annotation.TabOrder} Optional information about preferred tab + *


+ *

+ * You can check against implementation violations by using {@link com.djrapitops.plan.extension.extractor.ExtensionExtractor#validateAnnotations()} in your Unit Tests. + *

+ * Implementation violations: + * - No {@link com.djrapitops.plan.extension.annotation.PluginInfo} class annotation + * - Class contains no public methods with Provider annotations + * - Class contains private method with Provider annotation + * - Non-primitive return type when primitive is required (eg. Boolean instead of boolean) + * - Method doesn't have correct parameters (see above) + * - {@link com.djrapitops.plan.extension.annotation.BooleanProvider} is annotated with a {@link com.djrapitops.plan.extension.annotation.Conditional} that requires same condition the provider provides. + * - {@link com.djrapitops.plan.extension.annotation.Conditional} without a {@link com.djrapitops.plan.extension.annotation.BooleanProvider} that provides value for the condition + * - Annotation variable is over 50 characters (Or 150 if description) + * - Method name is over 50 characters (Used as an identifier for storage) * * @author Rsl1122 */ diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/ExtensionService.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/ExtensionService.java new file mode 100644 index 000000000..29355dbb9 --- /dev/null +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/ExtensionService.java @@ -0,0 +1,57 @@ +package com.djrapitops.plan.extension; + +import com.djrapitops.plan.extension.extractor.ExtensionExtractor; + +import java.util.Optional; + +/** + * Interface for registering {@link DataExtension}s. + *

+ * Obtaining instance: + * - Obtain instance with {@link ExtensionService#getInstance()}. + * - Make sure to catch a possible NoClassDefFoundError in case Plan is not installed + * - Catch IllegalStateException in case ExtensionService is not enabled + *

+ * Registering {@link DataExtension}: + * - Register your {@link DataExtension} with {@link ExtensionService#register(DataExtension)} + * - Catch a possible IllegalArgumentException in case the DataExtension implementation is invalid. + * + * @author Rsl1122 + */ +public interface ExtensionService { + + /** + * Obtain instance of ExtensionService. + * + * @return ExtensionService implementation. + * @throws NoClassDefFoundError If Plan is not installed and this class can not be found or if older Plan version is installed. + * @throws IllegalStateException If Plan is installed, but not enabled. + */ + static ExtensionService getInstance() { + return Optional.ofNullable(ExtensionServiceHolder.API) + .orElseThrow(() -> new IllegalStateException("ExtensionService has not been initialised yet.")); + } + + /** + * Register your {@link DataExtension} implementation. + *

+ * You can use {@link ExtensionExtractor#validateAnnotations()} in your Unit Tests to prevent IllegalArgumentExceptions here at runtime. + * + * @param extension Your DataExtension implementation, see {@link DataExtension} for requirements. + * @throws IllegalArgumentException If an implementation violation is found. + */ + void register(DataExtension extension); + + class ExtensionServiceHolder { + static ExtensionService API; + + private ExtensionServiceHolder() { + /* Static variable holder */ + } + + static void set(ExtensionService api) { + ExtensionServiceHolder.API = api; + } + } + +}