diff --git a/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java b/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java
index e1ae52a1c..3607f6675 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java
@@ -56,6 +56,10 @@ enum Capability {
* When the parameter is set to {@code true} the value from this Provider is shown on a table alongside players.
*/
DATA_EXTENSION_SHOW_IN_PLAYER_TABLE,
+ /**
+ * DataExtension API addition, {@link com.djrapitops.plan.extension.builder.ExtensionDataBuilder}.
+ */
+ DATA_EXTENSION_BUILDER_API,
/**
* {@link com.djrapitops.plan.query.QueryService} and {@link com.djrapitops.plan.query.CommonQueries}
*/
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 bada70e21..5806d6ea4 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
@@ -16,6 +16,9 @@
*/
package com.djrapitops.plan.extension;
+import com.djrapitops.plan.extension.annotation.PluginInfo;
+import com.djrapitops.plan.extension.builder.ExtensionDataBuilder;
+
/**
* Interface to implement data extensions with.
*
@@ -30,6 +33,9 @@ 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.
+ * {@link com.djrapitops.plan.extension.annotation.TableProvider} for {@link com.djrapitops.plan.extension.table.Table}s.
+ * {@link com.djrapitops.plan.extension.annotation.GroupProvider} for Player specific group names, such as permission groups.
+ * {@link com.djrapitops.plan.extension.annotation.DataBuilderProvider} for {@link ExtensionDataBuilder}s.
*
*
* Methods can have one of the following as method parameters:
@@ -90,4 +96,30 @@ public interface DataExtension {
};
}
+ /**
+ * Obtain a new {@link ExtensionDataBuilder}.
+ *
+ * Requires Capability DATA_EXTENSION_BUILDER_API
+ *
+ * @return new builder.
+ */
+ default ExtensionDataBuilder newExtensionDataBuilder() {
+ return ExtensionService.getInstance().newExtensionDataBuilder(this);
+ }
+
+ /**
+ * Get the name of the plugin from PluginInfo annotation.
+ *
+ * Requires Capability DATA_EXTENSION_BUILDER_API
+ *
+ * @return new builder.
+ */
+ default String getPluginName() {
+ PluginInfo annotation = getClass().getAnnotation(PluginInfo.class);
+ if (annotation == null) {
+ throw new IllegalArgumentException(getClass().getSimpleName() + " did not have @PluginInfo annotation!");
+ }
+ return annotation.name();
+ }
+
}
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
index 1d0be17cd..9bcf81c3a 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/extension/ExtensionService.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/ExtensionService.java
@@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension;
+import com.djrapitops.plan.extension.builder.ExtensionDataBuilder;
import com.djrapitops.plan.extension.extractor.ExtensionExtractor;
import java.util.Optional;
@@ -60,6 +61,16 @@ public interface ExtensionService {
*/
Optional register(DataExtension extension);
+ /**
+ * Obtain a new {@link ExtensionDataBuilder}, it is recommended to use {@link DataExtension#newExtensionDataBuilder()}.
+ *
+ * Requires Capability DATA_EXTENSION_BUILDER_API
+ *
+ * @param extension Extension for which this builder is.
+ * @return a new builder.
+ */
+ ExtensionDataBuilder newExtensionDataBuilder(DataExtension extension);
+
/**
* Unregister your {@link DataExtension} implementation.
*
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/Conditional.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/Conditional.java
index 17fd4ac83..191db0bd7 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/Conditional.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/Conditional.java
@@ -53,5 +53,4 @@ public @interface Conditional {
* @return {@code false} by default.
*/
boolean negated() default false;
-
}
\ No newline at end of file
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/DataBuilderProvider.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/DataBuilderProvider.java
new file mode 100644
index 000000000..0c49bb264
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/DataBuilderProvider.java
@@ -0,0 +1,39 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.annotation;
+
+import com.djrapitops.plan.extension.DataExtension;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Method annotation to provide a {@link com.djrapitops.plan.extension.builder.ExtensionDataBuilder}.
+ *
+ * Usage: {@code @DataBuilderProvider ExtensionDataBuilder method(UUID playerUUID)}
+ *
+ * ExtensionDataBuilder can be obtained with {@link DataExtension#newExtensionDataBuilder()}.
+ *
+ * @author AuroraLS3
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface DataBuilderProvider {
+
+}
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/DataValue.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/DataValue.java
new file mode 100644
index 000000000..66c94ef6c
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/DataValue.java
@@ -0,0 +1,40 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.builder;
+
+import java.util.Optional;
+
+/**
+ * Represents a value given to {@link ExtensionDataBuilder}.
+ *
+ * Please do not implement this class, it is an implementation detail.
+ * Obtain instances with {@link ValueBuilder}.
+ *
+ * @param Type of the value.
+ */
+public interface DataValue {
+
+ T getValue();
+
+ M getInformation(Class ofType);
+
+ default > Optional getMetadata(Class metadataType) {
+ if (getClass().equals(metadataType)) return Optional.of(metadataType.cast(this));
+ return Optional.empty();
+ }
+
+}
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/ExtensionDataBuilder.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/ExtensionDataBuilder.java
new file mode 100644
index 000000000..ffdcabe05
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/ExtensionDataBuilder.java
@@ -0,0 +1,109 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.builder;
+
+import com.djrapitops.plan.extension.DataExtension;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.table.Table;
+
+import java.util.function.Supplier;
+
+/**
+ * Builder API for Extension data.
+ *
+ * Requires Capability DATA_EXTENSION_BUILDER_API
+ *
+ * Obtain an instance with {@link DataExtension#newExtensionDataBuilder()}
+ *
+ * Used with {@link com.djrapitops.plan.extension.annotation.DataBuilderProvider}.
+ * See documentation on how to use the API.
+ *
+ * @author AuroraLS3
+ */
+public interface ExtensionDataBuilder {
+
+ /**
+ * Creates a new {@link ValueBuilder} in order to use addValue methods.
+ *
+ * Using same text for two values can be problematic as the text is used for id in the database.
+ *
+ * If you need to use {@link com.djrapitops.plan.extension.annotation.InvalidateMethod} with built values,
+ * lowercase 'text' and remove all whitespace. Example {@code valueBuilder("Times Jumped"); @InvalidateMethod("timesjumped")}
+ *
+ * @param text Text that should be displayed next to the value.
+ * @return a new value builder.
+ * @throws IllegalArgumentException If text is null or empty String.
+ */
+ ValueBuilder valueBuilder(String text);
+
+ /**
+ * Add a value.
+ *
+ * @param ofType Class for type of the data, matches what Provider annotations want.
+ * @param dataValue Use {@link ValueBuilder} to create one.
+ * @param Type of the data.
+ * @return This builder.
+ * @throws IllegalArgumentException If either parameter is null
+ */
+ ExtensionDataBuilder addValue(Class ofType, DataValue dataValue);
+
+ /**
+ * Compared to the other addValue method, this method allows you to use {@link com.djrapitops.plan.extension.NotReadyException} when building your data.
+ *
+ * @param ofType Class for type of the data, matches what Provider annotations want.
+ * @param dataValue Use {@link ValueBuilder} to create one.
+ * @param Type of the data.
+ * @return This builder.
+ * @throws IllegalArgumentException If either parameter is null
+ */
+ ExtensionDataBuilder addValue(Class ofType, Supplier> dataValue);
+
+ /**
+ * Add a table.
+ *
+ * @param name Name of the table, used in the database.
+ * @param table Table built using {@link Table#builder()}
+ * @param color Color of the table
+ * @return This builder.
+ */
+ default ExtensionDataBuilder addTable(String name, Table table, Color color) {
+ if (name == null) throw new IllegalArgumentException("'name' can not be null!");
+ return addTable(name, table, color, null);
+ }
+
+ /**
+ * Add a table to a specific tab.
+ *
+ * @param name Name of the table, used in the database.
+ * @param table Table built using {@link Table#builder()}
+ * @param color Color of the table
+ * @param tab Name of the tab, remember to define {@link com.djrapitops.plan.extension.annotation.TabInfo}.
+ * @return This builder.
+ */
+ default ExtensionDataBuilder addTable(String name, Table table, Color color, String tab) {
+ return addValue(Table.class, valueBuilder(name)
+ .showOnTab(tab)
+ .buildTable(table, color));
+ }
+
+ /**
+ * Adds all values and tables in another builder to this builder.
+ *
+ * @param builder Builder to combine with this one.
+ */
+ void addAll(ExtensionDataBuilder builder);
+}
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/ValueBuilder.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/ValueBuilder.java
new file mode 100644
index 000000000..1efea98f6
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/ValueBuilder.java
@@ -0,0 +1,318 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.builder;
+
+import com.djrapitops.plan.extension.FormatType;
+import com.djrapitops.plan.extension.annotation.BooleanProvider;
+import com.djrapitops.plan.extension.annotation.Conditional;
+import com.djrapitops.plan.extension.annotation.StringProvider;
+import com.djrapitops.plan.extension.annotation.Tab;
+import com.djrapitops.plan.extension.extractor.ExtensionMethod;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.icon.Family;
+import com.djrapitops.plan.extension.icon.Icon;
+import com.djrapitops.plan.extension.table.Table;
+
+import java.util.function.Supplier;
+
+/**
+ * Used for building {@link DataValue}s for {@link ExtensionDataBuilder#addValue(Class, DataValue)}.
+ *
+ * Requires Capability DATA_EXTENSION_BUILDER_API
+ *
+ * Obtain an instance with {@link ExtensionDataBuilder#valueBuilder(String)}.
+ */
+public interface ValueBuilder {
+
+ /**
+ * Description about the value that is shown on hover.
+ *
+ * @param description Describe what the value is about, maximum 150 characters.
+ * @return This builder.
+ */
+ ValueBuilder description(String description);
+
+ /**
+ * Display-priority of the value, highest value is placed top most.
+ *
+ * Two values with same priority may appear in a random order.
+ *
+ * @param priority Priority between 0 and {@code Integer.MAX_VALUE}.
+ * @return This builder.
+ */
+ ValueBuilder priority(int priority);
+
+ /**
+ * Show this value in the players table.
+ *
+ * @return This builder.
+ */
+ ValueBuilder showInPlayerTable();
+
+ /**
+ * Icon displayed next to the value.
+ *
+ * See https://fontawesome.com/icons (select 'free')) for icons
+ *
+ * @param iconName Name of the icon
+ * @param iconFamily Family of the icon
+ * @param iconColor Color of the icon
+ * @return This builder.
+ */
+ default ValueBuilder icon(String iconName, Family iconFamily, Color iconColor) {
+ return icon(Icon.called(iconName).of(iconFamily).of(iconColor).build());
+ }
+
+ /**
+ * Icon displayed next to the value.
+ *
+ * See https://fontawesome.com/icons (select 'free')) for icons
+ *
+ * @param icon Icon built using the methods in {@link Icon}.
+ * @return This builder.
+ */
+ ValueBuilder icon(Icon icon);
+
+ /**
+ * Show the value on a specific tab.
+ *
+ * Remember to define {@link com.djrapitops.plan.extension.annotation.TabInfo} annotation.
+ *
+ * @param tabName Name of the tab.
+ * @return This builder.
+ */
+ ValueBuilder showOnTab(String tabName);
+
+ /**
+ * {@link ValueBuilder#buildNumber(long)} specific method, format the value as a epoch ms timestamp.
+ *
+ * @return This builder.
+ */
+ default ValueBuilder formatAsDateWithYear() {
+ return format(FormatType.DATE_YEAR);
+ }
+
+ /**
+ * {@link ValueBuilder#buildNumber(long)} specific method, format the value as a epoch ms timestamp.
+ *
+ * @return This builder.
+ */
+ default ValueBuilder formatAsDateWithSeconds() {
+ return format(FormatType.DATE_SECOND);
+ }
+
+ /**
+ * {@link ValueBuilder#buildNumber(long)} specific method, format the value as milliseconds of time.
+ *
+ * @return This builder.
+ */
+ default ValueBuilder formatAsTimeAmount() {
+ return format(FormatType.TIME_MILLISECONDS);
+ }
+
+ /**
+ * {@link ValueBuilder#buildNumber(long)} specific method, format the value with {@link FormatType}
+ *
+ * @return This builder.
+ */
+ ValueBuilder format(FormatType formatType);
+
+ /**
+ * {@link ValueBuilder#buildString(String)} specific method, link the value to a player page.
+ *
+ * @return This builder.
+ */
+ ValueBuilder showAsPlayerPageLink();
+
+ /**
+ * Build a Boolean. Displayed as "Yes/No" on the page.
+ *
+ * @param value true/false
+ * @return a data value to give to {@link ExtensionDataBuilder}.
+ */
+ DataValue buildBoolean(boolean value);
+
+ /**
+ * Build a Boolean that provides a value for {@link Conditional}. Displayed as "Yes/No" on the page.
+ *
+ * @param value true/false
+ * @return a data value to give to {@link ExtensionDataBuilder}.
+ */
+ DataValue buildBooleanProvidingCondition(boolean value, String providedCondition);
+
+ /**
+ * Build a String.
+ *
+ * @param value any string. Limited to 50 characters.
+ * @return a data value to give to {@link ExtensionDataBuilder}.
+ */
+ DataValue buildString(String value);
+
+ /**
+ * Build a Number.
+ *
+ * @param value a non-floating point number.
+ * @return a data value to give to {@link ExtensionDataBuilder}.
+ */
+ DataValue buildNumber(long value);
+
+ /**
+ * Build a Floating point number.
+ *
+ * @param value a floating point number.
+ * @return a data value to give to {@link ExtensionDataBuilder}.
+ */
+ DataValue buildDouble(double value);
+
+ /**
+ * Build a Percentage.
+ *
+ * @param percentage value between 0.0 and 1.0
+ * @return a data value to give to {@link ExtensionDataBuilder}.
+ */
+ DataValue buildPercentage(double percentage);
+
+ /**
+ * Build a list of groups.
+ *
+ * @param groups names of groups a player is in.
+ * @return a data value to give to {@link ExtensionDataBuilder}.
+ */
+ DataValue buildGroup(String[] groups);
+
+ /**
+ * Build a table.
+ *
+ * @param table Table built using {@link Table#builder()}
+ * @param tableColor Color of the table
+ * @return a data value to give to {@link ExtensionDataBuilder}.
+ */
+ DataValue buildTable(Table table, Color tableColor);
+
+ /**
+ * Lambda version for conditional return or throwing {@link com.djrapitops.plan.extension.NotReadyException}.
+ *
+ * @see ValueBuilder#buildBoolean(boolean).
+ */
+ DataValue buildBoolean(Supplier value);
+
+ /**
+ * Lambda version for conditional return or throwing {@link com.djrapitops.plan.extension.NotReadyException}.
+ *
+ * @see ValueBuilder#buildBooleanProvidingCondition(boolean, String).
+ */
+ DataValue buildBooleanProvidingCondition(Supplier value, String providedCondition);
+
+ /**
+ * Lambda version for conditional return or throwing {@link com.djrapitops.plan.extension.NotReadyException}.
+ *
+ * @see ValueBuilder#buildString(String)
+ */
+ DataValue buildString(Supplier value);
+
+ /**
+ * Lambda version for conditional return or throwing {@link com.djrapitops.plan.extension.NotReadyException}.
+ *
+ * @see ValueBuilder#buildNumber(long)
+ */
+ DataValue buildNumber(Supplier value);
+
+ /**
+ * Lambda version for conditional return or throwing {@link com.djrapitops.plan.extension.NotReadyException}.
+ *
+ * @see ValueBuilder#buildDouble(double)
+ */
+ DataValue buildDouble(Supplier value);
+
+ /**
+ * Lambda version for conditional return or throwing {@link com.djrapitops.plan.extension.NotReadyException}.
+ *
+ * @see ValueBuilder#buildPercentage(double)
+ */
+ DataValue buildPercentage(Supplier percentage);
+
+ /**
+ * Lambda version for conditional return or throwing {@link com.djrapitops.plan.extension.NotReadyException}.
+ *
+ * @see ValueBuilder#buildGroup(String[])
+ */
+ DataValue buildGroup(Supplier groups);
+
+ /**
+ * Lambda version for conditional return or throwing {@link com.djrapitops.plan.extension.NotReadyException}.
+ *
+ * @see ValueBuilder#buildTable(Table, Color)
+ */
+ DataValue buildTable(Supplier table, Color tableColor);
+
+ /**
+ * Implementation detail - for abstracting annotations with the builder API.
+ *
+ * @param annotation BooleanProvider annotation.
+ * @return This builder.
+ */
+ ValueBuilder hideFromUsers(BooleanProvider annotation);
+
+ /**
+ * Implementation detail - for abstracting annotations with the builder API.
+ *
+ * @param conditional Conditional annotation.
+ * @return This builder.
+ */
+ ValueBuilder conditional(Conditional conditional);
+
+ /**
+ * Implementation detail - for abstracting annotations with the builder API.
+ *
+ * @param annotation StringProvider annotation.
+ * @return This builder.
+ */
+ default ValueBuilder showAsPlayerPageLink(StringProvider annotation) {
+ if (annotation.playerName()) return showAsPlayerPageLink();
+ return this;
+ }
+
+ /**
+ * Implementation detail - for abstracting annotations with the builder API.
+ *
+ * @param method Method this value is from.
+ * @return This builder.
+ */
+ ValueBuilder methodName(ExtensionMethod method);
+
+ /**
+ * Implementation detail - for abstracting annotations with the builder API.
+ *
+ * @param show true/false
+ * @return This builder.
+ */
+ default ValueBuilder showInPlayerTable(boolean show) {
+ if (show) return showInPlayerTable();
+ return this;
+ }
+
+ /**
+ * Implementation detail - for abstracting annotations with the builder API.
+ *
+ * @param annotation Tab annotation.
+ * @return This builder.
+ */
+ default ValueBuilder showOnTab(Tab annotation) {
+ if (annotation != null) return showOnTab(annotation.value());
+ return this;
+ }
+}
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/package-info.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/package-info.java
new file mode 100644
index 000000000..0ce6474ca
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/builder/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * DataExtension Builder API.
+ *
+ * Requires Capability DATA_EXTENSION_BUILDER_API
+ */
+package com.djrapitops.plan.extension.builder;
\ No newline at end of file
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java
index c7a062d17..71c2a4165 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java
@@ -19,11 +19,11 @@ package com.djrapitops.plan.extension.extractor;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.Group;
import com.djrapitops.plan.extension.annotation.*;
+import com.djrapitops.plan.extension.builder.ExtensionDataBuilder;
import com.djrapitops.plan.extension.table.Table;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
@@ -43,10 +43,12 @@ public final class ExtensionExtractor {
private final List warnings = new ArrayList<>();
private PluginInfo pluginInfo;
- private TabOrder tabOrder;
private List tabInformation;
private List invalidMethods;
private MethodAnnotations methodAnnotations;
+ private Map methods;
+ private Collection conditionalMethods;
+ private Collection tabAnnotations;
private static final String WAS_OVER_50_CHARACTERS = "' was over 50 characters.";
@@ -56,78 +58,126 @@ public final class ExtensionExtractor {
}
/**
- * Use this method in an unit test to validate your DataExtension.
- *
- * @throws IllegalArgumentException If an implementation error is found.
+ * @deprecated Use {@link DataExtension#getPluginName()} instead.
*/
- public void validateAnnotations() {
- extractAnnotationInformation();
-
- if (!warnings.isEmpty()) {
- throw new IllegalArgumentException("Warnings: " + warnings.toString());
- }
+ @Deprecated
+ public static String getPluginName(Class extensionClass) {
+ return getClassAnnotation(extensionClass, PluginInfo.class).map(PluginInfo::name)
+ .orElseThrow(() -> new IllegalArgumentException("Given class had no PluginInfo annotation"));
}
private static Optional getClassAnnotation(Class from, Class ofClass) {
return Optional.ofNullable(from.getAnnotation(ofClass));
}
- public static String getPluginName(Class extensionClass) {
- return getClassAnnotation(extensionClass, PluginInfo.class).map(PluginInfo::name)
- .orElseThrow(() -> new IllegalArgumentException("Given class had no PluginInfo annotation"));
- }
-
- private Method[] getMethods() {
- return extension.getClass().getMethods();
- }
-
- public void extractAnnotationInformation() {
+ /**
+ * Use this method in an unit test to validate your DataExtension.
+ *
+ * @throws IllegalArgumentException If an implementation error is found.
+ */
+ public void validateAnnotations() {
extractPluginInfo();
extractInvalidMethods();
-
- extractMethodAnnotations();
- validateMethodAnnotations();
-
- validateConditionals();
-
+ extractMethods();
extractTabInfo();
+
+ if (!warnings.isEmpty()) {
+ throw new IllegalArgumentException("Warnings: " + warnings.toString());
+ }
}
- private void extractMethodAnnotations() {
- methodAnnotations = new MethodAnnotations();
+ private Collection getExtensionMethods() {
+ List extensionMethods = new ArrayList<>();
+ for (Method method : extension.getClass().getMethods()) {
+ try {
+ extensionMethods.add(new ExtensionMethod(extension, method));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException(extensionName + '.' + e.getMessage());
+ }
+ }
+ return extensionMethods;
+ }
- for (Method method : getMethods()) {
- int modifiers = method.getModifiers();
- if (Modifier.isPrivate(modifiers)
- || Modifier.isProtected(modifiers)
- || Modifier.isStatic(modifiers)
- || Modifier.isNative(modifiers)) {
+ /**
+ * @deprecated No longer used anywhere, no-op.
+ */
+ @Deprecated
+ public void extractAnnotationInformation() {
+ // no-op
+ }
+
+ private void extractMethods() {
+ methodAnnotations = new MethodAnnotations();
+ methods = new EnumMap<>(ExtensionMethod.ParameterType.class);
+ methods.put(ExtensionMethod.ParameterType.SERVER_NONE, new ExtensionMethods());
+ methods.put(ExtensionMethod.ParameterType.PLAYER_STRING, new ExtensionMethods());
+ methods.put(ExtensionMethod.ParameterType.PLAYER_UUID, new ExtensionMethods());
+ methods.put(ExtensionMethod.ParameterType.GROUP, new ExtensionMethods());
+
+ conditionalMethods = new ArrayList<>();
+ tabAnnotations = new ArrayList<>();
+
+ for (ExtensionMethod method : getExtensionMethods()) {
+ if (method.isInaccessible()) {
continue;
}
- MethodAnnotations.get(method, BooleanProvider.class).ifPresent(annotation -> methodAnnotations.put(method, BooleanProvider.class, annotation));
- MethodAnnotations.get(method, NumberProvider.class).ifPresent(annotation -> methodAnnotations.put(method, NumberProvider.class, annotation));
- MethodAnnotations.get(method, DoubleProvider.class).ifPresent(annotation -> methodAnnotations.put(method, DoubleProvider.class, annotation));
- MethodAnnotations.get(method, PercentageProvider.class).ifPresent(annotation -> methodAnnotations.put(method, PercentageProvider.class, annotation));
- MethodAnnotations.get(method, StringProvider.class).ifPresent(annotation -> methodAnnotations.put(method, StringProvider.class, annotation));
+ method.getAnnotation(BooleanProvider.class).ifPresent(annotation -> {
+ validateMethod(method, annotation);
+ methods.get(method.getParameterType()).addBooleanMethod(method);
+ methodAnnotations.put(method.getMethod(), BooleanProvider.class, annotation);
+ });
+ method.getAnnotation(NumberProvider.class).ifPresent(annotation -> {
+ validateMethod(method, annotation);
+ methods.get(method.getParameterType()).addNumberMethod(method);
+ methodAnnotations.put(method.getMethod(), NumberProvider.class, annotation);
+ });
+ method.getAnnotation(DoubleProvider.class).ifPresent(annotation -> {
+ validateMethod(method, annotation);
+ methods.get(method.getParameterType()).addDoubleMethod(method);
+ methodAnnotations.put(method.getMethod(), DoubleProvider.class, annotation);
+ });
+ method.getAnnotation(PercentageProvider.class).ifPresent(annotation -> {
+ validateMethod(method, annotation);
+ methods.get(method.getParameterType()).addPercentageMethod(method);
+ methodAnnotations.put(method.getMethod(), PercentageProvider.class, annotation);
+ });
+ method.getAnnotation(StringProvider.class).ifPresent(annotation -> {
+ validateMethod(method, annotation);
+ methods.get(method.getParameterType()).addStringMethod(method);
+ methodAnnotations.put(method.getMethod(), StringProvider.class, annotation);
+ });
+ method.getAnnotation(TableProvider.class).ifPresent(annotation -> {
+ validateMethod(method, annotation);
+ methods.get(method.getParameterType()).addTableMethod(method);
+ methodAnnotations.put(method.getMethod(), TableProvider.class, annotation);
+ });
+ method.getAnnotation(GroupProvider.class).ifPresent(annotation -> {
+ validateMethod(method, annotation);
+ methods.get(method.getParameterType()).addGroupMethod(method);
+ methodAnnotations.put(method.getMethod(), GroupProvider.class, annotation);
+ });
+ method.getAnnotation(DataBuilderProvider.class).ifPresent(annotation -> {
+ validateMethod(method, annotation);
+ methods.get(method.getParameterType()).addDataBuilderMethod(method);
+ methodAnnotations.put(method.getMethod(), DataBuilderProvider.class, annotation);
+ });
- MethodAnnotations.get(method, Conditional.class).ifPresent(annotation -> methodAnnotations.put(method, Conditional.class, annotation));
- MethodAnnotations.get(method, Tab.class).ifPresent(annotation -> methodAnnotations.put(method, Tab.class, annotation));
-
- MethodAnnotations.get(method, TableProvider.class).ifPresent(annotation -> methodAnnotations.put(method, TableProvider.class, annotation));
- MethodAnnotations.get(method, GroupProvider.class).ifPresent(annotation -> methodAnnotations.put(method, GroupProvider.class, annotation));
+ method.getAnnotation(Conditional.class).ifPresent(annotation -> {
+ conditionalMethods.add(method.getMethod());
+ methodAnnotations.put(method.getMethod(), Conditional.class, annotation);
+ });
+ method.getAnnotation(Tab.class).ifPresent(annotation -> {
+ tabAnnotations.add(annotation);
+ methodAnnotations.put(method.getMethod(), Tab.class, annotation);
+ });
}
if (methodAnnotations.isEmpty()) {
throw new IllegalArgumentException(extensionName + " class had no methods annotated with a Provider annotation");
}
- try {
- methodAnnotations.makeMethodsAccessible();
- } catch (SecurityException failedToMakeAccessible) {
- throw new IllegalArgumentException(extensionName + " has non accessible Provider method that could not be made accessible: " +
- failedToMakeAccessible.getMessage(), failedToMakeAccessible);
- }
+ validateConditionals();
}
private void validateReturnType(Method method, Class expectedType) {
@@ -144,7 +194,7 @@ public final class ExtensionExtractor {
private void validateMethodAnnotationPropertyLength(String property, String name, int maxLength, Method method) {
if (property.length() > maxLength) {
- warnings.add(extensionName + "." + method.getName() + " '" + name + WAS_OVER_50_CHARACTERS);
+ warnings.add(extensionName + "." + method.getName() + " '" + name + "' was over " + maxLength + " characters.");
}
}
@@ -186,135 +236,109 @@ public final class ExtensionExtractor {
// Has valid parameter & it is acceptable.
}
- private void validateMethodAnnotations() {
- validateBooleanProviderAnnotations();
- validateNumberProviderAnnotations();
- validateDoubleProviderAnnotations();
- validatePercentageProviderAnnotations();
- validateStringProviderAnnotations();
- validateTableProviderAnnotations();
- validateGroupProviderAnnotations();
- }
+ private void validateMethod(ExtensionMethod extensionMethod, BooleanProvider annotation) {
+ Method method = extensionMethod.getMethod();
+ validateReturnType(method, boolean.class);
+ validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
+ validateMethodAnnotationPropertyLength(annotation.description(), "description", 150, method);
+ validateMethodAnnotationPropertyLength(annotation.conditionName(), "conditionName", 50, method);
+ validateMethodArguments(method, false, UUID.class, String.class, Group.class);
- private void validateBooleanProviderAnnotations() {
- for (Map.Entry booleanProvider : methodAnnotations.getMethodAnnotations(BooleanProvider.class).entrySet()) {
- Method method = booleanProvider.getKey();
- BooleanProvider annotation = booleanProvider.getValue();
+ String condition = extensionMethod.getAnnotation(Conditional.class).map(Conditional::value).orElse(null);
+ if (annotation.conditionName().equals(condition)) {
+ warnings.add(extensionName + "." + method.getName() + " can not be conditional of itself. required condition: " + condition + ", provided condition: " + annotation.conditionName());
+ }
- validateReturnType(method, boolean.class);
- validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
- validateMethodAnnotationPropertyLength(annotation.description(), "description", 150, method);
- validateMethodAnnotationPropertyLength(annotation.conditionName(), "conditionName", 50, method);
- validateMethodArguments(method, false, UUID.class, String.class, Group.class);
-
- String condition = MethodAnnotations.get(method, Conditional.class).map(Conditional::value).orElse(null);
- if (annotation.conditionName().equals(condition)) {
- warnings.add(extensionName + "." + method.getName() + " can not be conditional of itself. required condition: " + condition + ", provided condition: " + annotation.conditionName());
- }
-
- if (annotation.conditionName().isEmpty() && annotation.hidden()) {
- throw new IllegalArgumentException(extensionName + "." + method.getName() + " can not be 'hidden' without a 'conditionName'");
- }
+ if (annotation.conditionName().isEmpty() && annotation.hidden()) {
+ throw new IllegalArgumentException(extensionName + "." + method.getName() + " can not be 'hidden' without a 'conditionName'");
}
}
- private void validateNumberProviderAnnotations() {
- for (Map.Entry numberProvider : methodAnnotations.getMethodAnnotations(NumberProvider.class).entrySet()) {
- Method method = numberProvider.getKey();
- NumberProvider annotation = numberProvider.getValue();
+ private void validateMethod(ExtensionMethod extensionMethod, NumberProvider annotation) {
+ Method method = extensionMethod.getMethod();
- validateReturnType(method, long.class);
- validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
- validateMethodAnnotationPropertyLength(annotation.description(), "description", 150, method);
- validateMethodArguments(method, false, UUID.class, String.class, Group.class);
- }
+ validateReturnType(method, long.class);
+ validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
+ validateMethodAnnotationPropertyLength(annotation.description(), "description", 150, method);
+ validateMethodArguments(method, false, UUID.class, String.class, Group.class);
}
- private void validateDoubleProviderAnnotations() {
- for (Map.Entry doubleProvider : methodAnnotations.getMethodAnnotations(DoubleProvider.class).entrySet()) {
- Method method = doubleProvider.getKey();
- DoubleProvider annotation = doubleProvider.getValue();
+ private void validateMethod(ExtensionMethod extensionMethod, DoubleProvider annotation) {
+ Method method = extensionMethod.getMethod();
- validateReturnType(method, double.class);
- validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
- validateMethodAnnotationPropertyLength(annotation.description(), "description", 150, method);
- validateMethodArguments(method, false, UUID.class, String.class, Group.class);
- }
+ validateReturnType(method, double.class);
+ validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
+ validateMethodAnnotationPropertyLength(annotation.description(), "description", 150, method);
+ validateMethodArguments(method, false, UUID.class, String.class, Group.class);
}
- private void validatePercentageProviderAnnotations() {
- for (Map.Entry percentageProvider : methodAnnotations.getMethodAnnotations(PercentageProvider.class).entrySet()) {
- Method method = percentageProvider.getKey();
- PercentageProvider annotation = percentageProvider.getValue();
+ private void validateMethod(ExtensionMethod extensionMethod, PercentageProvider annotation) {
+ Method method = extensionMethod.getMethod();
- validateReturnType(method, double.class);
- validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
- validateMethodAnnotationPropertyLength(annotation.description(), "description", 150, method);
- validateMethodArguments(method, false, UUID.class, String.class, Group.class);
- }
+ validateReturnType(method, double.class);
+ validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
+ validateMethodAnnotationPropertyLength(annotation.description(), "description", 150, method);
+ validateMethodArguments(method, false, UUID.class, String.class, Group.class);
}
- private void validateStringProviderAnnotations() {
- for (Map.Entry stringProvider : methodAnnotations.getMethodAnnotations(StringProvider.class).entrySet()) {
- Method method = stringProvider.getKey();
- StringProvider annotation = stringProvider.getValue();
+ private void validateMethod(ExtensionMethod extensionMethod, StringProvider annotation) {
+ Method method = extensionMethod.getMethod();
- validateReturnType(method, String.class);
- validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
- validateMethodAnnotationPropertyLength(annotation.description(), "description", 150, method);
- validateMethodArguments(method, false, UUID.class, String.class, Group.class);
- }
+ validateReturnType(method, String.class);
+ validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
+ validateMethodAnnotationPropertyLength(annotation.description(), "description", 150, method);
+ validateMethodArguments(method, false, UUID.class, String.class, Group.class);
}
- private void validateTableProviderAnnotations() {
- for (Method method : methodAnnotations.getMethodAnnotations(TableProvider.class).keySet()) {
- validateReturnType(method, Table.class);
- validateMethodArguments(method, false, UUID.class, String.class, Group.class);
- }
+ private void validateMethod(ExtensionMethod extensionMethod, TableProvider annotation) {
+ Method method = extensionMethod.getMethod();
+
+ validateReturnType(method, Table.class);
+ validateMethodArguments(method, false, UUID.class, String.class, Group.class);
}
- private void validateGroupProviderAnnotations() {
- for (Map.Entry groupProvider : methodAnnotations.getMethodAnnotations(GroupProvider.class).entrySet()) {
- Method method = groupProvider.getKey();
- GroupProvider annotation = groupProvider.getValue();
+ private void validateMethod(ExtensionMethod extensionMethod, GroupProvider annotation) {
+ Method method = extensionMethod.getMethod();
- validateReturnType(method, String[].class);
- validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
- validateMethodArguments(method, true, UUID.class, String.class);
- }
+ validateReturnType(method, String[].class);
+ validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
+ validateMethodArguments(method, true, UUID.class, String.class);
+ }
+
+ private void validateMethod(ExtensionMethod extensionMethod, DataBuilderProvider annotation) {
+ Method method = extensionMethod.getMethod();
+
+ validateReturnType(method, ExtensionDataBuilder.class);
+ validateMethodArguments(method, false, UUID.class, String.class);
}
private void validateConditionals() {
- Collection conditionals = methodAnnotations.getAnnotations(Conditional.class);
- Collection conditionProviders = methodAnnotations.getAnnotations(BooleanProvider.class);
-
- Set providedConditions = conditionProviders.stream().map(BooleanProvider::conditionName).collect(Collectors.toSet());
-
- for (Conditional condition : conditionals) {
- String conditionName = condition.value();
-
- if (conditionName.length() > 50) {
- warnings.add(extensionName + ": '" + conditionName + "' conditionName was over 50 characters.");
- }
-
- if (!providedConditions.contains(conditionName)) {
- warnings.add(extensionName + ": '" + conditionName + "' Condition was not provided by any BooleanProvider.");
- }
- }
-
// Make sure that all methods annotated with Conditional have a Provider annotation
- Collection conditionalMethods = methodAnnotations.getMethodAnnotations(Conditional.class).keySet();
for (Method conditionalMethod : conditionalMethods) {
- if (!MethodAnnotations.hasAnyOf(conditionalMethod,
+ if (!hasAnyOf(conditionalMethod,
BooleanProvider.class, DoubleProvider.class, NumberProvider.class,
PercentageProvider.class, StringProvider.class, TableProvider.class,
- GroupProvider.class
+ GroupProvider.class, DataBuilderProvider.class
)) {
throw new IllegalArgumentException(extensionName + "." + conditionalMethod.getName() + " did not have any associated Provider for Conditional.");
}
+ if (hasAnyOf(conditionalMethod, DataBuilderProvider.class)) {
+ throw new IllegalArgumentException(extensionName + "." + conditionalMethod.getName() + " had Conditional, but DataBuilderProvider does not support it!");
+ }
}
}
+ private boolean hasAnyOf(Method method, Class>... annotationClasses) {
+ for (Annotation annotation : method.getAnnotations()) {
+ for (Class> annotationClass : annotationClasses) {
+ if (annotationClass.isAssignableFrom(annotation.getClass())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private Optional getClassAnnotation(Class ofClass) {
return getClassAnnotation(extension.getClass(), ofClass);
}
@@ -324,7 +348,7 @@ public final class ExtensionExtractor {
.orElseThrow(() -> new IllegalArgumentException("Given class had no PluginInfo annotation"));
if (pluginInfo.name().length() > 50) {
- warnings.add(extensionName + " PluginInfo 'name' was over 50 characters.");
+ warnings.add(extensionName + " PluginInfo 'name" + WAS_OVER_50_CHARACTERS);
}
}
@@ -343,17 +367,22 @@ public final class ExtensionExtractor {
}
});
- tabOrder = getClassAnnotation(TabOrder.class).orElse(null);
+ getClassAnnotation(TabOrder.class).map(TabOrder::value)
+ .ifPresent(order -> {
+ List orderAsList = Arrays.asList(order);
+ // Order by the 2nd list. https://stackoverflow.com/a/18130019
+ // O(n^2 log n), not very bad here because there aren't going to be more than 10 tabs maybe ever.
+ tabInformation.sort(Comparator.comparingInt(item -> orderAsList.indexOf(item.tab())));
+ });
- Map tabs = this.methodAnnotations.getMethodAnnotations(Tab.class);
- Set tabNames = tabs.values().stream().map(Tab::value).collect(Collectors.toSet());
+ Set tabNames = getTabAnnotations().stream().map(Tab::value).collect(Collectors.toSet());
// Check for unused TabInfo annotations
for (TabInfo tabInfo : tabInformation) {
String tabName = tabInfo.tab();
if (tabName.length() > 50) {
- warnings.add(extensionName + " TabInfo " + tabName + " name was over 50 characters.");
+ warnings.add(extensionName + " TabInfo " + tabName + " 'name" + WAS_OVER_50_CHARACTERS);
}
if (!tabNames.contains(tabName)) {
@@ -362,10 +391,9 @@ public final class ExtensionExtractor {
}
// Check Tab name lengths
- for (Map.Entry tab : tabs.entrySet()) {
- String tabName = tab.getValue().value();
+ for (String tabName : tabNames) {
if (tabName.length() > 50) {
- warnings.add(extensionName + "." + tab.getKey().getName() + " Tab '" + tabName + "' name was over 50 characters.");
+ warnings.add(extensionName + " Tab '" + tabName + "' 'name" + WAS_OVER_50_CHARACTERS);
}
}
}
@@ -391,22 +419,37 @@ public final class ExtensionExtractor {
}
public PluginInfo getPluginInfo() {
+ if (pluginInfo == null) extractPluginInfo();
return pluginInfo;
}
public Optional getTabOrder() {
- return Optional.ofNullable(tabOrder);
+ return getClassAnnotation(TabOrder.class);
+ }
+
+ public Collection getTabAnnotations() {
+ if (tabAnnotations == null) extractMethods();
+ return tabAnnotations;
}
public List getTabInformation() {
- return tabInformation != null ? tabInformation : Collections.emptyList();
+ if (tabInformation == null) extractTabInfo();
+ return tabInformation;
}
+ @Deprecated
public MethodAnnotations getMethodAnnotations() {
+ if (methodAnnotations == null) extractMethods();
return methodAnnotations;
}
+ public Map getMethods() {
+ if (methods == null) extractMethods();
+ return methods;
+ }
+
public List getInvalidateMethodAnnotations() {
- return invalidMethods != null ? invalidMethods : Collections.emptyList();
+ if (invalidMethods == null) extractInvalidMethods();
+ return invalidMethods;
}
}
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionMethod.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionMethod.java
new file mode 100644
index 000000000..af51d794a
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionMethod.java
@@ -0,0 +1,135 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.extractor;
+
+import com.djrapitops.plan.extension.DataExtension;
+import com.djrapitops.plan.extension.Group;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * Implementation detail, abstracts away some method reflection to a more usable API.
+ */
+public class ExtensionMethod {
+ private final DataExtension extension;
+ private final Method method;
+
+ private final Class> returnType;
+
+ public ExtensionMethod(DataExtension extension, Method method) {
+ this.extension = extension;
+ this.method = method;
+ returnType = method.getReturnType();
+ }
+
+ public boolean isInaccessible() {
+ int modifiers = method.getModifiers();
+ return Modifier.isPrivate(modifiers)
+ || Modifier.isProtected(modifiers)
+ || Modifier.isStatic(modifiers)
+ || Modifier.isNative(modifiers);
+ }
+
+ public Optional getAnnotation(Class ofType) {
+ return Optional.ofNullable(method.getAnnotation(ofType));
+ }
+
+ public T getExistingAnnotation(Class ofType) {
+ return getAnnotation(ofType).orElseThrow(() -> new IllegalArgumentException(method.getName() + " did not have " + ofType.getName() + " annotation"));
+ }
+
+ public T getAnnotationOrNull(Class ofType) {
+ return getAnnotation(ofType).orElse(null);
+ }
+
+ public ParameterType getParameterType() {
+ return ParameterType.getByMethodSignature(method);
+ }
+
+ public Class> getReturnType() {
+ return returnType;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public String getMethodName() {
+ return getMethod().getName();
+ }
+
+ public enum ParameterType {
+ SERVER_NONE(null),
+ PLAYER_STRING(String.class),
+ PLAYER_UUID(UUID.class),
+ GROUP(Group.class);
+
+ private final Class> type;
+
+ ParameterType(Class> type) {
+ this.type = type;
+ }
+
+ public static ParameterType getByMethodSignature(Method method) {
+ Class>[] parameters = method.getParameterTypes();
+ if (parameters.length == 0) return SERVER_NONE;
+
+ if (parameters.length > 1) {
+ // Has too many parameters
+ throw new IllegalArgumentException(method.getName() + " has too many parameters, only one parameter is required.");
+ }
+
+ Class> parameter = parameters[0];
+ if (String.class.equals(parameter)) return PLAYER_STRING;
+ if (UUID.class.equals(parameter)) return PLAYER_UUID;
+ if (Group.class.equals(parameter)) return GROUP;
+
+ throw new IllegalArgumentException(method.getName() + " does not have a valid parameter. Needs none, String, UUID or Group, had " + parameter.getSimpleName());
+ }
+
+ public Class> getType() {
+ return type;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ExtensionMethod that = (ExtensionMethod) o;
+ return Objects.equals(extension.getPluginName(), that.extension.getPluginName()) && Objects.equals(method, that.method) && Objects.equals(returnType, that.returnType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(extension.getPluginName(), method, returnType);
+ }
+
+ @Override
+ public String toString() {
+ return "ExtensionMethod{" +
+ "extension=" + extension +
+ ", method=" + method +
+ ", returnType=" + returnType +
+ '}';
+ }
+}
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionMethods.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionMethods.java
new file mode 100644
index 000000000..cfba74ba9
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionMethods.java
@@ -0,0 +1,145 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.extractor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Implementation detail, abstracts away method type reflection to a more usable API.
+ */
+public class ExtensionMethods {
+
+ private final List booleanProviders;
+ private final List numberProviders;
+ private final List doubleProviders;
+ private final List percentageProviders;
+ private final List stringProviders;
+ private final List tableProviders;
+ private final List groupProviders;
+ private final List dataBuilderProviders;
+
+ public ExtensionMethods() {
+ booleanProviders = new ArrayList<>();
+ numberProviders = new ArrayList<>();
+ doubleProviders = new ArrayList<>();
+ percentageProviders = new ArrayList<>();
+ stringProviders = new ArrayList<>();
+ tableProviders = new ArrayList<>();
+ groupProviders = new ArrayList<>();
+ dataBuilderProviders = new ArrayList<>();
+ }
+
+ public List getBooleanProviders() {
+ return booleanProviders;
+ }
+
+ public List getNumberProviders() {
+ return numberProviders;
+ }
+
+ public List getDoubleProviders() {
+ return doubleProviders;
+ }
+
+ public List getPercentageProviders() {
+ return percentageProviders;
+ }
+
+ public List getStringProviders() {
+ return stringProviders;
+ }
+
+ public List getTableProviders() {
+ return tableProviders;
+ }
+
+ public List getGroupProviders() {
+ return groupProviders;
+ }
+
+ public List getDataBuilderProviders() {
+ return dataBuilderProviders;
+ }
+
+ public void addBooleanMethod(ExtensionMethod method) {
+ booleanProviders.add(method);
+ }
+
+ public void addNumberMethod(ExtensionMethod method) {
+ numberProviders.add(method);
+ }
+
+ public void addDoubleMethod(ExtensionMethod method) {
+ doubleProviders.add(method);
+ }
+
+ public void addPercentageMethod(ExtensionMethod method) {
+ percentageProviders.add(method);
+ }
+
+ public void addStringMethod(ExtensionMethod method) {
+ stringProviders.add(method);
+ }
+
+ public void addTableMethod(ExtensionMethod method) {
+ tableProviders.add(method);
+ }
+
+ public void addGroupMethod(ExtensionMethod method) {
+ groupProviders.add(method);
+ }
+
+ public void addDataBuilderMethod(ExtensionMethod method) {
+ dataBuilderProviders.add(method);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ExtensionMethods that = (ExtensionMethods) o;
+ return Objects.equals(booleanProviders, that.booleanProviders)
+ && Objects.equals(numberProviders, that.numberProviders)
+ && Objects.equals(doubleProviders, that.doubleProviders)
+ && Objects.equals(percentageProviders, that.percentageProviders)
+ && Objects.equals(stringProviders, that.stringProviders)
+ && Objects.equals(tableProviders, that.tableProviders)
+ && Objects.equals(groupProviders, that.groupProviders)
+ && Objects.equals(dataBuilderProviders, that.dataBuilderProviders);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(booleanProviders, numberProviders, doubleProviders, percentageProviders, stringProviders, tableProviders, groupProviders, dataBuilderProviders);
+ }
+
+ @Override
+ public String toString() {
+ return "ExtensionMethods{" +
+ "booleanProviders=" + booleanProviders +
+ ", numberProviders=" + numberProviders +
+ ", doubleProviders=" + doubleProviders +
+ ", percentageProviders=" + percentageProviders +
+ ", stringProviders=" + stringProviders +
+ ", tableProviders=" + tableProviders +
+ ", groupProviders=" + groupProviders +
+ ", dataBuilderProviders=" + dataBuilderProviders +
+ '}';
+ }
+}
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/MethodAnnotations.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/MethodAnnotations.java
index 574c26281..1032c2556 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/MethodAnnotations.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/MethodAnnotations.java
@@ -27,7 +27,9 @@ import java.util.Optional;
* Implementation detail, utility class for handling method annotations.
*
* @author AuroraLS3
+ * @deprecated Old implementation used this.
*/
+@Deprecated
public class MethodAnnotations {
private final Map, Map> byAnnotationType;
diff --git a/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java b/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java
index 228d43c8e..860004788 100644
--- a/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java
+++ b/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java
@@ -19,9 +19,13 @@ package com.djrapitops.plan.extension.extractor;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.Group;
import com.djrapitops.plan.extension.annotation.*;
+import com.djrapitops.plan.extension.builder.ExtensionDataBuilder;
+import com.djrapitops.plan.extension.table.Table;
import org.junit.jupiter.api.Test;
-import java.util.UUID;
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -43,6 +47,13 @@ class ExtensionExtractorTest {
assertEquals("Given class had no PluginInfo annotation", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
}
+ @Test
+ void pluginInfoIsRequired2() {
+ class Extension implements DataExtension {}
+
+ assertEquals("Extension did not have @PluginInfo annotation!", assertThrows(IllegalArgumentException.class, new Extension()::getPluginName).getMessage());
+ }
+
@Test
void providerMethodsAreRequired() {
@PluginInfo(name = "Extension")
@@ -52,6 +63,14 @@ class ExtensionExtractorTest {
assertEquals("Extension class had no methods annotated with a Provider annotation", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
}
+ @Test
+ void extensionNameIsAvailable() {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {}
+
+ assertEquals("Extension", new Extension().getPluginName());
+ }
+
@Test
void publicProviderMethodsAreRequired() {
@PluginInfo(name = "Extension")
@@ -206,6 +225,20 @@ class ExtensionExtractorTest {
assertEquals("Extension.method has invalid return type. was: com.djrapitops.plan.extension.Group, expected: [Ljava.lang.String; (an array)", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
}
+ @Test
+ void dataBuilderProviderMustProvideDataBuilder() {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @DataBuilderProvider
+ public Group method(UUID playerUUID) {
+ return null;
+ }
+ }
+
+ ExtensionExtractor underTest = new ExtensionExtractor(new Extension());
+ assertEquals("Extension.method has invalid return type. was: com.djrapitops.plan.extension.Group, expected: com.djrapitops.plan.extension.builder.ExtensionDataBuilder", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
+ }
+
@Test
void booleanProviderCanNotSupplyItsOwnConditional() {
@PluginInfo(name = "Extension")
@@ -235,21 +268,6 @@ class ExtensionExtractorTest {
assertEquals("Extension.method did not have any associated Provider for Conditional.", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
}
- @Test
- void conditionalNeedsToBeProvided() {
- @PluginInfo(name = "Extension")
- class Extension implements DataExtension {
- @Conditional("hasJoined")
- @BooleanProvider(text = "Banned", conditionName = "isBanned")
- public boolean method(UUID playerUUID) {
- return false;
- }
- }
-
- ExtensionExtractor underTest = new ExtensionExtractor(new Extension());
- assertEquals("Warnings: [Extension: 'hasJoined' Condition was not provided by any BooleanProvider.]", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
- }
-
@Test
void methodNeedsValidParameters() {
@PluginInfo(name = "Extension")
@@ -280,4 +298,254 @@ class ExtensionExtractorTest {
assertEquals("Extension.method has too many parameters, only one of [class java.util.UUID, class java.lang.String, interface com.djrapitops.plan.extension.Group] is required as a parameter.", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
}
+ @Test
+ void methodsAreExtracted1() throws NoSuchMethodException {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @Conditional("hasJoined")
+ @BooleanProvider(text = "Test", conditionName = "isBanned")
+ public boolean method() {
+ return false;
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ Map result = underTest.getMethods();
+ Map expected = buildExpectedExtensionMethodMap(extension, ExtensionMethods::addBooleanMethod);
+
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void methodsAreExtracted2() throws NoSuchMethodException {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @Conditional("hasJoined")
+ @NumberProvider(text = "Test")
+ public long method() {
+ return 0;
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ Map result = underTest.getMethods();
+ Map expected = buildExpectedExtensionMethodMap(extension, ExtensionMethods::addNumberMethod);
+
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void methodsAreExtracted3() throws NoSuchMethodException {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @Conditional("hasJoined")
+ @DoubleProvider(text = "Test")
+ public double method() {
+ return 0;
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ Map result = underTest.getMethods();
+ Map expected = buildExpectedExtensionMethodMap(extension, ExtensionMethods::addDoubleMethod);
+
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void methodsAreExtracted4() throws NoSuchMethodException {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @Conditional("hasJoined")
+ @PercentageProvider(text = "Test")
+ public double method() {
+ return 0;
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ Map result = underTest.getMethods();
+ Map expected = buildExpectedExtensionMethodMap(extension, ExtensionMethods::addPercentageMethod);
+
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void methodsAreExtracted5() throws NoSuchMethodException {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @Conditional("hasJoined")
+ @StringProvider(text = "Test")
+ public String method() {
+ return "example";
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ Map result = underTest.getMethods();
+ Map expected = buildExpectedExtensionMethodMap(extension, ExtensionMethods::addStringMethod);
+
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void methodsAreExtracted6() throws NoSuchMethodException {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @TableProvider
+ public Table method() {
+ return Table.builder().build();
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ Map result = underTest.getMethods();
+ Map expected = buildExpectedExtensionMethodMap(extension, ExtensionMethods::addTableMethod);
+
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void methodsAreExtracted7() throws NoSuchMethodException {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @GroupProvider
+ public String[] method(UUID playerUUID) {
+ return new String[]{"example"};
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ Map result = underTest.getMethods();
+
+ Map expected = new EnumMap<>(ExtensionMethod.ParameterType.class);
+ for (ExtensionMethod.ParameterType value : ExtensionMethod.ParameterType.values()) {
+ expected.put(value, new ExtensionMethods());
+ }
+ expected.get(ExtensionMethod.ParameterType.PLAYER_UUID).addGroupMethod(new ExtensionMethod(extension, extension.getClass().getMethod("method", UUID.class)));
+
+ assertEquals(expected, result);
+ }
+
+ private Map buildExpectedExtensionMethodMap(DataExtension extension, BiConsumer addTo) throws NoSuchMethodException {
+ Map map = new EnumMap<>(ExtensionMethod.ParameterType.class);
+ for (ExtensionMethod.ParameterType value : ExtensionMethod.ParameterType.values()) {
+ map.put(value, new ExtensionMethods());
+ }
+ addTo.accept(
+ map.get(ExtensionMethod.ParameterType.SERVER_NONE),
+ new ExtensionMethod(extension, extension.getClass().getMethod("method"))
+ );
+ return map;
+ }
+
+ @Test
+ void methodsAreExtracted8() throws NoSuchMethodException {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @DataBuilderProvider
+ public ExtensionDataBuilder method() {
+ return null;
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ Map result = underTest.getMethods();
+ Map expected = buildExpectedExtensionMethodMap(extension, ExtensionMethods::addDataBuilderMethod);
+
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void tabsAreExtracted() throws NoSuchMethodException {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @Tab("Tab name")
+ @DataBuilderProvider
+ public ExtensionDataBuilder method() {
+ return null;
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+
+ Collection expected = Collections.singletonList(extension.getClass().getMethod("method").getAnnotation(Tab.class));
+ Collection result = underTest.getTabAnnotations();
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void conditionalsAreExtracted() throws NoSuchMethodException {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @Conditional("example")
+ @StringProvider(text = "Test")
+ public String method() {
+ return "example";
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+
+ String expected = Stream.of(extension.getClass().getMethod("method").getAnnotation(Conditional.class))
+ .map(Conditional::value).findFirst().orElseThrow(AssertionError::new);
+ String result = underTest.getMethodAnnotations().getAnnotations(Conditional.class).stream()
+ .map(Conditional::value).findFirst().orElseThrow(AssertionError::new);
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void textOver50CharsIsWarnedAbout() {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @StringProvider(text = "aaaaaAAAAAbbbbbBBBBBcccccCCCCCdddddDDDDDeeeeeEEEEEfffffFFFF")
+ public String method() {
+ return "example";
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ assertEquals(
+ "Warnings: [Extension.method 'text' was over 50 characters.]",
+ assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage()
+ );
+ }
+
+ @Test
+ void descriptionOver50CharsIsWarnedAbout() {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @StringProvider(
+ text = "a",
+ description = "aaaaaAAAAAbbbbbBBBBBcccccCCCCCdddddDDDDDeeeeeEEEEEaaaaaAAAAAbbbbbBBBBBcccccCCCCCdddddDDDDDeeeeeEEEEEaaaaaAAAAAbbbbbBBBBBcccccCCCCCdddddDDDDDeeeeeEEEEEfffffFFFFF"
+ )
+ public String method() {
+ return "example";
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ assertEquals(
+ "Warnings: [Extension.method 'description' was over 150 characters.]",
+ assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage()
+ );
+ }
+
+ @Test
+ void dataBuilderProviderCanNotHaveConditional() {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @Conditional("bad")
+ @DataBuilderProvider
+ public ExtensionDataBuilder method() {
+ return null;
+ }
+ }
+ Extension extension = new Extension();
+ ExtensionExtractor underTest = new ExtensionExtractor(extension);
+ assertEquals(
+ "Extension.method had Conditional, but DataBuilderProvider does not support it!",
+ assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage()
+ );
+ }
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java
index 886a1c6d4..868ffedf1 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java
@@ -23,7 +23,6 @@ import com.djrapitops.plan.delivery.web.ResolverSvc;
import com.djrapitops.plan.delivery.web.ResourceSvc;
import com.djrapitops.plan.delivery.webserver.NonProxyWebserverDisableChecker;
import com.djrapitops.plan.delivery.webserver.WebServerSystem;
-import com.djrapitops.plan.extension.ExtensionService;
import com.djrapitops.plan.extension.ExtensionSvc;
import com.djrapitops.plan.gathering.cache.CacheSystem;
import com.djrapitops.plan.gathering.importing.ImportSystem;
@@ -275,7 +274,7 @@ public class PlanSystem implements SubSystem {
return enabled;
}
- public ExtensionService getExtensionService() {
+ public ExtensionSvc getExtensionService() {
return extensionService;
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/exceptions/DataExtensionMethodCallException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/DataExtensionMethodCallException.java
index 393659151..9c29b5495 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/exceptions/DataExtensionMethodCallException.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/DataExtensionMethodCallException.java
@@ -16,8 +16,6 @@
*/
package com.djrapitops.plan.exceptions;
-import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
-
import java.util.Optional;
/**
@@ -28,21 +26,19 @@ import java.util.Optional;
public class DataExtensionMethodCallException extends IllegalStateException {
private final String pluginName;
- // Non serializable field due to Method not being serializable.
- private final transient MethodWrapper> method;
+ private final String methodName;
- public DataExtensionMethodCallException(Throwable cause, String pluginName, MethodWrapper> method) {
- super(cause);
+ public DataExtensionMethodCallException(String message, Throwable cause, String pluginName, String methodName) {
+ super(message, cause);
this.pluginName = pluginName;
- this.method = method;
+ this.methodName = methodName;
}
public String getPluginName() {
return pluginName;
}
- public Optional> getMethod() {
- // method is transient and might be lost if flushed to disk.
- return Optional.ofNullable(method);
+ public Optional getMethodName() {
+ return Optional.ofNullable(methodName);
}
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionSvc.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionSvc.java
index 72e71d987..876050ebf 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionSvc.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionSvc.java
@@ -16,12 +16,12 @@
*/
package com.djrapitops.plan.extension;
-import com.djrapitops.plan.exceptions.DataExtensionMethodCallException;
+import com.djrapitops.plan.extension.builder.ExtensionDataBuilder;
import com.djrapitops.plan.extension.implementation.CallerImplementation;
import com.djrapitops.plan.extension.implementation.ExtensionRegister;
import com.djrapitops.plan.extension.implementation.ExtensionWrapper;
-import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
-import com.djrapitops.plan.extension.implementation.providers.gathering.ProviderValueGatherer;
+import com.djrapitops.plan.extension.implementation.builder.ExtDataBuilder;
+import com.djrapitops.plan.extension.implementation.providers.gathering.DataValueGatherer;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.processing.Processing;
import com.djrapitops.plan.settings.config.ExtensionSettings;
@@ -55,7 +55,7 @@ public class ExtensionSvc implements ExtensionService {
private final PluginLogger logger;
private final ErrorLogger errorLogger;
- private final Map extensionGatherers;
+ private final Map extensionGatherers;
@Inject
public ExtensionSvc(
@@ -98,17 +98,17 @@ public class ExtensionSvc implements ExtensionService {
}
@Override
- public Optional register(DataExtension extension) {
- ExtensionWrapper extractor = new ExtensionWrapper(extension);
- String pluginName = extractor.getPluginName();
+ public Optional register(DataExtension dataExtension) {
+ ExtensionWrapper extension = new ExtensionWrapper(dataExtension);
+ String pluginName = extension.getPluginName();
if (shouldNotAllowRegistration(pluginName)) return Optional.empty();
- for (String warning : extractor.getWarnings()) {
+ for (String warning : extension.getWarnings()) {
logger.warn("DataExtension API implementation mistake for " + pluginName + ": " + warning);
}
- ProviderValueGatherer gatherer = new ProviderValueGatherer(extractor, dbSystem, serverInfo);
+ DataValueGatherer gatherer = new DataValueGatherer(extension, dbSystem, serverInfo, errorLogger);
gatherer.storeExtensionInformation();
extensionGatherers.put(pluginName, gatherer);
@@ -120,9 +120,12 @@ public class ExtensionSvc implements ExtensionService {
@Override
public void unregister(DataExtension extension) {
- ExtensionWrapper extractor = new ExtensionWrapper(extension);
- String pluginName = extractor.getPluginName();
- extensionGatherers.remove(pluginName);
+ extensionGatherers.remove(extension.getPluginName());
+ }
+
+ @Override
+ public ExtensionDataBuilder newExtensionDataBuilder(DataExtension extension) {
+ return new ExtDataBuilder(extension);
}
private boolean shouldNotAllowRegistration(String pluginName) {
@@ -145,61 +148,27 @@ public class ExtensionSvc implements ExtensionService {
}
public void updatePlayerValues(UUID playerUUID, String playerName, CallEvents event) {
- for (ProviderValueGatherer gatherer : extensionGatherers.values()) {
+ for (DataValueGatherer gatherer : extensionGatherers.values()) {
updatePlayerValues(gatherer, playerUUID, playerName, event);
}
}
- public void updatePlayerValues(ProviderValueGatherer gatherer, UUID playerUUID, String playerName, CallEvents event) {
+ public void updatePlayerValues(DataValueGatherer gatherer, UUID playerUUID, String playerName, CallEvents event) {
if (gatherer.shouldSkipEvent(event)) return;
if (playerUUID == null && playerName == null) return;
- try {
- gatherer.updateValues(playerUUID, playerName);
- } catch (DataExtensionMethodCallException methodCallFailed) {
- logFailure(playerName, methodCallFailed);
- methodCallFailed.getMethod().ifPresent(gatherer::disableMethodFromUse);
- } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) {
- ErrorContext.Builder context = ErrorContext.builder()
- .whatToDo("Report and/or disable " + gatherer.getPluginName() + " extension in the Plan config.")
- .related(gatherer.getPluginName())
- .related(event)
- .related("Player: " + playerName + " " + playerUUID);
- errorLogger.warn(unexpectedError, context.build());
- }
- }
-
- private void logFailure(String playerName, DataExtensionMethodCallException methodCallFailed) {
- Throwable cause = methodCallFailed.getCause();
- ErrorContext.Builder context = ErrorContext.builder()
- .whatToDo("Report and/or disable " + methodCallFailed.getPluginName() + " extension in the Plan config.")
- .related(methodCallFailed.getPluginName())
- .related("Method:" + methodCallFailed.getMethod().map(MethodWrapper::getMethodName).orElse("-"))
- .related("Player: " + playerName);
- errorLogger.warn(cause, context.build());
+ gatherer.updateValues(playerUUID, playerName);
}
public void updateServerValues(CallEvents event) {
- for (ProviderValueGatherer gatherer : extensionGatherers.values()) {
+ for (DataValueGatherer gatherer : extensionGatherers.values()) {
updateServerValues(gatherer, event);
}
}
- public void updateServerValues(ProviderValueGatherer gatherer, CallEvents event) {
+ public void updateServerValues(DataValueGatherer gatherer, CallEvents event) {
if (gatherer.shouldSkipEvent(event)) return;
- try {
- gatherer.updateValues();
- } catch (DataExtensionMethodCallException methodCallFailed) {
- logFailure("server", methodCallFailed);
- methodCallFailed.getMethod().ifPresent(gatherer::disableMethodFromUse);
- } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) {
- ErrorContext.Builder context = ErrorContext.builder()
- .whatToDo("Report and/or disable " + gatherer.getPluginName() + " extension in the Plan config.")
- .related(gatherer.getPluginName())
- .related(event)
- .related("Gathering for server");
- errorLogger.warn(unexpectedError, context.build());
- }
+ gatherer.updateValues();
}
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java
index 1908ee570..c3bf670a5 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java
@@ -19,7 +19,7 @@ package com.djrapitops.plan.extension.implementation;
import com.djrapitops.plan.extension.CallEvents;
import com.djrapitops.plan.extension.Caller;
import com.djrapitops.plan.extension.ExtensionSvc;
-import com.djrapitops.plan.extension.implementation.providers.gathering.ProviderValueGatherer;
+import com.djrapitops.plan.extension.implementation.providers.gathering.DataValueGatherer;
import com.djrapitops.plan.processing.Processing;
import java.util.UUID;
@@ -31,12 +31,12 @@ import java.util.UUID;
*/
public class CallerImplementation implements Caller {
- private final ProviderValueGatherer gatherer;
+ private final DataValueGatherer gatherer;
private final ExtensionSvc extensionService;
private final Processing processing;
public CallerImplementation(
- ProviderValueGatherer gatherer,
+ DataValueGatherer gatherer,
ExtensionSvc extensionService,
Processing processing
) {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ExtensionWrapper.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ExtensionWrapper.java
index 247065fd5..0a7f36d9c 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ExtensionWrapper.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ExtensionWrapper.java
@@ -20,19 +20,13 @@ import com.djrapitops.plan.extension.CallEvents;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.annotation.*;
import com.djrapitops.plan.extension.extractor.ExtensionExtractor;
-import com.djrapitops.plan.extension.extractor.MethodAnnotations;
+import com.djrapitops.plan.extension.extractor.ExtensionMethod;
+import com.djrapitops.plan.extension.extractor.ExtensionMethods;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
-import com.djrapitops.plan.extension.implementation.providers.*;
import com.djrapitops.plan.utilities.java.Lists;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
+import java.util.*;
import java.util.stream.Collectors;
/**
@@ -47,9 +41,12 @@ import java.util.stream.Collectors;
public class ExtensionWrapper {
private final ExtensionExtractor extractor;
- private final DataProviders providers;
private final DataExtension extension;
+ private final PluginInfo pluginInfo;
+ private final List tabInformation;
+ private final Map methods;
+
/**
* Create an ExtensionWrapper.
*
@@ -60,10 +57,9 @@ public class ExtensionWrapper {
this.extension = extension;
extractor = new ExtensionExtractor(this.extension);
- extractor.extractAnnotationInformation();
-
- providers = new DataProviders();
- extractProviders();
+ pluginInfo = extractor.getPluginInfo();
+ tabInformation = extractor.getTabInformation();
+ methods = extractor.getMethods();
}
public CallEvents[] getCallEvents() {
@@ -75,30 +71,28 @@ public class ExtensionWrapper {
}
public String getPluginName() {
- return extractor.getPluginInfo().name();
+ return pluginInfo.name();
}
public Icon getPluginIcon() {
- PluginInfo pluginInfo = extractor.getPluginInfo();
return new Icon(pluginInfo.iconFamily(), pluginInfo.iconName(), pluginInfo.color());
}
public Collection getPluginTabs() {
- Map tabInformation = extractor.getTabInformation()
- .stream().collect(Collectors.toMap(TabInfo::tab, Function.identity(), (one, two) -> one));
-
Map order = getTabOrder().map(this::orderToMap).orElse(new HashMap<>());
- // Extracts PluginTabs
- return extractor.getMethodAnnotations().getAnnotations(Tab.class).stream()
+ Set usedTabs = extractor.getTabAnnotations().stream()
.map(Tab::value)
- .distinct()
- .map(tabName -> {
- Optional tabInfo = Optional.ofNullable(tabInformation.get(tabName));
+ .collect(Collectors.toSet());
+ return extractor.getTabInformation()
+ .stream()
+ .filter(info -> usedTabs.contains(info.tab()))
+ .map(tabInfo -> {
+ String tabName = tabInfo.tab();
return new TabInformation(
tabName,
- tabInfo.map(info -> new Icon(info.iconFamily(), info.iconName(), Color.NONE)).orElse(null),
- tabInfo.map(TabInfo::elementOrder).orElse(null),
+ new Icon(tabInfo.iconFamily(), tabInfo.iconName(), Color.NONE),
+ tabInfo.elementOrder(),
order.getOrDefault(tabName, 100)
);
}).collect(Collectors.toList());
@@ -120,57 +114,23 @@ public class ExtensionWrapper {
return Lists.mapUnique(extractor.getInvalidateMethodAnnotations(), InvalidateMethod::value);
}
- public DataProviders getProviders() {
- return providers;
- }
-
- private void extractProviders() {
- PluginInfo pluginInfo = extractor.getPluginInfo();
-
- MethodAnnotations methodAnnotations = extractor.getMethodAnnotations();
- Map tabs = methodAnnotations.getMethodAnnotations(Tab.class);
- Map conditions = methodAnnotations.getMethodAnnotations(Conditional.class);
-
- extractProviders(pluginInfo, tabs, conditions, BooleanProvider.class, BooleanDataProvider::placeToDataProviders);
- extractProviders(pluginInfo, tabs, conditions, DoubleProvider.class, DoubleDataProvider::placeToDataProviders);
- extractProviders(pluginInfo, tabs, conditions, PercentageProvider.class, PercentageDataProvider::placeToDataProviders);
- extractProviders(pluginInfo, tabs, conditions, NumberProvider.class, NumberDataProvider::placeToDataProviders);
- extractProviders(pluginInfo, tabs, conditions, StringProvider.class, StringDataProvider::placeToDataProviders);
- extractProviders(pluginInfo, tabs, conditions, TableProvider.class, TableDataProvider::placeToDataProviders);
- extractProviders(pluginInfo, tabs, conditions, GroupProvider.class, GroupDataProvider::placeToDataProviders);
- }
-
- private void extractProviders(PluginInfo pluginInfo, Map tabs, Map conditions, Class ofKind, DataProviderFactory factory) {
- String pluginName = pluginInfo.name();
-
- for (Map.Entry entry : extractor.getMethodAnnotations().getMethodAnnotations(ofKind).entrySet()) {
- Method method = entry.getKey();
- T annotation = entry.getValue();
- Conditional conditional = conditions.get(method);
- Optional tab = Optional.ofNullable(tabs.get(method));
-
- factory.placeToDataProviders(
- providers, method, annotation,
- conditional,
- tab.map(Tab::value).orElse(null),
- pluginName
- );
- }
- }
-
public Collection getWarnings() {
return extractor.getWarnings();
}
- /**
- * Functional interface for defining a method that places required DataProvider to DataProviders.
- *
- * @param Type of the annotation on the method that is going to be extracted.
- */
- interface DataProviderFactory {
- void placeToDataProviders(
- DataProviders dataProviders,
- Method method, T annotation, Conditional condition, String tab, String pluginName
- );
+ public ExtensionExtractor getExtractor() {
+ return extractor;
+ }
+
+ public PluginInfo getPluginInfo() {
+ return pluginInfo;
+ }
+
+ public List getTabInformation() {
+ return tabInformation;
+ }
+
+ public Map getMethods() {
+ return methods;
}
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java
index 60f36a6b1..eee2219bc 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java
@@ -45,7 +45,13 @@ public class ProviderInformation extends ExtensionDescription {
private final boolean percentage; // affects where doubles are stored
private ProviderInformation(ProviderInformation.Builder builder) {
- super(builder.name, builder.text, builder.description, builder.icon, builder.priority);
+ super(
+ builder.name,
+ builder.text,
+ builder.description,
+ builder.icon != null ? builder.icon : Icon.called("cube").build(),
+ builder.priority
+ );
pluginName = builder.pluginName;
showInPlayersTable = builder.showInPlayersTable;
tab = builder.tab;
@@ -136,7 +142,7 @@ public class ProviderInformation extends ExtensionDescription {
private String text;
private String description;
private Icon icon;
- private int priority;
+ private int priority = 0;
private boolean showInPlayersTable = false;
private String tab; // can be null
private Conditional condition; // can be null
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/BooleanDataValue.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/BooleanDataValue.java
new file mode 100644
index 000000000..1fb7c2fec
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/BooleanDataValue.java
@@ -0,0 +1,31 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.implementation.builder;
+
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+
+import java.util.function.Supplier;
+
+public class BooleanDataValue extends BuiltDataValue {
+ public BooleanDataValue(Boolean value, ProviderInformation information) {
+ super(value, information);
+ }
+
+ public BooleanDataValue(Supplier supplier, ProviderInformation information) {
+ super(supplier, information);
+ }
+}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/BuiltDataValue.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/BuiltDataValue.java
new file mode 100644
index 000000000..cb37a2b04
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/BuiltDataValue.java
@@ -0,0 +1,83 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.implementation.builder;
+
+import com.djrapitops.plan.extension.builder.DataValue;
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+
+import java.util.Objects;
+import java.util.function.Supplier;
+
+public abstract class BuiltDataValue implements DataValue {
+
+ private final T value;
+ private final Supplier supplier;
+ private final ProviderInformation information;
+
+ protected BuiltDataValue(T value, ProviderInformation information) {
+ this(value, null, information);
+ }
+
+ protected BuiltDataValue(Supplier supplier, ProviderInformation information) {
+ this(null, supplier, information);
+ }
+
+ private BuiltDataValue(T value, Supplier supplier, ProviderInformation information) {
+ this.value = value;
+ this.supplier = supplier;
+ this.information = information;
+ }
+
+ @Override
+ public T getValue() {
+ if (value != null) return value;
+ if (supplier != null) return supplier.get();
+ return null;
+ }
+
+ public ProviderInformation getInformation() {
+ return information;
+ }
+
+ @Override
+ public M getInformation(Class ofType) {
+ if (ProviderInformation.class.equals(ofType)) return ofType.cast(getInformation());
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ BuiltDataValue> that = (BuiltDataValue>) o;
+ return Objects.equals(value, that.value) && Objects.equals(supplier, that.supplier) && Objects.equals(information, that.information);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value, supplier, information);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{" +
+ "value=" + value +
+ ", supplier=" + supplier +
+ ", information=" + information +
+ '}';
+ }
+}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/DoubleDataValue.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/DoubleDataValue.java
new file mode 100644
index 000000000..c22f14cc9
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/DoubleDataValue.java
@@ -0,0 +1,31 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.implementation.builder;
+
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+
+import java.util.function.Supplier;
+
+public class DoubleDataValue extends BuiltDataValue {
+ public DoubleDataValue(Double value, ProviderInformation information) {
+ super(value, information);
+ }
+
+ public DoubleDataValue(Supplier supplier, ProviderInformation information) {
+ super(supplier, information);
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/ExtDataBuilder.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/ExtDataBuilder.java
new file mode 100644
index 000000000..2fa5075ee
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/ExtDataBuilder.java
@@ -0,0 +1,149 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.implementation.builder;
+
+import com.djrapitops.plan.extension.DataExtension;
+import com.djrapitops.plan.extension.NotReadyException;
+import com.djrapitops.plan.extension.builder.DataValue;
+import com.djrapitops.plan.extension.builder.ExtensionDataBuilder;
+import com.djrapitops.plan.extension.builder.ValueBuilder;
+import com.djrapitops.plan.extension.implementation.providers.gathering.Conditions;
+
+import java.util.*;
+import java.util.function.Supplier;
+
+public class ExtDataBuilder implements ExtensionDataBuilder {
+
+ private final List values;
+ private final DataExtension extension;
+
+ public ExtDataBuilder(DataExtension extension) {
+ this.extension = extension;
+ values = new ArrayList<>();
+ }
+
+ @Override
+ public ValueBuilder valueBuilder(String text) {
+ if (text == null || text.isEmpty()) throw new IllegalArgumentException("'text' can't be null or empty");
+ return new ExtValueBuilder(text, extension);
+ }
+
+ @Override
+ public ExtensionDataBuilder addValue(Class ofType, DataValue dataValue) {
+ if (ofType != null && dataValue != null) values.add(new ClassValuePair(ofType, dataValue));
+ return this;
+ }
+
+ @Override
+ public ExtensionDataBuilder addValue(Class ofType, Supplier> dataValue) {
+ try {
+ if (ofType != null && dataValue != null) values.add(new ClassValuePair(ofType, dataValue.get()));
+ } catch (NotReadyException | UnsupportedOperationException ignored) {
+ // This exception is ignored by default to allow throwing errors inside the lambda to keep code clean.
+ }
+ // Other exceptions handled by ProviderValueGatherer during method call.
+ return this;
+ }
+
+ public List getValues() {
+ Collections.sort(values);
+ return values;
+ }
+
+ public String getExtensionName() {
+ return extension.getPluginName();
+ }
+
+ @Override
+ public void addAll(ExtensionDataBuilder builder) {
+ if (!(builder instanceof ExtDataBuilder)) return;
+ // From same DataExtension
+ if (!extension.getClass().equals(((ExtDataBuilder) builder).extension.getClass())) {
+ throw new IllegalArgumentException("Can not combine data from two different extensions! (" +
+ extension.getClass().getName() + ',' + ((ExtDataBuilder) builder).extension.getClass().getName() + ")");
+ }
+
+ this.values.addAll(((ExtDataBuilder) builder).values);
+ }
+
+ public static final class ClassValuePair implements Comparable {
+ private final Class> type;
+ private final DataValue> value;
+
+ public ClassValuePair(Class type, DataValue value) {
+ this.type = type;
+ this.value = value;
+ }
+
+ public Optional> getValue(Class ofType) {
+ if (type.equals(ofType)) {
+ return Optional.ofNullable((DataValue) value);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public int compareTo(ClassValuePair that) {
+ boolean thisIsBoolean = Boolean.class.isAssignableFrom(type) && value instanceof BooleanDataValue;
+ boolean otherIsBoolean = Boolean.class.isAssignableFrom(that.type) && that.value instanceof BooleanDataValue;
+ if (thisIsBoolean && !otherIsBoolean) {
+ return -1; // This is boolean, have it go first
+ } else if (!thisIsBoolean && otherIsBoolean) {
+ return 1; // Other is boolean, have it go first
+ } else if (thisIsBoolean) {
+ // Both are Booleans, so they might provide a condition
+
+ Optional otherCondition = ((BooleanDataValue) that.value).getInformation().getCondition();
+ String providedCondition = ((BooleanDataValue) value).getInformation().getProvidedCondition();
+ // Another provider's required condition is satisfied by this, have this first
+ if (otherCondition.filter(c -> Conditions.matchesCondition(c, providedCondition)).isPresent()) {
+ return -1;
+ }
+
+ // Required condition is satisfied by another provider, have that first
+ Optional condition = ((BooleanDataValue) value).getInformation().getCondition();
+ String otherProvidedCondition = ((BooleanDataValue) that.value).getInformation().getProvidedCondition();
+ if (condition.filter(c -> Conditions.matchesCondition(c, otherProvidedCondition)).isPresent()) {
+ return 1;
+ }
+ }
+ // Irrelevant, keep where is
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ClassValuePair that = (ClassValuePair) o;
+ return Objects.equals(type, that.type) && Objects.equals(value, that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, value);
+ }
+
+ @Override
+ public String toString() {
+ return "ClassValuePair{" +
+ "type=" + type +
+ ", value=" + value +
+ '}';
+ }
+ }
+}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/ExtValueBuilder.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/ExtValueBuilder.java
new file mode 100644
index 000000000..7af5b4060
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/ExtValueBuilder.java
@@ -0,0 +1,243 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.implementation.builder;
+
+import com.djrapitops.plan.extension.DataExtension;
+import com.djrapitops.plan.extension.FormatType;
+import com.djrapitops.plan.extension.annotation.BooleanProvider;
+import com.djrapitops.plan.extension.annotation.Conditional;
+import com.djrapitops.plan.extension.annotation.PluginInfo;
+import com.djrapitops.plan.extension.builder.DataValue;
+import com.djrapitops.plan.extension.builder.ValueBuilder;
+import com.djrapitops.plan.extension.extractor.ExtensionMethod;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.icon.Icon;
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+import com.djrapitops.plan.extension.table.Table;
+
+import java.util.function.Supplier;
+
+public class ExtValueBuilder implements ValueBuilder {
+
+ private final String pluginName;
+ private final String text;
+ private String providerName;
+ private String description;
+ private int priority = 0;
+ private boolean showInPlayerTable = false;
+ private Icon icon;
+ private String tabName;
+
+ private boolean hidden = false;
+ private boolean formatAsPlayerName = false;
+ private FormatType formatType = FormatType.NONE;
+ private Conditional conditional;
+
+ public ExtValueBuilder(String text, DataExtension extension) {
+ this.text = text;
+ pluginName = extension.getClass().getAnnotation(PluginInfo.class).name();
+ }
+
+ @Override
+ public ValueBuilder methodName(ExtensionMethod method) {
+ this.providerName = method.getMethod().getName();
+ return this;
+ }
+
+ @Override
+ public ValueBuilder description(String description) {
+ this.description = description;
+ return this;
+ }
+
+ @Override
+ public ValueBuilder priority(int priority) {
+ this.priority = priority;
+ return this;
+ }
+
+ @Override
+ public ValueBuilder showInPlayerTable() {
+ this.showInPlayerTable = true;
+ return this;
+ }
+
+ @Override
+ public ValueBuilder icon(Icon icon) {
+ this.icon = icon;
+ return this;
+ }
+
+ @Override
+ public ValueBuilder showOnTab(String tabName) {
+ this.tabName = tabName;
+ return this;
+ }
+
+ @Override
+ public ValueBuilder format(FormatType formatType) {
+ this.formatType = formatType;
+ return this;
+ }
+
+ @Override
+ public ValueBuilder showAsPlayerPageLink() {
+ formatAsPlayerName = true;
+ return this;
+ }
+
+ @Override
+ public ValueBuilder hideFromUsers(BooleanProvider annotation) {
+ this.hidden = annotation.hidden();
+ return this;
+ }
+
+ @Override
+ public ValueBuilder conditional(Conditional conditional) {
+ this.conditional = conditional;
+ return this;
+ }
+
+ private ProviderInformation getProviderInformation() {
+ return getProviderInformation(false, null);
+ }
+
+ private ProviderInformation getBooleanProviderInformation(String providedCondition) {
+ return getProviderInformation(false, providedCondition);
+ }
+
+ private ProviderInformation getPercentageProviderInformation() {
+ return getProviderInformation(true, null);
+ }
+
+ private ProviderInformation getProviderInformation(boolean percentage, String providedCondition) {
+ ProviderInformation.Builder builder = ProviderInformation.builder(pluginName)
+ .setName(providerName != null ? providerName
+ : text.toLowerCase().replaceAll("\\s", ""))
+ .setText(text)
+ .setDescription(description)
+ .setPriority(priority)
+ .setIcon(icon)
+ .setShowInPlayersTable(showInPlayerTable)
+ .setTab(tabName)
+ .setPlayerName(formatAsPlayerName)
+ .setFormatType(formatType)
+ .setHidden(hidden)
+ .setCondition(conditional);
+
+ if (percentage) {
+ builder = builder.setAsPercentage();
+ }
+
+ if (providedCondition != null && !providedCondition.isEmpty()) {
+ builder = builder.setProvidedCondition(providedCondition);
+ }
+
+ return builder.build();
+ }
+
+ private ProviderInformation getTableProviderInformation(Color tableColor) {
+ return ProviderInformation.builder(pluginName)
+ .setName(providerName != null ? providerName
+ : text.toLowerCase().replaceAll("\\s", ""))
+ .setPriority(0)
+ .setCondition(conditional)
+ .setTab(tabName)
+ .setTableColor(tableColor)
+ .build();
+ }
+
+ @Override
+ public DataValue buildBoolean(boolean value) {
+ return new BooleanDataValue(value, getProviderInformation());
+ }
+
+ @Override
+ public DataValue buildBooleanProvidingCondition(boolean value, String providedCondition) {
+ return new BooleanDataValue(value, getBooleanProviderInformation(providedCondition));
+ }
+
+ @Override
+ public DataValue buildString(String value) {
+ return new StringDataValue(value, getProviderInformation());
+ }
+
+ @Override
+ public DataValue buildNumber(long value) {
+ return new NumberDataValue(value, getProviderInformation());
+ }
+
+ @Override
+ public DataValue buildDouble(double value) {
+ return new DoubleDataValue(value, getProviderInformation());
+ }
+
+ @Override
+ public DataValue buildPercentage(double value) {
+ return new DoubleDataValue(value, getPercentageProviderInformation());
+ }
+
+ @Override
+ public DataValue buildGroup(String[] groups) {
+ return new GroupsDataValue(groups, getProviderInformation());
+ }
+
+ @Override
+ public DataValue buildBoolean(Supplier value) {
+ return new BooleanDataValue(value, getProviderInformation());
+ }
+
+ @Override
+ public DataValue buildBooleanProvidingCondition(Supplier value, String providedCondition) {
+ return new BooleanDataValue(value, getBooleanProviderInformation(providedCondition));
+ }
+
+ @Override
+ public DataValue buildString(Supplier value) {
+ return new StringDataValue(value, getProviderInformation());
+ }
+
+ @Override
+ public DataValue buildNumber(Supplier value) {
+ return new NumberDataValue(value, getProviderInformation());
+ }
+
+ @Override
+ public DataValue buildDouble(Supplier value) {
+ return new DoubleDataValue(value, getProviderInformation());
+ }
+
+ @Override
+ public DataValue buildPercentage(Supplier percentage) {
+ return new DoubleDataValue(percentage, getPercentageProviderInformation());
+ }
+
+ @Override
+ public DataValue buildGroup(Supplier groups) {
+ return new GroupsDataValue(groups, getProviderInformation());
+ }
+
+ @Override
+ public DataValue buildTable(Table table, Color tableColor) {
+ return new TableDataValue(table, getTableProviderInformation(tableColor));
+ }
+
+ @Override
+ public DataValue buildTable(Supplier table, Color tableColor) {
+ return new TableDataValue(table, getTableProviderInformation(tableColor));
+ }
+}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/GroupsDataValue.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/GroupsDataValue.java
new file mode 100644
index 000000000..013e49291
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/GroupsDataValue.java
@@ -0,0 +1,31 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.implementation.builder;
+
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+
+import java.util.function.Supplier;
+
+public class GroupsDataValue extends BuiltDataValue {
+ public GroupsDataValue(String[] value, ProviderInformation information) {
+ super(value, information);
+ }
+
+ public GroupsDataValue(Supplier supplier, ProviderInformation information) {
+ super(supplier, information);
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/NumberDataValue.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/NumberDataValue.java
new file mode 100644
index 000000000..46055e5f8
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/NumberDataValue.java
@@ -0,0 +1,31 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.implementation.builder;
+
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+
+import java.util.function.Supplier;
+
+public class NumberDataValue extends BuiltDataValue {
+ public NumberDataValue(Long value, ProviderInformation information) {
+ super(value, information);
+ }
+
+ public NumberDataValue(Supplier supplier, ProviderInformation information) {
+ super(supplier, information);
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/StringDataValue.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/StringDataValue.java
new file mode 100644
index 000000000..1ace9ab50
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/StringDataValue.java
@@ -0,0 +1,31 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.implementation.builder;
+
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+
+import java.util.function.Supplier;
+
+public class StringDataValue extends BuiltDataValue {
+ public StringDataValue(String value, ProviderInformation information) {
+ super(value, information);
+ }
+
+ public StringDataValue(Supplier supplier, ProviderInformation information) {
+ super(supplier, information);
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/TableDataValue.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/TableDataValue.java
new file mode 100644
index 000000000..ca27db653
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/builder/TableDataValue.java
@@ -0,0 +1,32 @@
+/*
+ * This file is part of Player Analytics (Plan).
+ *
+ * Plan is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License v3 as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Plan is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Plan. If not, see .
+ */
+package com.djrapitops.plan.extension.implementation.builder;
+
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+import com.djrapitops.plan.extension.table.Table;
+
+import java.util.function.Supplier;
+
+public class TableDataValue extends BuiltDataValue {
+ public TableDataValue(Table value, ProviderInformation information) {
+ super(value, information);
+ }
+
+ public TableDataValue(Supplier supplier, ProviderInformation information) {
+ super(supplier, information);
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/BooleanDataProvider.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/BooleanDataProvider.java
deleted file mode 100644
index 7fbffe4e0..000000000
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/BooleanDataProvider.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * This file is part of Player Analytics (Plan).
- *
- * Plan is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License v3 as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Plan is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Plan. If not, see .
- */
-package com.djrapitops.plan.extension.implementation.providers;
-
-import com.djrapitops.plan.extension.annotation.BooleanProvider;
-import com.djrapitops.plan.extension.annotation.Conditional;
-import com.djrapitops.plan.extension.icon.Icon;
-import com.djrapitops.plan.extension.implementation.ProviderInformation;
-
-import java.lang.reflect.Method;
-
-/**
- * Contains code that acts on {@link BooleanProvider} annotations.
- *
- * @author AuroraLS3
- */
-public class BooleanDataProvider {
-
- private BooleanDataProvider() {
- // Static method class
- }
-
- public static void placeToDataProviders(
- DataProviders dataProviders, Method method, BooleanProvider annotation,
- Conditional condition, String tab, String pluginName
- ) {
- ProviderInformation information = ProviderInformation.builder(pluginName)
- .setName(method.getName())
- .setText(annotation.text())
- .setDescription(annotation.description())
- .setPriority(annotation.priority())
- .setIcon(new Icon(
- annotation.iconFamily(),
- annotation.iconName(),
- annotation.iconColor())
- ).setShowInPlayersTable(annotation.showInPlayerTable())
- .setCondition(condition)
- .setTab(tab)
- .setHidden(annotation.hidden())
- .setProvidedCondition(annotation.conditionName())
- .build();
-
- MethodWrapper methodWrapper = new MethodWrapper<>(method, Boolean.class);
- dataProviders.put(new DataProvider<>(information, methodWrapper));
- }
-}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java
deleted file mode 100644
index dea2dd162..000000000
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * This file is part of Player Analytics (Plan).
- *
- * Plan is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License v3 as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Plan is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Plan. If not, see .
- */
-package com.djrapitops.plan.extension.implementation.providers;
-
-import com.djrapitops.plan.extension.implementation.MethodType;
-import com.djrapitops.plan.utilities.java.Lists;
-import com.djrapitops.plan.utilities.java.Maps;
-
-import java.util.*;
-
-/**
- * Group class for handling multiple different types of {@link DataProvider}s.
- *
- * @author AuroraLS3
- */
-public class DataProviders {
-
- private final Map, List>>> byMethodType;
-
- public DataProviders() {
- byMethodType = new EnumMap<>(MethodType.class);
- }
-
- public void put(DataProvider> provider) {
- MethodWrapper> method = provider.getMethod();
- MethodType methodType = method.getMethodType();
- Class> returnType = method.getReturnType();
-
- computeIfAbsent(methodType, returnType).add(provider);
- }
-
- private List> computeIfAbsent(MethodType methodType, Class> returnType) {
- return byMethodType.computeIfAbsent(methodType, Maps::create).computeIfAbsent(returnType, Lists::create);
- }
-
- public List> getProvidersByTypes(MethodType methodType, Class returnType) {
- Map, List>> byReturnType = byMethodType.getOrDefault(methodType, Collections.emptyMap());
- List> wildcardProvidersWithSpecificType = byReturnType.get(returnType);
- if (wildcardProvidersWithSpecificType == null) {
- return Collections.emptyList();
- }
- // Cast to T
- List> providers = new ArrayList<>();
- for (DataProvider> dataProvider : wildcardProvidersWithSpecificType) {
- providers.add((DataProvider) dataProvider);
- }
- return providers;
- }
-
- public List> getPlayerMethodsByType(Class returnType) {
- return getProvidersByTypes(MethodType.PLAYER, returnType);
- }
-
- public List> getServerMethodsByType(Class returnType) {
- return getProvidersByTypes(MethodType.SERVER, returnType);
- }
-
- public List> getGroupMethodsByType(Class returnType) {
- return getProvidersByTypes(MethodType.GROUP, returnType);
- }
-
- public void removeProviderWithMethod(MethodWrapper toRemove) {
- MethodType methodType = toRemove.getMethodType();
- Map, List>> byResultType = byMethodType.getOrDefault(methodType, Collections.emptyMap());
- if (byResultType.isEmpty()) {
- return;
- }
-
- Class returnType = toRemove.getReturnType();
- List> providers = getProvidersByTypes(methodType, returnType);
-
- DataProvider providerToRemove = null;
- for (DataProvider provider : providers) {
- if (provider.getMethod().equals(toRemove)) {
- providerToRemove = provider;
- break;
- }
- }
- providers.remove(providerToRemove);
- }
-}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DoubleDataProvider.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DoubleDataProvider.java
deleted file mode 100644
index 57dd81058..000000000
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DoubleDataProvider.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * This file is part of Player Analytics (Plan).
- *
- * Plan is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License v3 as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Plan is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Plan. If not, see .
- */
-package com.djrapitops.plan.extension.implementation.providers;
-
-import com.djrapitops.plan.extension.annotation.Conditional;
-import com.djrapitops.plan.extension.annotation.DoubleProvider;
-import com.djrapitops.plan.extension.icon.Icon;
-import com.djrapitops.plan.extension.implementation.ProviderInformation;
-
-import java.lang.reflect.Method;
-
-/**
- * Contains code that acts on {@link DoubleProvider} annotations.
- *
- * @author AuroraLS3
- */
-public class DoubleDataProvider {
-
- private DoubleDataProvider() {
- // Static method class
- }
-
- public static void placeToDataProviders(
- DataProviders dataProviders, Method method, DoubleProvider annotation,
- Conditional condition, String tab, String pluginName
- ) {
- ProviderInformation information = ProviderInformation.builder(pluginName)
- .setName(method.getName())
- .setText(annotation.text())
- .setDescription(annotation.description())
- .setPriority(annotation.priority())
- .setIcon(new Icon(
- annotation.iconFamily(),
- annotation.iconName(),
- annotation.iconColor())
- ).setShowInPlayersTable(annotation.showInPlayerTable())
- .setCondition(condition)
- .setTab(tab)
- .build();
-
- MethodWrapper methodWrapper = new MethodWrapper<>(method, Double.class);
-
- dataProviders.put(new DataProvider<>(information, methodWrapper));
- }
-}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/GroupDataProvider.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/GroupDataProvider.java
deleted file mode 100644
index 198599434..000000000
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/GroupDataProvider.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * This file is part of Player Analytics (Plan).
- *
- * Plan is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License v3 as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Plan is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Plan. If not, see .
- */
-package com.djrapitops.plan.extension.implementation.providers;
-
-import com.djrapitops.plan.extension.annotation.Conditional;
-import com.djrapitops.plan.extension.annotation.GroupProvider;
-import com.djrapitops.plan.extension.icon.Icon;
-import com.djrapitops.plan.extension.implementation.ProviderInformation;
-
-import java.lang.reflect.Method;
-
-/**
- * Contains code that acts on {@link GroupProvider} annotations.
- *
- * @author AuroraLS3
- */
-public class GroupDataProvider {
-
- private GroupDataProvider() {
- // Static method class
- }
-
- public static void placeToDataProviders(
- DataProviders dataProviders, Method method, GroupProvider annotation,
- Conditional condition, String tab, String pluginName
- ) {
- ProviderInformation information = ProviderInformation.builder(pluginName)
- .setName(method.getName())
- .setText(annotation.text())
- .setPriority(0)
- .setIcon(new Icon(
- annotation.iconFamily(),
- annotation.iconName(),
- annotation.groupColor())
- ).setShowInPlayersTable(true)
- .setCondition(condition)
- .setTab(tab)
- .build();
-
- MethodWrapper methodWrapper = new MethodWrapper<>(method, String[].class);
- dataProviders.put(new DataProvider<>(information, methodWrapper));
- }
-}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java
index d46d196e1..104ca8930 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java
@@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension.implementation.providers;
+import com.djrapitops.plan.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.NotReadyException;
import com.djrapitops.plan.extension.implementation.MethodType;
@@ -23,6 +24,7 @@ import com.djrapitops.plan.extension.implementation.MethodType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
+import java.util.Optional;
/**
* Wrap a Method so that it is easier to call.
@@ -47,17 +49,22 @@ public class MethodWrapper {
try {
return returnType.cast(with.usingOn(extension, method));
} catch (InvocationTargetException notReadyToBeCalled) {
- if (notReadyToBeCalled.getCause() instanceof NotReadyException
- || notReadyToBeCalled.getCause() instanceof UnsupportedOperationException) {
+ Throwable cause = notReadyToBeCalled.getCause();
+ if (cause instanceof NotReadyException || cause instanceof UnsupportedOperationException) {
return null; // Data or API not available to make the call.
} else {
- throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " could not be called: " + notReadyToBeCalled.getMessage(), notReadyToBeCalled);
+ throw new DataExtensionMethodCallException(getErrorMessage(extension, notReadyToBeCalled), notReadyToBeCalled, extension.getPluginName(), getMethodName());
}
} catch (IllegalAccessException e) {
- throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " could not be called: " + e.getMessage(), e);
+ throw new DataExtensionMethodCallException(extension.getPluginName() + '.' + getMethodName() + " could not be accessed: " + e.getMessage(), e, extension.getPluginName(), getMethodName());
}
}
+ private String getErrorMessage(DataExtension extension, InvocationTargetException e) {
+ Optional actualCause = Optional.ofNullable(e.getCause()); // InvocationTargetException
+ return extension.getPluginName() + '.' + getMethodName() + " errored: " + actualCause.orElse(e).toString();
+ }
+
public String getMethodName() {
return method.getName();
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/NumberDataProvider.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/NumberDataProvider.java
deleted file mode 100644
index 1a95573e0..000000000
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/NumberDataProvider.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * This file is part of Player Analytics (Plan).
- *
- * Plan is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License v3 as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Plan is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Plan. If not, see .
- */
-package com.djrapitops.plan.extension.implementation.providers;
-
-import com.djrapitops.plan.extension.annotation.Conditional;
-import com.djrapitops.plan.extension.annotation.NumberProvider;
-import com.djrapitops.plan.extension.icon.Icon;
-import com.djrapitops.plan.extension.implementation.ProviderInformation;
-
-import java.lang.reflect.Method;
-
-/**
- * Contains code that acts on {@link NumberProvider} annotations.
- *
- * @author AuroraLS3
- */
-public class NumberDataProvider {
-
- private NumberDataProvider() {
- // Static method class
- }
-
- public static void placeToDataProviders(
- DataProviders dataProviders, Method method, NumberProvider annotation,
- Conditional condition, String tab, String pluginName
- ) {
- ProviderInformation information = ProviderInformation.builder(pluginName)
- .setName(method.getName())
- .setText(annotation.text())
- .setDescription(annotation.description())
- .setPriority(annotation.priority())
- .setIcon(new Icon(
- annotation.iconFamily(),
- annotation.iconName(),
- annotation.iconColor())
- ).setShowInPlayersTable(annotation.showInPlayerTable())
- .setCondition(condition)
- .setTab(tab)
- .setFormatType(annotation.format())
- .build();
-
- MethodWrapper methodWrapper = new MethodWrapper<>(method, Long.class);
- dataProviders.put(new DataProvider<>(information, methodWrapper));
- }
-}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/PercentageDataProvider.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/PercentageDataProvider.java
deleted file mode 100644
index 4f4b400ab..000000000
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/PercentageDataProvider.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * This file is part of Player Analytics (Plan).
- *
- * Plan is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License v3 as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Plan is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Plan. If not, see .
- */
-package com.djrapitops.plan.extension.implementation.providers;
-
-import com.djrapitops.plan.extension.annotation.Conditional;
-import com.djrapitops.plan.extension.annotation.PercentageProvider;
-import com.djrapitops.plan.extension.icon.Icon;
-import com.djrapitops.plan.extension.implementation.ProviderInformation;
-
-import java.lang.reflect.Method;
-
-/**
- * Contains code that acts on {@link PercentageProvider} annotations.
- *
- * @author AuroraLS3
- */
-public class PercentageDataProvider {
-
- private PercentageDataProvider() {
- // Static method class
- }
-
- public static void placeToDataProviders(
- DataProviders dataProviders, Method method, PercentageProvider annotation,
- Conditional condition, String tab, String pluginName
- ) {
- ProviderInformation information = ProviderInformation.builder(pluginName)
- .setName(method.getName())
- .setText(annotation.text())
- .setDescription(annotation.description())
- .setPriority(annotation.priority())
- .setIcon(new Icon(
- annotation.iconFamily(),
- annotation.iconName(),
- annotation.iconColor())
- ).setShowInPlayersTable(annotation.showInPlayerTable())
- .setCondition(condition)
- .setTab(tab)
- .setAsPercentage()
- .build();
-
- MethodWrapper methodWrapper = new MethodWrapper<>(method, Double.class);
-
- dataProviders.put(new DataProvider<>(information, methodWrapper));
- }
-}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/StringDataProvider.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/StringDataProvider.java
deleted file mode 100644
index 8a588f423..000000000
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/StringDataProvider.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * This file is part of Player Analytics (Plan).
- *
- * Plan is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License v3 as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Plan is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Plan. If not, see .
- */
-package com.djrapitops.plan.extension.implementation.providers;
-
-import com.djrapitops.plan.extension.annotation.Conditional;
-import com.djrapitops.plan.extension.annotation.StringProvider;
-import com.djrapitops.plan.extension.icon.Icon;
-import com.djrapitops.plan.extension.implementation.ProviderInformation;
-
-import java.lang.reflect.Method;
-
-/**
- * Contains code that acts on {@link StringProvider} annotations.
- *
- * @author AuroraLS3
- */
-public class StringDataProvider {
-
- private StringDataProvider() {
- // Static method class
- }
-
- public static void placeToDataProviders(
- DataProviders dataProviders, Method method, StringProvider annotation,
- Conditional condition, String tab, String pluginName
- ) {
- ProviderInformation information = ProviderInformation.builder(pluginName)
- .setName(method.getName())
- .setText(annotation.text())
- .setDescription(annotation.description())
- .setPriority(annotation.priority())
- .setIcon(new Icon(
- annotation.iconFamily(),
- annotation.iconName(),
- annotation.iconColor())
- ).setShowInPlayersTable(annotation.showInPlayerTable())
- .setCondition(condition)
- .setTab(tab)
- .setPlayerName(annotation.playerName())
- .build();
-
- MethodWrapper methodWrapper = new MethodWrapper<>(method, String.class);
- dataProviders.put(new DataProvider<>(information, methodWrapper));
- }
-}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/TableDataProvider.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/TableDataProvider.java
deleted file mode 100644
index 8c0ed66c2..000000000
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/TableDataProvider.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * This file is part of Player Analytics (Plan).
- *
- * Plan is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License v3 as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Plan is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Plan. If not, see