Misc changes for v1.2

* Add @ApiVersion and appropriate targets
* Add ability to have @Command and @Permission annotations on classes
that implement CommandExecutor. Thanks Hex for the suggestion.
* Remove last reference to @Main.
* Update README.
* Bump version.
* Remove deprecated annotations.
This commit is contained in:
Senmori 2018-07-13 00:28:40 -04:00 committed by md_5
parent e677cffa5d
commit 3d4b0c8a11
24 changed files with 405 additions and 311 deletions

View file

@ -6,66 +6,70 @@ See the [wiki](https://www.spigotmc.org/wiki/plugin-yml/) for more information.
## Example Usage
```
package org.spigotmc.annotationtest;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginLoadOrder;
import org.bukkit.plugin.java.*;
import org.bukkit.plugin.java.annotation.*;
import org.bukkit.plugin.java.annotation.Commands.Cmd;
import org.bukkit.plugin.java.annotation.Permissions.Perm;
@Plugin(name = "TestPlugin", version = "1.0")
@Description(desc = "A test plugin")
@LoadOn(loadOn = PluginLoadOrder.POSTWORLD) // defaults to PluginLoadOrder.POSTWORLD if not preset
@Author(name = "md_5")
@Website(url = "spigotmc.org")
@LogPrefix(prefix = "Testing")
@Dependency(plugin = "WorldEdit")
@Dependency(plugin = "Towny")
@LoadBefore(plugin = "Essentials")
@SoftDependency(plugin = "FAWE")
@Description("A test plugin")
@LoadOrder(PluginLoadOrder.STARTUP)
@Author("md_5")
@Website("www.spigotmc.org")
@LogPrefix("Testing")
@Dependency("WorldEdit")
@Dependency("Towny")
@LoadBefore("Towny")
@SoftDependency("EssentialsX")
@Command(name = "foo", desc = "Foo command", aliases = {"foobar", "fubar"}, permission = "test.foo", permissionMessage = "You do not have permission!", usage = "/<command> [test|stop]")
@Permission(name = "test.foo", desc = "Allows foo command", defaultValue = PermissionDefault.OP)
@Permission(name = "test.*", desc = "Wildcard permission", defaultValue = PermissionDefault.OP, children = {@ChildPermission(name ="test.foo")})
public class Test extends JavaPlugin {}
@ApiVersion(ApiVersion.Target.v1_13)
public class TestPlugin extends JavaPlugin {
```
Output:
```
# Auto-generated plugin.yml, generated at 2018/03/06 18:15:44 by org.bukkit.plugin.java.annotation.PluginAnnotationProcessor
# Auto-generated plugin.yml, generated at 2018/07/12 22:16:27 by org.bukkit.plugin.java.annotation.PluginAnnotationProcessor
main: org.spigotmc.annotationtest.Test
# Auto-generated plugin.yml, generated at 2018/07/13 00:16:24 by org.bukkit.plugin.java.annotation.PluginAnnotationProcessor
main: org.spigotmc.spigot.TestPlugin
name: TestPlugin
version: '1.0'
description: A test plugin
load: POSTWORLD
load: STARTUP
author: md_5
website: spigotmc.org
website: www.spigotmc.org
prefix: Testing
depend:
- WorldEdit
- Towny
softdepend:
- FAWE
- EssentialsX
loadbefore:
- Essentials
- Towny
commands:
foo:
description: Foo command
aliases:
- foobar
- fubar
permission: test.foo
permission-message: You do not have permission!
usage: /<command> [test|stop]
TestCommand:
aliases: testext2
permission: test.testext
permission-message: Oopsy!
usage: /testext test test
permissions:
test.foo:
description: Allows foo command
default: op
test.*:
description: Wildcard permission
default: op
children:
test.foo: true
api-version: '1.13'
```
As of version 1.2.0-SNAPSHOT you can now also use the ```@Command``` and ```@Permission```
annotations on classes that implement CommandExecutor.
For example:
```
@Command(name = "TestCommand", aliases = "testext2", permission = "test.testext", permissionMessage = "Oopsy!", usage = "/testext test test")
@Permission(name = "test.testext", desc = "Provides access to /textext command", defaultValue = PermissionDefault.TRUE)
public class TestCommand implements CommandExecutor {
```
As of version 1.2.0-SNAPSHOT the ```@ApiVersion``` annotation was introduced to bring compatibility for
Bukkit's new ```api-version``` plugin.yml option. This defaults to ```ApiVersion.Target.DEFAULT``` if not specified or included.
All pre-1.13 plugins MUST use ```ApiVersion.Target.DEFAULT``` in order for the plugin to be loaded correctly.

View file

@ -10,7 +10,7 @@
<groupId>org.spigotmc</groupId>
<artifactId>plugin-annotations</artifactId>
<version>1.1.0-SNAPSHOT</version>
<version>1.2.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Plugin Annotations</name>

View file

@ -2,25 +2,28 @@ package org.bukkit.plugin.java.annotation;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.bukkit.command.CommandExecutor;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.annotation.command.Command;
import org.bukkit.plugin.java.annotation.command.Commands;
import org.bukkit.plugin.java.annotation.dependency.Dependency;
import org.bukkit.plugin.java.annotation.dependency.LoadBefore;
import org.bukkit.plugin.java.annotation.dependency.SoftDependency;
import org.bukkit.plugin.java.annotation.permission.ChildPermission;
import org.bukkit.plugin.java.annotation.permission.Permission;
import org.bukkit.plugin.java.annotation.permission.Permissions;
import org.bukkit.plugin.java.annotation.plugin.ApiVersion;
import org.bukkit.plugin.java.annotation.plugin.Description;
import org.bukkit.plugin.java.annotation.plugin.LoadOn;
import org.bukkit.plugin.java.annotation.plugin.LoadOrder;
import org.bukkit.plugin.java.annotation.plugin.LogPrefix;
import org.bukkit.plugin.java.annotation.plugin.Plugin;
import org.bukkit.plugin.java.annotation.plugin.UsesDatabase;
import org.bukkit.plugin.java.annotation.plugin.Website;
import org.bukkit.plugin.java.annotation.plugin.author.Author;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Tag;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
@ -31,231 +34,358 @@ 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.swing.text.DateFormatter;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@SupportedAnnotationTypes("org.bukkit.plugin.java.annotation.*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes( "org.bukkit.plugin.java.annotation.*" )
@SupportedSourceVersion( SourceVersion.RELEASE_8 )
public class PluginAnnotationProcessor extends AbstractProcessor {
private boolean hasMainBeenFound = false;
private static final DateTimeFormatter dFormat = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss", Locale.ENGLISH);
private static final DateTimeFormatter dFormat = DateTimeFormatter.ofPattern( "yyyy/MM/dd HH:mm:ss", Locale.ENGLISH );
@Override
public boolean process(Set<? extends TypeElement> annots, RoundEnvironment rEnv) {
Element main = null;
Element mainPluginElement = null;
hasMainBeenFound = false;
Set<? extends Element> elements = rEnv.getElementsAnnotatedWith(Plugin.class);
if(elements.size() > 1) {
raiseError("Found more than one plugin main class");
Set<? extends Element> elements = rEnv.getElementsAnnotatedWith( Plugin.class );
if ( elements.size() > 1 ) {
raiseError( "Found more than one plugin main class" );
return false;
}
if(elements.isEmpty()) {
if ( elements.isEmpty() ) {
return false;
}
if(hasMainBeenFound){
raiseError("The plugin class has already been located, aborting!");
if ( hasMainBeenFound ) {
raiseError( "The plugin class has already been located, aborting!" );
return false;
}
main = elements.iterator().next();
mainPluginElement = elements.iterator().next();
hasMainBeenFound = true;
TypeElement mainType;
if(main instanceof TypeElement){
mainType = (TypeElement) main;
TypeElement mainPluginType;
if ( mainPluginElement instanceof TypeElement ) {
mainPluginType = ( TypeElement ) mainPluginElement;
} else {
raiseError("Element annotated with @Main is not a type!", main);
raiseError( "Element annotated with @Plugin is not a type!", mainPluginElement );
return false;
}
if(!(mainType.getEnclosingElement() instanceof PackageElement) && !mainType.getModifiers().contains(Modifier.STATIC)){
raiseError("Element annotated with @Main is not top-level or static nested!", mainType);
if ( !( mainPluginType.getEnclosingElement() instanceof PackageElement ) && !mainPluginType.getModifiers().contains( Modifier.STATIC ) ) {
raiseError( "Element annotated with @Plugin is not top-level or static nested!", mainPluginType );
return false;
}
if(!processingEnv.getTypeUtils().isSubtype(mainType.asType(), fromClass(JavaPlugin.class))){
raiseError("Class annotated with @Main is not an subclass of JavaPlugin!", mainType);
if ( !processingEnv.getTypeUtils().isSubtype( mainPluginType.asType(), fromClass( JavaPlugin.class ) ) ) {
raiseError( "Class annotated with @Plugin is not an subclass of JavaPlugin!", mainPluginType );
}
Map<String, Object> yml = Maps.newLinkedHashMap(); // linked so we can maintain the same output into file for sanity
// populate mainName
final String mainName = mainType.getQualifiedName().toString();
yml.put("main", mainName); // always override this so we make sure the main class name is correct
final String mainName = mainPluginType.getQualifiedName().toString();
yml.put( "main", mainName ); // always override this so we make sure the main class name is correct
// populate plugin name
processAndPut(yml, "name", mainType, mainName.substring(mainName.lastIndexOf('.') + 1), Plugin.class, String.class, "name");
processAndPut( yml, "name", mainPluginType, mainName.substring( mainName.lastIndexOf( '.' ) + 1 ), Plugin.class, String.class, "name" );
// populate version
processAndPut(yml, "version", mainType, Plugin.DEFAULT_VERSION, Plugin.class, String.class, "version");
processAndPut( yml, "version", mainPluginType, Plugin.DEFAULT_VERSION, Plugin.class, String.class, "version" );
// populate plugin description
processAndPut(yml, "description", mainType, null, Description.class, String.class, "desc");
processAndPut( yml, "description", mainPluginType, null, Description.class, String.class );
// populate plugin load order
processAndPut(yml, "load", mainType, null, LoadOn.class, String.class,"loadOn");
processAndPut( yml, "load", mainPluginType, null, LoadOrder.class, String.class );
// authors
Author[] authors = mainType.getAnnotationsByType(Author.class);
Author[] authors = mainPluginType.getAnnotationsByType( Author.class );
List<String> authorMap = Lists.newArrayList();
for(Author auth : authors) {
authorMap.add(auth.name());
for ( Author auth : authors ) {
authorMap.add( auth.value() );
}
if(authorMap.size() > 1) {
yml.put("authors", authorMap);
} else if(authorMap.size() == 1) {
yml.put("author", authorMap.iterator().next());
if ( authorMap.size() > 1 ) {
yml.put( "authors", authorMap );
} else if ( authorMap.size() == 1 ) {
yml.put( "author", authorMap.iterator().next() );
}
// website
processAndPut(yml, "website", mainType, null, Website.class, String.class, "url");
processAndPut( yml, "website", mainPluginType, null, Website.class, String.class );
// prefix
processAndPut(yml, "prefix", mainType, null, LogPrefix.class, String.class, "prefix");
processAndPut( yml, "prefix", mainPluginType, null, LogPrefix.class, String.class );
// dependencies
Dependency[] dependencies = mainType.getAnnotationsByType(Dependency.class);
Dependency[] dependencies = mainPluginType.getAnnotationsByType( Dependency.class );
List<String> hardDependencies = Lists.newArrayList();
for(Dependency dep : dependencies) {
hardDependencies.add(dep.plugin());
for ( Dependency dep : dependencies ) {
hardDependencies.add( dep.value() );
}
if(!hardDependencies.isEmpty()) yml.putIfAbsent("depend", hardDependencies);
if ( !hardDependencies.isEmpty() ) yml.put( "depend", hardDependencies );
// soft-dependencies
SoftDependency[] softDependencies = mainType.getAnnotationsByType(SoftDependency.class);
String[] softDepArr = new String[softDependencies.length];
for(int i = 0; i < softDependencies.length; i++) {
softDepArr[i] = softDependencies[i].plugin();
SoftDependency[] softDependencies = mainPluginType.getAnnotationsByType( SoftDependency.class );
String[] softDepArr = new String[ softDependencies.length ];
for ( int i = 0; i < softDependencies.length; i++ ) {
softDepArr[ i ] = softDependencies[ i ].value();
}
if(softDepArr.length > 0) yml.putIfAbsent("softdepend", softDepArr);
if ( softDepArr.length > 0 ) yml.put( "softdepend", softDepArr );
// load-before
LoadBefore[] loadBefore = mainType.getAnnotationsByType(LoadBefore.class);
String[] loadBeforeArr = new String[loadBefore.length];
for(int i = 0; i < loadBefore.length; i++) {
loadBeforeArr[i] = loadBefore[i].plugin();
LoadBefore[] loadBefore = mainPluginType.getAnnotationsByType( LoadBefore.class );
String[] loadBeforeArr = new String[ loadBefore.length ];
for ( int i = 0; i < loadBefore.length; i++ ) {
loadBeforeArr[ i ] = loadBefore[ i ].value();
}
if(loadBeforeArr.length > 0) yml.putIfAbsent("loadbefore", loadBeforeArr);
if ( loadBeforeArr.length > 0 ) yml.put( "loadbefore", loadBeforeArr );
// commands
Command[] commands = mainType.getAnnotationsByType(Command.class);
Map<String, Object> commandMap = Maps.newLinkedHashMap();
for(Command command : commands) {
Map<String, Object> desc = Maps.newLinkedHashMap();
String name = command.name();
if(!command.desc().isEmpty()) desc.put("description", command.desc());
if(command.aliases().length != 0) desc.put("aliases", command.aliases());
if(!command.permission().isEmpty()) desc.put("permission", command.permission());
if(!command.permissionMessage().isEmpty()) desc.put("permission-message", command.permissionMessage());
if(!command.usage().isEmpty()) desc.put("usage", command.usage());
commandMap.put(name, desc);
// Begin processing external command annotations
Map<String, Map<String, Object>> commandMap = Maps.newLinkedHashMap();
boolean result = processExternalCommands( rEnv.getElementsAnnotatedWith( Command.class ), mainPluginType, commandMap );
if ( !result ) {
// #processExternalCommand already raised the errors
return false;
}
if(!commandMap.isEmpty()) yml.putIfAbsent("commands", commandMap);
// permissions
Permission[] permissions = mainType.getAnnotationsByType(Permission.class);
Map<String, Object> permMap = Maps.newLinkedHashMap();
for(Permission perm : permissions) {
Map<String, Object> desc = Maps.newLinkedHashMap();
String name = perm.name();
if(!perm.desc().isEmpty()) desc.put("description", perm.desc());
desc.put("default", perm.defaultValue().toString());
Map<String, Object> children = Maps.newLinkedHashMap();
for(ChildPermission child : perm.children()) {
children.put(child.name(), child.inherit());
Commands commands = mainPluginType.getAnnotation( Commands.class );
// Check main class for any command annotations
if ( commands != null ) {
Map<String, Map<String, Object>> merged = Maps.newLinkedHashMap();
merged.putAll( commandMap );
merged.putAll( this.processCommands( commands ) );
commandMap = merged;
}
yml.put( "commands", commandMap );
// Permissions
Map<String, Map<String, Object>> permissionMetadata = Maps.newLinkedHashMap();
Set<? extends Element> permissionAnnotations = rEnv.getElementsAnnotatedWith( Command.class );
if ( permissionAnnotations.size() > 0 ) {
for ( Element element : permissionAnnotations ) {
if ( element.equals( mainPluginElement ) ) {
continue;
}
if ( element.getAnnotation( Permission.class ) != null ) {
Permission permissionAnnotation = element.getAnnotation( Permission.class );
permissionMetadata.put( permissionAnnotation.name(), this.processPermission( permissionAnnotation ) );
}
}
}
Permissions permissions = mainPluginType.getAnnotation( Permissions.class );
if ( permissions != null ) {
Map<String, Map<String, Object>> joined = Maps.newLinkedHashMap();
joined.putAll( permissionMetadata );
joined.putAll( this.processPermissions( permissions ) );
permissionMetadata = joined;
}
yml.put( "permissions", permissionMetadata );
// api-version
if ( mainPluginType.getAnnotation( ApiVersion.class ) != null ) {
ApiVersion apiVersion = mainPluginType.getAnnotation( ApiVersion.class );
if ( apiVersion.value() != ApiVersion.Target.DEFAULT ) {
yml.put( "api-version", apiVersion.value().getVersion() );
}
if(!children.isEmpty()) desc.put("children", children);
permMap.put(name, desc);
}
if(!permMap.isEmpty()) yml.putIfAbsent("permissions", permMap);
// database D: //TODO: Remove me!
if(mainType.getAnnotation(UsesDatabase.class) != null) {
yml.put("database", true);
processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "Database support was dropped in Bukkit in version 1.12.", mainType);
}
Yaml yaml = new Yaml();
try {
FileObject file = this.processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "plugin.yml");
try(Writer w = file.openWriter()) {
w.append("# Auto-generated plugin.yml, generated at ")
.append(LocalDateTime.now().format(dFormat))
.append(" by ")
.append(this.getClass().getName())
.append("\n\n");
Yaml yaml = new Yaml();
FileObject file = this.processingEnv.getFiler().createResource( StandardLocation.CLASS_OUTPUT, "", "plugin.yml" );
try ( Writer w = file.openWriter() ) {
w.append( "# Auto-generated plugin.yml, generated at " )
.append( LocalDateTime.now().format( dFormat ) )
.append( " by " )
.append( this.getClass().getName() )
.append( "\n\n" );
// have to format the yaml explicitly because otherwise it dumps child nodes as maps within braces.
String raw = yaml.dumpAs(yml, Tag.MAP, DumperOptions.FlowStyle.BLOCK);
w.write(raw);
String raw = yaml.dumpAs( yml, Tag.MAP, DumperOptions.FlowStyle.BLOCK );
w.write( raw );
w.flush();
w.close();
}
// try with resources will close the Writer since it implements Closeable
} catch (IOException e) {
throw new RuntimeException(e);
} 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!");
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);
this.processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR, message );
}
private void raiseError(String message, Element element) {
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message, element);
this.processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR, message, element );
}
private TypeMirror fromClass(Class<?> clazz) {
return processingEnv.getElementUtils().getTypeElement(clazz.getName()).asType();
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) {
return processAndPut(map, name, el, defaultVal, annotationType, returnType, "value");
return processAndPut( map, name, el, defaultVal, annotationType, returnType, "value" );
}
private <A extends Annotation, R> R processAndPut(
Map<String, Object> map, String name, Element el, R defaultVal, Class<A> annotationType, Class<R> returnType, String methodName) {
R result = process(el, defaultVal, annotationType, returnType, methodName);
if(result != null)
map.putIfAbsent(name, result);
R result = process( el, defaultVal, annotationType, returnType, methodName );
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, String methodName) {
R result;
A ann = el.getAnnotation(annotationType);
if(ann == null) result = defaultVal;
A ann = el.getAnnotation( annotationType );
if ( ann == null ) result = defaultVal;
else {
try {
Method value = annotationType.getMethod(methodName);
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 (blame Choco if it does)
Method value = annotationType.getMethod( methodName );
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 (blame Choco if it does)
}
}
return result;
}
private boolean processExternalCommands(Set<? extends Element> commandExecutors, TypeElement mainPluginType, Map<String, Map<String, Object>> commandMetadata) {
for ( Element element : commandExecutors ) {
// Check to see if someone annotated a non-class with this
if ( !( element instanceof TypeElement ) ) {
this.raiseError( "Specified Command Executor class is not a class." );
return false;
}
TypeElement typeElement = ( TypeElement ) element;
if ( typeElement.equals( mainPluginType ) ) {
continue;
}
// Check to see if annotated class is actuall a command executor
TypeMirror mirror = this.processingEnv.getElementUtils().getTypeElement( CommandExecutor.class.getName() ).asType();
if ( !( this.processingEnv.getTypeUtils().isAssignable( typeElement.asType(), mirror ) ) ) {
this.raiseError( "Specified Command Executor class is not assignable from CommandExecutor " );
return false;
}
Command annotation = typeElement.getAnnotation( Command.class );
commandMetadata.put( annotation.name(), this.processCommand( annotation ) );
}
return true;
}
/**
* Processes a set of commands.
*
* @param commands The annotation.
*
* @return The generated command metadata.
*/
protected Map<String, Map<String, Object>> processCommands(Commands commands) {
Map<String, Map<String, Object>> commandList = Maps.newLinkedHashMap();
for ( Command command : commands.value() ) {
commandList.put( command.name(), this.processCommand( command ) );
}
return commandList;
}
/**
* Processes a single command.
*
* @param commandAnnotation The annotation.
*
* @return The generated command metadata.
*/
protected Map<String, Object> processCommand(Command commandAnnotation) {
Map<String, Object> command = Maps.newLinkedHashMap();
if ( commandAnnotation.aliases().length == 1 ) {
command.put( "aliases", commandAnnotation.aliases()[ 0 ] );
} else if ( commandAnnotation.aliases().length > 1 ) {
command.put( "aliases", commandAnnotation.aliases() );
}
if ( !"".equals( commandAnnotation.desc() ) ) {
command.put( "description", commandAnnotation.desc() );
}
if ( !"".equals( commandAnnotation.permission() ) ) {
command.put( "permission", commandAnnotation.permission() );
}
if ( !"".equals( commandAnnotation.permissionMessage() ) ) {
command.put( "permission-message", commandAnnotation.permissionMessage() );
}
if ( !"".equals( commandAnnotation.usage() ) ) {
command.put( "usage", commandAnnotation.usage() );
}
return command;
}
/**
* Processes a command.
*
* @param permissionAnnotation The annotation.
*
* @return The generated permission metadata.
*/
protected Map<String, Object> processPermission(Permission permissionAnnotation) {
Map<String, Object> permission = Maps.newLinkedHashMap();
if ( !"".equals( permissionAnnotation.desc() ) ) {
permission.put( "description", permissionAnnotation.desc() );
}
if ( PermissionDefault.OP != permissionAnnotation.defaultValue() ) {
permission.put( "default", permissionAnnotation.defaultValue().toString().toLowerCase() );
}
if ( permissionAnnotation.children().length > 0 ) {
Map<String, Boolean> childrenList = Maps.newLinkedHashMap(); // maintain order
for ( ChildPermission childPermission : permissionAnnotation.children() ) {
childrenList.put( childPermission.name(), childPermission.inherit() );
}
permission.put( "children", childrenList );
}
return permission;
}
/**
* Processes a set of permissions.
*
* @param permissions The annotation.
*
* @return The generated permission metadata.
*/
protected Map<String, Map<String, Object>> processPermissions(Permissions permissions) {
Map<String, Map<String, Object>> permissionList = Maps.newLinkedHashMap();
for ( Permission permission : permissions.value() ) {
permissionList.put( permission.name(), this.processPermission( permission ) );
}
return permissionList;
}
}

View file

@ -7,9 +7,12 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Part of the plugin annotations framework.
* <p>
* Represents a list of this plugin's registered command(s).
* Part of the plugin annotations framework.
* <p>
* Represents a list of this plugin's registered command(s).
* <br>
* This specific annotation should not be used by people who do not know
* how repeating annotations work.
*/
@Documented
@Target(ElementType.TYPE)

View file

@ -8,7 +8,12 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Defines a plugin dependency
* Defines a plugin dependency.
* <br>
* The plugin's <b>name</b> attribute is required in order to load the dependency.<br>
* If any plugin listed is not found the plugin will fail to load. <br>
* If multiple plugins list each other as a dependency, so that there are no plugins with an unloadable dependency,
* all plugins will fail to load.
*/
@Documented
@Target(ElementType.TYPE)
@ -18,5 +23,5 @@ public @interface Dependency {
/**
* A plugin that is required to be present in order for this plugin to load.
*/
String plugin();
String value();
}

View file

@ -7,9 +7,12 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Part of the plugin annotations framework.
* <p>
* Represents the plugins a plugin depends on in order to be loaded
* Part of the plugin annotations framework.
* <p>
* Represents the plugins a plugin depends on in order to be loaded
* <br>
* This specific annotation should not be used by people who do not know
* how repeating annotations work.
*/
@Documented
@Target(ElementType.TYPE)

View file

@ -11,6 +11,9 @@ import java.lang.annotation.Target;
* Part of the plugin annotations framework.
* <p>
* Represents the plugin this plugin should be loaded before
* <br>
* The plugin's <b>name</b> attribute is required in order to specify the target. <br>
* The plugin listed will be treated as a {@link SoftDependency}. <br>
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@ -20,5 +23,5 @@ public @interface LoadBefore {
/**
* A plugin that should be loaded after your plugin
*/
String plugin();
String value();
}

View file

@ -8,6 +8,9 @@ import java.lang.annotation.Target;
/**
* Defines a list of plugin to load after this plugin
* <br>
* This specific annotation should not be used by people who do not know
* how repeating annotations work.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)

View file

@ -11,6 +11,9 @@ import java.lang.annotation.Target;
/**
* Represents a soft (optional) dependency for this plugin.
* If this dependency is not present, the plugin will still load.
* <br>
* The <b>name</b> attribute of the plugin is required in order to specify the target. <br>
* Circular soft-dependencies are loaded arbitrarily.
*/
@Documented
@ -21,5 +24,5 @@ public @interface SoftDependency {
/**
* A plugin that is required in order for this plugin to have full functionality.
*/
String plugin();
String value();
}

View file

@ -7,10 +7,13 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Part of the plugin annotations framework.
* <p>
* Represents the plugins this plugin should try to load before this plugin will attempt to load.
* A plugin will still load if a soft dependency is not present.
* Part of the plugin annotations framework.
* <p>
* Represents the plugins this plugin should try to load before this plugin will attempt to load.
* A plugin will still load if a soft dependency is not present.
* <br>
* This specific annotation should not be used by people who do not know
* how repeating annotations work.
*/
@Documented

View file

@ -7,7 +7,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Defines a child permission for {@link Permission}
* Defines a child permission for a {@link Permission}
*/
@Documented
@Target(ElementType.TYPE)
@ -15,7 +15,7 @@ import java.lang.annotation.Target;
public @interface ChildPermission {
/**
* If true, this child node will inherit the parent {@link Permission}'s permission.
* If false, this child node inherits the inverse parent permission.
* If false, this child node inherits the inverse of the parent permission.
*/
boolean inherit() default true;

View file

@ -19,22 +19,22 @@ import java.lang.annotation.Target;
@Repeatable(Permissions.class)
public @interface Permission {
/**
* This perm's name.
* This permission's name.
*/
String name();
/**
* This perm's description.
* This permission's description.
*/
String desc() default "";
/**
* This perm's default {@link PermissionDefault}
* This permission's default {@link PermissionDefault}
*/
PermissionDefault defaultValue() default PermissionDefault.OP;
/**
* This permission's child nodes ({@link ChildPermission})
* This permission's child nodes ( {@link ChildPermission} )
*/
ChildPermission[] children() default {};
}

View file

@ -7,9 +7,12 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Part of the plugin annotations framework.
* <p>
* Represents a list of this plugin's registered name.
* Part of the plugin annotations framework.
* <p>
* Represents a list of this plugin's registered name.
* <br>
* This specific annotation should not be used by people who do not know
* how repeating annotations work.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)

View file

@ -0,0 +1,62 @@
package org.bukkit.plugin.java.annotation.plugin;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Collection;
/**
* This annotation specifies the api version of the plugin.
* <br>
* Defaults to {@link ApiVersion.Target#DEFAULT}.
* <br>
* Pre-1.13 plugins do not need to use this annotation.
*/
@Documented
@Retention( RetentionPolicy.SOURCE )
@Target( ElementType.TYPE )
public @interface ApiVersion {
Target value() default Target.DEFAULT;
/**
* Specifies the target api-version for this plugin.
*
* All pre-1.13 plugins must use {@link #DEFAULT}.
*/
public static enum Target {
/**
* This target version specifies that the plugin was made for pre-1.13 Spigot versions.
*/
DEFAULT( null ),
/**
* This target version specifies that the plugin was made with 1.13+ versions in mind.
*/
v1_13( "1.13", DEFAULT );
private final String version;
private final Collection<Target> conflictsWith = Sets.newLinkedHashSet();
private Target(String version, Target... conflictsWith) {
this.version = version;
this.conflictsWith.addAll( Lists.newArrayList( conflictsWith ) );
}
public String getVersion() {
return version;
}
public boolean conflictsWith(Target target) {
return this.conflictsWith.contains( target );
}
}
}

View file

@ -19,6 +19,6 @@ public @interface Description {
/**
* A human friendly description of the functionality this plugin provides.
*/
String desc();
String value();
}

View file

@ -17,11 +17,11 @@ import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface LoadOn {
public @interface LoadOrder {
/**
* Explicitly state when the plugin should be loaded.
* If not defined, will default to {@link PluginLoadOrder#POSTWORLD}.
* See {@link PluginLoadOrder}
*/
PluginLoadOrder loadOn();
PluginLoadOrder value() default PluginLoadOrder.POSTWORLD;
}

View file

@ -19,5 +19,5 @@ public @interface LogPrefix {
/**
* The name to use when logging to console instead of the plugin's name.
*/
String prefix();
String value();
}

View file

@ -1,62 +0,0 @@
package org.bukkit.plugin.java.annotation.plugin;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* DEPRECATED: Use {@link Plugin} instead.
* 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 @}Command(
* name = "foo",
* name = "Foo command",
* aliases = {"foobar", "fubar"},
* permission = "test.foo",
* permissionMessage = "You do not have permission!",
* usage = "/<command> [test|stop]"
* ),
* {@literal @}Command("bar")
* })
* {@literal @}Permissions({
* {@literal @}Perm(
* name = "test.foo",
* name = "Allows foo command",
* defaultValue = PermissionDefault.OP,
* ),
* {@literal @}Perm(
* name = "test.*",
* name = "Wildcard perm",
* defaultValue = PermissionDefault.OP,
* children = {"test.foo"}
* )
* })
* public class Test extends JavaPlugin { ... }
* </code>
* </pre>
* @deprecated use {@link Plugin} instead.
*/
@Deprecated
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Main {}

View file

@ -1,23 +0,0 @@
package org.bukkit.plugin.java.annotation.plugin;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
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.
* @deprecated use {@link Plugin#name()} instead.
*/
@Deprecated
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Name {
String name();
}

View file

@ -1,20 +0,0 @@
package org.bukkit.plugin.java.annotation.plugin;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Part of the plugin annotations framework.
* <p>
* Denotes this plugin as using Bukkit's bundled database system.
* @deprecated Bukkit no longer supports database(s) in the plugin.yml
*/
@Deprecated
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface UsesDatabase {}

View file

@ -1,26 +0,0 @@
package org.bukkit.plugin.java.annotation.plugin;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
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.
* @deprecated use {@link Plugin#version()} instead
*/
@Deprecated
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Version {
String version();
String DEFAULT_VERSION = "v0.0";
}

View file

@ -19,5 +19,5 @@ public @interface Website {
/**
* The url to the website where a user can download this plugin.
*/
String url();
String value();
}

View file

@ -21,5 +21,5 @@ public @interface Author {
/**
* The name of the person who developed this plugin.
*/
String name();
String value();
}

View file

@ -8,6 +8,9 @@ import java.lang.annotation.Target;
/**
* Represents a list of author(s) for this plugin.
* <br>
* This specific annotation should not be used by people who do not know
* how repeating annotations work.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)