<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
68 DeprecatedLevel,
69
70 ArtifactId,
71
72 GroupNode;
73
74 companion object {
75 val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias)
76 val memberLike = setOf(Function, Property, Field, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem, Attribute)
77 }
78 }
79
80 open class DocumentationNode(val name: String,
81 content: Content,
82 val kind: NodeKind) {
83
84 private val references = LinkedHashSet<DocumentationReference>()
85
86 var content: Content = content
87 private set
88
89 val summary: ContentNode get() = content.summary
90
91 val owner: DocumentationNode?
92 get() = references(RefKind.Owner).singleOrNull()?.to
93 val details: List<DocumentationNode>
<lambda>null94 get() = references(RefKind.Detail).map { it.to }
95 val members: List<DocumentationNode>
<lambda>null96 get() = references(RefKind.Member).map { it.to }.sortedBy { it.name }
97 val inheritedMembers: List<DocumentationNode>
<lambda>null98 get() = references(RefKind.InheritedMember).map { it.to }
99 val allInheritedMembers: List<DocumentationNode>
<lambda>null100 get() = recursiveInheritedMembers().sortedBy { it.name }
101 val inheritedCompanionObjectMembers: List<DocumentationNode>
<lambda>null102 get() = references(RefKind.InheritedCompanionObjectMember).map { it.to }
103 val extensions: List<DocumentationNode>
<lambda>null104 get() = references(RefKind.Extension).map { it.to }
105 val inheritors: List<DocumentationNode>
<lambda>null106 get() = references(RefKind.Inheritor).map { it.to }
107 val overrides: List<DocumentationNode>
<lambda>null108 get() = references(RefKind.Override).map { it.to }
109 val links: List<DocumentationNode>
<lambda>null110 get() = references(RefKind.Link).map { it.to }
111 val hiddenLinks: List<DocumentationNode>
<lambda>null112 get() = references(RefKind.HiddenLink).map { it.to }
113 val annotations: List<DocumentationNode>
<lambda>null114 get() = references(RefKind.Annotation).map { it.to }
115 val deprecation: DocumentationNode?
<lambda>null116 get() = references(RefKind.Deprecation).map { it.to }.firstOrNull()
117 val platforms: List<String>
<lambda>null118 get() = references(RefKind.Platform).map { it.to.name }
119 val externalType: DocumentationNode?
<lambda>null120 get() = references(RefKind.ExternalType).map { it.to }.firstOrNull()
121 val apiLevel: DocumentationNode
122 get() = detailOrNull(NodeKind.ApiLevel) ?: DocumentationNode("", Content.Empty, NodeKind.ApiLevel)
123 val deprecatedLevel: DocumentationNode
124 get() = detailOrNull(NodeKind.DeprecatedLevel) ?: DocumentationNode("", Content.Empty, NodeKind.DeprecatedLevel)
125 val artifactId: DocumentationNode
126 get() = detailOrNull(NodeKind.ArtifactId) ?: DocumentationNode("", Content.Empty, NodeKind.ArtifactId)
127 val attributes: List<DocumentationNode>
<lambda>null128 get() = details(NodeKind.Attribute).sortedBy { it.attributeRef!!.name }
129 val attributeRef: DocumentationNode?
<lambda>null130 get() = references(RefKind.AttributeRef).map { it.to }.firstOrNull()
131 val relatedAttributes: List<DocumentationNode>
132 get() = hiddenLinks(NodeKind.Attribute)
133 val supertypes: List<DocumentationNode>
134 get() = details(NodeKind.Supertype)
135 val signatureName = detailOrNull(NodeKind.Signature)?.name
136
137 val prettyName : String
138 get() = when(kind) {
139 NodeKind.Constructor -> owner!!.name
140 else -> name
141 }
142
143 val superclassType: DocumentationNode?
144 get() = when (kind) {
145 NodeKind.Supertype -> {
<lambda>null146 (links + listOfNotNull(externalType)).firstOrNull { it.kind in NodeKind.classLike }?.superclassType
147 }
148 NodeKind.Interface -> null
<lambda>null149 in NodeKind.classLike -> supertypes.firstOrNull {
150 (it.links + listOfNotNull(it.externalType)).any { it.isSuperclassFor(this) }
151 }
152 else -> null
153 }
154
155 val superclassTypeSequence: Sequence<DocumentationNode>
<lambda>null156 get() = generateSequence(superclassType) {
157 it.superclassType
158 }
159
160 // TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice
addReferenceTonull161 fun addReferenceTo(to: DocumentationNode, kind: RefKind) {
162 references.add(DocumentationReference(this, to, kind))
163 }
164
dropReferencesnull165 fun dropReferences(predicate: (DocumentationReference) -> Boolean) {
166 references.removeAll(predicate)
167 }
168
addAllReferencesFromnull169 fun addAllReferencesFrom(other: DocumentationNode) {
170 references.addAll(other.references)
171 }
172
updateContentnull173 fun updateContent(body: MutableContent.() -> Unit) {
174 if (content !is MutableContent) {
175 content = MutableContent()
176 }
177 (content as MutableContent).body()
178 }
<lambda>null179 fun details(kind: NodeKind): List<DocumentationNode> = details.filter { it.kind == kind }
<lambda>null180 fun members(kind: NodeKind): List<DocumentationNode> = members.filter { it.kind == kind }
<lambda>null181 fun hiddenLinks(kind: NodeKind): List<DocumentationNode> = hiddenLinks.filter { it.kind == kind }
<lambda>null182 fun inheritedMembers(kind: NodeKind): List<DocumentationNode> = inheritedMembers.filter { it.kind == kind }
<lambda>null183 fun inheritedCompanionObjectMembers(kind: NodeKind): List<DocumentationNode> = inheritedCompanionObjectMembers.filter { it.kind == kind }
<lambda>null184 fun links(kind: NodeKind): List<DocumentationNode> = links.filter { it.kind == kind }
185
<lambda>null186 fun detail(kind: NodeKind): DocumentationNode = details.filter { it.kind == kind }.single()
detailOrNullnull187 fun detailOrNull(kind: NodeKind): DocumentationNode? = details.filter { it.kind == kind }.singleOrNull()
<lambda>null188 fun member(kind: NodeKind): DocumentationNode = members.filter { it.kind == kind }.single()
<lambda>null189 fun link(kind: NodeKind): DocumentationNode = links.filter { it.kind == kind }.single()
190
<lambda>null191 fun references(kind: RefKind): List<DocumentationReference> = references.filter { it.kind == kind }
allReferencesnull192 fun allReferences(): Set<DocumentationReference> = references
193
194 override fun toString(): String {
195 return "$kind:$name"
196 }
197 }
198
199 class DocumentationModule(name: String, content: Content = Content.Empty)
200 : DocumentationNode(name, content, NodeKind.Module) {
201 val nodeRefGraph = NodeReferenceGraph()
202 }
203
204 val DocumentationNode.path: List<DocumentationNode>
205 get() {
206 val parent = owner ?: return listOf(this)
207 return parent.path + this
208 }
209
findOrCreatePackageNodenull210 fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode {
211 val existingNode = refGraph.lookup(packageName)
212 if (existingNode != null) {
213 return existingNode
214 }
215 val newNode = DocumentationNode(packageName,
216 packageContent.getOrElse(packageName) { Content.Empty },
217 NodeKind.Package)
218
219 refGraph.register(packageName, newNode)
220 module?.append(newNode, RefKind.Member)
221 return newNode
222 }
223
appendnull224 fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) {
225 addReferenceTo(child, kind)
226 when (kind) {
227 RefKind.Detail -> child.addReferenceTo(this, RefKind.Owner)
228 RefKind.Member -> child.addReferenceTo(this, RefKind.Owner)
229 RefKind.Owner -> child.addReferenceTo(this, RefKind.Member)
230 else -> { /* Do not add any links back for other types */
231 }
232 }
233 }
234
appendTextNodenull235 fun DocumentationNode.appendTextNode(text: String,
236 kind: NodeKind,
237 refKind: RefKind = RefKind.Detail) {
238 append(DocumentationNode(text, Content.Empty, kind), refKind)
239 }
240
qualifiedNamenull241 fun DocumentationNode.qualifiedName(): String {
242 if (kind == NodeKind.Type) {
243 return qualifiedNameFromType()
244 } else if (kind == NodeKind.Package) {
245 return name
246 }
247 return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
248 }
249
simpleNamenull250 fun DocumentationNode.simpleName() = name.substringAfterLast('.')
251
252 private fun DocumentationNode.recursiveInheritedMembers(): List<DocumentationNode> {
253 val allInheritedMembers = mutableListOf<DocumentationNode>()
254 recursiveInheritedMembers(allInheritedMembers)
255 return allInheritedMembers
256 }
257
recursiveInheritedMembersnull258 private fun DocumentationNode.recursiveInheritedMembers(allInheritedMembers: MutableList<DocumentationNode>) {
259 allInheritedMembers.addAll(inheritedMembers)
260 inheritedMembers.groupBy { it.owner!! } .forEach { (node, _) ->
261 node.recursiveInheritedMembers(allInheritedMembers)
262 }
263 }
264
isSuperclassFornull265 private fun DocumentationNode.isSuperclassFor(node: DocumentationNode): Boolean {
266 return when(node.kind) {
267 NodeKind.Object, NodeKind.Class, NodeKind.Enum -> kind == NodeKind.Class
268 NodeKind.Exception -> kind == NodeKind.Class || kind == NodeKind.Exception
269 else -> false
270 }
271 }
272
classNodeNameWithOuterClassnull273 fun DocumentationNode.classNodeNameWithOuterClass(): String {
274 assert(kind in NodeKind.classLike)
275 return path.dropWhile { it.kind !in NodeKind.classLike }.joinToString(separator = ".") { it.name }
276 }
277
deprecatedLevelMessagenull278 fun DocumentationNode.deprecatedLevelMessage(): String {
279 val kindName = when(kind) {
280 NodeKind.Enum -> "enum"
281 NodeKind.Interface -> "interface"
282 NodeKind.AnnotationClass -> "annotation"
283 NodeKind.Exception -> "exception"
284 else -> "class"
285 }
286 return "This $kindName was deprecated in API level ${deprecatedLevel.name}."
287 }
288
convertDeprecationDetailsToChildrennull289 fun DocumentationNode.convertDeprecationDetailsToChildren() {
290 val toProcess = details.toMutableList()
291 while (!toProcess.isEmpty()) {
292 var child = toProcess.removeAt(0)
293 if (child.details.isEmpty() && child.name != "") {
294 updateContent { text(child.name.cleanForAppending()) }
295 }
296 else toProcess.addAll(0, child.details)
297 }
298 }
299
300 /*
301 * Removes extraneous quotation marks and adds a ". " to make appending children readable.
302 */
Stringnull303 fun String.cleanForAppending(): String {
304 var result = this
305 if (this.first() == this.last() && this.first() == '"') result = result.substring(1, result.length - 1)
306 if (result[result.length - 2] != '.' && result.last() != ' ') result += ". "
307 return result
308 }