diff --git a/src/main/kotlin/io/openapiprocessor/spring/annotationprocessor/AnnotationProcessorFileHandler.kt b/src/main/kotlin/io/openapiprocessor/spring/annotationprocessor/AnnotationProcessorFileHandler.kt new file mode 100644 index 00000000..6842cb79 --- /dev/null +++ b/src/main/kotlin/io/openapiprocessor/spring/annotationprocessor/AnnotationProcessorFileHandler.kt @@ -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() { + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/openapiprocessor/spring/annotationprocessor/OpenAPISpringProcessor.kt b/src/main/kotlin/io/openapiprocessor/spring/annotationprocessor/OpenAPISpringProcessor.kt new file mode 100644 index 00000000..a50f6835 --- /dev/null +++ b/src/main/kotlin/io/openapiprocessor/spring/annotationprocessor/OpenAPISpringProcessor.kt @@ -0,0 +1,8 @@ +package io.openapiprocessor.spring.annotationprocessor + +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.SOURCE) +annotation class OpenAPISpringProcessor( + val apiPath: String, + val mapping: String = "" +) diff --git a/src/main/kotlin/io/openapiprocessor/spring/annotationprocessor/SpringAnnotationProcessor.kt b/src/main/kotlin/io/openapiprocessor/spring/annotationprocessor/SpringAnnotationProcessor.kt new file mode 100644 index 00000000..a19dbde5 --- /dev/null +++ b/src/main/kotlin/io/openapiprocessor/spring/annotationprocessor/SpringAnnotationProcessor.kt @@ -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 { + return mutableSetOf( + OpenAPISpringProcessor::class.java.canonicalName + ) + } + + override fun getSupportedOptions(): MutableSet { + return mutableSetOf( + OPTION_KEY_ROOT_PATH + ) + } + + override fun process(annotations: MutableSet, 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() + 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): ApiOptions { + val options = OptionsConverter().convertOptions(processorOptions as Map) + options.validate() + return options + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/openapiprocessor/spring/processor/GradlePluginFileHandler.kt b/src/main/kotlin/io/openapiprocessor/spring/processor/GradlePluginFileHandler.kt new file mode 100644 index 00000000..fb604b24 --- /dev/null +++ b/src/main/kotlin/io/openapiprocessor/spring/processor/GradlePluginFileHandler.kt @@ -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 + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/openapiprocessor/spring/processor/SpringProcessor.kt b/src/main/kotlin/io/openapiprocessor/spring/processor/SpringProcessor.kt index ced3e41c..2dc34764 100644 --- a/src/main/kotlin/io/openapiprocessor/spring/processor/SpringProcessor.kt +++ b/src/main/kotlin/io/openapiprocessor/spring/processor/SpringProcessor.kt @@ -73,7 +73,8 @@ class SpringProcessor: OpenApiProcessor, io.openapiprocessor.api.v1.OpenApiProce options, headerWriter, javaDocWriter - ) + ), + GradlePluginFileHandler(options) ) writer.write (api) 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 00000000..4028774a --- /dev/null +++ b/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +io.openapiprocessor.spring.annotationprocessor.SpringAnnotationProcessor \ No newline at end of file