diff --git a/api-doclet/src/main/kotlin/org/conscrypt/doclet/ClassIndex.kt b/api-doclet/src/main/kotlin/org/conscrypt/doclet/ClassIndex.kt index d7a4c8c6e..811d13a35 100644 --- a/api-doclet/src/main/kotlin/org/conscrypt/doclet/ClassIndex.kt +++ b/api-doclet/src/main/kotlin/org/conscrypt/doclet/ClassIndex.kt @@ -21,44 +21,56 @@ import javax.lang.model.element.TypeElement import kotlin.streams.toList class ClassIndex { - private val index = mutableMapOf() + private val index = mutableMapOf() - private fun put(classInfo: ClassInfo) { - index[classInfo.qualifiedName] = classInfo - } - - fun put(element: Element) { - put(ClassInfo(element as TypeElement)) - } - - fun get(qualifiedName: String) = index[qualifiedName] + private fun put(classInfo: ClassInfo) { + index[classInfo.qualifiedName] = classInfo + } - fun contains(qualifiedName: String) = index.containsKey(qualifiedName) + fun put(element: Element) { + put(ClassInfo(element as TypeElement)) + } - fun find(name: String) = if (contains(name)) get(name) else findSimple(name) + fun get(qualifiedName: String) = index[qualifiedName] + fun contains(qualifiedName: String) = index.containsKey(qualifiedName) + fun find(name: String) = if (contains(name)) get(name) else findSimple(name) + private fun findSimple(name: String) = classes().firstOrNull { it.simpleName == name } // XXX dups - private fun findSimple(name: String) = classes().firstOrNull { it.simpleName == name } // XXX dups + fun classes(): Collection = index.values - fun classes(): Collection = index.values + fun addVisible(elements: Set) { + elements + .filterIsInstance() + .filter(Element::isVisibleType) + .forEach(::put) + } - fun addVisible(elements: Set) { - elements.filterIsInstance().filter(Element::isVisibleType).forEach(::put) - } + private fun packages(): List = index.values.stream() + .map { it.packageName } + .distinct() + .sorted() + .toList() - private fun packages(): List = - index.values.stream().map { it.packageName }.distinct().sorted().toList() + private fun classesForPackage(packageName: String) = index.values.stream() + .filter { it.packageName == packageName } + .sorted() + .toList() - private fun classesForPackage(packageName: String) = - index.values.stream().filter { it.packageName == packageName }.sorted().toList() + fun generateHtml():String = html { + packages().forEach { packageName -> + div("package-section") { + h2("Package $packageName", "package-name") + ul("class-list") { + classesForPackage(packageName) + .forEach { c -> + li { + a(c.fileName, c.simpleName) + } + } - fun generateHtml(): String = html { - packages().forEach { packageName -> - div("package-section") { - h2("Package $packageName", "package-name") - ul("class-list") { - classesForPackage(packageName).forEach { c -> li { a(c.fileName, c.simpleName) } } + } + } } - } } - } } + diff --git a/api-doclet/src/main/kotlin/org/conscrypt/doclet/ClassInfo.kt b/api-doclet/src/main/kotlin/org/conscrypt/doclet/ClassInfo.kt index 1cfdc3c48..582885ba9 100644 --- a/api-doclet/src/main/kotlin/org/conscrypt/doclet/ClassInfo.kt +++ b/api-doclet/src/main/kotlin/org/conscrypt/doclet/ClassInfo.kt @@ -20,96 +20,117 @@ import javax.lang.model.element.Element import javax.lang.model.element.ExecutableElement import javax.lang.model.element.TypeElement + data class ClassInfo(val element: TypeElement) : Comparable { - val simpleName = element.simpleName.toString() - val qualifiedName = element.qualifiedName.toString() - val packageName = FilterDoclet.elementUtils.getPackageOf(element).qualifiedName.toString() - val fileName = qualifiedName.replace('.', '/') + ".html" + val simpleName = element.simpleName.toString() + val qualifiedName = element.qualifiedName.toString() + val packageName = FilterDoclet.elementUtils.getPackageOf(element).qualifiedName.toString() + val fileName = qualifiedName.replace('.', '/') + ".html" - override fun compareTo(other: ClassInfo) = qualifiedName.compareTo(other.qualifiedName) + override fun compareTo(other: ClassInfo) = qualifiedName.compareTo(other.qualifiedName) - private fun description() = html { - div("class-description") { compose { element.commentsAndTagTrees() } } - } + private fun description() = html { + div("class-description") { + compose { + element.commentsAndTagTrees() + } + } + } - private fun fields() = html { - val fields = element.children(Element::isVisibleField) - if (fields.isNotEmpty()) { - h2("Fields") - fields.forEach { field -> - div("member") { - h4(field.simpleName.toString()) - compose { field.commentsAndTagTrees() } + private fun fields() = html { + val fields = element.children(Element::isVisibleField) + if (fields.isNotEmpty()) { + h2("Fields") + fields.forEach { field -> + div("member") { + h4(field.simpleName.toString()) + compose { + field.commentsAndTagTrees() + } + } + } } - } } - } - private fun nestedClasses() = html { - val nested = element.children(Element::isVisibleType) - nested - .takeIf { it.isNotEmpty() } - ?.let { - h2("Nested Classes") - nested.forEach { cls -> - div("member") { - h4(cls.simpleName.toString()) - compose { cls.commentsAndTagTrees() } - } + private fun nestedClasses() = html { + val nested = element.children(Element::isVisibleType) + nested.takeIf { it.isNotEmpty() }?.let { + h2("Nested Classes") + nested.forEach { cls -> + div("member") { + h4(cls.simpleName.toString()) + compose { + cls.commentsAndTagTrees() + } + } + } } - } - } + } - private fun method(method: ExecutableElement) = html { - div("member") { - h4(method.simpleName.toString()) - pre(method.methodSignature(), "method-signature") - div("description") { - compose { method.commentTree() } - val params = method.paramTags() - val throwns = method.throwTags() - val returns = - if (method.isConstructor()) emptyList() else method.returnTag(method.returnType) + private fun method(method: ExecutableElement) = html { + div("member") { + h4(method.simpleName.toString()) + pre(method.methodSignature(), "method-signature") + div("description") { + compose { + method.commentTree() + } + val params = method.paramTags() + val throwns = method.throwTags() + val returns = if (method.isConstructor()) + emptyList() + else + method.returnTag(method.returnType) - if (params.size + returns.size + throwns.size > 0) { - div("params") { - table("params-table") { - rowGroup(params, title = "Parameters", colspan = 2) { - td { text(it.first) } - td { text(it.second) } - } - rowGroup(returns, title = "Returns", colspan = 2) { - td { text(it.first) } - td { text(it.second) } - } - rowGroup(throwns, title = "Throws", colspan = 2) { - td { text(it.first) } - td { text(it.second) } - } + if(params.size + returns.size + throwns.size > 0) { + div("params") { + table("params-table") { + rowGroup(params, title = "Parameters", colspan = 2) { + td {text(it.first)} + td {text(it.second)} + } + rowGroup(returns, title = "Returns", colspan = 2) { + td {text(it.first)} + td {text(it.second)} + } + rowGroup(throwns, title = "Throws", colspan = 2) { + td {text(it.first)} + td {text(it.second)} + } + } + } + } } - } } - } } - } - private fun executables(title: String, filter: (Element) -> Boolean) = html { - val methods = element.children(filter) - if (methods.isNotEmpty()) { - h2(title) - methods.forEach { compose { method(it as ExecutableElement) } } + private fun executables(title: String, filter: (Element) -> Boolean) = html { + val methods = element.children(filter) + if (methods.isNotEmpty()) { + h2(title) + methods.forEach { + compose { + method(it as ExecutableElement) + } + } + } } - } - - private fun constructors() = executables("Constructors", Element::isVisibleConstructor) - private fun methods() = executables("Public Methods", Element::isVisibleMethod) + private fun constructors() = executables("Constructors", Element::isVisibleConstructor) + private fun methods() = executables("Public Methods", Element::isVisibleMethod) - fun generateHtml() = html { - div("package-name") { text("Package: $packageName") } - h1(simpleName) - pre(element.signature(), "class-signature") + fun generateHtml() = html { + div("package-name") { text("Package: $packageName") } + h1(simpleName) + pre(element.signature(), "class-signature") - compose { description() + fields() + constructors() + methods() + nestedClasses() } - } + compose { + description() + + fields() + + constructors() + + methods() + + nestedClasses() + } + } } + diff --git a/api-doclet/src/main/kotlin/org/conscrypt/doclet/DocTreeUtils.kt b/api-doclet/src/main/kotlin/org/conscrypt/doclet/DocTreeUtils.kt index 9c179de3c..e3ccf8cc6 100644 --- a/api-doclet/src/main/kotlin/org/conscrypt/doclet/DocTreeUtils.kt +++ b/api-doclet/src/main/kotlin/org/conscrypt/doclet/DocTreeUtils.kt @@ -16,6 +16,7 @@ package org.conscrypt.doclet +import org.conscrypt.doclet.FilterDoclet.Companion.baseUrl import com.sun.source.doctree.DocCommentTree import com.sun.source.doctree.DocTree import com.sun.source.doctree.EndElementTree @@ -27,91 +28,83 @@ import com.sun.source.doctree.SeeTree import com.sun.source.doctree.StartElementTree import com.sun.source.doctree.TextTree import com.sun.source.doctree.ThrowsTree -import javax.lang.model.element.Element -import javax.lang.model.type.TypeMirror -import org.conscrypt.doclet.FilterDoclet.Companion.baseUrl import org.conscrypt.doclet.FilterDoclet.Companion.classIndex import org.conscrypt.doclet.FilterDoclet.Companion.docTrees +import javax.lang.model.element.Element +import javax.lang.model.type.TypeMirror -fun renderDocTreeList(treeList: List): String = - treeList.joinToString("\n", transform = ::renderDocTree) +fun renderDocTreeList(treeList: List):String = + treeList.joinToString("\n", transform = ::renderDocTree) -fun renderDocTree(docTree: DocTree): String = - when (docTree) { +fun renderDocTree(docTree: DocTree): String = when (docTree) { is TextTree -> docTree.body is LinkTree -> { - val reference = docTree.reference.toString() - val label = - if (docTree.label.isEmpty()) { - reference + val reference = docTree.reference.toString() + val label = if (docTree.label.isEmpty()) { + reference } else { - renderDocTreeList(docTree.label) + renderDocTreeList(docTree.label) } - createLink(reference, label) + createLink(reference, label) } - is StartElementTree, - is EndElementTree -> docTree.toString() + is StartElementTree, is EndElementTree -> docTree.toString() is LiteralTree -> "${docTree.body}" else -> error("[${docTree.javaClass} / ${docTree.kind} --- ${docTree}]") - } +} fun createLink(reference: String, label: String) = html { - val parts = reference.split('#') - val className = parts[0] - val anchor = if (parts.size > 1) "#${parts[1]}" else "" - val classInfo = classIndex.find(className) - val href = - if (classInfo != null) "${classInfo.simpleName}.html$anchor" - else "$baseUrl${className.replace('.', '/')}.html$anchor" - - a(href, label) + val parts = reference.split('#') + val className = parts[0] + val anchor = if (parts.size > 1) "#${parts[1]}" else "" + val classInfo = classIndex.find(className) + val href = if (classInfo != null) + "${classInfo.simpleName}.html$anchor" + else + "$baseUrl${className.replace('.', '/')}.html$anchor" + + a(href, label) } fun renderBlockTagList(tagList: List): String = - tagList.joinToString("\n", transform = ::renderBlockTag) + tagList.joinToString("\n", transform = ::renderBlockTag) -fun renderBlockTag(tag: DocTree) = - when (tag) { - is ParamTree, - is ReturnTree, - is ThrowsTree -> error("Unexpected block tag: $tag") - is SeeTree -> - html { +fun renderBlockTag(tag: DocTree) = when (tag) { + is ParamTree, is ReturnTree, is ThrowsTree -> error("Unexpected block tag: $tag") + is SeeTree -> html { br() p { - strong("See: ") - text(renderDocTreeList(tag.reference)) + strong("See: ") + text(renderDocTreeList(tag.reference)) } - } + } else -> tag.toString() - } +} inline fun Element.filterTags() = - docTree()?.blockTags?.filterIsInstance() ?: emptyList() + docTree()?.blockTags?.filterIsInstance() ?: emptyList() + +fun Element.paramTags() = filterTags() + .map { it.name.toString() to renderDocTreeList(it.description) } + .toList() -fun Element.paramTags() = - filterTags().map { it.name.toString() to renderDocTreeList(it.description) }.toList() fun Element.returnTag(returnType: TypeMirror): List> { - val list = mutableListOf>() - val descriptions = - filterTags().map { renderDocTreeList(it.description) }.singleOrNull() + val list = mutableListOf>() + val descriptions = filterTags() + .map { renderDocTreeList(it.description) } + .singleOrNull() - if (descriptions != null) { - list.add(returnType.toString() to descriptions) - } - return list + if (descriptions != null) { + list.add(returnType.toString() to descriptions) + } + return list } -fun Element.throwTags() = - filterTags() +fun Element.throwTags() = filterTags() .map { it.exceptionName.toString() to renderDocTreeList(it.description) } .toList() fun Element.docTree(): DocCommentTree? = docTrees.getDocCommentTree(this) - fun Element.commentTree() = docTree()?.let { renderDocTreeList(it.fullBody) } ?: "" - fun Element.tagTree() = docTree()?.let { renderBlockTagList(it.blockTags) } ?: "" - fun Element.commentsAndTagTrees() = commentTree() + tagTree() diff --git a/api-doclet/src/main/kotlin/org/conscrypt/doclet/ElementUtils.kt b/api-doclet/src/main/kotlin/org/conscrypt/doclet/ElementUtils.kt index d69eb2077..a9fcd00e3 100644 --- a/api-doclet/src/main/kotlin/org/conscrypt/doclet/ElementUtils.kt +++ b/api-doclet/src/main/kotlin/org/conscrypt/doclet/ElementUtils.kt @@ -27,101 +27,93 @@ import javax.lang.model.element.VariableElement import javax.lang.model.type.TypeMirror fun Element.isType() = isClass() || isInterface() || isEnum() - fun Element.isClass() = this is TypeElement && kind == ElementKind.CLASS - fun Element.isEnum() = this is TypeElement && kind == ElementKind.ENUM - fun Element.isInterface() = this is TypeElement && kind == ElementKind.INTERFACE - fun Element.isExecutable() = this is ExecutableElement - fun Element.isField() = this is VariableElement fun Element.isVisibleType() = isType() && isVisible() - fun Element.isVisibleMethod() = isExecutable() && isVisible() && kind == ElementKind.METHOD - -fun Element.isVisibleConstructor() = - isExecutable() && isVisible() && kind == ElementKind.CONSTRUCTOR - +fun Element.isVisibleConstructor() = isExecutable() && isVisible() && kind == ElementKind.CONSTRUCTOR fun Element.isVisibleField() = isField() && isVisible() - fun Element.isPublic() = modifiers.contains(Modifier.PUBLIC) - fun Element.isPrivate() = !isPublic() // Ignore protected for now :) - fun Element.isHidden() = isPrivate() || hasHideMarker() || parentIsHidden() - fun Element.isVisible() = !isHidden() - fun Element.hasHideMarker() = hasAnnotation("org.conscrypt.Internal") || hasHideTag() +fun Element.children(filterFunction: (Element) -> Boolean) = enclosedElements + .filter(filterFunction) + .toList() -fun Element.children(filterFunction: (Element) -> Boolean) = - enclosedElements.filter(filterFunction).toList() +fun Element.parentIsHidden(): Boolean + = if (enclosingElement.isType()) enclosingElement.isHidden() else false -fun Element.parentIsHidden(): Boolean = - if (enclosingElement.isType()) enclosingElement.isHidden() else false +fun Element.hasAnnotation(annotationName: String): Boolean = annotationMirrors + .map { it.annotationType.toString() } + .any { it == annotationName } -fun Element.hasAnnotation(annotationName: String): Boolean = - annotationMirrors.map { it.annotationType.toString() }.any { it == annotationName } fun Element.hasHideTag(): Boolean { - return docTree()?.blockTags?.any { tag -> tag is UnknownBlockTagTree && tag.tagName == "hide" } - ?: false + return docTree()?.blockTags?.any { + tag -> tag is UnknownBlockTagTree && tag.tagName == "hide" + } ?: false } fun ExecutableElement.isConstructor() = kind == ElementKind.CONSTRUCTOR - fun ExecutableElement.name() = if (isConstructor()) parentName() else simpleName.toString() - fun ExecutableElement.parentName() = enclosingElement.simpleName.toString() fun ExecutableElement.methodSignature(): String { - val modifiers = modifiers.joinToString(" ") - val returnType = if (isConstructor()) "" else "${formatType(returnType)} " + val modifiers = modifiers.joinToString(" ") + val returnType = if (isConstructor()) "" else "${formatType(returnType)} " - val typeParams = - typeParameters - .takeIf { it.isNotEmpty() } - ?.joinToString(separator = ", ", prefix = "<", postfix = ">") { it.asType().toString() } ?: "" + val typeParams = typeParameters.takeIf { it.isNotEmpty() } + ?.joinToString(separator = ", ", prefix = "<", postfix = ">") { + it.asType().toString() } ?: "" - val parameters = - parameters.joinToString(", ") { param -> "${formatType(param.asType())} ${param.simpleName}" } + val parameters = parameters.joinToString(", ") { param -> + "${formatType(param.asType())} ${param.simpleName}" + } - val exceptions = thrownTypes.joinToString(", ").prefixIfNotEmpty(" throws ") - return "$modifiers $typeParams$returnType${simpleName}($parameters)$exceptions" + val exceptions = thrownTypes + .joinToString(", ") + .prefixIfNotEmpty(" throws ") + return "$modifiers $typeParams$returnType${simpleName}($parameters)$exceptions" } fun formatType(typeMirror: TypeMirror): String { - return if (typeMirror.kind.isPrimitive) { - typeMirror.toString() - } else { - typeMirror.toString().split('.').last() - } + return if (typeMirror.kind.isPrimitive) { + typeMirror.toString() + } else { + typeMirror.toString() + .split('.') + .last() + } } fun TypeElement.signature(): String { - val modifiers = modifiers.joinToString(" ") - val kind = this.kind.toString().lowercase(Locale.getDefault()) + val modifiers = modifiers.joinToString(" ") + val kind = this.kind.toString().lowercase(Locale.getDefault()) - val superName = superDisplayName(superclass) + val superName = superDisplayName(superclass) - val interfaces = interfaces.joinToString(", ").prefixIfNotEmpty(" implements ") + val interfaces = interfaces + .joinToString(", ") + .prefixIfNotEmpty(" implements ") - return "$modifiers $kind $simpleName$superName$interfaces" + return "$modifiers $kind $simpleName$superName$interfaces" } fun superDisplayName(mirror: TypeMirror): String { - return when (mirror.toString()) { - "none", - "java.lang.Object" -> "" - else -> " extends $mirror " - } + return when (mirror.toString()) { + "none", "java.lang.Object" -> "" + else -> " extends $mirror " + } } -private fun String.prefixIfNotEmpty(prefix: String): String = - if (isNotEmpty()) prefix + this else this +private fun String.prefixIfNotEmpty(prefix: String): String + = if (isNotEmpty()) prefix + this else this -private fun String.suffixIfNotEmpty(prefix: String): String = - if (isNotEmpty()) this + prefix else this +private fun String.suffixIfNotEmpty(prefix: String): String + = if (isNotEmpty()) this + prefix else this \ No newline at end of file diff --git a/api-doclet/src/main/kotlin/org/conscrypt/doclet/FilterDoclet.kt b/api-doclet/src/main/kotlin/org/conscrypt/doclet/FilterDoclet.kt index ec6fab59a..77db33fff 100644 --- a/api-doclet/src/main/kotlin/org/conscrypt/doclet/FilterDoclet.kt +++ b/api-doclet/src/main/kotlin/org/conscrypt/doclet/FilterDoclet.kt @@ -17,6 +17,9 @@ package org.conscrypt.doclet import com.sun.source.util.DocTrees +import jdk.javadoc.doclet.Doclet +import jdk.javadoc.doclet.DocletEnvironment +import jdk.javadoc.doclet.Reporter import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths @@ -24,104 +27,128 @@ import java.util.Locale import javax.lang.model.SourceVersion import javax.lang.model.util.Elements import javax.lang.model.util.Types -import jdk.javadoc.doclet.Doclet -import jdk.javadoc.doclet.DocletEnvironment -import jdk.javadoc.doclet.Reporter class FilterDoclet : Doclet { - companion object { - lateinit var docTrees: DocTrees - lateinit var elementUtils: Elements - lateinit var typeUtils: Types - lateinit var outputPath: Path - var baseUrl: String = "https://docs.oracle.com/javase/8/docs/api/" - val CSS_FILENAME = "styles.css" - var outputDir = "." - var docTitle = "DTITLE" - var windowTitle = "WTITLE" - var noTimestamp: Boolean = false - val classIndex = ClassIndex() - } - - override fun init(locale: Locale?, reporter: Reporter?) = Unit // TODO - - override fun getName() = "FilterDoclet" - - override fun getSupportedSourceVersion() = SourceVersion.latest() - - override fun run(environment: DocletEnvironment): Boolean { - docTrees = environment.docTrees - elementUtils = environment.elementUtils - typeUtils = environment.typeUtils - outputPath = Paths.get(outputDir) - Files.createDirectories(outputPath) - - classIndex.addVisible(environment.includedElements) - - try { - generateClassFiles() - generateIndex() - return true - } catch (e: Exception) { - System.err.println("Error generating documentation: " + e.message) - e.printStackTrace() - return false + companion object { + lateinit var docTrees: DocTrees + lateinit var elementUtils: Elements + lateinit var typeUtils: Types + lateinit var outputPath: Path + var baseUrl: String = "https://docs.oracle.com/javase/8/docs/api/" + val CSS_FILENAME = "styles.css" + var outputDir = "." + var docTitle = "DTITLE" + var windowTitle = "WTITLE" + var noTimestamp: Boolean = false + val classIndex = ClassIndex() } - } - private fun generateClassFiles() = classIndex.classes().forEach(::generateClassFile) - - private fun generateIndex() { - val indexPath = outputPath.resolve("index.html") + override fun init(locale: Locale?, reporter: Reporter?) = Unit // TODO + override fun getName() = "FilterDoclet" + override fun getSupportedSourceVersion() = SourceVersion.latest() + + override fun run(environment: DocletEnvironment): Boolean { + docTrees = environment.docTrees + elementUtils = environment.elementUtils + typeUtils = environment.typeUtils + outputPath = Paths.get(outputDir) + Files.createDirectories(outputPath) + + classIndex.addVisible(environment.includedElements) + + try { + generateClassFiles() + generateIndex() + return true + } catch (e: Exception) { + System.err.println("Error generating documentation: " + e.message) + e.printStackTrace() + return false + } + } - html { - body(title = docTitle, stylesheet = relativePath(indexPath, CSS_FILENAME)) { - div("index-container") { - h1(docTitle, "index-title") - compose { classIndex.generateHtml() } - } + private fun generateClassFiles() = classIndex.classes().forEach(::generateClassFile) + + private fun generateIndex() { + val indexPath = outputPath.resolve("index.html") + + html { + body( + title = docTitle, + stylesheet = relativePath(indexPath, CSS_FILENAME), + ) { + div("index-container") { + h1(docTitle, "index-title") + compose { + classIndex.generateHtml() + } + } + } + }.let { + Files.newBufferedWriter(indexPath).use { writer -> + writer.write(it) + } } - } - .let { Files.newBufferedWriter(indexPath).use { writer -> writer.write(it) } } - } - - private fun generateClassFile(classInfo: ClassInfo) { - val classFilePath = outputPath.resolve(classInfo.fileName) - Files.createDirectories(classFilePath.parent) - val simpleName = classInfo.simpleName - - html { - body( - title = "$simpleName - conscrypt-openjdk API", - stylesheet = relativePath(classFilePath, CSS_FILENAME), - ) { - compose { classInfo.generateHtml() } + } + + private fun generateClassFile(classInfo: ClassInfo) { + val classFilePath = outputPath.resolve(classInfo.fileName) + Files.createDirectories(classFilePath.parent) + val simpleName = classInfo.simpleName + + html { + body( + title = "$simpleName - conscrypt-openjdk API", + stylesheet = relativePath(classFilePath, CSS_FILENAME), + ) { + compose { + classInfo.generateHtml() + } + } + }.let { + Files.newBufferedWriter(classFilePath).use { writer -> + writer.write(it) + } } - } - .let { Files.newBufferedWriter(classFilePath).use { writer -> writer.write(it) } } - } + } - private fun relativePath(from: Path, to: String): String { - val fromDir = from.parent - val toPath = Paths.get(outputDir).resolve(to) + private fun relativePath(from: Path, to: String): String { + val fromDir = from.parent + val toPath = Paths.get(outputDir).resolve(to) - if (fromDir == null) { - return to + if (fromDir == null) { + return to + } + + val relativePath = fromDir.relativize(toPath) + return relativePath.toString().replace('\\', '/') } - val relativePath = fromDir.relativize(toPath) - return relativePath.toString().replace('\\', '/') - } - - override fun getSupportedOptions(): Set { - return setOf( - StringOption("-d", "", "Destination directory for output files") { d: String -> - outputDir = d - }, - StringOption("-doctitle", "", "Document title") { t: String -> docTitle = t }, - StringOption("-windowtitle", "<title>", "Window title") { w: String -> windowTitle = w }, - StringOption("-link", "<link>", "Link") { l: String -> baseUrl = l }, - BooleanOption("-notimestamp", "Something") { noTimestamp = true }, - ) - } -} + override fun getSupportedOptions(): Set<Doclet.Option> { + return setOf<Doclet.Option>( + StringOption( + "-d", + "<directory>", + "Destination directory for output files" + ) { d: String -> outputDir = d }, + StringOption( + "-doctitle", + "<title>", + "Document title" + ) { t: String -> docTitle = t }, + StringOption( + "-windowtitle", + "<title>", + "Window title" + ) { w: String -> windowTitle = w }, + StringOption( + "-link", + "<link>", + "Link" + ) { l: String -> baseUrl = l }, + BooleanOption( + "-notimestamp", + "Something" + ) { noTimestamp = true }) + } +} \ No newline at end of file diff --git a/api-doclet/src/main/kotlin/org/conscrypt/doclet/HtmlBuilder.kt b/api-doclet/src/main/kotlin/org/conscrypt/doclet/HtmlBuilder.kt index 07866b21a..0c2758b2a 100644 --- a/api-doclet/src/main/kotlin/org/conscrypt/doclet/HtmlBuilder.kt +++ b/api-doclet/src/main/kotlin/org/conscrypt/doclet/HtmlBuilder.kt @@ -17,252 +17,268 @@ package org.conscrypt.doclet private typealias Block = HtmlBuilder.() -> Unit - private fun Block.render(): String = HtmlBuilder().apply(this).toString() class HtmlBuilder { - private val content = StringBuilder() - - override fun toString() = content.toString() - - fun text(fragment: () -> String): StringBuilder = text(fragment()) - - fun text(text: String): StringBuilder = content.append(text) + private val content = StringBuilder() + override fun toString() = content.toString() - fun compose(fragment: () -> String) { - content.append(fragment()) - } + fun text(fragment: () -> String): StringBuilder = text(fragment()) + fun text(text: String): StringBuilder = content.append(text) + fun compose(fragment: () -> String) { + content.append(fragment()) + } - fun body(title: String, stylesheet: String, content: Block) { - text( - """ + fun body(title: String, stylesheet: String, content: Block) { + text(""" <!DOCTYPE html> <html><head> <link rel="stylesheet" type="text/css" href="$stylesheet"> <meta charset="UTF-8"> <title>$title - """ - .trimIndent() + content.render() + "" - ) - } - - private fun tagBlock( - tag: String, - cssClass: String? = null, - colspan: Int? = null, - id: String? = null, - block: Block, - ) { - content.append("\n<$tag") - cssClass?.let { content.append(""" class="$it"""") } - colspan?.let { content.append(""" colspan="$it"""") } - id?.let { content.append(""" id="$it"""") } - content.append(">") - content.append(block.render()) - content.append("\n") - } - - fun div(cssClass: String? = null, id: String? = null, block: Block) = - tagBlock("div", cssClass = cssClass, colspan = null, id, block) - - fun ul(cssClass: String? = null, id: String? = null, block: Block) = - tagBlock("ul", cssClass = cssClass, colspan = null, id, block) - - fun ol(cssClass: String? = null, id: String? = null, block: Block) = - tagBlock("ol", cssClass = cssClass, colspan = null, id, block) - - fun table(cssClass: String? = null, id: String? = null, block: Block) = - tagBlock("table", cssClass = cssClass, colspan = null, id, block) - - fun tr(cssClass: String? = null, id: String? = null, block: Block) = - tagBlock("tr", cssClass = cssClass, colspan = null, id, block) - - fun th(cssClass: String? = null, colspan: Int? = null, id: String? = null, block: Block) = - tagBlock("th", cssClass, colspan, id, block) - - fun td(cssClass: String? = null, colspan: Int? = null, id: String? = null, block: Block) = - tagBlock("td", cssClass, colspan, id, block) - - private fun tagValue(tag: String, value: String, cssClass: String? = null) { - val classText = cssClass?.let { """ class="$it"""" } ?: "" - content.append("<$tag$classText>$value\n") - } - - fun h1(heading: String, cssClass: String? = null) = tagValue("h1", heading, cssClass) - - fun h1(cssClass: String? = null, block: Block) = h1(block.render(), cssClass) - - fun h2(heading: String, cssClass: String? = null) = tagValue("h2", heading, cssClass) - - fun h2(cssClass: String? = null, block: Block) = h2(block.render(), cssClass) - - fun h3(heading: String, cssClass: String? = null) = tagValue("h3", heading, cssClass) - - fun h3(cssClass: String? = null, block: Block) = h2(block.render(), cssClass) - - fun h4(heading: String, cssClass: String? = null) = tagValue("h4", heading, cssClass) - - fun h4(cssClass: String? = null, block: Block) = h2(block.render(), cssClass) - - fun h5(heading: String, cssClass: String? = null) = tagValue("h5", heading, cssClass) - - fun h5(cssClass: String? = null, block: Block) = h2(block.render(), cssClass) - - fun p(text: String, cssClass: String? = null) = tagValue("p", text, cssClass) - - fun p(cssClass: String? = null, block: Block) = p(block.render(), cssClass) - - fun b(text: String, cssClass: String? = null) = tagValue("b", text, cssClass) - - fun b(cssClass: String? = null, block: Block) = b(block.render(), cssClass) - - fun pre(text: String, cssClass: String? = null) = tagValue("pre", text, cssClass) - - fun pre(cssClass: String? = null, block: Block) = pre(block.render(), cssClass) - - fun code(text: String, cssClass: String? = null) = tagValue("code", text, cssClass) - - fun code(cssClass: String? = null, block: Block) = code(block.render(), cssClass) - - fun strong(text: String, cssClass: String? = null) = tagValue("strong", text, cssClass) - - fun strong(cssClass: String? = null, block: Block) = strong(block.render(), cssClass) - - fun br() = content.append("
\n") - - fun a(href: String, label: String) { - content.append("""$label""") - } - - fun a(href: String, block: Block) = a(href, block.render()) + """.trimIndent() + + content.render() + + "") + } - fun a(href: String) = a(href, href) + private fun tagBlock( + tag: String, cssClass: String? = null, colspan: Int? = null, id: String? = null, block: Block) + { + content.append("\n<$tag") + cssClass?.let { content.append(""" class="$it"""") } + colspan?.let { content.append(""" colspan="$it"""") } + id?.let { content.append(""" id="$it"""") } + content.append(">") + content.append(block.render()) + content.append("\n") + } - fun li(text: String, cssClass: String? = null) = tagValue("li", text, cssClass) + fun div(cssClass: String? = null, id: String? = null, block: Block) = + tagBlock("div", cssClass = cssClass, colspan = null, id, block) + fun ul(cssClass: String? = null, id: String? = null, block: Block) = + tagBlock("ul", cssClass = cssClass, colspan = null, id, block) + fun ol(cssClass: String? = null, id: String? = null, block: Block) = + tagBlock("ol", cssClass = cssClass, colspan = null, id, block) + fun table(cssClass: String? = null, id: String? = null, block: Block) = + tagBlock("table", cssClass = cssClass, colspan = null, id, block) + fun tr(cssClass: String? = null, id: String? = null, block: Block) = + tagBlock("tr", cssClass = cssClass, colspan = null, id, block) + fun th(cssClass: String? = null, colspan: Int? = null, id: String? = null, block: Block) = + tagBlock("th", cssClass, colspan, id, block) + fun td(cssClass: String? = null, colspan: Int? = null, id: String? = null, block: Block) = + tagBlock("td", cssClass, colspan, id, block) + + private fun tagValue(tag: String, value: String, cssClass: String? = null) { + val classText = cssClass?.let { """ class="$it"""" } ?: "" + content.append("<$tag$classText>$value\n") + } - fun li(cssClass: String? = null, block: Block) = li(block.render(), cssClass) + fun h1(heading: String, cssClass: String? = null) = tagValue("h1", heading, cssClass) + fun h1(cssClass: String? = null, block: Block) = h1(block.render(), cssClass) + fun h2(heading: String, cssClass: String? = null) = tagValue("h2", heading, cssClass) + fun h2(cssClass: String? = null, block: Block) = h2(block.render(), cssClass) + fun h3(heading: String, cssClass: String? = null) = tagValue("h3", heading, cssClass) + fun h3(cssClass: String? = null, block: Block) = h2(block.render(), cssClass) + fun h4(heading: String, cssClass: String? = null) = tagValue("h4", heading, cssClass) + fun h4(cssClass: String? = null, block: Block) = h2(block.render(), cssClass) + fun h5(heading: String, cssClass: String? = null) = tagValue("h5", heading, cssClass) + fun h5(cssClass: String? = null, block: Block) = h2(block.render(), cssClass) + + fun p(text: String, cssClass: String? = null) = tagValue("p", text, cssClass) + fun p(cssClass: String? = null, block: Block) = p(block.render(), cssClass) + fun b(text: String, cssClass: String? = null) = tagValue("b", text, cssClass) + fun b(cssClass: String? = null, block: Block) = b(block.render(), cssClass) + fun pre(text: String, cssClass: String? = null) = tagValue("pre", text, cssClass) + fun pre(cssClass: String? = null, block: Block) = pre(block.render(), cssClass) + fun code(text: String, cssClass: String? = null) = tagValue("code", text, cssClass) + fun code(cssClass: String? = null, block: Block) = code(block.render(), cssClass) + fun strong(text: String, cssClass: String? = null) = tagValue("strong", text, cssClass) + fun strong(cssClass: String? = null, block: Block) = strong(block.render(), cssClass) + + fun br() = content.append("
\n") + fun a(href: String, label: String) { + content.append("""$label""") + } + fun a(href: String, block: Block) = a(href, block.render()) + fun a(href: String) = a(href, href) - fun items( - collection: Iterable, - cssClass: String? = null, - transform: HtmlBuilder.(T) -> Unit = { text(it.toString()) }, - ) { - collection.forEach { li(cssClass = cssClass) { transform(it) } } - } + fun li(text: String, cssClass: String? = null) = tagValue("li", text, cssClass) + fun li(cssClass: String? = null, block: Block) = li(block.render(), cssClass) - fun row( - item: T, - rowClass: String? = null, - cellClass: String? = null, - span: Int? = null, - transform: HtmlBuilder.(T) -> Unit = { td { it.toString() } }, - ) { - tr(cssClass = rowClass) { transform(item) } - } + fun items(collection: Iterable, cssClass: String? = null, + transform: HtmlBuilder.(T) -> Unit = { text(it.toString()) }) { + collection.forEach { + li(cssClass = cssClass) { transform(it) } + } + } - fun rowGroup( - rows: Collection, - title: String? = null, - rowClass: String? = null, - cellClass: String? = null, - colspan: Int? = null, - transform: HtmlBuilder.(T) -> Unit, - ) { - if (rows.isNotEmpty()) { - title?.let { tr { th(colspan = colspan) { strong(it) } } } - rows.forEach { tr { transform(it) } } + fun row(item: T, rowClass: String? = null, cellClass: String? = null, + span: Int? = null, + transform: HtmlBuilder.(T) -> Unit = { td {it.toString() } }) { + tr(cssClass = rowClass) { + transform(item) + } + } + fun rowGroup(rows: Collection, title: String? = null, rowClass: String? = null, cellClass: String? = null, + colspan: Int? = null, + transform: HtmlBuilder.(T) -> Unit) { + if(rows.isNotEmpty()) { + title?.let { + tr { + th(colspan = colspan) { + strong(it) + } + } + } + rows.forEach { + tr { + transform(it) + } + } + } } - } } fun html(block: Block) = block.render() fun exampleSubfunction() = html { - h1("Headings from exampleSubfunction") - listOf("one", "two", "three").forEach { h1(it) } + h1("Headings from exampleSubfunction") + listOf("one", "two", "three").forEach { + h1(it) + } } fun example() = html { - val fruits = listOf("Apple", "Banana", "Cherry") - body(stylesheet = "path/to/stylesheet.css", title = "Page Title") { - div(cssClass = "example-class") { - text { "This is a div" } - h1 { text("Heading1a") } - h2 { a("www.google.com", "Heading with a link") } - h3("Heading with CSS class", "my-class") - h2("h2", "my-class") - p("Hello world") - compose { exampleSubfunction() } - br() - a("www.google.com") { - text("a link with ") - b("bold") - text(" text.") - } + val fruits = listOf("Apple", "Banana", "Cherry") + body( + stylesheet = "path/to/stylesheet.css", + title = "Page Title" + ) { + div(cssClass = "example-class") { + text { + "This is a div" + } + h1 { + text("Heading1a") + } + h2 { + a("www.google.com", "Heading with a link") + } + h3("Heading with CSS class", "my-class") + h2("h2", "my-class") + p("Hello world") + compose { + exampleSubfunction() + } + br() + a("www.google.com") { + text("a link with ") + b("bold") + text(" text.") + } + + } + h1("Lists") + + h2("Unordered list:") + ul { + li("First item") + li("Second item") + li { + text { "Complex item with " } + b { text { "bold text" } } + } + ul { + li("First nested item") + li("Second nested item") + } + } + + h2("Ordered list:") + ol { + li("First item") + li("Second item") + li { + text { "Item with a " } + a(href = "https://example.com") { text { "link" } } + } + } + h2("List item iteration") + ul { + // Default + items(fruits) + // Text transform + items(fruits) { + text("I like ${it}.") + } + // HTML transform with a CSS class + items(fruits, "myclass") { + a("www.google.com") { + b(it) + } + } + } + ol("ol-class") { + items((1..5).asIterable()) { + text("Item $it") + } + } } - h1("Lists") - - h2("Unordered list:") - ul { - li("First item") - li("Second item") - li { - text { "Complex item with " } - b { text { "bold text" } } - } - ul { - li("First nested item") - li("Second nested item") - } - } - - h2("Ordered list:") - ol { - li("First item") - li("Second item") - li { - text { "Item with a " } - a(href = "https://example.com") { text { "link" } } - } - } - h2("List item iteration") - ul { - // Default - items(fruits) - // Text transform - items(fruits) { text("I like ${it}.") } - // HTML transform with a CSS class - items(fruits, "myclass") { a("www.google.com") { b(it) } } - } - ol("ol-class") { items((1..5).asIterable()) { text("Item $it") } } - } - val data1 = listOf(1, 2) - val data2 = "3" to "4" - val data3 = listOf("tag1" to "Some value", "tag2" to "Next Value", "tag3" to "Another value") + val data1 = listOf(1, 2) + val data2 = "3" to "4" + val data3 = listOf( + "tag1" to "Some value", + "tag2" to "Next Value", + "tag3" to "Another value" + ) - table("table-class") { - tr { - th { text("First column") } - th { text("Second column") } - } - tr("tr-class") { - td("td-class") { text("Data 1") } - td(colspan = 2, id = "foo") { text("Data 2") } - } - tr { td() { text("Data 3") } } - row(data1, "c1") { a(href = "www.google.com") { text("$it") } } - row(data2) { p: Pair -> - td { text(p.first) } - td { text(p.second) } - } - rowGroup(data3, title = "Row Group", colspan = 2) { p: Pair -> - td { text(p.first) } - td { text(p.second) } + table("table-class") { + tr { + th { + text("First column") + } + th { + text("Second column") + + } + } + tr("tr-class") { + td("td-class") { + text("Data 1") + } + td(colspan = 2, id = "foo") { + text("Data 2") + } + } + tr { + td() { + text("Data 3") + } + } + row(data1, "c1") { + a(href="www.google.com") { text("$it") } + } + row(data2) { p:Pair -> + td { + text(p.first) + } + td { + text(p.second) + } + + } + rowGroup(data3, title = "Row Group", colspan=2) { p: Pair -> + td { + text(p.first) + } + td { + text(p.second) + } + } } - } } fun main() { - example().let(::println) + example().let(::println) } diff --git a/api-doclet/src/main/kotlin/org/conscrypt/doclet/Options.kt b/api-doclet/src/main/kotlin/org/conscrypt/doclet/Options.kt index 37707afe8..3fc57b0ff 100644 --- a/api-doclet/src/main/kotlin/org/conscrypt/doclet/Options.kt +++ b/api-doclet/src/main/kotlin/org/conscrypt/doclet/Options.kt @@ -16,43 +16,38 @@ package org.conscrypt.doclet -import java.util.function.Consumer import jdk.javadoc.doclet.Doclet.Option +import java.util.function.Consumer abstract class BaseOption(private val name: String) : Option { - override fun getKind() = Option.Kind.STANDARD - - override fun getNames(): List = listOf(name) + override fun getKind() = Option.Kind.STANDARD + override fun getNames(): List = listOf(name) } -class StringOption( - name: String, - private val parameters: String, - private val description: String, - private val action: Consumer, +class StringOption(name: String, + private val parameters: String, + private val description: String, + private val action: Consumer ) : BaseOption(name) { - override fun getArgumentCount() = 1 - - override fun getDescription(): String = description - - override fun getParameters(): String = parameters - - override fun process(option: String, arguments: MutableList): Boolean { - action.accept(arguments[0]) - return true - } + override fun getArgumentCount() = 1 + override fun getDescription(): String = description + override fun getParameters(): String = parameters + + override fun process(option: String, arguments: MutableList): Boolean { + action.accept(arguments[0]) + return true + } } -class BooleanOption(name: String, private val description: String, private val action: Runnable) : - BaseOption(name) { - override fun getArgumentCount() = 0 - - override fun getDescription(): String = description - - override fun getParameters(): String = "" - - override fun process(option: String, arguments: MutableList): Boolean { - action.run() - return true - } +class BooleanOption(name: String, + private val description: String, + private val action: Runnable): BaseOption(name) { + override fun getArgumentCount() = 0 + override fun getDescription(): String = description + override fun getParameters(): String = "" + + override fun process(option: String, arguments: MutableList): Boolean { + action.run() + return true + } }