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