<lambda>null1 package org.jetbrains.dokka
2
3 import java.util.*
4
5 enum class NodeKind {
6 Unknown,
7
8 Package,
9 Class,
10 Interface,
11 Enum,
12 AnnotationClass,
13 Exception,
14 EnumItem,
15 Object,
16 TypeAlias,
17
18 Constructor,
19 Function,
20 Property,
21 Field,
22
23 CompanionObjectProperty,
24 CompanionObjectFunction,
25
26 Parameter,
27 Receiver,
28 TypeParameter,
29 Type,
30 Supertype,
31 UpperBound,
32 LowerBound,
33
34 TypeAliasUnderlyingType,
35
36 Modifier,
37 NullabilityModifier,
38
39 Module,
40
41 ExternalClass,
42 Annotation,
43
44 Value,
45
46 SourceUrl,
47 SourcePosition,
48 Signature,
49
50 ExternalLink,
51 QualifiedName,
52 Platform,
53
54 AllTypes,
55
56 /**
57 * A note which is rendered once on a page documenting a group of overloaded functions.
58 * Needs to be generated equally on all overloads.
59 */
60 OverloadGroupNote,
61
62 Attribute,
63
64 AttributeRef,
65
66 ApiLevel,
67 SdkExtSince,
68
69 DeprecatedLevel,
70
71 ArtifactId,
72
73 GroupNode;
74
75 companion object {
76 val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias)
77 val memberLike = setOf(Function, Property, Field, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem, Attribute)
78 }
79 }
80
81 open class DocumentationNode(val name: String,
82 content: Content,
83 val kind: NodeKind) {
84
85 private val references = LinkedHashSet<DocumentationReference>()
86
87 var content: Content = content
88 private set
89
90 val summary: ContentNode get() = content.summary
91
92 val owner: DocumentationNode?
93 get() = references(RefKind.Owner).singleOrNull()?.to
94 val details: List<DocumentationNode>
<lambda>null95 get() = references(RefKind.Detail).map { it.to }
96 val members: List<DocumentationNode>
<lambda>null97 get() = references(RefKind.Member).map { it.to }.sortedBy { it.name }
98 val inheritedMembers: List<DocumentationNode>
<lambda>null99 get() = references(RefKind.InheritedMember).map { it.to }
100 val allInheritedMembers: List<DocumentationNode>
<lambda>null101 get() = recursiveInheritedMembers().sortedBy { it.name }
102 val inheritedCompanionObjectMembers: List<DocumentationNode>
<lambda>null103 get() = references(RefKind.InheritedCompanionObjectMember).map { it.to }
104 val extensions: List<DocumentationNode>
<lambda>null105 get() = references(RefKind.Extension).map { it.to }
106 val inheritors: List<DocumentationNode>
<lambda>null107 get() = references(RefKind.Inheritor).map { it.to }
108 val overrides: List<DocumentationNode>
<lambda>null109 get() = references(RefKind.Override).map { it.to }
110 val links: List<DocumentationNode>
<lambda>null111 get() = references(RefKind.Link).map { it.to }
112 val hiddenLinks: List<DocumentationNode>
<lambda>null113 get() = references(RefKind.HiddenLink).map { it.to }
114 val annotations: List<DocumentationNode>
<lambda>null115 get() = references(RefKind.Annotation).map { it.to }
116 val deprecation: DocumentationNode?
<lambda>null117 get() = references(RefKind.Deprecation).map { it.to }.firstOrNull()
118 val platforms: List<String>
<lambda>null119 get() = references(RefKind.Platform).map { it.to.name }
120 val externalType: DocumentationNode?
<lambda>null121 get() = references(RefKind.ExternalType).map { it.to }.firstOrNull()
122 val apiLevel: DocumentationNode
123 get() = detailOrNull(NodeKind.ApiLevel) ?: DocumentationNode("", Content.Empty, NodeKind.ApiLevel)
124 val sdkExtSince: DocumentationNode
125 get() = detailOrNull(NodeKind.SdkExtSince) ?: DocumentationNode("", Content.Empty, NodeKind.SdkExtSince)
126 val deprecatedLevel: DocumentationNode
127 get() = detailOrNull(NodeKind.DeprecatedLevel) ?: DocumentationNode("", Content.Empty, NodeKind.DeprecatedLevel)
128 val artifactId: DocumentationNode
129 get() = detailOrNull(NodeKind.ArtifactId) ?: DocumentationNode("", Content.Empty, NodeKind.ArtifactId)
130 val attributes: List<DocumentationNode>
<lambda>null131 get() = details(NodeKind.Attribute).sortedBy { it.attributeRef!!.name }
132 val attributeRef: DocumentationNode?
<lambda>null133 get() = references(RefKind.AttributeRef).map { it.to }.firstOrNull()
134 val relatedAttributes: List<DocumentationNode>
135 get() = hiddenLinks(NodeKind.Attribute)
136 val supertypes: List<DocumentationNode>
137 get() = details(NodeKind.Supertype)
138 val signatureName = detailOrNull(NodeKind.Signature)?.name
139
140 val prettyName : String
141 get() = when(kind) {
142 NodeKind.Constructor -> owner!!.name
143 else -> name
144 }
145
146 val superclassType: DocumentationNode?
147 get() = when (kind) {
148 NodeKind.Supertype -> {
<lambda>null149 (links + listOfNotNull(externalType)).firstOrNull { it.kind in NodeKind.classLike }?.superclassType
150 }
151 NodeKind.Interface -> null
<lambda>null152 in NodeKind.classLike -> supertypes.firstOrNull {
153 (it.links + listOfNotNull(it.externalType)).any { it.isSuperclassFor(this) }
154 }
155 else -> null
156 }
157
158 val superclassTypeSequence: Sequence<DocumentationNode>
<lambda>null159 get() = generateSequence(superclassType) {
160 it.superclassType
161 }
162
163 // TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice
addReferenceTonull164 fun addReferenceTo(to: DocumentationNode, kind: RefKind) {
165 references.add(DocumentationReference(this, to, kind))
166 }
167
dropReferencesnull168 fun dropReferences(predicate: (DocumentationReference) -> Boolean) {
169 references.removeAll(predicate)
170 }
171
addAllReferencesFromnull172 fun addAllReferencesFrom(other: DocumentationNode) {
173 references.addAll(other.references)
174 }
175
updateContentnull176 fun updateContent(body: MutableContent.() -> Unit) {
177 if (content !is MutableContent) {
178 content = MutableContent()
179 }
180 (content as MutableContent).body()
181 }
<lambda>null182 fun details(kind: NodeKind): List<DocumentationNode> = details.filter { it.kind == kind }
<lambda>null183 fun members(kind: NodeKind): List<DocumentationNode> = members.filter { it.kind == kind }
<lambda>null184 fun hiddenLinks(kind: NodeKind): List<DocumentationNode> = hiddenLinks.filter { it.kind == kind }
<lambda>null185 fun inheritedMembers(kind: NodeKind): List<DocumentationNode> = inheritedMembers.filter { it.kind == kind }
<lambda>null186 fun inheritedCompanionObjectMembers(kind: NodeKind): List<DocumentationNode> = inheritedCompanionObjectMembers.filter { it.kind == kind }
<lambda>null187 fun links(kind: NodeKind): List<DocumentationNode> = links.filter { it.kind == kind }
188
<lambda>null189 fun detail(kind: NodeKind): DocumentationNode = details.filter { it.kind == kind }.single()
detailOrNullnull190 fun detailOrNull(kind: NodeKind): DocumentationNode? = details.filter { it.kind == kind }.singleOrNull()
<lambda>null191 fun member(kind: NodeKind): DocumentationNode = members.filter { it.kind == kind }.single()
<lambda>null192 fun link(kind: NodeKind): DocumentationNode = links.filter { it.kind == kind }.single()
193
<lambda>null194 fun references(kind: RefKind): List<DocumentationReference> = references.filter { it.kind == kind }
allReferencesnull195 fun allReferences(): Set<DocumentationReference> = references
196
197 override fun toString(): String {
198 return "$kind:$name"
199 }
200 }
201
202 class DocumentationModule(name: String, content: Content = Content.Empty)
203 : DocumentationNode(name, content, NodeKind.Module) {
204 val nodeRefGraph = NodeReferenceGraph()
205 }
206
207 val DocumentationNode.path: List<DocumentationNode>
208 get() {
209 val parent = owner ?: return listOf(this)
210 return parent.path + this
211 }
212
findOrCreatePackageNodenull213 fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode {
214 val existingNode = refGraph.lookup(packageName)
215 if (existingNode != null) {
216 return existingNode
217 }
218 val newNode = DocumentationNode(packageName,
219 packageContent.getOrElse(packageName) { Content.Empty },
220 NodeKind.Package)
221
222 refGraph.register(packageName, newNode)
223 module?.append(newNode, RefKind.Member)
224 return newNode
225 }
226
appendnull227 fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) {
228 addReferenceTo(child, kind)
229 when (kind) {
230 RefKind.Detail -> child.addReferenceTo(this, RefKind.Owner)
231 RefKind.Member -> child.addReferenceTo(this, RefKind.Owner)
232 RefKind.Owner -> child.addReferenceTo(this, RefKind.Member)
233 else -> { /* Do not add any links back for other types */
234 }
235 }
236 }
237
appendTextNodenull238 fun DocumentationNode.appendTextNode(text: String,
239 kind: NodeKind,
240 refKind: RefKind = RefKind.Detail) {
241 append(DocumentationNode(text, Content.Empty, kind), refKind)
242 }
243
qualifiedNamenull244 fun DocumentationNode.qualifiedName(): String {
245 if (kind == NodeKind.Type) {
246 return qualifiedNameFromType()
247 } else if (kind == NodeKind.Package) {
248 return name
249 }
250 return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
251 }
252
simpleNamenull253 fun DocumentationNode.simpleName() = name.substringAfterLast('.')
254
255 private fun DocumentationNode.recursiveInheritedMembers(): List<DocumentationNode> {
256 val allInheritedMembers = mutableListOf<DocumentationNode>()
257 recursiveInheritedMembers(allInheritedMembers)
258 return allInheritedMembers
259 }
260
recursiveInheritedMembersnull261 private fun DocumentationNode.recursiveInheritedMembers(allInheritedMembers: MutableList<DocumentationNode>) {
262 allInheritedMembers.addAll(inheritedMembers)
263 inheritedMembers.groupBy { it.owner!! } .forEach { (node, _) ->
264 node.recursiveInheritedMembers(allInheritedMembers)
265 }
266 }
267
isSuperclassFornull268 private fun DocumentationNode.isSuperclassFor(node: DocumentationNode): Boolean {
269 return when(node.kind) {
270 NodeKind.Object, NodeKind.Class, NodeKind.Enum -> kind == NodeKind.Class
271 NodeKind.Exception -> kind == NodeKind.Class || kind == NodeKind.Exception
272 else -> false
273 }
274 }
275
classNodeNameWithOuterClassnull276 fun DocumentationNode.classNodeNameWithOuterClass(): String {
277 assert(kind in NodeKind.classLike)
278 return path.dropWhile { it.kind !in NodeKind.classLike }.joinToString(separator = ".") { it.name }
279 }
280
deprecatedLevelMessagenull281 fun DocumentationNode.deprecatedLevelMessage(): String {
282 val kindName = when(kind) {
283 NodeKind.Enum -> "enum"
284 NodeKind.Interface -> "interface"
285 NodeKind.AnnotationClass -> "annotation"
286 NodeKind.Exception -> "exception"
287 else -> "class"
288 }
289 return "This $kindName was deprecated in API level ${deprecatedLevel.name}."
290 }
291
convertDeprecationDetailsToChildrennull292 fun DocumentationNode.convertDeprecationDetailsToChildren() {
293 val toProcess = details.toMutableList()
294 while (!toProcess.isEmpty()) {
295 var child = toProcess.removeAt(0)
296 if (child.details.isEmpty() && child.name != "") {
297 updateContent { text(child.name.cleanForAppending()) }
298 }
299 else toProcess.addAll(0, child.details)
300 }
301 }
302
303 /*
304 * Removes extraneous quotation marks and adds a ". " to make appending children readable.
305 */
Stringnull306 fun String.cleanForAppending(): String {
307 var result = this
308 if (this.first() == this.last() && this.first() == '"') result = result.substring(1, result.length - 1)
309 if (result[result.length - 2] != '.' && result.last() != ' ') result += ". "
310 return result
311 }