aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/main/kotlin/model/Documentable.kt16
-rw-r--r--core/src/main/kotlin/model/documentableProperties.kt14
-rw-r--r--core/src/main/kotlin/model/properties/PropertyContainer.kt13
-rw-r--r--core/src/main/kotlin/model/properties/properties.kt6
-rw-r--r--core/src/main/kotlin/model/typeWrappers.kt4
-rw-r--r--plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentableTranslator.kt241
6 files changed, 229 insertions, 65 deletions
diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt
index 48f00474..73a5f9d3 100644
--- a/core/src/main/kotlin/model/Documentable.kt
+++ b/core/src/main/kotlin/model/Documentable.kt
@@ -3,6 +3,8 @@ package org.jetbrains.dokka.model
import com.intellij.psi.PsiNamedElement
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.dokka.model.properties.ExtraProperty
+import org.jetbrains.dokka.model.properties.MergeStrategy
import org.jetbrains.dokka.model.properties.PropertyContainer
import org.jetbrains.dokka.model.properties.WithExtraProperties
import org.jetbrains.dokka.pages.PlatformData
@@ -72,10 +74,10 @@ interface WithType {
}
interface WithAbstraction {
- val modifier: Modifier
+ val modifier: Modifier?
enum class Modifier {
- Abstract, Open, Final
+ Abstract, Open, Final, Static
}
}
@@ -145,7 +147,7 @@ data class Class(
override val generics: List<TypeParameter>,
override val supertypes: PlatformDependent<List<DRI>>,
override val documentation: PlatformDependent<DocumentationNode>,
- override val modifier: WithAbstraction.Modifier,
+ override val modifier: WithAbstraction.Modifier?,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<Class> = PropertyContainer.empty()
) : Classlike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithSupertypes,
@@ -206,7 +208,7 @@ data class Function(
override val type: TypeWrapper,
override val generics: List<TypeParameter>,
override val receiver: Parameter?,
- override val modifier: WithAbstraction.Modifier,
+ override val modifier: WithAbstraction.Modifier?,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<Function> = PropertyContainer.empty()
) : Documentable(), Callable, WithGenerics, WithExtraProperties<Function> {
@@ -285,8 +287,8 @@ data class Property(
override val type: TypeWrapper,
override val receiver: Parameter?,
val setter: Function?,
- val getter: Function,
- override val modifier: WithAbstraction.Modifier,
+ val getter: Function?,
+ override val modifier: WithAbstraction.Modifier?,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<Property> = PropertyContainer.empty()
) : Documentable(), Callable, WithExtraProperties<Property> {
@@ -348,4 +350,4 @@ sealed class DocumentableSource(val path: String)
class DescriptorDocumentableSource(val descriptor: DeclarationDescriptor) :
DocumentableSource(descriptor.toSourceElement.containingFile.toString())
-class PsiDocumentableSource(val psi: PsiNamedElement) : DocumentableSource(psi.containingFile.virtualFile.path) \ No newline at end of file
+class PsiDocumentableSource(val psi: PsiNamedElement) : DocumentableSource(psi.containingFile.virtualFile.path)
diff --git a/core/src/main/kotlin/model/documentableProperties.kt b/core/src/main/kotlin/model/documentableProperties.kt
new file mode 100644
index 00000000..38a06451
--- /dev/null
+++ b/core/src/main/kotlin/model/documentableProperties.kt
@@ -0,0 +1,14 @@
+package org.jetbrains.dokka.model
+
+import org.jetbrains.dokka.model.properties.ExtraProperty
+import org.jetbrains.dokka.model.properties.MergeStrategy
+
+data class InheritedFunction(val isInherited: Boolean): ExtraProperty<Function> {
+ object InheritedFunctionKey: ExtraProperty.Key<Function, Boolean> {
+ override fun mergeStrategyFor(left: Boolean, right: Boolean) = MergeStrategy.Fail {
+ throw IllegalArgumentException("Function inheritance should be consistent!")
+ }
+ }
+ override val key: ExtraProperty.Key<Function, Boolean> =
+ InheritedFunctionKey
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/model/properties/PropertyContainer.kt b/core/src/main/kotlin/model/properties/PropertyContainer.kt
index 50216278..d30c6844 100644
--- a/core/src/main/kotlin/model/properties/PropertyContainer.kt
+++ b/core/src/main/kotlin/model/properties/PropertyContainer.kt
@@ -1,16 +1,15 @@
package org.jetbrains.dokka.model.properties
-import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
class PropertyContainer<C : Any> internal constructor(
- @PublishedApi internal val map: Map<Property.Key<C, *>, Property<C>>
+ @PublishedApi internal val map: Map<ExtraProperty.Key<C, *>, ExtraProperty<C>>
) {
- operator fun <D : C> plus(prop: Property<D>): PropertyContainer<D> =
+ operator fun <D : C> plus(prop: ExtraProperty<D>): PropertyContainer<D> =
PropertyContainer(map + (prop.key to prop))
// TODO: Add logic for caching calculated properties
- inline operator fun <reified T : Any> get(key: Property.Key<C, T>): T? = when (val prop = map[key]) {
+ inline operator fun <reified T : Any> get(key: ExtraProperty.Key<C, T>): T? = when (val prop = map[key]) {
is T? -> prop
else -> throw ClassCastException("Property for $key stored under not matching key type.")
}
@@ -27,7 +26,7 @@ interface WithExtraProperties<C : Any> {
}
fun <C> C.mergeExtras(left: C, right: C): C where C : Any, C : WithExtraProperties<C> {
- val aggregatedExtras: List<List<Property<C>>> =
+ val aggregatedExtras: List<List<ExtraProperty<C>>> =
(left.extra.map.values + right.extra.map.values)
.groupBy { it.key }
.values
@@ -37,12 +36,12 @@ fun <C> C.mergeExtras(left: C, right: C): C where C : Any, C : WithExtraProperti
@Suppress("UNCHECKED_CAST")
val strategies: List<MergeStrategy<C>> = toMerge.map { (l, r) ->
- (l.key as Property.Key<C, Property<C>>).mergeStrategyFor(l, r)
+ (l.key as ExtraProperty.Key<C, ExtraProperty<C>>).mergeStrategyFor(l, r)
}
strategies.firstIsInstanceOrNull<MergeStrategy.Fail>()?.error?.invoke()
- val replaces: List<Property<C>> = strategies.filterIsInstance<MergeStrategy.Replace<C>>().map { it.newProperty }
+ val replaces: List<ExtraProperty<C>> = strategies.filterIsInstance<MergeStrategy.Replace<C>>().map { it.newProperty }
val needingFullMerge: List<(preMerged: C, left: C, right: C) -> C> =
strategies.filterIsInstance<MergeStrategy.Full<C>>().map { it.merger }
diff --git a/core/src/main/kotlin/model/properties/properties.kt b/core/src/main/kotlin/model/properties/properties.kt
index 4ca44926..83f8d63d 100644
--- a/core/src/main/kotlin/model/properties/properties.kt
+++ b/core/src/main/kotlin/model/properties/properties.kt
@@ -1,6 +1,6 @@
package org.jetbrains.dokka.model.properties
-interface Property<in C : Any> {
+interface ExtraProperty<in C : Any> {
interface Key<in C: Any, T: Any> {
fun mergeStrategyFor(left: T, right: T): MergeStrategy<C> = MergeStrategy.Fail {
throw NotImplementedError("Property merging for $this is not implemented")
@@ -9,12 +9,12 @@ interface Property<in C : Any> {
val key: Key<C, *>
}
-interface CalculatedProperty<in C: Any, T: Any>: Property.Key<C, T> {
+interface CalculatedProperty<in C: Any, T: Any>: ExtraProperty.Key<C, T> {
fun calculate(subject: C): T
}
sealed class MergeStrategy<in C> {
- class Replace<in C : Any>(val newProperty: Property<C>): MergeStrategy<C>()
+ class Replace<in C : Any>(val newProperty: ExtraProperty<C>): MergeStrategy<C>()
object Remove: MergeStrategy<Any>()
class Full<C: Any>(val merger: (preMerged: C, left: C, right: C) -> C): MergeStrategy<C>()
class Fail(val error: () -> Nothing): MergeStrategy<Any>()
diff --git a/core/src/main/kotlin/model/typeWrappers.kt b/core/src/main/kotlin/model/typeWrappers.kt
index 5719c6d3..b26a3f6d 100644
--- a/core/src/main/kotlin/model/typeWrappers.kt
+++ b/core/src/main/kotlin/model/typeWrappers.kt
@@ -94,4 +94,8 @@ class JavaTypeWrapper : TypeWrapper {
override fun toString(): String {
return constructorFqName.orEmpty()
}
+
+ companion object {
+ val VOID = JavaTypeWrapper(listOf("void"), listOf(), null, true)
+ }
}
diff --git a/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentableTranslator.kt
index 364cb34b..da06e621 100644
--- a/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentableTranslator.kt
@@ -1,5 +1,7 @@
package org.jetbrains.dokka.base.transformers.psi
+import com.intellij.lang.jvm.JvmModifier
+import com.intellij.lang.jvm.types.JvmReferenceType
import com.intellij.psi.*
import org.jetbrains.dokka.JavadocParser
import org.jetbrains.dokka.links.Callable
@@ -7,7 +9,11 @@ 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.Annotation
+import org.jetbrains.dokka.model.Enum
import org.jetbrains.dokka.model.Function
+import org.jetbrains.dokka.model.InheritedFunction
+import org.jetbrains.dokka.model.properties.PropertyContainer
import org.jetbrains.dokka.pages.PlatformData
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.transformers.psi.PsiToDocumentableTranslator
@@ -27,16 +33,22 @@ object DefaultPsiToDocumentableTranslator : PsiToDocumentableTranslator {
platformData,
context.logger
)
- return Module(moduleName,
+ return Module(
+ moduleName,
psiFiles.map { psiFile ->
val dri = DRI(packageName = psiFile.packageName)
Package(
dri,
emptyList(),
emptyList(),
- psiFile.classes.map { docParser.parseClass(it, dri) }
+ psiFile.classes.map { docParser.parseClasslike(it, dri) },
+ emptyList(),
+ PlatformDependent.empty(),
+ listOf(platformData)
)
- }
+ },
+ PlatformDependent.empty(),
+ listOf(platformData)
)
}
@@ -47,11 +59,6 @@ object DefaultPsiToDocumentableTranslator : PsiToDocumentableTranslator {
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
@@ -60,69 +67,203 @@ object DefaultPsiToDocumentableTranslator : PsiToDocumentableTranslator {
}
} ?: 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
+ private val PsiMethod.hash: Int
+ get() = "$returnType $name$parameterList".hashCode()
+
+ private val PsiClassType.shouldBeIgnored: Boolean
+ get() = isClass("java.lang.Enum") || isClass("java.lang.Object")
+
+ private fun PsiClassType.isClass(qName: String): Boolean {
+ val shortName = qName.substringAfterLast('.')
+ if (className == shortName) {
+ val psiClass = resolve()
+ return psiClass?.qualifiedName == qName
}
+ return false
+ }
+
+ private fun <T> T.toPlatformDependant() =
+ PlatformDependent(mapOf(platformData to this))
+
+ fun parseClasslike(psi: PsiClass, parent: DRI): Classlike = with(psi) {
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 ancestorsSet = hashSetOf<DRI>()
+ val superMethodsKeys = hashSetOf<Int>()
+ val superMethods = mutableListOf<PsiMethod>()
+ methods.forEach { superMethodsKeys.add(it.hash) }
+ fun addAncestors(element: PsiClass) {
+ ancestorsSet.add(element.toDRI())
+ element.interfaces.forEach(::addAncestors)
+ element.superClass?.let(::addAncestors)
+ }
+
+ fun parseSupertypes(superTypes: Array<PsiClassType>) {
+ superTypes.forEach { type ->
+ (type as? PsiClassType)?.takeUnless { type.shouldBeIgnored }?.resolve()?.let {
+ it.methods.forEach { method ->
+ val hash = method.hash
+ if (!method.isConstructor && !superMethodsKeys.contains(hash) &&
+ method.getVisibility() != Visibilities.PRIVATE
+ ) {
+ superMethodsKeys.add(hash)
+ superMethods.add(method)
+ }
+ }
+ addAncestors(it)
+ parseSupertypes(it.superTypes)
+ }
+ }
+ }
+ parseSupertypes(superTypes)
+ val documentation = javadocParser.parseDocumentation(this).toPlatformDependant()
+ val allFunctions = methods.mapNotNull { if (!it.isConstructor) parseFunction(it, dri) else null } +
+ superMethods.map { parseFunction(it, dri, isInherited = true) }
+ val source = PsiDocumentableSource(this).toPlatformDependant()
+ val classlikes = innerClasses.map { parseClasslike(it, dri) }
+ val visibility = getVisibility().toPlatformDependant()
+ val ancestors = ancestorsSet.toList().toPlatformDependant()
+ return when {
+ isAnnotationType ->
+ Annotation(
+ name.orEmpty(),
+ dri,
+ documentation,
+ source,
+ allFunctions,
+ fields.mapNotNull { parseField(it, dri) },
+ classlikes,
+ visibility,
+ null,
+ constructors.map { parseFunction(it, dri, true) },
+ listOf(platformData)
+ )
+ isEnum -> Enum(
+ dri,
+ name.orEmpty(),
+ fields.filterIsInstance<PsiEnumConstant>().map { entry ->
+ EnumEntry(
+ dri.withClass("$name.${entry.name}"),
+ entry.name.orEmpty(),
+ javadocParser.parseDocumentation(entry).toPlatformDependant(),
+ emptyList(),
+ emptyList(),
+ emptyList(),
+ listOf(platformData)
+ )
+ },
+ documentation,
+ source,
+ allFunctions,
+ fields.filter { it !is PsiEnumConstant }.map { parseField(it, dri) },
+ classlikes,
+ visibility,
+ null,
+ constructors.map { parseFunction(it, dri, true) },
+ ancestors,
+ listOf(platformData)
+ )
+ isInterface -> Interface(
+ dri,
+ name.orEmpty(),
+ documentation,
+ source,
+ allFunctions,
+ fields.mapNotNull { parseField(it, dri) },
+ classlikes,
+ visibility,
+ null,
+ mapTypeParameters(dri),
+ ancestors,
+ listOf(platformData)
+ )
+ else -> Class(
+ dri,
+ name.orEmpty(),
+ constructors.map { parseFunction(it, dri, true) },
+ allFunctions,
+ fields.mapNotNull { parseField(it, dri) },
+ classlikes,
+ source,
+ visibility,
+ null,
+ mapTypeParameters(dri),
+ ancestors,
+ documentation,
+ getModifier(),
+ listOf(platformData)
+ )
}
- }*/
- 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 {
+ private fun parseFunction(
+ psi: PsiMethod,
+ parent: DRI,
+ isConstructor: Boolean = false,
+ isInherited: 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,
+ javadocParser.parseDocumentation(psiParameter).toPlatformDependant(),
JavaTypeWrapper(psiParameter.type),
- null,
- getComment(psi)
+ listOf(platformData)
)
},
+ javadocParser.parseDocumentation(psi).toPlatformDependant(),
+ PsiDocumentableSource(psi).toPlatformDependant(),
+ psi.getVisibility().toPlatformDependant(),
+ psi.returnType?.let { JavaTypeWrapper(type = it) } ?: JavaTypeWrapper.VOID,
+ psi.mapTypeParameters(dri),
null,
- getComment(psi),
- visibility = mapOf(platformData to psi.getVisibility())
+ psi.getModifier(),
+ listOf(platformData),
+ PropertyContainer.empty<Function>() + InheritedFunction(
+ isInherited
+ )
+
)
}
+ private fun PsiModifierListOwner.getModifier() = when {
+ hasModifier(JvmModifier.ABSTRACT) -> WithAbstraction.Modifier.Abstract
+ hasModifier(JvmModifier.STATIC) -> WithAbstraction.Modifier.Static
+ hasModifier(JvmModifier.FINAL) -> WithAbstraction.Modifier.Final
+ else -> null
+ }
+
+ private fun PsiTypeParameterListOwner.mapTypeParameters(dri: DRI): List<TypeParameter> {
+ fun mapProjections(bounds: Array<JvmReferenceType>) =
+ if (bounds.isEmpty()) listOf(Projection.Star) else bounds.mapNotNull {
+ (it as PsiClassType).let { classType ->
+ Projection.Nullable(Projection.TypeConstructor(classType.resolve()!!.toDRI(), emptyList()))
+ }
+ }
+ return typeParameters.mapIndexed { index, type ->
+ TypeParameter(
+ dri.copy(genericTarget = index),
+ type.name.orEmpty(),
+ javadocParser.parseDocumentation(type).toPlatformDependant(),
+ mapProjections(type.bounds),
+ listOf(platformData)
+ )
+ }
+ }
+
+ private fun PsiQualifiedNamedElement.toDRI() =
+ DRI(qualifiedName.orEmpty().substringBeforeLast('.', ""), name)
+
private fun parseField(psi: PsiField, parent: DRI): Property {
val dri = parent.copy(
callable = Callable(
@@ -134,11 +275,15 @@ object DefaultPsiToDocumentableTranslator : PsiToDocumentableTranslator {
return Property(
dri,
psi.name!!, // TODO: Investigate if this is indeed nullable
+ javadocParser.parseDocumentation(psi).toPlatformDependant(),
+ PsiDocumentableSource(psi).toPlatformDependant(),
+ psi.getVisibility().toPlatformDependant(),
+ JavaTypeWrapper(psi.type),
+ null,
null,
null,
- getComment(psi),
- accessors = emptyList(),
- visibility = mapOf(platformData to psi.getVisibility())
+ psi.getModifier(),
+ listOf(platformData)
)
}
}