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 import com.sun.source.util.DocTrees
20 import jdk.javadoc.doclet.Doclet
21 import jdk.javadoc.doclet.DocletEnvironment
22 import jdk.javadoc.doclet.Reporter
23 import java.nio.file.Files
24 import java.nio.file.Path
25 import java.nio.file.Paths
26 import java.util.Locale
27 import javax.lang.model.SourceVersion
28 import javax.lang.model.element.Element
29 import javax.lang.model.util.Elements
30 import javax.lang.model.util.Types
31
32 /**
33 * A Doclet which can filter out internal APIs in various ways and then render the results
34 * as HTML.
35 *
36 * See also: The Element.isFiltered extension function below to see what is filtered.
37 */
38 class FilterDoclet : Doclet {
39 companion object {
40 lateinit var docTrees: DocTrees
41 lateinit var elementUtils: Elements
42 lateinit var typeUtils: Types
43 lateinit var outputPath: Path
44 lateinit var cssPath: Path
45 var baseUrl: String = "https://docs.oracle.com/en/java/javase/21/docs/api/java.base/"
46 const val CSS_FILENAME = "styles.css"
47 var outputDir = "."
48 var docTitle = "DOC TITLE"
49 var windowTitle = "WINDOW TITLE"
50 var noTimestamp: Boolean = false
51 val classIndex = ClassIndex()
52 }
53
54 override fun init(locale: Locale?, reporter: Reporter?) = Unit // TODO
55 override fun getName() = "FilterDoclet"
56 override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
57
58 override fun run(environment: DocletEnvironment): Boolean {
59 docTrees = environment.docTrees
60 elementUtils = environment.elementUtils
61 typeUtils = environment.typeUtils
62 outputPath = Paths.get(outputDir)
63 cssPath = outputPath.resolve(CSS_FILENAME)
64 Files.createDirectories(outputPath)
65
66 classIndex.addVisible(environment.includedElements)
67
68 try {
69 generateClassFiles()
70 generateIndex()
71 return true
72 } catch (e: Exception) {
73 System.err.println("Error generating documentation: " + e.message)
74 e.printStackTrace()
75 return false
76 }
77 }
78
79 private fun generateClassFiles() = classIndex.classes().forEach(::generateClassFile)
80
81 private fun generateIndex() {
82 val indexPath = outputPath.resolve("index.html")
83
84 html {
85 body(
86 title = docTitle,
87 stylesheet = relativePath(indexPath, cssPath),
88 ) {
89 div("index-container") {
90 h1(docTitle, "index-title")
91 compose {
92 classIndex.generateHtml()
93 }
94 }
95 }
96 }.let {
97 Files.newBufferedWriter(indexPath).use { writer ->
98 writer.write(it)
99 }
100 }
101 }
102
103 private fun generateClassFile(classInfo: ClassInfo) {
104 val classFilePath = outputPath.resolve(classInfo.fileName)
105 Files.createDirectories(classFilePath.parent)
106 val name = classInfo.innerName()
107
108 html {
109 body(
110 title = "$name - Conscrypt API",
111 stylesheet = relativePath(classFilePath, cssPath),
112 ) {
113 compose {
114 classInfo.generateHtml()
115 }
116 }
117 }.let {
118 Files.newBufferedWriter(classFilePath).use { writer ->
119 writer.write(it)
120 }
121 }
122 }
123
124 private fun relativePath(from: Path, to: Path) = from.parent.relativize(to).toString()
125
126 override fun getSupportedOptions(): Set<Doclet.Option> {
127 return setOf<Doclet.Option>(
128 StringOption(
129 "-d",
130 "<directory>",
131 "Destination directory for output files"
132 ) { d: String -> outputDir = d },
133 StringOption(
134 "-doctitle",
135 "<title>",
136 "Document title"
137 ) { t: String -> docTitle = t },
138 StringOption(
139 "-windowtitle",
140 "<title>",
141 "Window title"
142 ) { w: String -> windowTitle = w },
143 StringOption(
144 "-link",
145 "<link>",
146 "Link"
147 ) { l: String -> baseUrl = l },
148 BooleanOption(
149 "-notimestamp",
150 "Something"
151 ) { noTimestamp = true })
152 }
153 }
154
155 // Called to determine whether to filter each public API element.
Elementnull156 fun Element.isFiltered() =
157 hasJavadocTag("hide") || hasAnnotation("org.conscrypt.Internal")
158