blob: 1533ce9ac97a305fd687d791a9e55fbbcbab8c32 [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.devsite.renderer.converters
import com.google.devsite.renderer.Language
import org.jetbrains.dokka.base.transformers.documentables.isException
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.DAnnotation
import org.jetbrains.dokka.model.DClass
import org.jetbrains.dokka.model.DClasslike
import org.jetbrains.dokka.model.DEnum
import org.jetbrains.dokka.model.DFunction
import org.jetbrains.dokka.model.DInterface
import org.jetbrains.dokka.model.DObject
import org.jetbrains.dokka.model.DProperty
import org.jetbrains.dokka.model.Documentable
import org.jetbrains.dokka.model.ExtraModifiers
import org.jetbrains.dokka.model.Nullable
import org.jetbrains.dokka.model.Projection
import org.jetbrains.dokka.model.Variance
import org.jetbrains.dokka.model.WithChildren
import org.jetbrains.dokka.model.properties.WithExtraProperties
import org.jetbrains.dokka.model.toAdditionalModifiers
/** Recursively expands all children. */
internal val <T> WithChildren<T>.explodedChildren: List<T>
get() = children + children.filterIsInstance<WithChildren<T>>().flatMap { it.explodedChildren }
/**
* Returns the type's name. Do not use [Documentable.name] as it won't include the outer class.
*/
internal fun DClasslike.name() = dri.classNames!!
internal fun DClasslike.packageName() = dri.packageName!!
private val baseClasses = listOf("kotlin.Any", "java.lang.Object", "kotlin.Enum",
"java.lang.Enum", "java.lang.annotation.Annotation")
/**
* Returns true if this dri is from a build in base class like Any, Object, Enum, Annotation
*/
internal fun DRI.isFromBaseClass(): Boolean {
val classAndPackage = packageName?.plus(".").plus(classNames)
return baseClasses.contains(classAndPackage)
}
/** @return true if this is a nullable type, false otherwise */
internal fun Projection.isNullable(): Boolean = when (this) {
is Nullable -> true
is Variance<*> -> inner.isNullable()
else -> false
}
/**
* @param displayLanguage the Language of the docs this Documentable will be displayed in
* @return the String name that represents this type when displayed
*/
fun Documentable.stringForType(displayLanguage: Language): String = when (this) {
is DClass -> "class"
is DInterface -> "interface"
is DEnum -> "enum"
is DAnnotation -> "annotation"
is DFunction -> when (displayLanguage) {
Language.JAVA -> "method"
Language.KOTLIN -> "function"
}
is DProperty -> when (displayLanguage) {
Language.JAVA -> "field"
Language.KOTLIN -> "property"
}
is DObject -> "object"
else -> error("Unsupported type: $this")
}
/* Returns if a class is an Exception or not
isException, the built in method in Dokka, only considers its supertype so we also look for
functions that are Throwable
https://github.com/Kotlin/dokka/issues/1557
*/
val DClass.isExceptionClass: Boolean
get() = isException || functions.any { function -> function.dri.classNames == "Throwable" }
// TODO(b/173138586) replace with something else when implementing JvmName
val DClasslike.isSynthetic: Boolean
get() = name().endsWith("Kt")
/**
* Converts a top level function to its representation under a Java synthetic class
* and with JvmName
* Replaces the dri to point to the synthetic class and applies the static modifier
*/
fun DFunction.withJavaSynthetic(syntheticClassName: String): DFunction {
val jvmName = jvmName() ?: name
return copy(
name = jvmName,
// this needs to be the dri IN the synthetic class
dri = dri.copy(
classNames = syntheticClassName,
callable = dri.callable?.copy(name = jvmName)
),
// put the static modifier on functions in the synthetic class
extra = extra.addAll(sourceSets.map {
mapOf(it to setOf(ExtraModifiers.JavaOnlyModifiers.Static)).toAdditionalModifiers()
})
)
}
/**
* Converts a level function to its presentation with JvmName
*/
fun DFunction.withJvmName(): DFunction {
val jvmName = jvmName() ?: return this
return copy(
name = jvmName,
dri = dri.copy(callable = dri.callable?.copy(name = jvmName))
)
}
/**
* Returns the value of the @JvmName for this function if one exists or null
*/
fun WithExtraProperties<*>.jvmName(): String? {
val jvmNameAnnotation = annotations().filter { it.dri.classNames.equals("JvmName") }
return if (jvmNameAnnotation.isEmpty()) {
null
} else {
jvmNameAnnotation.first().params.getValue("name").toComponent().replace("\"", "")
}
}
/**
* Filters out elements that are annotated with @JvmSynthetic
*/
fun <T> List<T>.filterOutJvmSynthetic(): List<T>
where T : WithExtraProperties<*> = this.filterNot {
it.annotations().any { it.dri.classNames.equals("JvmSynthetic") }
}