mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-11-18 16:45:12 +01:00
Plan API 5.0-R0.1: DATA_EXTENSION_GROUPS Capability
This commit is contained in:
parent
65ddbb2ef0
commit
a7978c8bda
@ -2,7 +2,7 @@ plugins {
|
||||
id "com.jfrog.bintray" version "1.8.4"
|
||||
}
|
||||
|
||||
ext.apiVersion = '0.0.6'
|
||||
ext.apiVersion = '5.0-R0.1'
|
||||
|
||||
bintray {
|
||||
user = System.getenv('BINTRAY_USER')
|
||||
@ -15,7 +15,7 @@ bintray {
|
||||
issueTrackerUrl = 'https://github.com/plan-player-analytics/Plan/issues'
|
||||
version {
|
||||
name = "$apiVersion"
|
||||
desc = "Plan APIv5 version $apiVersion"
|
||||
desc = "Plan API version $apiVersion"
|
||||
}
|
||||
publications = ['BintrayPublication']
|
||||
}
|
||||
|
@ -41,6 +41,10 @@ enum Capability {
|
||||
* DataExtension API table package, TableProvider, Table and Table.Factory
|
||||
*/
|
||||
DATA_EXTENSION_TABLES,
|
||||
/**
|
||||
* DataExtension API groups, GroupProvider and Group parameter methods
|
||||
*/
|
||||
DATA_EXTENSION_GROUPS,
|
||||
/**
|
||||
* DataExtension API addition, allows throwing {@link com.djrapitops.plan.extension.NotReadyException} inside a Provider method when your API is not ready for a method call.
|
||||
*/
|
||||
|
@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Method annotation to provide a boolean value about a Player.
|
||||
* Method annotation to provide a boolean value.
|
||||
* <p>
|
||||
* Usage: {@code @BooleanProvider boolean method(UUID playerUUID)}
|
||||
* <p>
|
||||
|
@ -28,6 +28,9 @@ import java.lang.annotation.Target;
|
||||
* If {@link com.djrapitops.plan.extension.annotation.BooleanProvider} for the condition is not specified the
|
||||
* method tagged with this annotation will not be called, (Condition is assumed false).
|
||||
*
|
||||
* Please note that Conditional does not cross method parameter boundaries - (Conditional on a player method does not
|
||||
* take into account conditionals of server).
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Method annotation to provide a double value about a Player.
|
||||
* Method annotation to provide a double value.
|
||||
* <p>
|
||||
* Usage: {@code @DoubleProvider double method(UUID playerUUID)}
|
||||
*
|
||||
|
@ -0,0 +1,64 @@
|
||||
package com.djrapitops.plan.extension.annotation;
|
||||
|
||||
import com.djrapitops.plan.extension.icon.Color;
|
||||
import com.djrapitops.plan.extension.icon.Family;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Method annotation to provide {@code Group[]} array about a Player.
|
||||
* <p>
|
||||
* This method annotation only works when used with {@code UUID} or {@code String} as a method parameter (for Players).
|
||||
* <p>
|
||||
* For example:
|
||||
* {@code @GroupProvider public Group[] getJobs(UUID playerUUID) {}}
|
||||
* <p>
|
||||
* Group data is parsed as Table for /server & /network page and similar to {@link StringProvider} for /player page.
|
||||
* <p>
|
||||
* Requires Capability {@code DATA_EXTENSION_GROUPS}
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface GroupProvider {
|
||||
|
||||
/**
|
||||
* Text displayed before the value, limited to 50 characters.
|
||||
* <p>
|
||||
* Should inform the user what the group names represent, for example
|
||||
* "Town" or "Job"
|
||||
*
|
||||
* @return String of max 50 characters, remainder will be clipped.
|
||||
*/
|
||||
String text() default "Group";
|
||||
|
||||
/**
|
||||
* Determine the color of the table header for this group.
|
||||
*
|
||||
* @return Preferred color. If none are specified defaults are used.
|
||||
*/
|
||||
Color groupColor() default Color.NONE;
|
||||
|
||||
/**
|
||||
* Name of Font Awesome icon.
|
||||
* <p>
|
||||
* See https://fontawesome.com/icons (select 'free')) for icons and their {@link Family}.
|
||||
*
|
||||
* @return Name of the icon, if name is not valid no icon is shown.
|
||||
*/
|
||||
String iconName() default "circle";
|
||||
|
||||
/**
|
||||
* Family of Font Awesome icon.
|
||||
* <p>
|
||||
* See https://fontawesome.com/icons (select 'free')) for icons and their {@link Family}.
|
||||
*
|
||||
* @return Family that matches an icon, if there is no icon for this family no icon is shown.
|
||||
*/
|
||||
Family iconFamily() default Family.SOLID;
|
||||
|
||||
}
|
@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Method annotation to provide a long (64bit number) value about a Player.
|
||||
* Method annotation to provide a long (64bit number) value.
|
||||
* <p>
|
||||
* If you want to return int values, use this provider with a long as
|
||||
* return type of the method.
|
||||
|
@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Method annotation to provide a double (Percentage) about a Player.
|
||||
* Method annotation to provide a double (Percentage).
|
||||
* <p>
|
||||
* Usage: {@code @PercentageProvider double method(UUID playerUUID)}
|
||||
* <p>
|
||||
|
@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Method annotation to provide a String value about a Player.
|
||||
* Method annotation to provide a String value.
|
||||
* <p>
|
||||
* Usage: {@code @StringProvider String method(UUID playerUUID)}
|
||||
* <p>
|
||||
|
@ -98,8 +98,10 @@ public final class ExtensionExtractor {
|
||||
|
||||
for (Method method : getMethods()) {
|
||||
int modifiers = method.getModifiers();
|
||||
if (!Modifier.isPublic(modifiers)
|
||||
|| Modifier.isStatic(modifiers)) {
|
||||
if (Modifier.isPrivate(modifiers)
|
||||
|| Modifier.isProtected(modifiers)
|
||||
|| Modifier.isStatic(modifiers)
|
||||
|| Modifier.isNative(modifiers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -113,17 +115,30 @@ public final class ExtensionExtractor {
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void validateReturnType(Method method, Class<T> expectedType) {
|
||||
Class<?> returnType = method.getReturnType();
|
||||
if (!expectedType.isAssignableFrom(returnType)) {
|
||||
throw new IllegalArgumentException(extensionName + "." + method.getName() + " has invalid return type. was: " + returnType.getName() + ", expected: " + expectedType.getName());
|
||||
String expectedName = expectedType.getName();
|
||||
throw new IllegalArgumentException(extensionName + "." + method.getName() +
|
||||
" has invalid return type. was: " +
|
||||
returnType.getName() +
|
||||
", expected: " +
|
||||
(expectedName.startsWith("[L") ? expectedName + " (an array)" : expectedName));
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,6 +193,7 @@ public final class ExtensionExtractor {
|
||||
validatePercentageProviderAnnotations();
|
||||
validateStringProviderAnnotations();
|
||||
validateTableProviderAnnotations();
|
||||
validateGroupProviderAnnotations();
|
||||
}
|
||||
|
||||
private void validateBooleanProviderAnnotations() {
|
||||
@ -215,9 +231,9 @@ public final class ExtensionExtractor {
|
||||
}
|
||||
|
||||
private void validateDoubleProviderAnnotations() {
|
||||
for (Map.Entry<Method, DoubleProvider> numberProvider : methodAnnotations.getMethodAnnotations(DoubleProvider.class).entrySet()) {
|
||||
Method method = numberProvider.getKey();
|
||||
DoubleProvider annotation = numberProvider.getValue();
|
||||
for (Map.Entry<Method, DoubleProvider> doubleProvider : methodAnnotations.getMethodAnnotations(DoubleProvider.class).entrySet()) {
|
||||
Method method = doubleProvider.getKey();
|
||||
DoubleProvider annotation = doubleProvider.getValue();
|
||||
|
||||
validateReturnType(method, double.class);
|
||||
validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
|
||||
@ -227,9 +243,9 @@ public final class ExtensionExtractor {
|
||||
}
|
||||
|
||||
private void validatePercentageProviderAnnotations() {
|
||||
for (Map.Entry<Method, PercentageProvider> numberProvider : methodAnnotations.getMethodAnnotations(PercentageProvider.class).entrySet()) {
|
||||
Method method = numberProvider.getKey();
|
||||
PercentageProvider annotation = numberProvider.getValue();
|
||||
for (Map.Entry<Method, PercentageProvider> percentageProvider : methodAnnotations.getMethodAnnotations(PercentageProvider.class).entrySet()) {
|
||||
Method method = percentageProvider.getKey();
|
||||
PercentageProvider annotation = percentageProvider.getValue();
|
||||
|
||||
validateReturnType(method, double.class);
|
||||
validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
|
||||
@ -239,9 +255,9 @@ public final class ExtensionExtractor {
|
||||
}
|
||||
|
||||
private void validateStringProviderAnnotations() {
|
||||
for (Map.Entry<Method, StringProvider> numberProvider : methodAnnotations.getMethodAnnotations(StringProvider.class).entrySet()) {
|
||||
Method method = numberProvider.getKey();
|
||||
StringProvider annotation = numberProvider.getValue();
|
||||
for (Map.Entry<Method, StringProvider> stringProvider : methodAnnotations.getMethodAnnotations(StringProvider.class).entrySet()) {
|
||||
Method method = stringProvider.getKey();
|
||||
StringProvider annotation = stringProvider.getValue();
|
||||
|
||||
validateReturnType(method, String.class);
|
||||
validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
|
||||
@ -257,6 +273,17 @@ public final class ExtensionExtractor {
|
||||
}
|
||||
}
|
||||
|
||||
private void validateGroupProviderAnnotations() {
|
||||
for (Map.Entry<Method, GroupProvider> groupProvider : methodAnnotations.getMethodAnnotations(GroupProvider.class).entrySet()) {
|
||||
Method method = groupProvider.getKey();
|
||||
GroupProvider annotation = groupProvider.getValue();
|
||||
|
||||
validateReturnType(method, Group[].class);
|
||||
validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method);
|
||||
validateMethodArguments(method, true, UUID.class, String.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateConditionals() {
|
||||
Collection<Conditional> conditionals = methodAnnotations.getAnnotations(Conditional.class);
|
||||
Collection<BooleanProvider> conditionProviders = methodAnnotations.getAnnotations(BooleanProvider.class);
|
||||
@ -280,7 +307,8 @@ public final class ExtensionExtractor {
|
||||
for (Method conditionalMethod : conditionalMethods) {
|
||||
if (!MethodAnnotations.hasAnyOf(conditionalMethod,
|
||||
BooleanProvider.class, DoubleProvider.class, NumberProvider.class,
|
||||
PercentageProvider.class, StringProvider.class
|
||||
PercentageProvider.class, StringProvider.class, TableProvider.class,
|
||||
GroupProvider.class
|
||||
)) {
|
||||
throw new IllegalArgumentException(extensionName + "." + conditionalMethod.getName() + " did not have any associated Provider for Conditional.");
|
||||
}
|
||||
|
@ -73,4 +73,13 @@ public class MethodAnnotations {
|
||||
public String toString() {
|
||||
return "MethodAnnotations{" + byAnnotationType + '}';
|
||||
}
|
||||
|
||||
void makeMethodsAccessible() {
|
||||
byAnnotationType.values().stream()
|
||||
.map(Map::keySet)
|
||||
.flatMap(Collection::stream)
|
||||
.distinct()
|
||||
.filter(method -> !method.isAccessible())
|
||||
.forEach(method -> method.setAccessible(true));
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
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 org.junit.jupiter.api.Test;
|
||||
import org.junit.platform.runner.JUnitPlatform;
|
||||
@ -180,6 +181,34 @@ class ExtensionExtractorTest {
|
||||
assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: com.djrapitops.plan.extension.table.Table", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void groupProviderMustGroupArray() {
|
||||
@PluginInfo(name = "Extension")
|
||||
class Extension implements DataExtension {
|
||||
@GroupProvider
|
||||
public Double method(UUID playerUUID) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ExtensionExtractor underTest = new ExtensionExtractor(new Extension());
|
||||
assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: [Lcom.djrapitops.plan.extension.Group; (an array)", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void groupProviderMustGroupArray2() {
|
||||
@PluginInfo(name = "Extension")
|
||||
class Extension implements DataExtension {
|
||||
@GroupProvider
|
||||
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: [Lcom.djrapitops.plan.extension.Group; (an array)", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleanProviderCanNotSupplyItsOwnConditional() {
|
||||
@PluginInfo(name = "Extension")
|
||||
|
Loading…
Reference in New Issue
Block a user