aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/main/kotlin/CoreExtensions.kt2
-rw-r--r--core/src/main/kotlin/DokkaGenerator.kt30
-rw-r--r--core/src/main/kotlin/Java/JavadocParser.kt373
-rw-r--r--core/src/main/kotlin/links/DRI.kt5
-rw-r--r--core/src/main/kotlin/model/Documentable.kt2
-rw-r--r--core/src/main/kotlin/pages/PageContentBuilder.kt2
-rw-r--r--core/src/main/kotlin/plugability/DefaultExtensions.kt2
-rw-r--r--core/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt166
-rw-r--r--core/src/main/kotlin/transformers/psi/PsiToDocumentationTranslator.kt14
9 files changed, 593 insertions, 3 deletions
diff --git a/core/src/main/kotlin/CoreExtensions.kt b/core/src/main/kotlin/CoreExtensions.kt
index c1871118..46b241a7 100644
--- a/core/src/main/kotlin/CoreExtensions.kt
+++ b/core/src/main/kotlin/CoreExtensions.kt
@@ -10,6 +10,7 @@ import org.jetbrains.dokka.transformers.documentation.DocumentationNodeMerger
import org.jetbrains.dokka.transformers.documentation.DocumentationNodeTransformer
import org.jetbrains.dokka.transformers.documentation.DocumentationToPageTranslator
import org.jetbrains.dokka.transformers.pages.PageNodeTransformer
+import org.jetbrains.dokka.transformers.psi.PsiToDocumentationTranslator
import kotlin.reflect.KProperty
@@ -19,6 +20,7 @@ import kotlin.reflect.KProperty
*/
object CoreExtensions {
val descriptorToDocumentationTranslator by coreExtension<DescriptorToDocumentationTranslator>()
+ val psiToDocumentationTranslator by coreExtension<PsiToDocumentationTranslator>()
val documentationMerger by coreExtension<DocumentationNodeMerger>()
val documentationTransformer by coreExtension<DocumentationNodeTransformer>()
val commentsToContentConverter by coreExtension<CommentsToContentConverter>()
diff --git a/core/src/main/kotlin/DokkaGenerator.kt b/core/src/main/kotlin/DokkaGenerator.kt
index 036cbfda..350ee5df 100644
--- a/core/src/main/kotlin/DokkaGenerator.kt
+++ b/core/src/main/kotlin/DokkaGenerator.kt
@@ -1,5 +1,8 @@
package org.jetbrains.dokka
+import com.intellij.openapi.vfs.VirtualFileManager
+import com.intellij.psi.PsiJavaFile
+import com.intellij.psi.PsiManager
import org.jetbrains.dokka.analysis.AnalysisEnvironment
import org.jetbrains.dokka.analysis.DokkaResolutionFacade
import org.jetbrains.dokka.model.Module
@@ -8,11 +11,13 @@ import org.jetbrains.dokka.pages.PlatformData
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.single
import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
@@ -64,7 +69,8 @@ class DokkaGenerator(
fun createDocumentationModels(
platforms: Map<PlatformData, EnvironmentAndFacade>,
context: DokkaContext
- ) = platforms.map { (pdata, _) -> translateDescriptors(pdata, context) }
+ ) = platforms.map { (pdata, _) -> translateDescriptors(pdata, context) } +
+ platforms.map { (pdata, _) -> translatePsi(pdata, context) }
fun mergeDocumentationModels(
modulesFromPlatforms: List<Module>,
@@ -123,6 +129,28 @@ class DokkaGenerator(
.invoke(platformData.name, packageFragments, platformData, context)
}
+ private fun translatePsi(platformData: PlatformData, context: DokkaContext): Module {
+ val (environment, _) = context.platforms.getValue(platformData)
+
+ val sourceRoots = environment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS)
+ ?.filterIsInstance<JavaSourceRoot>()
+ ?.map { it.file }
+ ?: listOf()
+ val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file")
+
+ val psiFiles = sourceRoots.map { sourceRoot ->
+ sourceRoot.absoluteFile.walkTopDown().mapNotNull {
+ localFileSystem.findFileByPath(it.path)?.let { vFile ->
+ PsiManager.getInstance(environment.project).findFile(vFile) as? PsiJavaFile
+ }
+ }.toList()
+ }.flatten()
+
+ return context.single(CoreExtensions.psiToDocumentationTranslator)
+ .invoke(psiFiles, platformData, context)
+
+ }
+
private class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector {
override fun clear() {
seenErrors = false
diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt
new file mode 100644
index 00000000..5058634a
--- /dev/null
+++ b/core/src/main/kotlin/Java/JavadocParser.kt
@@ -0,0 +1,373 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.*
+import com.intellij.psi.impl.source.tree.JavaDocElementType
+import com.intellij.psi.javadoc.*
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.util.containers.isNullOrEmpty
+import kotlinx.html.P
+import org.jetbrains.dokka.model.doc.*
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.kotlin.utils.keysToMap
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+import org.jsoup.nodes.Node
+import org.jsoup.nodes.TextNode
+import java.net.URI
+
+interface JavaDocumentationParser {
+ fun parseDocumentation(element: PsiNamedElement): DocumentationNode
+}
+
+class JavadocParser(
+ private val logger: DokkaLogger
+) : JavaDocumentationParser {
+
+ override fun parseDocumentation(element: PsiNamedElement): DocumentationNode {
+ val docComment = (element as? PsiDocCommentOwner)?.docComment ?: return DocumentationNode(emptyList())
+ val nodes =
+ convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element)
+ return DocumentationNode(nodes.map { Description(it) })
+ /*val firstParagraphContents = nodes.takeWhile { it !is ContentParagraph }
+ val firstParagraph = ContentParagraph()
+ if (firstParagraphContents.isNotEmpty()) {
+ firstParagraphContents.forEach { firstParagraph.append(it) }
+ result.add(firstParagraph)
+ }
+
+ result.appendAll(nodes.drop(firstParagraphContents.size))
+
+ if (element is PsiMethod) {
+ val tagsByName = element.searchInheritedTags()
+ for ((tagName, tags) in tagsByName) {
+ for ((tag, context) in tags) {
+ val section = result.addSection(javadocSectionDisplayName(tagName), tag.getSubjectName())
+ val signature = signatureProvider.signature(element)
+ when (tagName) {
+ "param" -> {
+ section.appendTypeElement(signature) {
+ it.details
+ .find { node -> node.kind == NodeKind.Parameter && node.name == tag.getSubjectName() }
+ ?.detailOrNull(NodeKind.Type)
+ }
+ }
+ "return" -> {
+ section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) }
+ }
+ }
+ section.appendAll(convertJavadocElements(tag.contentElements(), context))
+ }
+ }
+ }
+
+ docComment.tags.forEach { tag ->
+ when (tag.name) {
+ "see" -> result.convertSeeTag(tag)
+ "deprecated" -> {
+ deprecatedContent = Content().apply {
+ appendAll(convertJavadocElements(tag.contentElements(), element))
+ }
+ }
+ in tagsToInherit -> {}
+ else -> {
+ val subjectName = tag.getSubjectName()
+ val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName)
+
+ section.appendAll(convertJavadocElements(tag.contentElements(), element))
+ }
+ }
+ }
+ return JavadocParseResult(result, deprecatedContent)*/
+ }
+
+ private val tagsToInherit = setOf("param", "return", "throws")
+
+ private data class TagWithContext(val tag: PsiDocTag, val context: PsiNamedElement)
+/*
+ private fun PsiMethod.searchInheritedTags(): Map<String, Collection<TagWithContext>> {
+
+ val output = tagsToInherit.keysToMap { mutableMapOf<String?, TagWithContext>() }
+
+ fun recursiveSearch(methods: Array<PsiMethod>) {
+ for (method in methods) {
+ recursiveSearch(method.findSuperMethods())
+ }
+ for (method in methods) {
+ for (tag in method.docComment?.tags.orEmpty()) {
+ if (tag.name in tagsToInherit) {
+ output[tag.name]!![tag.getSubjectName()] = TagWithContext(tag, method)
+ }
+ }
+ }
+ }
+
+ recursiveSearch(arrayOf(this))
+ return output.mapValues { it.value.values }
+ }
+*/
+
+ private fun PsiDocTag.contentElements(): Iterable<PsiElement> {
+ val tagValueElements = children
+ .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME }
+ .dropWhile { it is PsiWhiteSpace }
+ .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS }
+ return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements
+ }
+
+ private fun convertJavadocElements(elements: Iterable<PsiElement>, element: PsiNamedElement): List<DocTag> {
+ val doc = Jsoup.parse(expandAllForElements(elements, element))
+ return doc.body().childNodes().mapNotNull { convertHtmlNode(it) }
+ }
+
+ private fun expandAllForElements(elements: Iterable<PsiElement>, element: PsiNamedElement): String {
+ val htmlBuilder = StringBuilder()
+ elements.forEach {
+ if (it is PsiInlineDocTag) {
+ htmlBuilder.append(convertInlineDocTag(it, element))
+ } else {
+ htmlBuilder.append(it.text)
+ }
+ }
+ return htmlBuilder.toString().trim()
+ }
+
+ private fun convertHtmlNode(node: Node, insidePre: Boolean = false): DocTag? = when (node) {
+ is TextNode -> Text(body = if (insidePre) node.wholeText else node.text())
+ is Element -> createBlock(node)
+ else -> null
+ }
+
+ private fun createBlock(element: Element): DocTag {
+ val children = element.childNodes().mapNotNull { convertHtmlNode(it) }
+ return when (element.tagName()) {
+ "p" -> P(children)
+ "b" -> B(children)
+ "strong" -> Strong(children)
+ "i" -> I(children)
+ "em" -> Em(children)
+// "s", "del" -> ContentStrikethrough()
+ "code" -> Code(children)
+ "pre" -> Pre(children)
+ "ul" -> Ul(children)
+ "ol" -> Ol(children)
+ "li" -> Li(children)
+// "a" -> createLink(element)
+// "br" -> ContentBlock().apply { hardLineBreak() }
+ else -> Text(body = element.ownText())
+ }
+ }
+/*
+
+ private fun createLink(element: Element): DocTag {
+ return when {
+ element.hasAttr("docref") -> {
+ val docref = element.attr("docref")
+ ContentNodeLazyLink(docref) { refGraph.lookupOrWarn(docref, logger) }
+ }
+ element.hasAttr("href") -> {
+ val href = element.attr("href")
+
+ val uri = try {
+ URI(href)
+ } catch (_: Exception) {
+ null
+ }
+
+ if (uri?.isAbsolute == false) {
+ ContentLocalLink(href)
+ } else {
+ ContentExternalLink(href)
+ }
+ }
+ element.hasAttr("name") -> {
+ ContentBookmark(element.attr("name"))
+ }
+ else -> ContentBlock()
+ }
+ }
+
+
+ private fun convertSeeTag(tag: PsiDocTag) {
+ val linkElement = tag.linkElement() ?: return
+ val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null)
+
+ val valueElement = tag.referenceElement()
+ val externalLink = resolveExternalLink(valueElement)
+ val text = ContentText(linkElement.text)
+
+ val linkSignature by lazy { resolveInternalLink(valueElement) }
+ val node = when {
+ externalLink != null -> {
+ val linkNode = ContentExternalLink(externalLink)
+ linkNode.append(text)
+ linkNode
+ }
+ linkSignature != null -> {
+ val linkNode =
+ ContentNodeLazyLink(
+ (tag.valueElement ?: linkElement).text
+ ) { refGraph.lookupOrWarn(linkSignature!!, logger) }
+ linkNode.append(text)
+ linkNode
+ }
+ else -> text
+ }
+ seeSection.append(node)
+ }
+*/
+ private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) {
+ "link", "linkplain" -> {
+ val valueElement = tag.referenceElement()
+ val externalLink = resolveExternalLink(valueElement)
+ val linkSignature by lazy { resolveInternalLink(valueElement) }
+ if (externalLink != null || linkSignature != null) {
+ val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text
+ val linkTarget = if (externalLink != null) "href=\"$externalLink\"" else "docref=\"$linkSignature\""
+ val link = "<a $linkTarget>$labelText</a>"
+ if (tag.name == "link") "<code>$link</code>" else link
+ } else if (valueElement != null) {
+ valueElement.text
+ } else {
+ ""
+ }
+ }
+ "code", "literal" -> {
+ val text = StringBuilder()
+ tag.dataElements.forEach { text.append(it.text) }
+ val escaped = text.toString().trimStart()
+ if (tag.name == "code") "<code>$escaped</code>" else escaped
+ }
+ "inheritDoc" -> {
+ val result = (element as? PsiMethod)?.let {
+ // @{inheritDoc} is only allowed on functions
+ val parent = tag.parent
+ when (parent) {
+ is PsiDocComment -> element.findSuperDocCommentOrWarn()
+ is PsiDocTag -> element.findSuperDocTagOrWarn(parent)
+ else -> null
+ }
+ }
+ result ?: tag.text
+ }
+ else -> tag.text
+ }
+
+ private fun PsiDocTag.referenceElement(): PsiElement? =
+ linkElement()?.let {
+ if (it.node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) {
+ PsiTreeUtil.findChildOfType(it, PsiJavaCodeReferenceElement::class.java)
+ } else {
+ it
+ }
+ }
+
+ private fun PsiDocTag.linkElement(): PsiElement? =
+ valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace }
+
+ private fun resolveExternalLink(valueElement: PsiElement?): String? {
+ /*val target = valueElement?.reference?.resolve()
+ if (target != null) {
+ return externalDocumentationLinkResolver.buildExternalDocumentationLink(target)
+ }*/
+ return null
+ }
+
+ private fun resolveInternalLink(valueElement: PsiElement?): String? {
+ /*val target = valueElement?.reference?.resolve()
+ if (target != null) {
+ return signatureProvider.signature(target)
+ }*/
+ return null
+ }
+
+ fun PsiDocTag.getSubjectName(): String? {
+ if (name == "param" || name == "throws" || name == "exception") {
+ return valueElement?.text
+ }
+ return null
+ }
+
+ private fun PsiMethod.findSuperDocCommentOrWarn(): String {
+ val method = findFirstSuperMethodWithDocumentation(this)
+ if (method != null) {
+ val descriptionElements = method.docComment?.descriptionElements?.dropWhile {
+ it.text.trim().isEmpty()
+ } ?: return ""
+
+ return expandAllForElements(descriptionElements, method)
+ }
+ logger.warn("No docs found on supertype with {@inheritDoc} method ${this.name} in ${this.containingFile.name}}")
+ return ""
+ }
+
+
+ private fun PsiMethod.findSuperDocTagOrWarn(elementToExpand: PsiDocTag): String {
+ val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, this)
+
+ if (result != null) {
+ val (method, tag) = result
+
+ val contentElements = tag.contentElements().dropWhile { it.text.trim().isEmpty() }
+
+ val expandedString = expandAllForElements(contentElements, method)
+
+ return expandedString
+ }
+ logger.warn("No docs found on supertype for @${elementToExpand.name} ${elementToExpand.getSubjectName()} with {@inheritDoc} method ${this.name} in ${this.containingFile.name}}")
+ return ""
+ }
+
+ private fun findFirstSuperMethodWithDocumentation(current: PsiMethod): PsiMethod? {
+ val superMethods = current.findSuperMethods()
+ for (method in superMethods) {
+ val docs = method.docComment?.descriptionElements?.dropWhile { it.text.trim().isEmpty() }
+ if (!docs.isNullOrEmpty()) {
+ return method
+ }
+ }
+ for (method in superMethods) {
+ val result = findFirstSuperMethodWithDocumentation(method)
+ if (result != null) {
+ return result
+ }
+ }
+
+ return null
+ }
+
+ private fun findFirstSuperMethodWithDocumentationforTag(elementToExpand: PsiDocTag, current: PsiMethod): Pair<PsiMethod, PsiDocTag>? {
+ val superMethods = current.findSuperMethods()
+ val mappedFilteredTags = superMethods.map {
+ it to it.docComment?.tags?.filter { it.name == elementToExpand.name }
+ }
+
+ for ((method, tags) in mappedFilteredTags) {
+ tags ?: continue
+ for (tag in tags) {
+ val (tagSubject, elementSubject) = when (tag.name) {
+ "throws" -> {
+ // match class names only for throws, ignore possibly fully qualified path
+ // TODO: Always match exactly here
+ tag.getSubjectName()?.split(".")?.last() to elementToExpand.getSubjectName()?.split(".")?.last()
+ }
+ else -> {
+ tag.getSubjectName() to elementToExpand.getSubjectName()
+ }
+ }
+
+ if (tagSubject == elementSubject) {
+ return method to tag
+ }
+ }
+ }
+
+ for (method in superMethods) {
+ val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, method)
+ if (result != null) {
+ return result
+ }
+ }
+ return null
+ }
+
+}
diff --git a/core/src/main/kotlin/links/DRI.kt b/core/src/main/kotlin/links/DRI.kt
index 0fe2900b..404813fa 100644
--- a/core/src/main/kotlin/links/DRI.kt
+++ b/core/src/main/kotlin/links/DRI.kt
@@ -110,6 +110,11 @@ sealed class TypeReference {
}
}
+data class JavaClassReference(val name: String): TypeReference() {
+ override val isNullable = true
+ override fun toString(): String = name
+}
+
data class TypeParam(val bounds: List<TypeReference>, override val isNullable: Boolean) : TypeReference()
data class TypeConstructor(
diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt
index ac0b41d9..3f50667a 100644
--- a/core/src/main/kotlin/model/Documentable.kt
+++ b/core/src/main/kotlin/model/Documentable.kt
@@ -152,7 +152,7 @@ private fun String.shorten(maxLength: Int) = lineSequence().first().let {
interface TypeWrapper {
val constructorFqName: String?
val constructorNamePathSegments: List<String>
- val arguments: List<KotlinTypeWrapper>
+ val arguments: List<TypeWrapper>
val dri: DRI?
}
interface ClassKind
diff --git a/core/src/main/kotlin/pages/PageContentBuilder.kt b/core/src/main/kotlin/pages/PageContentBuilder.kt
index cc53586e..25ddc2f1 100644
--- a/core/src/main/kotlin/pages/PageContentBuilder.kt
+++ b/core/src/main/kotlin/pages/PageContentBuilder.kt
@@ -168,7 +168,7 @@ private fun PageContentBuilder.type(t: TypeWrapper) {
link(t.constructorNamePathSegments.last(), t.dri!!)
else (this as? DefaultPageContentBuilder)?.let {
logger.error("type $t cannot be resolved")
- text("???")
+ text(t.toString())
}
list(t.arguments, prefix = "<", suffix = ">") {
type(it)
diff --git a/core/src/main/kotlin/plugability/DefaultExtensions.kt b/core/src/main/kotlin/plugability/DefaultExtensions.kt
index b3ff4248..91ab9cb8 100644
--- a/core/src/main/kotlin/plugability/DefaultExtensions.kt
+++ b/core/src/main/kotlin/plugability/DefaultExtensions.kt
@@ -8,6 +8,7 @@ import org.jetbrains.dokka.resolvers.DefaultLocationProviderFactory
import org.jetbrains.dokka.transformers.descriptors.DefaultDescriptorToDocumentationTranslator
import org.jetbrains.dokka.transformers.documentation.DefaultDocumentationNodeMerger
import org.jetbrains.dokka.transformers.documentation.DefaultDocumentationToPageTranslator
+import org.jetbrains.dokka.transformers.psi.DefaultPsiToDocumentationTranslator
internal object DefaultExtensions {
@@ -20,6 +21,7 @@ internal object DefaultExtensions {
internal fun <T : Any, E : ExtensionPoint<T>> get(point: E, fullContext: DokkaContext): List<T> =
when (point) {
CoreExtensions.descriptorToDocumentationTranslator -> DefaultDescriptorToDocumentationTranslator
+ CoreExtensions.psiToDocumentationTranslator -> DefaultPsiToDocumentationTranslator
CoreExtensions.documentationMerger -> DefaultDocumentationNodeMerger
CoreExtensions.commentsToContentConverter -> converter.get(fullContext)
CoreExtensions.documentationToPageTranslator -> DefaultDocumentationToPageTranslator
diff --git a/core/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt b/core/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt
new file mode 100644
index 00000000..d3ad4526
--- /dev/null
+++ b/core/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt
@@ -0,0 +1,166 @@
+package org.jetbrains.dokka.transformers.psi
+
+import com.intellij.psi.*
+import com.intellij.psi.impl.source.PsiClassReferenceType
+import org.jetbrains.dokka.JavadocParser
+import org.jetbrains.dokka.links.*
+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.utilities.DokkaLogger
+
+object DefaultPsiToDocumentationTranslator : PsiToDocumentationTranslator {
+
+ override fun invoke(
+ psiFiles: List<PsiJavaFile>,
+ platformData: PlatformData,
+ context: DokkaContext
+ ): Module {
+ val docParser = DokkaPsiParser(platformData, context.logger)
+ return Module( "JavaModule",
+ 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)))
+ }
+
+ 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)
+ }
+ }*/
+ return Class(
+ dri,
+ name.orEmpty(),
+ kind,
+ constructors.map { parseFunction(it, dri, true) },
+ methods.mapNotNull { if (!it.isConstructor) parseFunction(it, dri) else null },
+ fields.mapNotNull { parseField(it, dri) },
+ innerClasses.map { parseClass(it, dri) },
+ null,
+ emptyList(),
+ mutableSetOf()
+ )
+ }
+
+ 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),
+ getComment(psi)
+ )
+ },
+ null,
+ getComment(psi)
+ )
+ }
+
+ private fun parseField(psi: PsiField, parent: DRI): Property {
+ val dri = parent.copy(
+ callable = Callable(
+ psi.name,
+ JavaClassReference(psi.containingClass?.name.orEmpty()),
+ emptyList()
+ )
+ )
+ return Property(
+ dri,
+ psi.name,
+ null,
+ null,
+ getComment(psi)
+ )
+ }
+ }
+}
+
+enum class JavaClassKindTypes : ClassKind {
+ CLASS,
+ INTERFACE,
+ ENUM_CLASS,
+ ENUM_ENTRY,
+ ANNOTATION_CLASS;
+}
+
+class JavaTypeWrapper(
+ type: PsiType
+) : TypeWrapper {
+
+ override val constructorFqName: String?
+ override val constructorNamePathSegments: List<String>
+ override val arguments: List<JavaTypeWrapper>
+ override val dri: DRI?
+
+ init {
+ if (type is PsiClassReferenceType) {
+ val resolved = type.resolve()
+ constructorFqName = resolved?.qualifiedName
+ constructorNamePathSegments = resolved?.qualifiedName?.split('.') ?: emptyList()
+ arguments = type.parameters.mapNotNull {
+ if (it is PsiClassReferenceType) JavaTypeWrapper(it) else null
+ }
+ dri = fromPsi(type)
+ } else {
+ type as PsiPrimitiveType
+ constructorFqName = type.name
+ constructorNamePathSegments = type.name.split('.')
+ arguments = emptyList()
+ dri = null
+ }
+ }
+
+ private fun fromPsi(type: PsiClassReferenceType): DRI {
+ val className = type.className
+ val pkg = type.canonicalText.removeSuffix(className).removeSuffix(".")
+ return DRI(packageName = pkg, classNames = className)
+ }
+
+ override fun toString(): String {
+ return constructorFqName.orEmpty()
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/transformers/psi/PsiToDocumentationTranslator.kt b/core/src/main/kotlin/transformers/psi/PsiToDocumentationTranslator.kt
new file mode 100644
index 00000000..5a1209b1
--- /dev/null
+++ b/core/src/main/kotlin/transformers/psi/PsiToDocumentationTranslator.kt
@@ -0,0 +1,14 @@
+package org.jetbrains.dokka.transformers.psi
+
+import com.intellij.psi.PsiJavaFile
+import org.jetbrains.dokka.model.Module
+import org.jetbrains.dokka.pages.PlatformData
+import org.jetbrains.dokka.plugability.DokkaContext
+
+interface PsiToDocumentationTranslator {
+ fun invoke(
+ psiFiles: List<PsiJavaFile>,
+ platformData: PlatformData,
+ context: DokkaContext
+ ): Module
+}