• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.conscrypt.doclet
18 
19 private typealias Block = HtmlBuilder.() -> Unit
20 private fun Block.render(): String = HtmlBuilder().apply(this).toString()
21 
22 class HtmlBuilder {
23     private val content = StringBuilder()
24     override fun toString() = content.toString()
25 
26     fun text(fragment: () -> String): StringBuilder = text(fragment())
27     fun text(text: String): StringBuilder = content.append(text)
28     fun compose(fragment: () -> String) {
29         content.append(fragment())
30     }
31 
32     fun body(title: String, stylesheet: String, content: Block) {
33         text("""
34              <!DOCTYPE html>
35              <html><head>
36                <link rel="stylesheet" type="text/css" href="$stylesheet">
37                <meta charset="UTF-8">
38                <title>$title</title>
39              </head>
40              <body>""".trimIndent() +
41              content.render() +
42              "</body></html>")
43     }
44 
45     private fun tagBlock(
46         tag: String,
47         cssClass: String? = null,
48         colspan: Int? = null,
49         id: String? = null,
50         block: Block,
51         inline: Boolean? = false)
52     {
53         content.append("<$tag")
54         cssClass?.let { content.append(""" class="$it"""") }
55         colspan?.let { content.append(""" colspan="$it"""") }
56         id?.let { content.append(""" id="$it"""") }
57         content.append(">")
58         if(inline == false) {
59             content.append("\n")
60         }
61         content.append(block.render())
62         content.append("</$tag>\n")
63     }
64 
65     fun div(cssClass: String? = null, id: String? = null, block: Block) =
66         tagBlock("div", cssClass = cssClass, colspan = null, id, block)
67     fun ul(cssClass: String? = null, id: String? = null, block: Block) =
68         tagBlock("ul", cssClass = cssClass, colspan = null, id, block)
69     fun ol(cssClass: String? = null, id: String? = null, block: Block) =
70         tagBlock("ol", cssClass = cssClass, colspan = null, id, block)
71     fun table(cssClass: String? = null, id: String? = null, block: Block) =
72         tagBlock("table", cssClass = cssClass, colspan = null, id, block)
73     fun tr(cssClass: String? = null, id: String? = null, block: Block) =
74         tagBlock("tr", cssClass = cssClass, colspan = null, id, block)
75     fun th(cssClass: String? = null, colspan: Int? = null, id: String? = null, block: Block) =
76         tagBlock("th", cssClass, colspan, id, block, true)
77     fun td(cssClass: String? = null, colspan: Int? = null, id: String? = null, block: Block) =
78         tagBlock("td", cssClass, colspan, id, block, true)
79 
80     private fun tagValue(tag: String, value: String, cssClass: String? = null, id: String? = null) {
81         val classText = cssClass?.let { """ class="$it"""" } ?: ""
82         val idText = id?.let { """ id="$it"""" } ?: ""
83         content.append("<$tag$classText$idText>$value</$tag>")
84     }
85 
86     private fun tagValueNl(tag: String, value: String, cssClass: String? = null) {
87         tagValue(tag, value, cssClass)
88         content.append("\n")
89     }
90 
91     fun h1(heading: String, cssClass: String? = null) = tagValueNl("h1", heading, cssClass)
92     fun h1(cssClass: String? = null, block: Block) = h1(block.render(), cssClass)
93     fun h2(heading: String, cssClass: String? = null) = tagValueNl("h2", heading, cssClass)
94     fun h2(cssClass: String? = null, block: Block) = h2(block.render(), cssClass)
95     fun h3(heading: String, cssClass: String? = null) = tagValueNl("h3", heading, cssClass)
96     fun h3(cssClass: String? = null, block: Block) = h2(block.render(), cssClass)
97     fun h4(heading: String, cssClass: String? = null) = tagValueNl("h4", heading, cssClass)
98     fun h4(cssClass: String? = null, block: Block) = h2(block.render(), cssClass)
99     fun h5(heading: String, cssClass: String? = null) = tagValueNl("h5", heading, cssClass)
100     fun h5(cssClass: String? = null, block: Block) = h2(block.render(), cssClass)
101 
102     fun p(text: String, cssClass: String? = null) = tagValueNl("p", text, cssClass)
103     fun p(cssClass: String? = null, block: Block) = p(block.render(), cssClass)
104 
105     fun b(text: String, cssClass: String? = null) = tagValue("b", text, cssClass)
106     fun b(cssClass: String? = null, block: Block) = b(block.render(), cssClass)
107     fun pre(text: String, cssClass: String? = null) = tagValue("pre", text, cssClass)
108     fun pre(cssClass: String? = null, block: Block) = pre(block.render(), cssClass)
109     fun code(text: String, cssClass: String? = null) = tagValue("code", text, cssClass)
110     fun code(cssClass: String? = null, block: Block) = code(block.render(), cssClass)
111     fun strong(text: String, cssClass: String? = null) = tagValue("strong", text, cssClass)
112     fun strong(cssClass: String? = null, block: Block) = strong(block.render(), cssClass)
113 
114     fun br() = content.append("<br/>\n")
115     fun a(href: String, label: String) {
116         content.append("""<a href="$href">$label</a>""")
117     }
118     fun a(href: String, block: Block) = a(href, block.render())
119     fun a(href: String) = a(href, href)
120 
121     fun li(text: String, cssClass: String? = null) = tagValueNl("li", text, cssClass)
122     fun li(cssClass: String? = null, block: Block) = li(block.render(), cssClass)
123 
124     fun <T> items(collection: Iterable<T>, cssClass: String? = null,
125                   transform: HtmlBuilder.(T) -> Unit = { text(it.toString()) }) {
126         collection.forEach {
127             li(cssClass = cssClass) { transform(it) }
128         }
129     }
130 
131     fun <T> row(item: T, rowClass: String? = null, cellClass: String? = null,
132                 span: Int? = null,
133                 transform: HtmlBuilder.(T) -> Unit = { td {it.toString() } }) {
134         tr(cssClass = rowClass) {
135             transform(item)
136         }
137     }
138     fun <T> rowGroup(rows: Collection<T>, title: String? = null, rowClass: String? = null, cellClass: String? = null,
139                  colspan: Int? = null,
140                 transform: HtmlBuilder.(T) -> Unit) {
141         if(rows.isNotEmpty()) {
142             title?.let {
143                 tr {
144                     th(colspan = colspan) {
145                         strong(it)
146                     }
147                 }
148             }
149             rows.forEach {
150                 tr {
151                     transform(it)
152                 }
153             }
154         }
155     }
156 }
157 
158 fun html(block: Block) = block.render()
159 
160 fun exampleSubfunction() = html {
161     h1("Headings from exampleSubfunction")
162     listOf("one", "two", "three").forEach {
163         h2(it)
164     }
165 }
166 
167 fun example() = html {
168     val fruits = listOf("Apple", "Banana", "Cherry")
169     body(
170         stylesheet = "path/to/stylesheet.css",
171         title = "Page Title"
172     ) {
173         div(cssClass = "example-class") {
174             text {
175                 "This is a div"
176             }
177             h1 {
178                 text("Heading1a")
179             }
180             h2 {
181                 a("www.google.com", "Heading with a link")
182             }
183             h3("Heading with CSS class", "my-class")
184             h2("h2", "my-class")
185             p("Hello world")
186             compose {
187                 exampleSubfunction()
188             }
189             br()
190             a("www.google.com") {
191                 text("a link with ")
192                 b("bold")
193                 text(" text.")
194             }
195 
196         }
197         h1("Lists")
198 
199         h2("Unordered list:")
200         ul {
201             li("First item")
202             li("Second item")
203             li {
204                 text { "Complex item with " }
205                 b { text { "bold text" } }
206             }
207             ul {
208                 li("First nested item")
209                 li("Second nested item")
210             }
211         }
212 
213         h2("Ordered list:")
214         ol {
215             li("First item")
216             li("Second item")
217             li {
218                 text { "Item with a " }
219                 a(href = "https://example.com") { text { "link" } }
220             }
221         }
222         h2("List item iteration")
223         ul {
224             // Default
225             items(fruits)
226             // Text transform
227             items(fruits) {
228                 text("I like ${it}.")
229             }
230             // HTML transform with a CSS class
231             items(fruits, "myclass") {
232                 a("www.google.com") {
233                     b(it)
234                 }
235             }
236         }
237         ol("ol-class") {
238             items((1..5).asIterable()) {
239                 text("Item $it")
240             }
241         }
242         val data1 = listOf(1, 2)
243         val data2 = "3" to "4"
244         val data3 = listOf(
245             "key1" to "Some value",
246             "key2" to "Next Value",
247             "key3" to "Another value"
248         )
249 
250         table(cssClass = "table-class", id = "tableId") {
251             tr {
252                 th {
253                     text("First column")
254                 }
255                 th {
256                     text("Second column")
257 
258                 }
259             }
260             tr("tr-class") {
261                 td("td-class") {
262                     text("Data 1")
263                 }
264                 td(colspan = 2, id = "foo") {
265                     text("Data 2")
266                 }
267             }
268             tr {
269                 td() {
270                     text("Data 3")
271                 }
272             }
273             row(data1, "c1") {
274                 a(href = "www.google.com") { text("$it") }
275             }
276             row(data2) { p: Pair<String, String> ->
277                 td {
278                     text(p.first)
279                 }
280                 td {
281                     text(p.second)
282                 }
283 
284             }
285             rowGroup(data3, title = "Row Group", colspan = 2) { p: Pair<String, String> ->
286                 td {
287                     text(p.first)
288                 }
289                 td {
290                     text(p.second)
291                 }
292             }
293         }
294     }
295 }
296 
297 fun main() {
298     example().let(::println)
299 }
300