FreeMarker.kt

  1. package com.hexagontk.templates.freemarker

  2. import com.hexagontk.core.ResourceNotFoundException
  3. import com.hexagontk.core.urlOf
  4. import com.hexagontk.templates.TemplatePort
  5. import freemarker.cache.StringTemplateLoader
  6. import freemarker.cache.TemplateLookupContext
  7. import freemarker.cache.TemplateLookupResult
  8. import freemarker.cache.TemplateLookupStrategy
  9. import freemarker.cache.URLTemplateLoader
  10. import freemarker.template.Configuration
  11. import freemarker.template.Template
  12. import freemarker.template.Version
  13. import java.io.StringWriter
  14. import java.net.URL
  15. import java.util.*

  16. class FreeMarker : TemplatePort {

  17.     private val freeMarkerVersion = Version("2.3.34")

  18.     object AdapterTemplateLookupStrategy : TemplateLookupStrategy() {
  19.         override fun lookup(ctx: TemplateLookupContext): TemplateLookupResult =
  20.             try {
  21.                 val templateName = ctx.templateName
  22.                 urlOf(templateName)
  23.                 ctx.lookupWithAcquisitionStrategy(templateName)
  24.             }
  25.             catch (e: ResourceNotFoundException) {
  26.                 ctx.createNegativeLookupResult()
  27.             }
  28.     }

  29.     object AdapterTemplateLoader : URLTemplateLoader() {
  30.         override fun getURL(name: String): URL =
  31.             urlOf(name)
  32.     }

  33.     override fun render(url: URL, context: Map<String, *>, locale: Locale): String {
  34.         val configuration = Configuration(freeMarkerVersion).apply {
  35.             defaultEncoding = "UTF-8"
  36.             templateLookupStrategy = AdapterTemplateLookupStrategy
  37.             templateLoader = AdapterTemplateLoader
  38.         }

  39.         val template = configuration.getTemplate(url.toString())
  40.         return processTemplate(template, context, locale)
  41.     }

  42.     override fun render(
  43.         name: String, templates: Map<String, String>, context: Map<String, *>, locale: Locale
  44.     ): String {
  45.         val configuration = Configuration(freeMarkerVersion).apply {
  46.             defaultEncoding = "UTF-8"
  47.             templateLoader = StringTemplateLoader().apply {
  48.                 templates.forEach { (k, v) -> putTemplate(k, v) }
  49.             }
  50.         }

  51.         val template = configuration.getTemplate(name)
  52.         return processTemplate(template, context, locale)
  53.     }

  54.     private fun processTemplate(
  55.         template: Template,
  56.         context: Map<String, *>,
  57.         locale: Locale
  58.     ): String =
  59.         StringWriter().use {
  60.             val templateUrl = context["_template_"]
  61.             val freemarkerContext = context + ("_template_" to templateUrl.toString())
  62.             val env = template.createProcessingEnvironment(freemarkerContext, it)
  63.             env.locale = locale
  64.             env.process()
  65.             it.buffer.toString()
  66.         }
  67. }