<lambda>null1 package org.jetbrains.dokka
2 
3 import org.jetbrains.dokka.Formats.nameWithOuterClass
4 import org.jetbrains.dokka.LanguageService.RenderMode
5 
6 /**
7  * Implements [LanguageService] and provides rendering of symbols in Java language
8  */
9 class JavaLanguageService : LanguageService {
10     override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
11         return ContentText(when (node.kind) {
12             NodeKind.Package -> renderPackage(node)
13             in NodeKind.classLike -> renderClass(node)
14 
15             NodeKind.TypeParameter -> renderTypeParameter(node)
16             NodeKind.Type,
17             NodeKind.UpperBound -> renderType(node)
18 
19             NodeKind.Constructor,
20             NodeKind.Function -> renderFunction(node)
21             NodeKind.Property -> renderProperty(node)
22             else -> "${node.kind}: ${node.name}"
23         })
24     }
25 
26     override fun renderName(node: DocumentationNode): String {
27         return when (node.kind) {
28             NodeKind.Constructor -> node.owner!!.name
29             else -> node.name
30         }
31     }
32 
33     override fun renderNameWithOuterClass(node: DocumentationNode): String {
34         return when (node.kind) {
35             NodeKind.Constructor -> node.owner!!.nameWithOuterClass()
36             else -> node.nameWithOuterClass()
37         }
38     }
39 
40     override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null
41 
42     private fun renderPackage(node: DocumentationNode): String {
43         return "package ${node.name}"
44     }
45 
46     private fun renderModifier(node: DocumentationNode): String {
47         return when (node.name) {
48             "open" -> ""
49             "internal" -> ""
50             else -> node.name
51         }
52     }
53 
54     fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) {
55         "kotlin.Array" ->
56             node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } ?:
57                     DocumentationNode("Object", node.content, NodeKind.ExternalClass)
58 
59         "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray",
60         "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" ->
61             DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type)
62 
63         else -> null
64     }
65 
66     fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) {
67         "kotlin.Array" ->
68             1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0)
69 
70         "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray",
71         "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" ->
72             1
73         else -> 0
74     }
75 
76     fun renderType(node: DocumentationNode): String {
77         return when (node.name) {
78             "Unit" -> "void"
79             "Int" -> "int"
80             "Long" -> "long"
81             "Double" -> "double"
82             "Float" -> "float"
83             "Char" -> "char"
84             "Boolean" -> "bool"
85         // TODO: render arrays
86             else -> node.name
87         }
88     }
89 
90     private fun renderTypeParameter(node: DocumentationNode): String {
91         val constraints = node.details(NodeKind.UpperBound)
92         return if (constraints.none())
93             node.name
94         else {
95             node.name + " extends " + constraints.map { renderType(node) }.joinToString()
96         }
97     }
98 
99     private fun renderParameter(node: DocumentationNode): String {
100         return "${renderType(node.detail(NodeKind.Type))} ${node.name}"
101     }
102 
103     private fun renderTypeParametersForNode(node: DocumentationNode): String {
104         return StringBuilder().apply {
105             val typeParameters = node.details(NodeKind.TypeParameter)
106             if (typeParameters.any()) {
107                 append("<")
108                 append(typeParameters.map { renderTypeParameter(it) }.joinToString())
109                 append("> ")
110             }
111         }.toString()
112     }
113 
114     private fun renderModifiersForNode(node: DocumentationNode): String {
115         val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" }
116         if (modifiers.none())
117             return ""
118         return modifiers.joinToString(" ", postfix = " ")
119     }
120 
121     private fun renderClass(node: DocumentationNode): String {
122         return StringBuilder().apply {
123             when (node.kind) {
124                 NodeKind.Class -> append("class ")
125                 NodeKind.Interface -> append("interface ")
126                 NodeKind.Enum -> append("enum ")
127                 NodeKind.EnumItem -> append("enum value ")
128                 NodeKind.Object -> append("class ")
129                 else -> throw IllegalArgumentException("Node $node is not a class-like object")
130             }
131 
132             append(node.name)
133             append(renderTypeParametersForNode(node))
134         }.toString()
135     }
136 
137     private fun renderFunction(node: DocumentationNode): String {
138         return StringBuilder().apply {
139             when (node.kind) {
140                 NodeKind.Constructor -> append(node.owner?.name)
141                 NodeKind.Function -> {
142                     append(renderTypeParametersForNode(node))
143                     append(renderType(node.detail(NodeKind.Type)))
144                     append(" ")
145                     append(node.name)
146                 }
147                 else -> throw IllegalArgumentException("Node $node is not a function-like object")
148             }
149 
150             val receiver = node.details(NodeKind.Receiver).singleOrNull()
151             append("(")
152             if (receiver != null)
153                 (listOf(receiver) + node.details(NodeKind.Parameter)).map { renderParameter(it) }.joinTo(this)
154             else
155                 node.details(NodeKind.Parameter).map { renderParameter(it) }.joinTo(this)
156 
157             append(")")
158         }.toString()
159     }
160 
161     private fun renderProperty(node: DocumentationNode): String {
162         return StringBuilder().apply {
163             when (node.kind) {
164                 NodeKind.Property -> append("val ")
165                 else -> throw IllegalArgumentException("Node $node is not a property")
166             }
167             append(renderTypeParametersForNode(node))
168             val receiver = node.details(NodeKind.Receiver).singleOrNull()
169             if (receiver != null) {
170                 append(renderType(receiver.detail(NodeKind.Type)))
171                 append(".")
172             }
173 
174             append(node.name)
175             append(": ")
176             append(renderType(node.detail(NodeKind.Type)))
177         }.toString()
178     }
179 }