| /* |
| * 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.impl |
| |
| import com.google.devsite.renderer.Language |
| import com.google.devsite.renderer.converters.explodedChildren |
| import com.google.devsite.renderer.converters.filterOutJvmSynthetic |
| import com.google.devsite.renderer.converters.isExceptionClass |
| import com.google.devsite.renderer.converters.name |
| import com.google.devsite.renderer.converters.withJavaSynthetic |
| import kotlinx.coroutines.CoroutineScope |
| import kotlinx.coroutines.Deferred |
| import kotlinx.coroutines.Job |
| import kotlinx.coroutines.async |
| import kotlinx.coroutines.launch |
| import org.jetbrains.dokka.links.DRI |
| import org.jetbrains.dokka.links.withClass |
| 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.DModule |
| import org.jetbrains.dokka.model.DPackage |
| import org.jetbrains.dokka.model.DProperty |
| import org.jetbrains.dokka.model.DTypeAlias |
| import org.jetbrains.dokka.model.Documentable |
| import org.jetbrains.dokka.model.JavaModifier |
| import org.jetbrains.dokka.model.JavaVisibility |
| import org.jetbrains.dokka.model.WithSources |
| import org.jetbrains.dokka.model.properties.PropertyContainer |
| |
| /** |
| * Centralized place to retrieve documentables. |
| * |
| * All doc rewriting should occur here. |
| */ |
| internal class DocumentablesHolder(module: DModule, scope: CoroutineScope) { |
| private val packages = scope.async { computePackages(module) } |
| |
| private val classlikes = mutableMapOf<DRI, Deferred<List<DClasslike>>>() |
| private val classes = mutableMapOf<DRI, Deferred<List<DClass>>>() |
| private val syntheticClasses = mutableMapOf<DRI, Deferred<List<DClass>>>() |
| private val enums = mutableMapOf<DRI, Deferred<List<DEnum>>>() |
| private val interfaces = mutableMapOf<DRI, Deferred<List<DInterface>>>() |
| private val annotations = mutableMapOf<DRI, Deferred<List<DAnnotation>>>() |
| private val typeAliases = mutableMapOf<DRI, Deferred<List<DTypeAlias>>>() |
| private val exceptions = mutableMapOf<DRI, Deferred<List<DClass>>>() |
| |
| private val allClasslikes: Deferred<List<DClasslike>> |
| private val nestedClasslikesJob: Job |
| private val nestedClasslikes = mutableMapOf<DRI, Deferred<List<DClasslike>>>() |
| private val subclassGraph: Deferred<Map<DRI, Subclasses>> |
| |
| init { |
| scope.apply { |
| for (packageDoc in module.packages) { |
| val children = async { packageDoc.explodedChildren } |
| val syntheticClassList = async { computeSyntheticClasses(packageDoc) } |
| val classlikesList = async { computeClasslikes(children.await(), |
| syntheticClassList.await()) } |
| val classList = async { computeClasses(children.await()) } |
| |
| val enumList = async { computeEnums(children.await()) } |
| val interfaceList = async { computeInterfaces(children.await()) } |
| val annotationList = async { computeAnnotations(children.await()) } |
| val typeAliasList = async { computeTypesAliases(packageDoc) } |
| val exceptionList = async { computeExceptions(children.await()) } |
| |
| classlikes[packageDoc.dri] = classlikesList |
| classes[packageDoc.dri] = classList |
| syntheticClasses[packageDoc.dri] = syntheticClassList |
| enums[packageDoc.dri] = enumList |
| interfaces[packageDoc.dri] = interfaceList |
| annotations[packageDoc.dri] = annotationList |
| typeAliases[packageDoc.dri] = typeAliasList |
| exceptions[packageDoc.dri] = exceptionList |
| } |
| } |
| |
| allClasslikes = scope.async { computeClasslikes(module, syntheticClasses) } |
| subclassGraph = scope.async { computeSubclassGraph(allClasslikes.await()) } |
| |
| nestedClasslikesJob = scope.launch { |
| for (classlike in allClasslikes.await()) { |
| val nestedClasslikesList = |
| async { computeClasslikes(classlike.explodedChildren) } |
| |
| nestedClasslikes[classlike.dri] = nestedClasslikesList |
| } |
| } |
| } |
| |
| suspend fun packages(): List<DPackage> = packages.await() |
| |
| suspend fun allClasslikes(): List<DClasslike> = allClasslikes.await() |
| |
| suspend fun subclassGraph(): Map<DRI, Subclasses> = subclassGraph.await() |
| |
| suspend fun classlikesFor(packageDoc: DPackage): List<DClasslike> = |
| classlikes.getValue(packageDoc.dri).await() |
| |
| suspend fun classlikesFor(classlike: DClasslike): List<DClasslike> { |
| nestedClasslikesJob.join() |
| return nestedClasslikes.getValue(classlike.dri).await() |
| } |
| |
| suspend fun classesFor(packageDoc: DPackage, displayLanguage: Language): List<DClass> { |
| if (displayLanguage == Language.JAVA) { |
| return (classes.getValue(packageDoc.dri).await() + |
| syntheticClasses.getValue(packageDoc.dri).await()).sortedBy { it.name() } |
| } else { |
| return classes.getValue(packageDoc.dri).await() |
| } |
| } |
| |
| suspend fun enumsFor(packageDoc: DPackage): List<DEnum> = |
| enums.getValue(packageDoc.dri).await() |
| |
| suspend fun interfacesFor(packageDoc: DPackage): List<DInterface> = |
| interfaces.getValue(packageDoc.dri).await() |
| |
| suspend fun annotationsFor(packageDoc: DPackage): List<DAnnotation> = |
| annotations.getValue(packageDoc.dri).await() |
| |
| suspend fun typeAliasesFor(packageDoc: DPackage): List<DTypeAlias> = |
| typeAliases.getValue(packageDoc.dri).await() |
| |
| suspend fun exceptionsFor(packageDoc: DPackage): List<DClass> = |
| exceptions.getValue(packageDoc.dri).await() |
| |
| private fun computePackages(module: DModule): List<DPackage> { |
| return module.packages.sortedBy { it.name } |
| } |
| |
| private suspend fun computeClasslikes( |
| module: DModule, |
| syntheticClasses: MutableMap<DRI, Deferred<List<DClass>>> |
| ): List<DClasslike> { |
| return computeClasslikes(module.packages.flatMap { classlikesFor(it) }) + |
| syntheticClasses.values.flatMap { it.await() } |
| } |
| |
| private fun computeClasslikes( |
| docs: List<Documentable>, |
| syntheticClasses: List<DClass> = emptyList() |
| ): List<DClasslike> { |
| return (docs.filterIsInstance<DClasslike>() + syntheticClasses).sortedBy { it.name() } |
| } |
| |
| private fun computeClasses(docs: List<Documentable>): List<DClass> { |
| return docs.filterIsInstance<DClass>().filterNot { it.isExceptionClass }.sortedBy { |
| it.name() |
| } |
| } |
| |
| /** Computes the syntheticClasses from top level functions that are used to document Kotlin as |
| * Java |
| */ |
| private fun computeSyntheticClasses(packageDoc: DPackage): List<DClass> { |
| // functions that are JvmSynthetic are not accessible from Java so they should not appear |
| // in the documentation |
| val javaFunctions = packageDoc.functions.filterOutJvmSynthetic() |
| val javaProperties = packageDoc.properties.filterOutJvmSynthetic() |
| return (javaFunctions + javaProperties) |
| .mapToSyntheticNames() |
| .map { (syntheticClassName, nodes) -> |
| DClass( |
| dri = packageDoc.dri.withClass(syntheticClassName), |
| name = syntheticClassName, |
| // TODO (b/168340963) handle kotlin as java properties |
| properties = nodes.filterIsInstance<DProperty>(), |
| constructors = emptyList(), |
| functions = nodes.filterIsInstance<DFunction>().map { |
| it.withJavaSynthetic(syntheticClassName) |
| }.sortedBy { it.name }, |
| classlikes = emptyList(), |
| sources = emptyMap(), |
| expectPresentInSet = null, |
| visibility = packageDoc.sourceSets.map { it to JavaVisibility.Public }.toMap(), |
| companion = null, |
| generics = emptyList(), |
| supertypes = emptyMap(), |
| documentation = emptyMap(), |
| modifier = packageDoc.sourceSets.map { it to JavaModifier.Final }.toMap(), |
| sourceSets = packageDoc.sourceSets, |
| isExpectActual = false, |
| extra = PropertyContainer.empty() |
| ) |
| } |
| } |
| |
| /** Returns a map from String name of synthetic class that this Function (WithSources) would be |
| * in to the Functions that are part of those classes |
| * |
| * This method uses the filename with "Kt" appended |
| * TODO(b/173138586): this should be using @file:jvmname when that's fixed by JB |
| * **/ |
| private fun <T : WithSources> List<T>.mapToSyntheticNames() = |
| map { it.sources to it } |
| .groupBy({ (location, _) -> |
| location.let { |
| it.entries.first().value.path.split("/").last().split(".").first() + "Kt" |
| } |
| }) { it.second } |
| |
| private fun computeEnums(docs: List<Documentable>): List<DEnum> { |
| return docs.filterIsInstance<DEnum>().sortedBy { it.name() } |
| } |
| |
| private fun computeInterfaces(docs: List<Documentable>): List<DInterface> { |
| return docs.filterIsInstance<DInterface>().sortedBy { it.name() } |
| } |
| |
| private fun computeAnnotations(docs: List<Documentable>): List<DAnnotation> { |
| return docs.filterIsInstance<DAnnotation>().sortedBy { it.name() } |
| } |
| |
| private fun computeTypesAliases(packageDoc: DPackage): List<DTypeAlias> { |
| return packageDoc.typealiases.sortedBy { it.name } |
| } |
| |
| private fun computeExceptions(docs: List<Documentable>): List<DClass> { |
| return docs.filterIsInstance<DClass>().filter { it.isExceptionClass }.sortedBy { it.name() } |
| } |
| } |