All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.chestmc.common.annotations.Module.kt Maven / Gradle / Ivy

package net.chestmc.common.annotations

import com.google.auto.service.AutoService
import kotlinx.serialization.Serializable
import net.chestmc.common.extensions.transform
import org.bukkit.plugin.Plugin
import org.bukkit.plugin.PluginLoadOrder
import org.yaml.snakeyaml.Yaml
import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment
import javax.annotation.processing.SupportedAnnotationTypes
import javax.annotation.processing.SupportedSourceVersion
import javax.lang.model.SourceVersion
import javax.lang.model.element.Element
import javax.lang.model.element.TypeElement
import javax.tools.StandardLocation

/**
 * A module prototype for serializing plugin.yml with [Module].
 */
@Serializable
data class ModulePrototype(
  val main: String = "",
  val name: String = "",
  val version: String = "1.0",
  val author: String = "",
  val description: String = "",
  val website: String = "",
  val load: PluginLoadOrder = PluginLoadOrder.POSTWORLD,
  val authors: List = emptyList(),
  val depend: List = emptyList(),
  val softDepend: List = emptyList(),
  val loadBefore: List = emptyList()
) {
  constructor(template: TypeElement, module: Module) : this(
    template.qualifiedName.toString(),
    module.name.ifBlank { template.simpleName.toString() },
    module.version,
    module.author,
    module.description,
    module.website,
    module.load,
    module.authors.toList(),
    module.depend.toList(),
    module.softDepend.toList(),
    module.loadBefore.toList()
  )

  fun toMap(): Map = LinkedHashMap().transform {
    put("main", main)
    put("name", name)
    put("version", version)
    put("author", author)
    put("description", description)
    put("website", website)
    put("load", load.name)
    put("authors", authors.toTypedArray())
    put("depend", depend.toTypedArray())
    put("softdepend", softDepend.toTypedArray())
    put("loadbefore", loadBefore.toTypedArray())
  }

}

/**
 * A module annotation represents a plugin settings.
 * All settings configured by this module will be inserted in the `plugin.yml`.
 * Note that this will be processed in compile-time, using the [ModuleProcessor]
 */
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class Module(
  val name: String = "",
  val version: String = "1.0",
  val author: String = "",
  val description: String = "",
  val website: String = "",
  val load: PluginLoadOrder = PluginLoadOrder.POSTWORLD,
  val authors: Array = [],
  val depend: Array = [],
  val softDepend: Array = [],
  val loadBefore: Array = []
)

/**
 * A annotation processor for processing [Module] annotations.
 * This processor is responsable to create the `plugin.yml` and
 * inserts the values inside of them.
 */
@SupportedAnnotationTypes("net.chestmc.common.annotations.Module")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor::class)
class ModuleProcessor : StandardProcessor() {

  override fun process(annotations: MutableSet, env: RoundEnvironment): Boolean {
    val elements = env.getElementsAnnotatedWith(Module::class.java)
    if (elements.isNullOrEmpty())
      return false

    if (!elements.verifyIfMoreThanOne())
      return false

    val type = elements.first() as TypeElement

    if (!type.isSupported())
      return false

    create(type)
    return true
  }


  /**
   * Verify if a element is extending any of [Plugin] interface.
   */
  fun TypeElement.isSupported(): Boolean {
    val pluginType = elements.getTypeElement("org.bukkit.plugin.Plugin").asType()
    return if (types.isAssignable(superclass, pluginType)) true
    else error("The element annotated with @Module not extends any type of org.bukkit.plugin.Plugin")
  }

  /**
   * Verify if this set of elements has more than one element.
   */
  fun Set.verifyIfMoreThanOne(): Boolean {
    if (size > 1) {
      val all = map { it as TypeElement }.joinToString { it.qualifiedName.toString() }
      return error("More than one @Module found. Elements annotateds with @Module: $all")
    }
    return true
  }

  /**
   * Creates the plugin.yml.
   */
  fun create(type: TypeElement) {
    val prototype = ModulePrototype(type, type.getAnnotation(Module::class.java))
    filer.createResource(StandardLocation.CLASS_OUTPUT, "", "plugin.yml")
      .openWriter()
      .use {
        Yaml().dump(prototype.toMap(), it)
        it.flush()
      }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy