Skip to content

Implement prototype for annotation processing #145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.openapiprocessor.spring.annotationprocessor

import io.openapiprocessor.core.writer.java.FileHandler
import java.io.BufferedWriter
import java.io.Writer
import javax.annotation.processing.Filer

class AnnotationProcessorFileHandler(
private val filer: Filer
) : FileHandler {
override fun createApiWriter(packageName: String, className: String): Writer {
val fileObject = filer.createSourceFile("$packageName.$className")
return BufferedWriter(fileObject.openWriter())
}

override fun createModelWriter(packageName: String, className: String): Writer {
val fileObject = filer.createSourceFile("$packageName.$className")
return BufferedWriter(fileObject.openWriter())
}

override fun createTargetFolders() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.openapiprocessor.spring.annotationprocessor

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class OpenAPISpringProcessor(
val apiPath: String,
val mapping: String = ""
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package io.openapiprocessor.spring.annotationprocessor

import io.openapiprocessor.core.converter.ApiConverter
import io.openapiprocessor.core.converter.ApiOptions
import io.openapiprocessor.core.converter.OptionsConverter
import io.openapiprocessor.core.parser.Parser
import io.openapiprocessor.core.writer.java.*
import io.openapiprocessor.spring.processor.SpringFramework
import io.openapiprocessor.spring.processor.SpringFrameworkAnnotations
import io.openapiprocessor.spring.writer.java.HeaderWriter
import io.openapiprocessor.spring.writer.java.MappingAnnotationWriter
import io.openapiprocessor.spring.writer.java.ParameterAnnotationWriter
import java.lang.RuntimeException
import java.nio.file.Paths
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.Filer
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.TypeElement
import kotlin.io.path.notExists

class SpringAnnotationProcessor : AbstractProcessor() {
companion object {
@JvmStatic
val OPTION_KEY_ROOT_PATH = "io.openapiprocessor.project.root"
}

private lateinit var filer: Filer

override fun init(processingEnv: ProcessingEnvironment) {
super.init(processingEnv)
this.filer = processingEnv.filer
}

override fun getSupportedSourceVersion(): SourceVersion {
return SourceVersion.latest()
}

override fun getSupportedAnnotationTypes(): MutableSet<String> {
return mutableSetOf(
OpenAPISpringProcessor::class.java.canonicalName
)
}

override fun getSupportedOptions(): MutableSet<String> {
return mutableSetOf(
OPTION_KEY_ROOT_PATH
)
}

override fun process(annotations: MutableSet<out TypeElement>, roundEnv: RoundEnvironment): Boolean {
val annotatedElements = roundEnv.getElementsAnnotatedWith(OpenAPISpringProcessor::class.java)

val rootPathString = processingEnv.options[OPTION_KEY_ROOT_PATH]
?: throw RuntimeException("ROOT PATH IS NOT SET")

val rootPath = Paths.get(rootPathString)

if (rootPath.notExists()) {
throw RuntimeException("ROOT PATH NOT EXISTS")
}

for (annotatedElement in annotatedElements) {
val annotation = annotatedElement.getAnnotation(OpenAPISpringProcessor::class.java)

val processorOptions = HashMap<String, Any>()
val apiPath = rootPath.resolve(annotation.apiPath)
if (apiPath.notExists()) {
throw RuntimeException("ROOT PATH NOT EXISTS")
}
processorOptions["apiPath"] = apiPath.toUri().toString()

if (annotation.mapping.isNotEmpty()) {
val mappingPath = rootPath.resolve(annotation.mapping)
if (mappingPath.notExists()) {
throw RuntimeException("ROOT PATH NOT EXISTS")
}
processorOptions["mapping"] = mappingPath.toUri().toString()
}

try {
val parser = Parser()
val openapi = parser.parse(processorOptions)
if (processorOptions["showWarnings"] != null) {
openapi.printWarnings()
}

val framework = SpringFramework()
val frameworkAnnotations = SpringFrameworkAnnotations()

val options = convertOptions(processorOptions)
val cv = ApiConverter(options, framework)
val api = cv.convert(openapi)

val headerWriter = HeaderWriter()
val beanValidationFactory = BeanValidationFactory()
val javaDocWriter = JavaDocWriter()

val writer = ApiWriter(
options,
InterfaceWriter(
options,
headerWriter,
MethodWriter(
options,
MappingAnnotationWriter(),
ParameterAnnotationWriter(frameworkAnnotations),
beanValidationFactory,
javaDocWriter
),
frameworkAnnotations,
beanValidationFactory,
DefaultImportFilter()
),
DataTypeWriter(
options,
headerWriter,
beanValidationFactory),
StringEnumWriter(headerWriter),
InterfaceDataTypeWriter(
options,
headerWriter,
javaDocWriter
),
AnnotationProcessorFileHandler(filer)
)

writer.write(api)
} catch (e: Exception) {
// log.error("processing failed!", e)
throw e
}
}

return false
}

@Suppress("UNCHECKED_CAST")
private fun convertOptions(processorOptions: Map<String, *>): ApiOptions {
val options = OptionsConverter().convertOptions(processorOptions as Map<String, Any>)
options.validate()
return options
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.openapiprocessor.spring.processor

import io.openapiprocessor.core.converter.ApiOptions
import io.openapiprocessor.core.support.toURI
import io.openapiprocessor.core.writer.java.FileHandler
import io.openapiprocessor.core.writer.java.PathWriter
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.BufferedWriter
import java.io.Writer
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths

class GradlePluginFileHandler(
private val options: ApiOptions
) : FileHandler {
private val log: Logger = LoggerFactory.getLogger(this.javaClass.name)
private lateinit var apiFolder: Path
private lateinit var modelFolder: Path

override fun createApiWriter(packageName: String, className: String): Writer {
val target = apiFolder.resolve("${className}.java")
return BufferedWriter(PathWriter(target))
}

override fun createModelWriter(packageName: String, className: String): Writer {
val target = modelFolder.resolve("${className}.java")
return BufferedWriter(PathWriter(target))
}

override fun createTargetFolders() {
val rootPkg = options.packageName.replace(".", "/")
val apiPkg = listOf(rootPkg, "api").joinToString("/")
val modelPkg = listOf(rootPkg, "model").joinToString("/")

apiFolder = createTargetPackage(apiPkg)
log.debug("created target folder: {}", apiFolder.toAbsolutePath().toString())

modelFolder = createTargetPackage(modelPkg)
log.debug("created target folder: {}", modelFolder.toAbsolutePath().toString())
}

private fun createTargetPackage(apiPkg: String): Path {
val root = options.targetDir
val pkg = listOf(root, apiPkg).joinToString("/")

val target = Paths.get (toURI(pkg))
Files.createDirectories(target)
return target
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ class SpringProcessor: OpenApiProcessor, io.openapiprocessor.api.v1.OpenApiProce
options,
headerWriter,
javaDocWriter
)
),
GradlePluginFileHandler(options)
)

writer.write (api)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.openapiprocessor.spring.annotationprocessor.SpringAnnotationProcessor