aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/base/src')
-rw-r--r--plugins/base/src/main/kotlin/resolvers/local/BaseLocationProvider.kt141
-rw-r--r--plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt20
2 files changed, 145 insertions, 16 deletions
diff --git a/plugins/base/src/main/kotlin/resolvers/local/BaseLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/BaseLocationProvider.kt
new file mode 100644
index 00000000..ab633fdc
--- /dev/null
+++ b/plugins/base/src/main/kotlin/resolvers/local/BaseLocationProvider.kt
@@ -0,0 +1,141 @@
+package org.jetbrains.dokka.base.resolvers.local
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.query
+import java.lang.IllegalStateException
+import java.net.HttpURLConnection
+import java.net.URL
+import java.net.URLConnection
+
+abstract class BaseLocationProvider(protected val dokkaContext: DokkaContext) : LocationProvider {
+
+ protected val externalLocationProviderFactories =
+ dokkaContext.plugin<DokkaBase>().query { externalLocationProviderFactory }
+
+ protected fun getExternalLocation(
+ dri: DRI,
+ sourceSets: Set<DokkaSourceSet>
+ ): String {
+ val jdkToExternalDocumentationLinks = dokkaContext.configuration.sourceSets
+ .filter { sourceSet ->
+ sourceSets.contains(sourceSet)
+ }
+ .groupBy({ it.jdkVersion }, { it.externalDocumentationLinks })
+ .map { it.key to it.value.flatten().distinct() }.toMap()
+
+ val toResolve: MutableMap<Int, MutableList<DokkaConfiguration.ExternalDocumentationLink>> = mutableMapOf()
+ for ((jdk, links) in jdkToExternalDocumentationLinks) {
+ for (link in links) {
+ val info = cache[link.packageListUrl]
+ if (info == null) {
+ toResolve.getOrPut(jdk) { mutableListOf() }.add(link)
+ } else if (info.packages.contains(dri.packageName)) {
+ return link.url.toExternalForm() + getLink(dri, info)
+ }
+ }
+ }
+ // Not in cache, resolve packageLists
+ for ((jdk, links) in toResolve) {
+ for (link in links) {
+ if (dokkaContext.configuration.offlineMode && link.packageListUrl.protocol.toLowerCase() != "file")
+ continue
+ val locationInfo =
+ loadPackageList(jdk, link.packageListUrl)
+ if (locationInfo.packages.contains(dri.packageName)) {
+ return link.url.toExternalForm() + getLink(dri, locationInfo)
+ }
+ }
+ toResolve.remove(jdk)
+ }
+ return ""
+ }
+
+ private val cache: MutableMap<URL, DefaultLocationProvider.LocationInfo> = mutableMapOf()
+
+
+ private fun getLink(dri: DRI, locationInfo: DefaultLocationProvider.LocationInfo): String =
+ locationInfo.locations[dri.packageName + "." + dri.classNames]
+ ?: // Not sure if it can be here, previously it shadowed only kotlin/dokka related sources, here it shadows both dokka/javadoc, cause I cannot distinguish what LocationProvider has been hypothetically chosen
+ if (locationInfo.externalLocationProvider != null)
+ with(locationInfo.externalLocationProvider) {
+ dri.toLocation()
+ }
+ else
+ throw IllegalStateException("Have not found any convenient ExternalLocationProvider for $dri DRI!")
+
+ private fun loadPackageList(jdk: Int, url: URL): DefaultLocationProvider.LocationInfo {
+ val packageListStream = url.doOpenConnectionToReadContent().getInputStream()
+ val (params, packages) =
+ packageListStream
+ .bufferedReader()
+ .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } }
+
+ val paramsMap = params.asSequence()
+ .map { it.removePrefix(DOKKA_PARAM_PREFIX).split(":", limit = 2) }
+ .groupBy({ (key, _) -> key }, { (_, value) -> value })
+
+ val format = paramsMap["format"]?.singleOrNull() ?: when {
+ jdk < 8 -> "javadoc1" // Covers JDK 1 - 7
+ jdk < 10 -> "javadoc8" // Covers JDK 8 - 9
+ else -> "javadoc10" // Covers JDK 10+
+ }
+
+ val locations = paramsMap["location"].orEmpty()
+ .map { it.split("\u001f", limit = 2) }
+ .map { (key, value) -> key to value }
+ .toMap()
+
+ val externalLocationProvider =
+ externalLocationProviderFactories.asSequence().map { it.getExternalLocationProvider(format) }
+ .filterNotNull().take(1).firstOrNull()
+
+ val info = DefaultLocationProvider.LocationInfo(
+ externalLocationProvider,
+ packages.toSet(),
+ locations
+ )
+ cache[url] = info
+ return info
+ }
+
+ private fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection {
+ val connection = this.openConnection().apply {
+ connectTimeout = timeout
+ readTimeout = timeout
+ }
+
+ when (connection) {
+ is HttpURLConnection -> {
+ return when (connection.responseCode) {
+ in 200..299 -> {
+ connection
+ }
+ HttpURLConnection.HTTP_MOVED_PERM,
+ HttpURLConnection.HTTP_MOVED_TEMP,
+ HttpURLConnection.HTTP_SEE_OTHER -> {
+ if (redirectsAllowed > 0) {
+ val newUrl = connection.getHeaderField("Location")
+ URL(newUrl).doOpenConnectionToReadContent(timeout, redirectsAllowed - 1)
+ } else {
+ throw RuntimeException("Too many redirects")
+ }
+ }
+ else -> {
+ throw RuntimeException("Unhandled http code: ${connection.responseCode}")
+ }
+ }
+ }
+ else -> return connection
+ }
+ }
+
+ companion object {
+ const val DOKKA_PARAM_PREFIX = "\$dokka."
+ }
+
+} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt
index 6c4869d8..e48aa926 100644
--- a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt
+++ b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt
@@ -2,30 +2,23 @@ package org.jetbrains.dokka.base.resolvers.local
import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
-import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProvider
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
-import org.jetbrains.dokka.plugability.plugin
-import org.jetbrains.dokka.plugability.query
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLConnection
import java.util.*
private const val PAGE_WITH_CHILDREN_SUFFIX = "index"
-private const val DOKKA_PARAM_PREFIX = "\$dokka."
open class DefaultLocationProvider(
protected val pageGraphRoot: RootPageNode,
- protected val dokkaContext: DokkaContext
-) : LocationProvider {
+ dokkaContext: DokkaContext
+) : BaseLocationProvider(dokkaContext) {
protected open val extension = ".html"
- protected val externalLocationProviderFactories =
- dokkaContext.plugin<DokkaBase>().query { externalLocationProviderFactory }
-
protected val pagesIndex: Map<DRI, ContentPage> = pageGraphRoot.asSequence().filterIsInstance<ContentPage>()
.map { it.dri.map { dri -> dri to it } }.flatten()
.groupingBy { it.first }
@@ -49,12 +42,7 @@ open class DefaultLocationProvider(
override fun resolve(dri: DRI, sourceSets: Set<DokkaSourceSet>, context: PageNode?): String =
pagesIndex[dri]?.let { resolve(it, context) } ?:
// Not found in PageGraph, that means it's an external link
- getLocation(
- dri,
- sourceSets
- .groupBy({ it.jdkVersion }, { it.externalDocumentationLinks })
- .map { it.key to it.value.flatten().distinct() }.toMap()
- )
+ getExternalLocation(dri, sourceSets)
override fun resolveRoot(node: PageNode): String =
pathTo(pageGraphRoot, node).removeSuffix(PAGE_WITH_CHILDREN_SUFFIX)
@@ -105,7 +93,7 @@ open class DefaultLocationProvider(
// Not in cache, resolve packageLists
for ((jdk, links) in toResolve) {
for (link in links) {
- if (dokkaContext.configuration.offlineMode && link.packageListUrl.protocol.toLowerCase() != "file")
+ if(dokkaContext.configuration.offlineMode && link.packageListUrl.protocol.toLowerCase() != "file")
continue
val locationInfo =
loadPackageList(jdk, link.packageListUrl)