summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorXor Boole <mcyoung@mit.edu>2015-02-20 19:52:58 -0500
committerXor Boole <mcyoung@mit.edu>2015-02-20 19:52:58 -0500
commitdf545f4fc89a706003b939b32d4f0bae1574dca5 (patch)
tree349acf02f5b409ed5c5f2c77105d5722db7c183d /src
downloadplugin-annotations-df545f4fc89a706003b939b32d4f0bae1574dca5.tar
plugin-annotations-df545f4fc89a706003b939b32d4f0bae1574dca5.tar.gz
plugin-annotations-df545f4fc89a706003b939b32d4f0bae1574dca5.tar.lz
plugin-annotations-df545f4fc89a706003b939b32d4f0bae1574dca5.tar.xz
plugin-annotations-df545f4fc89a706003b939b32d4f0bae1574dca5.zip
Initial Commit
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/Author.java18
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/Commands.java52
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/DependsOn.java17
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/Description.java17
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/LoadBefore.java17
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/LoadOn.java18
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/LogPrefix.java16
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/Main.java55
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/Name.java18
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/Permissions.java50
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/PluginAnnotationProcessor.java194
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/SoftDependsOn.java17
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/UsesDatabase.java13
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/Version.java20
-rw-r--r--src/main/java/org/bukkit/plugin/java/annotation/Website.java17
-rw-r--r--src/main/resources/META-INF/services/javax.annotation.processing.Processor1
16 files changed, 540 insertions, 0 deletions
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/Author.java b/src/main/java/org/bukkit/plugin/java/annotation/Author.java
new file mode 100644
index 0000000..d226d7d
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/Author.java
@@ -0,0 +1,18 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents the author(s) of the plugin. Translates to {@code author}
+ * in plugin.yml if a single author, otherwise {@code authors}
+ */
+
+@Target(ElementType.TYPE)
+public @interface Author {
+
+ public String[] value();
+
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/Commands.java b/src/main/java/org/bukkit/plugin/java/annotation/Commands.java
new file mode 100644
index 0000000..c730b59
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/Commands.java
@@ -0,0 +1,52 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents a list of this plugin's registered commands.
+ */
+
+@Target(ElementType.TYPE)
+public @interface Commands { // TODO: in java 8, make repeatable.
+
+ public Cmd[] value();
+
+ @Target({})
+ public static @interface Cmd {
+
+ /**
+ * This command's name.
+ */
+ public String value();
+
+ /**
+ * This command's description.
+ */
+
+ public String desc() default "";
+
+ /**
+ * This command's aliases.
+ */
+ public String[] aliases() default {};
+
+ /**
+ * This command's permission node.
+ */
+ public String permission() default "";
+
+ /**
+ * This command's permission-check-fail message.
+ */
+ public String permissionMessage() default "";
+
+ /**
+ * This command's usage message.
+ */
+ public String usage() default "";
+ }
+
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/DependsOn.java b/src/main/java/org/bukkit/plugin/java/annotation/DependsOn.java
new file mode 100644
index 0000000..892a267
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/DependsOn.java
@@ -0,0 +1,17 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents the plugin's hard dependencies.
+ */
+
+@Target(ElementType.TYPE)
+public @interface DependsOn {
+
+ public String[] value();
+
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/Description.java b/src/main/java/org/bukkit/plugin/java/annotation/Description.java
new file mode 100644
index 0000000..4595d06
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/Description.java
@@ -0,0 +1,17 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents a short description for the plugin.
+ */
+
+@Target(ElementType.TYPE)
+public @interface Description {
+
+ public String value();
+
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/LoadBefore.java b/src/main/java/org/bukkit/plugin/java/annotation/LoadBefore.java
new file mode 100644
index 0000000..d0802f4
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/LoadBefore.java
@@ -0,0 +1,17 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents the plugins this plugin should be loaded before
+ */
+
+@Target(ElementType.TYPE)
+public @interface LoadBefore {
+
+ public String[] value();
+
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/LoadOn.java b/src/main/java/org/bukkit/plugin/java/annotation/LoadOn.java
new file mode 100644
index 0000000..9e5372d
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/LoadOn.java
@@ -0,0 +1,18 @@
+package org.bukkit.plugin.java.annotation;
+
+import org.bukkit.plugin.PluginLoadOrder;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents the optional load order of the plugin.
+ */
+
+@Target(ElementType.TYPE)
+public @interface LoadOn {
+
+ public PluginLoadOrder value();
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/LogPrefix.java b/src/main/java/org/bukkit/plugin/java/annotation/LogPrefix.java
new file mode 100644
index 0000000..e8d6ed4
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/LogPrefix.java
@@ -0,0 +1,16 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents the prefix used for the plugin's log entries, defaults to plugin name.
+ */
+
+@Target(ElementType.TYPE)
+public @interface LogPrefix {
+
+ public String value();
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/Main.java b/src/main/java/org/bukkit/plugin/java/annotation/Main.java
new file mode 100644
index 0000000..ebef467
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/Main.java
@@ -0,0 +1,55 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Marks this class (which <i>must</i> subclass JavaPlugin) as this plugin's main class.
+ * <p>
+ * This class is part of the plugin annotation framework that automates plugin.yml.
+ * <p>
+ * Example:
+ * <pre>
+ * <code>{@literal @}Main
+ * {@literal @}Name("Test")
+ * {@literal @}Version("v1.0")
+ * {@literal @}Description("A test plugin.")
+ * {@literal @}LoadOn(PluginLoadOrder.POSTWORLD)
+ * {@literal @}Author("md_5")
+ * {@literal @}Website("spigotmc.org")
+ * {@literal @}UsesDatabase
+ * {@literal @}DependsOn({"WorldEdit", "Towny"})
+ * {@literal @}SoftDependsOn("Vault")
+ * {@literal @}LogPrefix("Testing")
+ * {@literal @}LoadBefore("Essentials")
+ * {@literal @}Commands({
+ * {@literal @}Cmd(
+ * value = "foo",
+ * desc = "Foo command",
+ * aliases = {"foobar", "fubar"},
+ * permission = "test.foo",
+ * permissionMessage = "You do not have permission!",
+ * usage = "/<command> [test|stop]"
+ * ),
+ * {@literal @}Cmd("bar")
+ * })
+ * {@literal @}Permissions({
+ * {@literal @}Perm(
+ * value = "test.foo",
+ * desc = "Allows foo command",
+ * defaultValue = PermissionDefault.OP,
+ * ),
+ * {@literal @}Perm(
+ * value = "test.*",
+ * desc = "Wildcard perm",
+ * defaultValue = PermissionDefault.OP,
+ * children = {"test.foo"}
+ * )
+ * })
+ * public class Test extends JavaPlugin { ... }
+ * </code>
+ * </pre>
+ */
+
+@Target(ElementType.TYPE)
+public @interface Main {}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/Name.java b/src/main/java/org/bukkit/plugin/java/annotation/Name.java
new file mode 100644
index 0000000..2ef196b
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/Name.java
@@ -0,0 +1,18 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents the name of the plugin.
+ * <p>
+ * If not present in a class annotated with {@link Main} the name defaults to Class.getSimpleName() and will emmit a warning.
+ */
+
+@Target(ElementType.TYPE)
+public @interface Name {
+
+ public String value();
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/Permissions.java b/src/main/java/org/bukkit/plugin/java/annotation/Permissions.java
new file mode 100644
index 0000000..c6097c3
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/Permissions.java
@@ -0,0 +1,50 @@
+package org.bukkit.plugin.java.annotation;
+
+import org.bukkit.permissions.PermissionDefault;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents a list of this plugin's registered permissions.
+ */
+
+@Target(ElementType.TYPE)
+public @interface Permissions { // TODO: in java 8, make repeatable.
+
+ public Perm[] value();
+
+ @Target({})
+ public static @interface Perm {
+
+ /**
+ * This perm's name.
+ */
+ public String value();
+
+ /**
+ * This perm's description.
+ */
+
+ public String desc() default "";
+
+ /**
+ * This perm's default.
+ */
+ public PermissionDefault defaultValue() default PermissionDefault.OP;
+
+ /**
+ * This perm's child nodes
+ */
+ public String[] children() default {};
+
+ /**
+ * This perms's negated child nodes
+ */
+ public String[] antichildren() default {};
+
+ }
+
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/PluginAnnotationProcessor.java b/src/main/java/org/bukkit/plugin/java/annotation/PluginAnnotationProcessor.java
new file mode 100644
index 0000000..9266c97
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/PluginAnnotationProcessor.java
@@ -0,0 +1,194 @@
+package org.bukkit.plugin.java.annotation;
+
+import org.bukkit.plugin.PluginLoadOrder;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.yaml.snakeyaml.Yaml;
+
+import javax.annotation.processing.*;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+@SupportedAnnotationTypes("org.bukkit.plugin.java.annotation.*")
+@SupportedSourceVersion(SourceVersion.RELEASE_6)
+public class PluginAnnotationProcessor extends AbstractProcessor {
+
+ private boolean hasMainBeenFound = false;
+
+ private static final DateFormat dFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annots, RoundEnvironment rEnv) {
+ Element main = null;
+ for(Element el : rEnv.getElementsAnnotatedWith(Main.class)) {
+ if(main != null){
+ raiseError("More than one class with @Main found, aborting!");
+ return false;
+ }
+ main = el;
+ }
+
+ if(main == null) return false;
+
+ if(hasMainBeenFound){
+ raiseError("More than one class with @Main found, aborting!");
+ return false;
+ }
+ hasMainBeenFound = true;
+
+ TypeElement mainType;
+ if(main instanceof TypeElement){
+ mainType = (TypeElement) main;
+ } else {
+ raiseError("Element annotated with @Main is not a type!");
+ return false;
+ }
+
+ if(!(mainType.getEnclosingElement() instanceof PackageElement) && !mainType.getModifiers().contains(Modifier.STATIC)){
+ raiseError("Element annotated with @Main is not top-level or static nested!");
+ return false;
+ }
+
+ if(!processingEnv.getTypeUtils().isSubtype(mainType.asType(), fromClass(JavaPlugin.class))){
+ raiseError("Class annotated with @Main is not an subclass of JavaPlugin!");
+ }
+
+ Map<String, Object> yml = new HashMap<String, Object>();
+
+ final String mainName = mainType.getQualifiedName().toString();
+ yml.put("main", mainName);
+
+ processAndPut(yml, "name", mainType, mainName.substring(mainName.lastIndexOf('.') + 1), Name.class, String.class);
+
+ processAndPut(yml, "version", mainType, Version.DEFAULT_VERSION, Version.class, String.class);
+
+ processAndPut(yml, "description", mainType, null, Description.class, String.class);
+
+ processAndPut(yml, "load", mainType, null, LoadOn.class, String.class);
+
+ {
+ String[] authors = process(mainType, new String[0], Author.class, String[].class);
+ switch(authors.length) {
+ case 0: break;
+ case 1: yml.put("author", authors[0]); break;
+ default: yml.put("authors", authors); break;
+ }
+ }
+
+ processAndPut(yml, "website", mainType, null, Website.class, String.class);
+
+ if(mainType.getAnnotation(UsesDatabase.class) != null) yml.put("database", true);
+
+ processAndPut(yml, "depend", mainType, null, DependsOn.class, String[].class);
+
+ processAndPut(yml, "softdepend", mainType, null, SoftDependsOn.class, String[].class);
+
+ processAndPut(yml, "prefix", mainType, null, LogPrefix.class, String.class);
+
+ processAndPut(yml, "loadbefore", mainType, null, LoadBefore.class, String[].class);
+
+ Commands.Cmd[] commands = process(mainType, new Commands.Cmd[0], Commands.class, Commands.Cmd[].class);
+
+ Map<String, Object> commandMap = new HashMap<String, Object>();
+
+ for(Commands.Cmd cmd : commands) {
+ String name = cmd.value();
+ Map<String, Object> desc = new HashMap<String, Object>();
+ if(!cmd.desc().isEmpty()) desc.put("description", cmd.desc());
+ if(cmd.aliases().length != 0) desc.put("aliases", cmd.aliases());
+ if(!cmd.permission().isEmpty()) desc.put("permission", cmd.permission());
+ if(!cmd.permissionMessage().isEmpty()) desc.put("permission-message", cmd.permissionMessage());
+ if(!cmd.usage().isEmpty()) desc.put("usage", cmd.usage());
+ commandMap.put(name, desc);
+ }
+
+ if(!commandMap.isEmpty()) yml.put("commands", commandMap);
+
+ Permissions.Perm[] perms = process(mainType, new Permissions.Perm[0], Permissions.class, Permissions.Perm[].class);
+
+ Map<String, Object> permMap = new HashMap<String, Object>();
+
+ for(Permissions.Perm perm : perms) {
+ String name = perm.value();
+ Map<String, Object> desc = new HashMap<String, Object>();
+ if(!perm.desc().isEmpty()) desc.put("description", perm.desc());
+ desc.put("default", perm.defaultValue().toString());
+ Map<String, Object> children = new HashMap<String, Object>();
+ for(String p : perm.children()) children.put(p, true);
+ for(String p : perm.antichildren()) children.put(p, false);
+ if(!children.isEmpty()) desc.put("children", children);
+ permMap.put(name, desc);
+ }
+
+ if(!permMap.isEmpty()) yml.put("permissions", permMap);
+
+ Yaml yaml = new Yaml();
+
+ try {
+ FileObject file = this.processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "plugin.yml");
+ Writer w = file.openWriter();
+ try{
+ w.append("# Auto-generated plugin.yml, generated at ").append(dFormat.format(new Date())).append(" by ").append(this.getClass().getName()).append("\n\n");
+ yaml.dump(yml, w);
+ } finally {
+ w.flush();
+ w.close();
+ }
+
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "NOTE: You are using org.bukkit.plugin.java.annotation, an experimental API!");
+
+ return true;
+ }
+
+ private void raiseError(String message) {
+ this.processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message);
+ }
+
+ private TypeMirror fromClass(Class<?> clazz) {
+ return processingEnv.getElementUtils().getTypeElement(clazz.getName()).asType();
+ }
+
+ private <A extends Annotation, R> R processAndPut(
+ Map<String, Object> map, String name, Element el, R defaultVal, Class<A> annotationType, Class<R> returnType) {
+ R result = process(el, defaultVal, annotationType, returnType);
+ if(result != null)
+ map.put(name, result);
+ return result;
+ }
+ private <A extends Annotation, R> R process(Element el, R defaultVal, Class<A> annotationType, Class<R> returnType) {
+ R result;
+ A ann = el.getAnnotation(annotationType);
+ if(ann == null) result = defaultVal;
+ else {
+ try {
+ Method value = annotationType.getMethod("value");
+ Object res = value.invoke(ann);
+ result = (R) (returnType == String.class ? res.toString() : returnType.cast(res));
+ } catch (Exception e) {
+ throw new RuntimeException(e); // shouldn't happen in theory
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/SoftDependsOn.java b/src/main/java/org/bukkit/plugin/java/annotation/SoftDependsOn.java
new file mode 100644
index 0000000..81e8662
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/SoftDependsOn.java
@@ -0,0 +1,17 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents the plugin's soft dependencies.
+ */
+
+@Target(ElementType.TYPE)
+public @interface SoftDependsOn {
+
+ public String[] value();
+
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/UsesDatabase.java b/src/main/java/org/bukkit/plugin/java/annotation/UsesDatabase.java
new file mode 100644
index 0000000..e73ebfc
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/UsesDatabase.java
@@ -0,0 +1,13 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Denotes this plugin as using Bukkit's bundled database system.
+ */
+
+@Target(ElementType.TYPE)
+public @interface UsesDatabase {}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/Version.java b/src/main/java/org/bukkit/plugin/java/annotation/Version.java
new file mode 100644
index 0000000..2d7ab66
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/Version.java
@@ -0,0 +1,20 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents the version of the plugin.
+ * <p>
+ * If not present in a class annotated with {@link Main} the name defaults to "v0.0" and will emmit a warning.
+ */
+
+@Target(ElementType.TYPE)
+public @interface Version {
+
+ public String value();
+
+ public static final String DEFAULT_VERSION = "v0.0";
+}
diff --git a/src/main/java/org/bukkit/plugin/java/annotation/Website.java b/src/main/java/org/bukkit/plugin/java/annotation/Website.java
new file mode 100644
index 0000000..385483f
--- /dev/null
+++ b/src/main/java/org/bukkit/plugin/java/annotation/Website.java
@@ -0,0 +1,17 @@
+package org.bukkit.plugin.java.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Part of the plugin annotations framework.
+ * <p>
+ * Represents the website of the plugin.
+ */
+
+@Target(ElementType.TYPE)
+public @interface Website {
+
+ public String value();
+
+} \ No newline at end of file
diff --git a/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..387e75c
--- /dev/null
+++ b/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+org.bukkit.plugin.java.annotation.PluginAnnotationProcessor