From 3c7c57acfeff1e3cdf2e8cbcd4fdea859a4b8c97 Mon Sep 17 00:00:00 2001 From: Thomas Schuehly Date: Thu, 10 Aug 2023 22:54:43 +0200 Subject: [PATCH] removed unneeded parser for new annotation based parser --- .../spring/viewcomponent/core/ExampleVC.kt | 7 -- .../core/ViewComponentAutoConfiguration.kt | 8 +- .../core/action/CapturingResponseWrapper.kt | 84 --------------- .../core/action/ViewActionConfiguration.kt | 5 +- .../core/action/ViewActionFilter.kt | 29 ----- .../core/action/ViewActionParser.kt | 102 ------------------ .../core/action/ViewActionProcessor.kt | 26 ----- .../core/action/ViewActionRegistry.kt | 4 + .../viewcomponent/core/action/ViewActions.kt | 10 +- .../ViewContextMethodReturnValueHandler.kt | 16 --- .../javax.annotation.processing.Processor | 1 - .../core/action/ViewActionParserTest.kt | 95 ---------------- .../thymeleaf-kotlin-example/build.gradle.kts | 11 -- .../application/web/TestController.kt | 1 + .../web/action/ActionViewComponent.html | 2 +- processor/build.gradle.kts | 3 +- .../processor/KotlinProcessor.kt | 21 ---- .../processor/KotlinProcessorProvider.kt | 16 --- .../viewcomponent/processor/Processor.kt | 74 +++++++++---- ...ols.ksp.processing.SymbolProcessorProvider | 1 - 20 files changed, 73 insertions(+), 443 deletions(-) delete mode 100644 core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/ExampleVC.kt delete mode 100644 core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/CapturingResponseWrapper.kt delete mode 100644 core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionFilter.kt delete mode 100644 core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionParser.kt delete mode 100644 core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionProcessor.kt delete mode 100644 core/src/main/resources/META-INF/services/javax.annotation.processing.Processor delete mode 100644 core/src/test/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionParserTest.kt delete mode 100644 processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/KotlinProcessor.kt delete mode 100644 processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/KotlinProcessorProvider.kt delete mode 100644 processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider diff --git a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/ExampleVC.kt b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/ExampleVC.kt deleted file mode 100644 index 395e97a..0000000 --- a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/ExampleVC.kt +++ /dev/null @@ -1,7 +0,0 @@ -package de.tschuehly.spring.viewcomponent.core - -import de.tschuehly.spring.viewcomponent.core.component.ViewComponent - -@ViewComponent -class ExampleVC { -} \ No newline at end of file diff --git a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/ViewComponentAutoConfiguration.kt b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/ViewComponentAutoConfiguration.kt index 429142b..32836c1 100644 --- a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/ViewComponentAutoConfiguration.kt +++ b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/ViewComponentAutoConfiguration.kt @@ -12,15 +12,9 @@ import java.io.File import java.time.Duration @Configuration -@ComponentScan("de.tschuehly.spring.viewcomponent.core.component") +@ComponentScan("de.tschuehly.spring.viewcomponent.core") class ViewComponentAutoConfiguration( ) { - - - @ConditionalOnProperty("spring.view-component.view-action.enabled", havingValue = "true") - @ComponentScan("de.tschuehly.spring.viewcomponent.core.action") - class ViewActionConfiguration {} - @Bean @ConditionalOnProperty("spring.view-component.local-development") fun viewComponentFileSystemWatcher(applicationContext: ApplicationContext): FileSystemWatcher { diff --git a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/CapturingResponseWrapper.kt b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/CapturingResponseWrapper.kt deleted file mode 100644 index 198f2e1..0000000 --- a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/CapturingResponseWrapper.kt +++ /dev/null @@ -1,84 +0,0 @@ -package de.tschuehly.spring.viewcomponent.core.action - -import jakarta.servlet.ServletOutputStream -import jakarta.servlet.WriteListener -import jakarta.servlet.http.HttpServletResponse -import jakarta.servlet.http.HttpServletResponseWrapper -import java.io.ByteArrayOutputStream -import java.io.IOException -import java.io.OutputStreamWriter -import java.io.PrintWriter - -internal class CapturingResponseWrapper(response: HttpServletResponse) : HttpServletResponseWrapper(response) { - private val capture: ByteArrayOutputStream - private var output: ServletOutputStream? = null - private var writer: PrintWriter? = null - var viewComponentBean: Any? = null - - init { - capture = ByteArrayOutputStream(response.bufferSize) - } - - override fun getOutputStream(): ServletOutputStream { - check(writer == null) { "getWriter() has already been called on this response." } - if (output == null) { - output = object : ServletOutputStream() { - override fun isReady(): Boolean { - return true - } - - override fun setWriteListener(writeListener: WriteListener) {} - - @Throws(IOException::class) - override fun write(b: Int) { - capture.write(b) - } - - @Throws(IOException::class) - override fun flush() { - capture.flush() - } - - @Throws(IOException::class) - override fun close() { - capture.close() - } - } - } - return output!! - } - - @Throws(IOException::class) - override fun getWriter(): PrintWriter { - check(output == null) { "getOutputStream() has already been called on this response." } - if (writer == null) { - writer = PrintWriter(OutputStreamWriter(capture, characterEncoding)) - } - return writer!! - } - - @Throws(IOException::class) - override fun flushBuffer() { - super.flushBuffer() - if (writer != null) { - writer!!.flush() - } else if (output != null) { - output!!.flush() - } - } - - @get:Throws(IOException::class) - val captureAsBytes: ByteArray - get() { - if (writer != null) { - writer!!.close() - } else if (output != null) { - output!!.close() - } - return capture.toByteArray() - } - - @get:Throws(IOException::class) - val captureAsString: String - get() = String(captureAsBytes, charset(characterEncoding)) -} \ No newline at end of file diff --git a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionConfiguration.kt b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionConfiguration.kt index 7f984cb..cf11454 100644 --- a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionConfiguration.kt +++ b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionConfiguration.kt @@ -11,6 +11,8 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl import org.springframework.web.util.pattern.PathPatternParser import java.lang.reflect.Method import de.tschuehly.spring.viewcomponent.core.action.ViewActionRegistry.PathMapping +import org.slf4j.LoggerFactory + @Configuration class ViewActionConfiguration( val context: ApplicationContext, @@ -23,8 +25,9 @@ class ViewActionConfiguration( fun registerViewActionEndpoints() { val viewComponentBeans = context.getBeansWithAnnotation(ViewComponent::class.java) - viewComponentBeans.forEach { (viewComponentName, viewComponentBean) -> + viewComponentBeans.forEach { (_, viewComponentBean) -> val beanType = ClassUtils.getUserClass(viewComponentBean.javaClass) + val viewComponentName = beanType.simpleName val viewComponentMethods = beanType.methods processViewComponentBean(viewComponentMethods, viewComponentName, viewComponentBean) } diff --git a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionFilter.kt b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionFilter.kt deleted file mode 100644 index d997f2d..0000000 --- a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionFilter.kt +++ /dev/null @@ -1,29 +0,0 @@ -package de.tschuehly.spring.viewcomponent.core.action - -import jakarta.servlet.Filter -import jakarta.servlet.FilterChain -import jakarta.servlet.ServletRequest -import jakarta.servlet.ServletResponse -import jakarta.servlet.http.HttpServletResponse -import org.springframework.core.Ordered -import org.springframework.core.annotation.Order -import org.springframework.stereotype.Component - -@Component -@Order(Ordered.HIGHEST_PRECEDENCE) -class ViewActionFilter( - private val viewActionParser: ViewActionParser -) : Filter { - override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { - val capturingResponseWrapper = CapturingResponseWrapper(response as HttpServletResponse) - chain.doFilter(request,capturingResponseWrapper) - val viewComponentName = capturingResponseWrapper.viewComponentBean?.javaClass?.simpleName?.lowercase() - val htmlString = capturingResponseWrapper.captureAsString - if(viewComponentName == null){ - response.writer.write(htmlString) - return - } - val parsedHTML = viewActionParser.parseViewComponent(viewComponentName, htmlString) - response.writer.write(parsedHTML) - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionParser.kt b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionParser.kt deleted file mode 100644 index 4354e49..0000000 --- a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionParser.kt +++ /dev/null @@ -1,102 +0,0 @@ -package de.tschuehly.spring.viewcomponent.core.action - -import de.tschuehly.spring.viewcomponent.core.component.ViewComponentProcessingException -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import org.springframework.stereotype.Service -import org.springframework.web.bind.annotation.RequestMethod - -@Service -class ViewActionParser( - val viewActionRegistry: ViewActionRegistry -) { - fun parseViewComponent(viewComponentName: String, htmlString: String): String { - val document = Document("") - addHtmxAttrForNestedViewComponents(document) - processRootElementViewComponent(document, viewComponentName) - return document.outerHtml() - } - - - private fun processRootElementViewComponent(document: Document, viewComponentName: String) { - - if (document.getElementsByAttribute(ViewActionConstant.attributeName).isEmpty()) { - // No view:actions in root viewcomponent to process - return - } - val childElement = getSingleChildElement(document) - - if (childElement.nodeName() == "html") { - // set id on body if it is a whole page that is returned, because htmx cannot swap html element itself - val bodyElement = document.selectFirst("body") - ?: throw ViewComponentProcessingException( - "No body tag in the root ViewComponent found, this is required", - null - ) - bodyElement.attr("id", viewComponentName) - replaceViewActionAttrWithHtmxAttr(bodyElement) - } else { - childElement.attr("id", viewComponentName) - replaceViewActionAttrWithHtmxAttr(childElement) - } - } - - private fun getSingleChildElement(document: Document): Element { - val firstChild = document.firstElementChild() - if (document.lastElementChild() != firstChild || firstChild == null) throw ViewComponentProcessingException( - "ViewComponent need to have one root html node", - null - ) - return firstChild - } - - private fun addHtmxAttrForNestedViewComponents(document: Element) { - val nestedViewComponents = document.getElementsByAttribute(ViewActionConstant.nestedViewComponentAttributeName) - nestedViewComponents.forEach { viewComponentElement -> - replaceViewActionAttrWithHtmxAttr(viewComponentElement) - } - } - - private fun replaceViewActionAttrWithHtmxAttr( - viewComponentElement: Element, - - ) { - val viewComponentName = viewComponentElement.id() - if (viewComponentName == "") throw ViewComponentProcessingException( - "Id with name of viewcomponent is needed on the viewcomponent element, this should be set " + - "by the templating engine when processing nested viewcomponents", null - ) - val postViewActions = viewComponentElement.getElementsByAttribute(ViewActionConstant.attributeName) - postViewActions.forEach { el -> - val splitMethodAttribute = el.attr(ViewActionConstant.attributeName).split("?") - val methodName = splitMethodAttribute[0] - el.removeAttr(ViewActionConstant.attributeName) - val viewActionMapping = viewActionRegistry.getMapping(viewComponentName, methodName) - val path = splitMethodAttribute.getOrNull(1)?.let { pathAttributeString -> - "${viewActionMapping.path}?$pathAttributeString" - } ?: viewActionMapping.path - - el.attr( - getHXAttr(viewActionMapping.requestMethod), - path - ) - if (el.attr("hx-target") == "") { - el.attr("hx-target", "#$viewComponentName") - } - if (el.attr("hx-swap") == "") { - el.attr("hx-swap", "outerHTML") - } - } - } - - fun getHXAttr(requestMethod: RequestMethod): String { - return when (requestMethod) { - RequestMethod.GET -> "hx-get" - RequestMethod.POST -> "hx-post" - RequestMethod.PUT -> "hx-put" - RequestMethod.PATCH -> "hx-patch" - RequestMethod.DELETE -> "hx-delete" - else -> throw ViewComponentProcessingException("RequestMethod: ${requestMethod.name} not supported", null) - } - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionProcessor.kt b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionProcessor.kt deleted file mode 100644 index 9738194..0000000 --- a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionProcessor.kt +++ /dev/null @@ -1,26 +0,0 @@ -package de.tschuehly.spring.viewcomponent.core.action - -import javax.annotation.processing.AbstractProcessor -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.TypeElement -import javax.tools.Diagnostic - - - - -@SupportedAnnotationTypes("de.tschuehly.spring.viewcomponent.core.component.ViewComponent") -@SupportedSourceVersion(SourceVersion.RELEASE_17) -class ViewActionProcessor : AbstractProcessor() { - override fun process(annotations: MutableSet, roundEnv: RoundEnvironment): Boolean { - processingEnv.messager.printMessage(Diagnostic.Kind.ERROR,"Hello World") - for (annotation in annotations) { - for (element in roundEnv.getElementsAnnotatedWith(annotation)) { - processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, "found @ViewComponent at $element") - } - } - return true - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionRegistry.kt b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionRegistry.kt index bd94314..db76976 100644 --- a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionRegistry.kt +++ b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionRegistry.kt @@ -1,5 +1,6 @@ package de.tschuehly.spring.viewcomponent.core.action +import org.slf4j.LoggerFactory import org.springframework.stereotype.Component import org.springframework.web.bind.annotation.RequestMethod import java.lang.reflect.Method @@ -8,11 +9,14 @@ import java.lang.reflect.Method class ViewActionRegistry { private val viewActionMapping = mutableMapOf() + private val logger = LoggerFactory.getLogger(ViewActionRegistry::class.java) + fun registerMapping(viewComponentName: String, mapping: PathMapping) { val key = viewActionKey(viewComponentName, mapping.method.name) if (viewActionMapping.containsKey(key)) { throw ViewActionRegistryException("Cannot register duplicate path mapping") } + logger.info("Registered endpoint ${mapping.path} to $viewComponentName::${mapping.method.name}") viewActionMapping[key] = mapping } diff --git a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActions.kt b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActions.kt index 60cdb89..b76afb9 100644 --- a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActions.kt +++ b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActions.kt @@ -3,31 +3,31 @@ package de.tschuehly.spring.viewcomponent.core.action @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) annotation class PostViewAction( - val path: String = "" + val path: String = "" ) @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) annotation class GetViewAction( - val path: String = "" + val path: String = "" ) @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) annotation class PutViewAction( - val path: String = "" + val path: String = "" ) @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) annotation class PatchViewAction( - val path: String = "" + val path: String = "" ) @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) annotation class DeleteViewAction( - val path: String = "" + val path: String = "" ) diff --git a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/component/ViewContextMethodReturnValueHandler.kt b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/component/ViewContextMethodReturnValueHandler.kt index a37af54..0690ca7 100644 --- a/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/component/ViewContextMethodReturnValueHandler.kt +++ b/core/src/main/kotlin/de/tschuehly/spring/viewcomponent/core/component/ViewContextMethodReturnValueHandler.kt @@ -1,7 +1,6 @@ package de.tschuehly.spring.viewcomponent.core.component import de.tschuehly.spring.viewcomponent.core.IViewContext -import de.tschuehly.spring.viewcomponent.core.action.CapturingResponseWrapper import de.tschuehly.spring.viewcomponent.core.toMap import jakarta.servlet.http.HttpServletResponseWrapper import org.springframework.core.MethodParameter @@ -28,21 +27,6 @@ class ViewContextMethodReturnValueHandler : HandlerMethodReturnValueHandler { } as IViewContext mavContainer.view = viewContext.componentTemplate mavContainer.addAllAttributes(viewContext.contextAttributes.toMap()) - val response = webRequest.nativeResponse - if (response is HttpServletResponseWrapper) { - setResponseBean(response,viewContext) - } } - - private fun setResponseBean(wrapper: HttpServletResponseWrapper, viewContext: IViewContext) { - if (wrapper is CapturingResponseWrapper) { - wrapper.viewComponentBean = viewContext.componentBean - return - } - val response = wrapper.response - if(response is HttpServletResponseWrapper){ - setResponseBean(response, viewContext) - } - } } \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/core/src/main/resources/META-INF/services/javax.annotation.processing.Processor deleted file mode 100644 index 196533b..0000000 --- a/core/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ /dev/null @@ -1 +0,0 @@ -de.tschuehly.spring.viewcomponent.core.action.ViewActionProcessor \ No newline at end of file diff --git a/core/src/test/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionParserTest.kt b/core/src/test/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionParserTest.kt deleted file mode 100644 index 8192231..0000000 --- a/core/src/test/kotlin/de/tschuehly/spring/viewcomponent/core/action/ViewActionParserTest.kt +++ /dev/null @@ -1,95 +0,0 @@ -package de.tschuehly.spring.viewcomponent.core.action - -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance -import org.springframework.web.bind.annotation.RequestMethod - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -@Disabled -class ViewActionParserTest { - - @BeforeAll - fun setup() { - registry.registerMapping( - "actionviewcomponent", - ViewActionRegistry.PathMapping( - "/actionviewcomponent/countup", - requestMethod = RequestMethod.GET, - method = this.javaClass.getMethod("countUp"), - ) - ) - registry.registerMapping( - "actionviewcomponent", - ViewActionRegistry.PathMapping( - "/actionviewcomponent/additem", - requestMethod = RequestMethod.POST, - method = this.javaClass.getMethod("addItem"), - ) - ) - } - - val registry = ViewActionRegistry() - val parser = ViewActionParser(registry) - - @Test - fun parseJavaScript() { - val ouput = parser.parseViewComponent("test", testHtml) - assertEquals(testHtml, ouput) - } - - - @Test - fun parseViewActiontmlStructure(){ - val output = parser.parseViewComponent("actionViewComponent", - fixture("unparsed/ViewActionHtmlStructure.html")) - assertEquals(output,fixture("parsed/ViewActionHtmlStructure.html")) - } - - @Test - fun parseViewActionSimpleDiv() { - val output = parser.parseViewComponent( - "actionViewComponent", - """
- -
- - -
-
""" - ) - assertEquals( -"""
- -
- -
-
""", - output - ) - } - - @Test - fun parseNestedViewComponent() { - val result = parser.parseViewComponent("actionViewComponent",fixture("unparsed/nestedViewComponent.html")) - assertEquals(fixture("parsed/nestedViewComponent.html"),result) - } - - fun countUp() = "" - fun addItem() = "" - - val testHtml = """""" - - private fun fixture(path: String): String { - return ViewActionParserTest::class.java.getResource("/fixtures/$path")?.readText()?.replace("\r\n","\n") - ?: throw Exception("Fixture file not found") - } - -} diff --git a/examples/thymeleaf-kotlin-example/build.gradle.kts b/examples/thymeleaf-kotlin-example/build.gradle.kts index 56c437d..2941e91 100644 --- a/examples/thymeleaf-kotlin-example/build.gradle.kts +++ b/examples/thymeleaf-kotlin-example/build.gradle.kts @@ -51,15 +51,4 @@ tasks.withType { tasks.withType { useJUnitPlatform() -} - - - -sourceSets { - main { - resources { - srcDir("src/main/kotlin") - exclude("**/*.kt") - } - } } \ No newline at end of file diff --git a/examples/thymeleaf-kotlin-example/src/main/kotlin/de/tschuehly/spring/viewcomponent/thymeleaf/application/web/TestController.kt b/examples/thymeleaf-kotlin-example/src/main/kotlin/de/tschuehly/spring/viewcomponent/thymeleaf/application/web/TestController.kt index 8653ef8..d2339b3 100644 --- a/examples/thymeleaf-kotlin-example/src/main/kotlin/de/tschuehly/spring/viewcomponent/thymeleaf/application/web/TestController.kt +++ b/examples/thymeleaf-kotlin-example/src/main/kotlin/de/tschuehly/spring/viewcomponent/thymeleaf/application/web/TestController.kt @@ -18,6 +18,7 @@ class TestController( private val headerViewComponent: HeaderViewComponent, private val actionViewComponent: ActionViewComponent ) { + @GetMapping("/") fun indexComponent() = indexViewComponent.render() diff --git a/examples/thymeleaf-kotlin-example/src/main/kotlin/de/tschuehly/spring/viewcomponent/thymeleaf/application/web/action/ActionViewComponent.html b/examples/thymeleaf-kotlin-example/src/main/kotlin/de/tschuehly/spring/viewcomponent/thymeleaf/application/web/action/ActionViewComponent.html index e75510d..1929fb3 100644 --- a/examples/thymeleaf-kotlin-example/src/main/kotlin/de/tschuehly/spring/viewcomponent/thymeleaf/application/web/action/ActionViewComponent.html +++ b/examples/thymeleaf-kotlin-example/src/main/kotlin/de/tschuehly/spring/viewcomponent/thymeleaf/application/web/action/ActionViewComponent.html @@ -24,7 +24,7 @@

ViewAction Post AddItem

- + diff --git a/processor/build.gradle.kts b/processor/build.gradle.kts index 43d0457..79fc1da 100644 --- a/processor/build.gradle.kts +++ b/processor/build.gradle.kts @@ -15,8 +15,7 @@ repositories { } dependencies { - implementation("com.google.devtools.ksp:symbol-processing-api:1.8.21-1.0.11") - + implementation("de.tschuehly:spring-view-component-core:0.6.1-SNAPSHOT") testImplementation(platform("org.junit:junit-bom:5.9.1")) testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/KotlinProcessor.kt b/processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/KotlinProcessor.kt deleted file mode 100644 index 1516ea3..0000000 --- a/processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/KotlinProcessor.kt +++ /dev/null @@ -1,21 +0,0 @@ -package de.tschuehly.spring.viewcomponent.processor - -import com.google.devtools.ksp.processing.CodeGenerator -import com.google.devtools.ksp.processing.KSPLogger -import com.google.devtools.ksp.processing.Resolver -import com.google.devtools.ksp.processing.SymbolProcessor -import com.google.devtools.ksp.symbol.KSAnnotated - -class KotlinProcessor(val options: Map, - val logger: KSPLogger, - val codeGenerator: CodeGenerator) : SymbolProcessor { - override fun process(resolver: Resolver): List { - logger.info("Hello World") - val symbols = resolver.getSymbolsWithAnnotation("de.tschuehly.spring.viewcomponent.core.component.ViewComponent") - symbols.forEach { - println(it.javaClass.simpleName) - } - return emptyList() - } -} - diff --git a/processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/KotlinProcessorProvider.kt b/processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/KotlinProcessorProvider.kt deleted file mode 100644 index da37db4..0000000 --- a/processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/KotlinProcessorProvider.kt +++ /dev/null @@ -1,16 +0,0 @@ -package de.tschuehly.spring.viewcomponent.processor - -import com.google.devtools.ksp.processing.SymbolProcessor -import com.google.devtools.ksp.processing.SymbolProcessorEnvironment -import com.google.devtools.ksp.processing.SymbolProcessorProvider - -class KotlinProcessorProvider : SymbolProcessorProvider { - - override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { - return KotlinProcessor( - codeGenerator = environment.codeGenerator, - logger = environment.logger, - options = environment.options - ) - } -} \ No newline at end of file diff --git a/processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/Processor.kt b/processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/Processor.kt index 8c08618..dc02b5c 100644 --- a/processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/Processor.kt +++ b/processor/src/main/kotlin/de/tschuehly/spring/viewcomponent/processor/Processor.kt @@ -1,11 +1,13 @@ package de.tschuehly.spring.viewcomponent.processor +import de.tschuehly.spring.viewcomponent.core.action.* import java.nio.file.FileSystems import javax.annotation.processing.AbstractProcessor 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.Diagnostic import kotlin.io.path.createDirectories @@ -23,39 +25,50 @@ class Processor : AbstractProcessor() { val directoryPath = "${element.enclosingElement}".replace(".", seperator) val elementPath = "${element.simpleName}".replace(".", seperator) val srcDirPath = FileSystems.getDefault() - .getPath("", *rootDir.toTypedArray(), "src", "main", "kotlin", directoryPath) + .getPath("", *rootDir.toTypedArray(), "src", "main", "kotlin", directoryPath) val srcHtmlFile = srcDirPath.resolve("$elementPath.html").toFile() val resourceDir = FileSystems.getDefault() - .getPath("", *rootDir.toTypedArray(), "build", "resources", "main", directoryPath) + .getPath("", *rootDir.toTypedArray(), "build", "resources", "main", directoryPath) resourceDir.createDirectories() val resourceHtmlFile = resourceDir.resolve("$elementPath.html").toFile() - val methodNameList = element.enclosedElements.mapNotNull { it.simpleName.toString() } if (resourceHtmlFile.exists()) { resourceHtmlFile.delete() } resourceHtmlFile.createNewFile() val resourceFileWriter = resourceHtmlFile.printWriter() - srcHtmlFile.forEachLine { - if (it.contains("view:action")) { - val startOfViewAction = it.indexOf("view:action=\"") - val endOfViewAction = - startOfViewAction + 13 + it.substring(startOfViewAction + 13).indexOf("\"") - val methodName = it.substring(startOfViewAction + 13, endOfViewAction) - if (methodNameList.contains(methodName) == false) { - throw RuntimeException("viewAction method $methodName does not exist") + + val viewComponentName = element.simpleName.toString().lowercase() + srcHtmlFile.forEachLine { htmlLine -> + + if (htmlLine.contains("view:action")) { + val beforeViewAction = htmlLine.split("view:action=\"")[0] + val viewActionAttrVal = htmlLine.split("view:action=\"")[1].split("\"")[0] + val afterViewActionAttrVal = htmlLine.split("view:action=\"")[1].split("\"")[1] + val methodName = if (viewActionAttrVal.startsWith("|") && + viewActionAttrVal.contains("?")) { + viewActionAttrVal.substring(1,viewActionAttrVal.indexOf("?")).lowercase() + } else { + viewActionAttrVal.lowercase() } - val newLine = it.replaceRange( - IntRange(startOfViewAction, endOfViewAction), - "hx-get=\"/${element.simpleName.toString().lowercase()}/${methodName.lowercase()}\"" - ) + val method = element.enclosedElements.find { it.simpleName.toString().lowercase() == methodName } + ?: throw RuntimeException("viewAction method $methodName does not exist") + + val (htmxAttr, path) = getHtmxAttribute(element, method, methodName) + val htmxAttrVal = viewActionAttrVal.replace(methodName,path,true) + val newLine = "$beforeViewAction$htmxAttr\"$htmxAttrVal\" hx-target=\"#$viewComponentName\"$afterViewActionAttrVal" + resourceFileWriter.write(newLine) resourceFileWriter.println() - processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, newLine) + processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, "old: $htmlLine") + processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, "new: $newLine") + } else if (htmlLine.contains(" { + method.getAnnotation(GetViewAction::class.java)?.let { + val action = if (it.path == "") "/${element.simpleName}/$methodName" else it.path + return Pair("hx-get=", action.lowercase()) + } + method.getAnnotation(PostViewAction::class.java)?.let { + val action = if (it.path == "") "/${element.simpleName}/$methodName" else it.path + return Pair("hx-post=", action.lowercase()) + } + method.getAnnotation(PutViewAction::class.java)?.let { + val action = if (it.path == "") "/${element.simpleName}/$methodName" else it.path + return Pair("hx-put=", action.lowercase()) + } + method.getAnnotation(PatchViewAction::class.java)?.let { + val action = if (it.path == "") "/${element.simpleName}/$methodName" else it.path + return Pair("hx-patch=", action.lowercase()) + } + method.getAnnotation(DeleteViewAction::class.java)?.let { + val action = if (it.path == "") "/${element.simpleName}/$methodName" else it.path + return Pair("hx-delete=", action.lowercase()) + } + throw RuntimeException("No annotation found on ${method.simpleName}") + } + } diff --git a/processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider deleted file mode 100644 index f6b14e9..0000000 --- a/processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider +++ /dev/null @@ -1 +0,0 @@ -de.tschuehly.spring.viewcomponent.processor.KotlinProcessorProvider \ No newline at end of file