aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main/kotlin/renderers/html/innerTemplating/HtmlTemplater.kt
blob: 1638c9c0c5bbf1b756a5ac1a64628040740de6b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*
 * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
 */

package org.jetbrains.dokka.base.renderers.html.innerTemplating

import freemarker.cache.ClassTemplateLoader
import freemarker.cache.FileTemplateLoader
import freemarker.cache.MultiTemplateLoader
import freemarker.log.Logger
import freemarker.template.Configuration
import freemarker.template.TemplateExceptionHandler
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.DokkaBaseConfiguration
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.configuration
import java.io.StringWriter


public enum class DokkaTemplateTypes(
    public val path: String
) {
    BASE("base.ftl")
}

public typealias TemplateMap = Map<String, Any?>

public class HtmlTemplater(
    context: DokkaContext
) {

    init {
        // to disable logging, but it isn't reliable see [Logger.SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY]
        // (use SLF4j further)
        System.setProperty(
            Logger.SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY,
            System.getProperty(Logger.SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY) ?: Logger.LIBRARY_NAME_NONE
        )
    }

    private val configuration = configuration<DokkaBase, DokkaBaseConfiguration>(context)
    private val templaterConfiguration =
        Configuration(Configuration.VERSION_2_3_31).apply { configureTemplateEngine() }

    private fun Configuration.configureTemplateEngine() {
        val loaderFromResources = ClassTemplateLoader(javaClass, "/dokka/templates")
        templateLoader = configuration?.templatesDir?.let {
            MultiTemplateLoader(
                arrayOf(
                    FileTemplateLoader(it),
                    loaderFromResources
                )
            )
        } ?: loaderFromResources

        unsetLocale()
        defaultEncoding = "UTF-8"
        templateExceptionHandler = TemplateExceptionHandler.RETHROW_HANDLER
        logTemplateExceptions = false
        wrapUncheckedExceptions = true
        fallbackOnNullLoopVariable = false
        templateUpdateDelayMilliseconds = Long.MAX_VALUE
    }

    public fun setupSharedModel(model: TemplateMap) {
        templaterConfiguration.setSharedVariables(model)
    }

    public fun renderFromTemplate(
        templateType: DokkaTemplateTypes,
        generateModel: () -> TemplateMap
    ): String {
        val out = StringWriter()
        // Freemarker has own thread-safe cache to keep templates
        val template = templaterConfiguration.getTemplate(templateType.path)
        val model = generateModel()
        template.process(model, out)

        return out.toString()
    }
}