aboutsummaryrefslogtreecommitdiff
path: root/plugins/base
diff options
context:
space:
mode:
authorPaweł Marks <pmarks@virtuslab.com>2020-02-17 09:32:35 +0100
committerPaweł Marks <Kordyjan@users.noreply.github.com>2020-02-18 13:28:23 +0100
commitf625cef495d625d81ee22e950083f57cc4fab875 (patch)
treed4587be659154a3000ee58fe2297e55604e418e8 /plugins/base
parent3b200cf10e0c50c2eee4b9da3f7039d678fa4aad (diff)
downloaddokka-f625cef495d625d81ee22e950083f57cc4fab875.tar.gz
dokka-f625cef495d625d81ee22e950083f57cc4fab875.tar.bz2
dokka-f625cef495d625d81ee22e950083f57cc4fab875.zip
Moves PsiToDocumentablesTranslator to the base plugin
Diffstat (limited to 'plugins/base')
-rw-r--r--plugins/base/build.gradle.kts4
-rw-r--r--plugins/base/src/main/kotlin/DokkaBase.kt10
-rw-r--r--plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt127
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt216
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt50
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt68
-rw-r--r--plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt145
7 files changed, 620 insertions, 0 deletions
diff --git a/plugins/base/build.gradle.kts b/plugins/base/build.gradle.kts
index ba0b5753..b42d7e28 100644
--- a/plugins/base/build.gradle.kts
+++ b/plugins/base/build.gradle.kts
@@ -7,3 +7,7 @@ publishing {
}
}
+dependencies {
+ implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.10")
+}
+
diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt
index 34c0ffae..bafd30ff 100644
--- a/plugins/base/src/main/kotlin/DokkaBase.kt
+++ b/plugins/base/src/main/kotlin/DokkaBase.kt
@@ -4,13 +4,19 @@ import org.jetbrains.dokka.CoreExtensions
import org.jetbrains.dokka.base.transformers.descriptors.DefaultDescriptorToDocumentationTranslator
import org.jetbrains.dokka.base.transformers.documentables.DefaultDocumentableMerger
import org.jetbrains.dokka.base.transformers.documentables.DefaultDocumentablesToPageTranslator
+import org.jetbrains.dokka.base.transformers.psi.DefaultPsiToDocumentationTranslator
import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.jetbrains.dokka.renderers.html.HtmlRenderer
class DokkaBase: DokkaPlugin() {
val descriptorToDocumentationTranslator by extending(isFallback = true) {
CoreExtensions.descriptorToDocumentationTranslator providing ::DefaultDescriptorToDocumentationTranslator
}
+ val psiToDocumentationTranslator by extending(isFallback = true) {
+ CoreExtensions.psiToDocumentationTranslator with DefaultPsiToDocumentationTranslator
+ }
+
val documentableMerger by extending(isFallback = true) {
CoreExtensions.documentableMerger with DefaultDocumentableMerger
}
@@ -18,4 +24,8 @@ class DokkaBase: DokkaPlugin() {
val documentablesToPageTranslator by extending(isFallback = true) {
CoreExtensions.documentablesToPageTranslator with DefaultDocumentablesToPageTranslator
}
+
+ val htmlRenderer by extending {
+ CoreExtensions.renderer providing ::HtmlRenderer
+ }
} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
new file mode 100644
index 00000000..c6183cf3
--- /dev/null
+++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
@@ -0,0 +1,127 @@
+package org.jetbrains.dokka.base.renderers
+
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.renderers.OutputWriter
+import org.jetbrains.dokka.renderers.Renderer
+import org.jetbrains.dokka.resolvers.LocationProvider
+import org.jetbrains.dokka.transformers.pages.PageNodeTransformer
+
+abstract class DefaultRenderer<T>(
+ protected val context: DokkaContext
+) : Renderer {
+
+ protected val outputWriter = context.single(CoreExtensions.outputWriter)
+
+ protected lateinit var locationProvider: LocationProvider
+ private set
+
+ protected open val preprocessors: Iterable<PageNodeTransformer> = emptyList()
+
+ abstract fun T.buildHeader(level: Int, content: T.() -> Unit)
+ abstract fun T.buildLink(address: String, content: T.() -> Unit)
+ abstract fun T.buildList(node: ContentList, pageContext: ContentPage)
+ abstract fun T.buildNewLine()
+ abstract fun T.buildResource(node: ContentEmbeddedResource, pageContext: ContentPage)
+ abstract fun T.buildTable(node: ContentTable, pageContext: ContentPage)
+ abstract fun T.buildText(textNode: ContentText)
+ abstract fun T.buildNavigation(page: PageNode)
+
+ abstract fun buildPage(page: ContentPage, content: (T, ContentPage) -> Unit): String
+ abstract fun buildError(node: ContentNode)
+
+ open fun T.buildGroup(node: ContentGroup, pageContext: ContentPage) {
+ node.children.forEach { it.build(this, pageContext) }
+ }
+
+ open fun T.buildLinkText(nodes: List<ContentNode>, pageContext: ContentPage) {
+ nodes.forEach { it.build(this, pageContext) }
+ }
+
+ open fun T.buildCode(code: List<ContentNode>, language: String, pageContext: ContentPage) {
+ code.forEach { it.build(this, pageContext) }
+ }
+
+ open fun T.buildHeader(node: ContentHeader, pageContext: ContentPage) {
+ buildHeader(node.level) { node.children.forEach { it.build(this, pageContext) } }
+ }
+
+ open fun ContentNode.build(builder: T, pageContext: ContentPage) =
+ builder.buildContentNode(this, pageContext)
+
+ open fun T.buildContentNode(node: ContentNode, pageContext: ContentPage) {
+ when (node) {
+ is ContentText -> buildText(node)
+ is ContentHeader -> buildHeader(node, pageContext)
+ is ContentCode -> buildCode(node.children, node.language, pageContext)
+ is ContentDRILink -> buildLink(
+ locationProvider.resolve(node.address, node.platforms.toList(), pageContext)
+ ) {
+ buildLinkText(node.children, pageContext)
+ }
+ is ContentResolvedLink -> buildLink(node.address) { buildLinkText(node.children, pageContext) }
+ is ContentEmbeddedResource -> buildResource(node, pageContext)
+ is ContentList -> buildList(node, pageContext)
+ is ContentTable -> buildTable(node, pageContext)
+ is ContentGroup -> buildGroup(node, pageContext)
+ else -> buildError(node)
+ }
+ }
+
+ open fun buildPageContent(context: T, page: ContentPage) {
+ context.buildNavigation(page)
+ page.content.build(context, page)
+ }
+
+ open fun renderPage(page: PageNode) {
+ val path by lazy { locationProvider.resolve(page, skipExtension = true) }
+ when (page) {
+ is ContentPage -> outputWriter.write(path, buildPage(page) { c, p -> buildPageContent(c, p) }, ".html")
+ is RendererSpecificPage -> when (val strategy = page.strategy) {
+ is RenderingStrategy.Copy -> outputWriter.writeResources(strategy.from, path)
+ is RenderingStrategy.Write -> outputWriter.write(path, strategy.text, "")
+ is RenderingStrategy.Callback -> outputWriter.write(path, strategy.instructions(this, page), ".html")
+ RenderingStrategy.DoNothing -> Unit
+ }
+ else -> throw AssertionError(
+ "Page ${page.name} cannot be rendered by renderer as it is not renderer specific nor contains content"
+ )
+ }
+ }
+
+ open fun renderPages(root: PageNode) {
+ renderPage(root)
+ root.children.forEach { renderPages(it) }
+ }
+
+ // reimplement this as preprocessor
+ open fun renderPackageList(root: ContentPage) =
+ getPackageNamesAndPlatforms(root)
+ .keys
+ .joinToString("\n")
+ .also { outputWriter.write("${root.name}/package-list", it, "") }
+
+ open fun getPackageNamesAndPlatforms(root: PageNode): Map<String, List<PlatformData>> =
+ root.children
+ .map(::getPackageNamesAndPlatforms)
+ .fold(emptyMap<String, List<PlatformData>>()) { e, acc -> acc + e } +
+ if (root is PackagePageNode) {
+ mapOf(root.name to root.platforms())
+ } else {
+ emptyMap()
+ }
+
+ override fun render(root: RootPageNode) {
+ val newRoot = preprocessors.fold(root) { acc, t -> t(acc) }
+
+ locationProvider =
+ context.single(CoreExtensions.locationProviderFactory).getLocationProvider(newRoot)
+
+ root.children<ModulePageNode>().forEach { renderPackageList(it) }
+
+ renderPages(newRoot)
+ }
+}
+
+fun ContentPage.platforms() = this.content.platforms.toList() \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
new file mode 100644
index 00000000..c9270681
--- /dev/null
+++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
@@ -0,0 +1,216 @@
+package org.jetbrains.dokka.renderers.html
+
+import kotlinx.html.*
+import kotlinx.html.stream.createHTML
+import org.jetbrains.dokka.base.renderers.DefaultRenderer
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.Function
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.renderers.OutputWriter
+import java.io.File
+
+open class HtmlRenderer(
+ context: DokkaContext
+) : DefaultRenderer<FlowContent>(context) {
+
+ private val pageList = mutableListOf<String>()
+
+ override val preprocessors = listOf(
+ RootCreator,
+ SearchPageInstaller,
+ ResourceInstaller,
+ NavigationPageInstaller,
+ StyleAndScriptsAppender
+ )
+
+ override fun FlowContent.buildList(node: ContentList, pageContext: ContentPage) =
+ if (node.ordered) ol {
+ buildListItems(node.children, pageContext)
+ }
+ else ul {
+ buildListItems(node.children, pageContext)
+ }
+
+ open fun OL.buildListItems(items: List<ContentNode>, pageContext: ContentPage) {
+ items.forEach {
+ if (it is ContentList)
+ buildList(it, pageContext)
+ else
+ li { it.build(this, pageContext) }
+ }
+ }
+
+ open fun UL.buildListItems(items: List<ContentNode>, pageContext: ContentPage) {
+ items.forEach {
+ if (it is ContentList)
+ buildList(it, pageContext)
+ else
+ li { it.build(this, pageContext) }
+ }
+ }
+
+ override fun FlowContent.buildResource(
+ node: ContentEmbeddedResource,
+ pageContext: ContentPage
+ ) { // TODO: extension point there
+ val imageExtensions = setOf("png", "jpg", "jpeg", "gif", "bmp", "tif", "webp", "svg")
+ return if (File(node.address).extension.toLowerCase() in imageExtensions) {
+ //TODO: add imgAttrs parsing
+ val imgAttrs = node.extras.filterIsInstance<HTMLSimpleAttr>().joinAttr()
+ img(src = node.address, alt = node.altText)
+ } else {
+ println("Unrecognized resource type: $node")
+ }
+ }
+
+ override fun FlowContent.buildTable(node: ContentTable, pageContext: ContentPage) {
+ table {
+ thead {
+ node.header.forEach {
+ tr {
+ it.children.forEach {
+ th {
+ it.build(this@table, pageContext)
+ }
+ }
+ }
+ }
+ }
+ tbody {
+ node.children.forEach {
+ tr {
+ it.children.forEach {
+ td {
+ it.build(this, pageContext)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ override fun FlowContent.buildHeader(level: Int, content: FlowContent.() -> Unit) {
+ when (level) {
+ 1 -> h1(block = content)
+ 2 -> h2(block = content)
+ 3 -> h3(block = content)
+ 4 -> h4(block = content)
+ 5 -> h5(block = content)
+ else -> h6(block = content)
+ }
+ }
+
+ override fun FlowContent.buildNavigation(page: PageNode) =
+ locationProvider.ancestors(page).asReversed().forEach { node ->
+ text("/")
+ if (node.isNavigable) buildLink(node, page)
+ else text(node.name)
+ }
+
+ private fun FlowContent.buildLink(to: PageNode, from: PageNode) =
+ buildLink(locationProvider.resolve(to, from)) {
+ text(to.name)
+ }
+
+ fun FlowContent.buildLink(
+ to: DRI,
+ platforms: List<PlatformData>,
+ from: PageNode? = null,
+ block: FlowContent.() -> Unit
+ ) = buildLink(locationProvider.resolve(to, platforms, from), block)
+
+ override fun buildError(node: ContentNode) {
+ context.logger.error("Unknown ContentNode type: $node")
+ }
+
+ override fun FlowContent.buildNewLine() {
+ br()
+ }
+
+ override fun FlowContent.buildLink(address: String, content: FlowContent.() -> Unit) =
+ a(href = address, block = content)
+
+ override fun FlowContent.buildCode(code: List<ContentNode>, language: String, pageContext: ContentPage) {
+ buildNewLine()
+ code.forEach {
+ +((it as? ContentText)?.text ?: run { context.logger.error("Cannot cast $it as ContentText!"); "" })
+ buildNewLine()
+ }
+ }
+
+ override fun renderPage(page: PageNode) {
+ super.renderPage(page)
+ if (page is ContentPage) {
+ pageList.add(
+ """{ "name": "${page.name}", ${if (page is ClasslikePageNode) "\"class\": \"${page.name}\"," else ""} "location": "${locationProvider.resolve(
+ page
+ )}" }"""
+ )
+ }
+ }
+
+ override fun FlowContent.buildText(textNode: ContentText) {
+ text(textNode.text)
+ }
+
+ override fun render(root: RootPageNode) {
+ super.render(root)
+ outputWriter.write("scripts/pages", "var pages = [\n${pageList.joinToString(",\n")}\n]", ".js")
+ }
+
+ private fun PageNode.root(path: String) = locationProvider.resolveRoot(this) + path
+
+ override fun buildPage(page: ContentPage, content: (FlowContent, ContentPage) -> Unit): String =
+ buildHtml(page, page.embeddedResources) { content(this, page) }
+
+ open fun buildHtml(page: PageNode, resources: List<String>, content: FlowContent.() -> Unit) =
+ createHTML().html {
+ head {
+ title(page.name)
+ with(resources) {
+ filter { it.substringBefore('?').substringAfterLast('.') == "css" }
+ .forEach { link(rel = LinkRel.stylesheet, href = page.root(it)) }
+ filter { it.substringBefore('?').substringAfterLast('.') == "js" }
+ .forEach { script(type = ScriptType.textJavaScript, src = page.root(it)) { async = true } }
+ }
+ script { unsafe { +"""var pathToRoot = "${locationProvider.resolveRoot(page)}";""" } }
+ }
+ body {
+ div {
+ id = "navigation"
+ div {
+ id = "searchBar"
+ form(action = page.root("-search.html"), method = FormMethod.get) {
+ id = "searchForm"
+ input(type = InputType.search, name = "query")
+ input(type = InputType.submit) { value = "Search" }
+ }
+ }
+ div {
+ id = "sideMenu"
+ }
+ }
+ div {
+ id = "content"
+ content()
+ }
+ }
+ }
+}
+
+fun List<HTMLMetadata>.joinAttr() = joinToString(" ") { it.key + "=" + it.value }
+
+private fun PageNode.pageKind() = when (this) {
+ is PackagePageNode -> "package"
+ is ClasslikePageNode -> "class"
+ is MemberPageNode -> when (this.documentable) {
+ is Function -> "function"
+ else -> "other"
+ }
+ else -> "other"
+}
+
+private val PageNode.isNavigable: Boolean
+ get() = this !is RendererSpecificPage || strategy != RenderingStrategy.DoNothing \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
new file mode 100644
index 00000000..4a2fb40d
--- /dev/null
+++ b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
@@ -0,0 +1,50 @@
+package org.jetbrains.dokka.renderers.html
+
+import kotlinx.html.*
+import kotlinx.html.stream.createHTML
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.pages.PageNode
+import org.jetbrains.dokka.pages.PlatformData
+import org.jetbrains.dokka.pages.RendererSpecificPage
+import org.jetbrains.dokka.pages.RenderingStrategy
+
+class NavigationPage(val root: NavigationNode) : RendererSpecificPage {
+ override val name = "navigation"
+
+ override val children = emptyList<PageNode>()
+
+ override fun modified(name: String, children: List<PageNode>) = this
+
+ override val strategy = RenderingStrategy<HtmlRenderer> {
+ createHTML().visit(root, "nav-submenu", this)
+ }
+
+ private fun <R> TagConsumer<R>.visit(node: NavigationNode, navId: String, renderer: HtmlRenderer): R =
+ with(renderer) {
+ div("sideMenuPart") {
+ id = navId
+ div("overview") {
+ buildLink(node.dri, node.platforms) { +node.name }
+ if (node.children.isNotEmpty()) {
+ span("navButton") {
+ onClick = """document.getElementById("$navId").classList.toggle("hidden");"""
+ span("navButtonContent")
+ }
+ }
+ }
+ node.children.withIndex().forEach { (n, p) -> visit(p, "$navId-$n", renderer) }
+ }
+ }
+}
+
+class NavigationNode(
+ val name: String,
+ val dri: DRI,
+ val platforms: List<PlatformData>,
+ val children: List<NavigationNode>
+)
+
+fun NavigationPage.transform(block: (NavigationNode) -> NavigationNode) = NavigationPage(root.transform(block))
+
+fun NavigationNode.transform(block: (NavigationNode) -> NavigationNode) =
+ run(block).let { NavigationNode(it.name, it.dri, it.platforms, it.children.map(block)) }
diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
new file mode 100644
index 00000000..09164d97
--- /dev/null
+++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
@@ -0,0 +1,68 @@
+package org.jetbrains.dokka.renderers.html
+
+import kotlinx.html.h1
+import kotlinx.html.id
+import kotlinx.html.table
+import kotlinx.html.tbody
+import org.jetbrains.dokka.base.renderers.platforms
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.transformers.pages.PageNodeTransformer
+
+object RootCreator : PageNodeTransformer {
+ override fun invoke(input: RootPageNode) =
+ RendererSpecificRootPage("", listOf(input), RenderingStrategy.DoNothing)
+}
+
+object SearchPageInstaller : PageNodeTransformer {
+ override fun invoke(input: RootPageNode) = input.modified(children = input.children + searchPage)
+
+ private val searchPage = RendererSpecificResourcePage(
+ name = "Search",
+ children = emptyList(),
+ strategy = RenderingStrategy<HtmlRenderer> {
+ buildHtml(it, listOf("styles/style.css", "scripts/pages.js")) {
+ h1 {
+ id = "searchTitle"
+ text("Search results for ")
+ }
+ table {
+ tbody {
+ id = "searchTable"
+ }
+ }
+ }
+ })
+}
+
+object NavigationPageInstaller : PageNodeTransformer {
+ override fun invoke(input: RootPageNode) = input.modified(
+ children = input.children + NavigationPage(
+ input.children.filterIsInstance<ContentPage>().single().let(::visit)
+ )
+ )
+
+ private fun visit(page: ContentPage): NavigationNode = NavigationNode(
+ page.name,
+ page.dri.first(),
+ page.platforms(),
+ page.children.filterIsInstance<ContentPage>().map { visit(it) })
+}
+
+object ResourceInstaller : PageNodeTransformer {
+ override fun invoke(input: RootPageNode) = input.modified(children = input.children + resourcePages)
+
+ private val resourcePages = listOf("styles", "scripts", "images").map {
+ RendererSpecificResourcePage(it, emptyList(), RenderingStrategy.Copy("/dokka/$it"))
+ }
+}
+
+object StyleAndScriptsAppender : PageNodeTransformer {
+ override fun invoke(input: RootPageNode) = input.transformContentPagesTree {
+ it.modified(
+ embeddedResources = it.embeddedResources + listOf(
+ "styles/style.css",
+ "scripts/navigationLoader.js"
+ )
+ )
+ }
+}
diff --git a/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt b/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt
new file mode 100644
index 00000000..b913ae88
--- /dev/null
+++ b/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt
@@ -0,0 +1,145 @@
+package org.jetbrains.dokka.base.transformers.psi
+
+import com.intellij.psi.*
+import org.jetbrains.dokka.JavadocParser
+import org.jetbrains.dokka.links.Callable
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.JavaClassReference
+import org.jetbrains.dokka.links.withClass
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.Function
+import org.jetbrains.dokka.pages.PlatformData
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.transformers.psi.PsiToDocumentationTranslator
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.kotlin.descriptors.Visibilities
+
+object DefaultPsiToDocumentationTranslator : PsiToDocumentationTranslator {
+
+ override fun invoke(
+ moduleName: String,
+ psiFiles: List<PsiJavaFile>,
+ platformData: PlatformData,
+ context: DokkaContext
+ ): Module {
+ val docParser =
+ DokkaPsiParser(
+ platformData,
+ context.logger
+ )
+ return Module(moduleName,
+ psiFiles.map { psiFile ->
+ val dri = DRI(packageName = psiFile.packageName)
+ Package(
+ dri,
+ emptyList(),
+ emptyList(),
+ psiFile.classes.map { docParser.parseClass(it, dri) }
+ )
+ }
+ )
+ }
+
+ class DokkaPsiParser(
+ private val platformData: PlatformData,
+ logger: DokkaLogger
+ ) {
+
+ private val javadocParser: JavadocParser = JavadocParser(logger)
+
+ private fun getComment(psi: PsiNamedElement): List<PlatformInfo> {
+ val comment = javadocParser.parseDocumentation(psi)
+ return listOf(BasePlatformInfo(comment, listOf(platformData)))
+ }
+
+ private fun PsiModifierListOwner.getVisibility() = modifierList?.children?.toList()?.let { ml ->
+ when {
+ ml.any { it.text == PsiKeyword.PUBLIC } -> Visibilities.PUBLIC
+ ml.any { it.text == PsiKeyword.PROTECTED } -> Visibilities.PROTECTED
+ else -> Visibilities.PRIVATE
+ }
+ } ?: Visibilities.PRIVATE
+
+ fun parseClass(psi: PsiClass, parent: DRI): Class = with(psi) {
+ val kind = when {
+ isAnnotationType -> JavaClassKindTypes.ANNOTATION_CLASS
+ isInterface -> JavaClassKindTypes.INTERFACE
+ isEnum -> JavaClassKindTypes.ENUM_CLASS
+ else -> JavaClassKindTypes.CLASS
+ }
+ val dri = parent.withClass(name.toString())
+ /*superTypes.filter { !ignoreSupertype(it) }.forEach {
+ node.appendType(it, NodeKind.Supertype)
+ val superClass = it.resolve()
+ if (superClass != null) {
+ link(superClass, node, RefKind.Inheritor)
+ }
+ }*/
+ val inherited = emptyList<DRI>() //listOf(psi.superClass) + psi.interfaces // TODO DRIs of inherited
+ val actual = getComment(psi).map { ClassPlatformInfo(it, inherited) }
+
+ return Class(
+ dri = dri,
+ name = name.orEmpty(),
+ kind = kind,
+ constructors = constructors.map { parseFunction(it, dri, true) },
+ functions = methods.mapNotNull { if (!it.isConstructor) parseFunction(it, dri) else null },
+ properties = fields.mapNotNull { parseField(it, dri) },
+ classlikes = innerClasses.map { parseClass(it, dri) },
+ expected = null,
+ actual = actual,
+ extra = mutableSetOf(),
+ visibility = mapOf(platformData to psi.getVisibility())
+ )
+ }
+
+ private fun parseFunction(psi: PsiMethod, parent: DRI, isConstructor: Boolean = false): Function {
+ val dri = parent.copy(callable = Callable(
+ psi.name,
+ JavaClassReference(psi.containingClass?.name.orEmpty()),
+ psi.parameterList.parameters.map { parameter ->
+ JavaClassReference(parameter.type.canonicalText)
+ }
+ )
+ )
+ return Function(
+ dri,
+ if (isConstructor) "<init>" else psi.name,
+ psi.returnType?.let { JavaTypeWrapper(type = it) },
+ isConstructor,
+ null,
+ psi.parameterList.parameters.mapIndexed { index, psiParameter ->
+ Parameter(
+ dri.copy(target = index + 1),
+ psiParameter.name,
+ JavaTypeWrapper(psiParameter.type),
+ null,
+ getComment(psi)
+ )
+ },
+ null,
+ getComment(psi),
+ visibility = mapOf(platformData to psi.getVisibility())
+ )
+ }
+
+ private fun parseField(psi: PsiField, parent: DRI): Property {
+ val dri = parent.copy(
+ callable = Callable(
+ psi.name!!, // TODO: Investigate if this is indeed nullable
+ JavaClassReference(psi.containingClass?.name.orEmpty()),
+ emptyList()
+ )
+ )
+ return Property(
+ dri,
+ psi.name!!, // TODO: Investigate if this is indeed nullable
+ null,
+ null,
+ getComment(psi),
+ accessors = emptyList(),
+ visibility = mapOf(platformData to psi.getVisibility())
+ )
+ }
+ }
+}