aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin
diff options
context:
space:
mode:
authorKamil Doległo <kamilok1965@interia.pl>2019-10-29 11:46:04 +0100
committerKamil Doległo <kamilok1965@interia.pl>2019-10-29 15:49:53 +0100
commit14a290009098b777521b1dedb551047fb66ba73b (patch)
treeae68219051b080ff8888308d5521aaeea7431fb4 /core/src/main/kotlin
parent5f358199788fefb78f5db7791e718480793a77fc (diff)
downloaddokka-14a290009098b777521b1dedb551047fb66ba73b.tar.gz
dokka-14a290009098b777521b1dedb551047fb66ba73b.tar.bz2
dokka-14a290009098b777521b1dedb551047fb66ba73b.zip
[WIP] new model
Diffstat (limited to 'core/src/main/kotlin')
-rw-r--r--core/src/main/kotlin/links/DRI.kt70
-rw-r--r--core/src/main/kotlin/pages/ContentNodes.kt76
-rw-r--r--core/src/main/kotlin/pages/PageNodes.kt39
-rw-r--r--core/src/main/kotlin/renderers/DefaultRenderer.kt61
-rw-r--r--core/src/main/kotlin/renderers/FileWriter.kt32
-rw-r--r--core/src/main/kotlin/renderers/HtmlRenderer.kt51
-rw-r--r--core/src/main/kotlin/renderers/Renderer.kt14
-rw-r--r--core/src/main/kotlin/resolvers/DefaultLocationProvider.kt80
-rw-r--r--core/src/main/kotlin/resolvers/ExternalLocationProvider.kt98
-rw-r--r--core/src/main/kotlin/resolvers/LocationProvider.kt10
-rw-r--r--core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt29
-rw-r--r--core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt8
12 files changed, 568 insertions, 0 deletions
diff --git a/core/src/main/kotlin/links/DRI.kt b/core/src/main/kotlin/links/DRI.kt
new file mode 100644
index 00000000..64989701
--- /dev/null
+++ b/core/src/main/kotlin/links/DRI.kt
@@ -0,0 +1,70 @@
+package org.jetbrains.dokka.links
+
+import java.text.ParseException
+
+/**
+ * [DRI] stands for DokkaResourceIdentifier
+ */
+data class DRI(val packageName: String? = null,
+ val classNames: String? = null,
+ val callable: Callable? = null,
+ val target: Int? = null,
+ val extra: String? = null) {
+
+ constructor(packageName: String? = null,
+ classNames: String? = null,
+ callableName: String? = null,
+ signature: String? = null,
+ target: Int? = null,
+ extra: String? = null) : this(packageName, classNames, Callable.from(callableName, signature), target, extra)
+
+ override fun toString(): String =
+ "${packageName.orEmpty()}/${classNames.orEmpty()}/${callable?.name.orEmpty()}/${callable?.signature().orEmpty()}/${target?.toString().orEmpty()}/${extra.orEmpty()}"
+
+ companion object {
+ fun from(s: String): DRI = try {
+ s.split('/')
+ .map { it.takeIf(String::isNotBlank) }
+ .let { (packageName, classNames, callableName, callableSignature, target, ext) ->
+ DRI(
+ packageName,
+ classNames,
+ try { Callable.from(callableName, callableSignature) } catch(e: ParseException) { null },
+ target?.toInt(),
+ ext
+ )
+ }
+ } catch (e: Throwable) {
+ throw ParseException(s, 0)
+ }
+ }
+}
+
+data class Callable(val name: String, val receiver: String, val returnType: String, val params: List<String>) {
+ fun signature() = "$receiver.$returnType.${params.joinToString(".")}"
+ companion object {
+ fun from(name: String?, signature: String?): Callable = try {
+ signature.toString()
+ .split('#', ignoreCase = false, limit = 3)
+ .let { (receiver, returnType, params) ->
+ Callable(
+ name.toString(),
+ receiver,
+ returnType,
+ params.split('#')
+ )
+ }
+ } catch (e: Throwable) {
+ throw ParseException(signature, 0)
+ }
+
+ fun from(s: String): Callable = try {
+ s.split('/').let { (name, signature) -> from(name, signature) }
+ } catch (e: Throwable) {
+ throw ParseException(s, 0)
+ }
+ }
+}
+
+private operator fun <T> List<T>.component6(): T = get(5)
+
diff --git a/core/src/main/kotlin/pages/ContentNodes.kt b/core/src/main/kotlin/pages/ContentNodes.kt
new file mode 100644
index 00000000..6519c313
--- /dev/null
+++ b/core/src/main/kotlin/pages/ContentNodes.kt
@@ -0,0 +1,76 @@
+package org.jetbrains.dokka.pages
+
+import org.jetbrains.dokka.links.DRI
+
+interface ContentNode {
+ val platforms: List<PlatformData>
+ val annotations: List<Annotation>
+}
+
+/** Comment consisting of parts, eg. [ContentText]s, [ContentLink]s and so on */
+data class ContentComment(val parts: List<ContentNode>,
+ override val platforms: List<PlatformData>,
+ override val annotations: List<Annotation> = emptyList()
+): ContentNode
+
+/** Simple text */
+data class ContentText(val text: String,
+ override val platforms: List<PlatformData>,
+ override val annotations: List<Annotation> = emptyList()
+): ContentNode
+
+///** Headers */ TODO for next iteration
+//data class ContentHeader(val text: String,
+// val level: Int,
+// override val platforms: List<PlatformData>,
+// override val annotations: List<Annotation> = emptyList()
+//): ContentNode
+//
+///** Lists */
+//data class ContentList(val items: List<ContentNode>,
+// val ordered: Boolean,
+// override val platforms: List<PlatformData>,
+// override val annotations: List<Annotation> = emptyList()
+//): ContentNode
+//
+///** Styled elements, eg. bold, strikethrough, emphasis and so on **/
+//data class ContentStyle(val items: List<ContentNode>,
+// val ordered: Boolean,
+// override val platforms: List<PlatformData>,
+// override val annotations: List<Annotation> = emptyList()
+//): ContentNode
+
+/** Code blocks */
+data class ContentCode(val code: String,
+ override val platforms: List<PlatformData>,
+ override val annotations: List<Annotation> = emptyList()
+): ContentNode
+
+/** Symbols, eg. `open fun foo(): String`, or `class Bar`, consisting of parts like [ContentText] and [ContentLink] */
+data class ContentSymbol(val parts: List<ContentNode>,
+ override val platforms: List<PlatformData>,
+ override val annotations: List<Annotation> = emptyList()
+): ContentNode
+
+/** All links that have te be resolved */
+data class ContentLink(val text: String,
+ val address: DRI,
+ override val platforms: List<PlatformData>,
+ override val annotations: List<Annotation> = emptyList()
+): ContentNode
+
+/** Blocks of [ContentNode]s with name, eg. Functions, Types, Properties, etc. */
+data class ContentBlock(val name: String,
+ val children: List<ContentNode>,
+ override val platforms: List<PlatformData>,
+ override val annotations: List<Annotation> = emptyList()
+): ContentNode
+
+/** Logical grouping of [ContentNode]s, eg. [ContentLink], [ContentText] and [ContentSymbol] for one entity */
+data class ContentGroup(val children: List<ContentNode>,
+ override val platforms: List<PlatformData>,
+ override val annotations: List<Annotation> = emptyList()
+): ContentNode
+
+/** All annotations */
+data class Annotation(val name: String)
diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt
new file mode 100644
index 00000000..1bd7f02f
--- /dev/null
+++ b/core/src/main/kotlin/pages/PageNodes.kt
@@ -0,0 +1,39 @@
+package org.jetbrains.dokka.pages
+
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.links.DRI
+
+abstract class PageNode(
+ val name: String,
+ val content: List<ContentNode>,
+ val parent: PageNode?,
+ val dri: DRI?
+// val declarationNode: DeclarationNode
+) {
+ val children: List<PageNode>
+ get() = _children
+
+ private val _children: MutableList<PageNode> = mutableListOf()
+
+ fun appendChildren(children: List<PageNode>) = _children.addAll(children)
+ fun appendChild(child: PageNode) = _children.add(child)
+
+}
+
+class ModulePageNode(name: String, content: List<ContentNode>, parent: PageNode?): PageNode(name, content, parent, null)
+class PackagePageNode(name: String, content: List<ContentNode>, parent: PageNode, dri: DRI): PageNode(name, content, parent, dri)
+class ClassPageNode(name: String, content: List<ContentNode>, parent: PageNode, dri: DRI): PageNode(name, content, parent, dri) // class, companion object
+class MemberPageNode(name: String, content: List<ContentNode>, parent: PageNode, dri: DRI): PageNode(name, content, parent, dri) // functions, extension functions, properties
+
+
+data class PlatformData(val platformName: String, val platformType: Platform)
+
+fun PageNode.platforms(): List<PlatformData> = this.content.flatMap { it.platforms }.distinct() // TODO: Override equals???
+fun PageNode.dfs(predicate: (PageNode) -> Boolean): PageNode? = if (predicate(this)) { this } else { this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() }
+
+
+
+// Navigation??
+
+// content modifier?
+//data class ContentLink(val link: String): ContentNode \ No newline at end of file
diff --git a/core/src/main/kotlin/renderers/DefaultRenderer.kt b/core/src/main/kotlin/renderers/DefaultRenderer.kt
new file mode 100644
index 00000000..b2647d22
--- /dev/null
+++ b/core/src/main/kotlin/renderers/DefaultRenderer.kt
@@ -0,0 +1,61 @@
+package org.jetbrains.dokka.renderers
+
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.resolvers.LocationProvider
+
+abstract class DefaultRenderer(val outputDir: String, val fileWriter: FileWriter, val locationProvider: LocationProvider): Renderer {
+
+ protected abstract fun buildHeader(level: Int, text: String): String
+ protected abstract fun buildNewLine(): String
+ protected abstract fun buildLink(text: String, address: String): String
+ protected abstract fun buildCode(code: String): String
+ protected abstract fun buildNavigation(): String // TODO
+ protected open fun buildText(text: String): String = text
+ protected open fun buildGroup(children: List<ContentNode>): String = children.joinToString { it.build() }
+ protected open fun buildComment(parts: List<ContentNode>): String = parts.joinToString { it.build() }
+ protected open fun buildSymbol(parts: List<ContentNode>): String = parts.joinToString { it.build() }
+ protected open fun buildBlock(name: String, content: List<ContentNode>) = buildHeader(2, name) + content.joinToString("\n") { it.build() }
+
+ protected open fun ContentNode.build(): String =
+ when(this) {
+ is ContentText -> buildText(this.text)
+ is ContentComment -> buildComment(this.parts)
+ is ContentSymbol -> buildSymbol(this.parts)
+ is ContentCode -> buildCode(this.code)
+ is ContentBlock -> buildBlock(this.name, this.children)
+ is ContentLink -> buildLink(this.text, locationProvider.resolve(this.address, this.platforms))
+ is ContentGroup -> buildGroup(this.children)
+ else -> ""
+ }
+
+ protected open fun buildPageContent(page: PageNode): String =
+ buildHeader(1, page.name) + page.content.joinToString("\n") { it.build() }
+
+ protected open fun renderPage(page: PageNode) = fileWriter.write(locationProvider.resolve(page), buildPageContent(page))
+
+ protected open fun renderPages(root: PageNode) {
+ renderPage(root)
+ root.children.forEach { renderPages(it) }
+ }
+
+ protected open fun renderPackageList(root: PageNode) =
+ getPackageNamesAndPlatforms(root)
+ .keys
+ .joinToString("\n")
+ .also { fileWriter.write("package-list", it, "") }
+
+ protected open fun getPackageNamesAndPlatforms(root: PageNode): Map<String, List<PlatformData>> =
+ root.children
+ .map(::getPackageNamesAndPlatforms)
+ .fold(emptyMap<String, List<PlatformData>>()) { e, acc -> acc + e } +
+ if (root is PackagePageNode) {
+ mapOf(root.name to root.platforms())
+ } else {
+ emptyMap()
+ }
+
+ override fun render(root: PageNode) {
+ renderPackageList(root)
+ renderPages(root)
+ }
+}
diff --git a/core/src/main/kotlin/renderers/FileWriter.kt b/core/src/main/kotlin/renderers/FileWriter.kt
new file mode 100644
index 00000000..319926dd
--- /dev/null
+++ b/core/src/main/kotlin/renderers/FileWriter.kt
@@ -0,0 +1,32 @@
+package org.jetbrains.dokka.renderers
+
+import java.io.File
+import java.io.IOException
+import java.nio.file.Paths
+
+class FileWriter(val root: String, val extension: String){
+ private val createdFiles: MutableMap<String, Int> = mutableMapOf()
+
+ fun write(path: String, text: String, ext: String = extension){
+ if (createdFiles.getOrDefault(path, 0) > 0) {
+ println("ERROR. An attempt to write $root/$path several times!")
+ return
+ }
+
+ try {
+ println("Writing $root/$path$ext")
+ val dir = Paths.get(root, path.dropLastWhile { it != '/' }).toFile()
+ dir.mkdirsOrFail()
+ Paths.get(root, "$path$ext").toFile().writeText(text)
+ } catch (e : Throwable) {
+ println("Failed to write $this. ${e.message}")
+ e.printStackTrace()
+ }
+ }
+
+ private fun File.mkdirsOrFail() {
+ if (!mkdirs() && !exists()) {
+ throw IOException("Failed to create directory $this")
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/renderers/HtmlRenderer.kt b/core/src/main/kotlin/renderers/HtmlRenderer.kt
new file mode 100644
index 00000000..3a6e1576
--- /dev/null
+++ b/core/src/main/kotlin/renderers/HtmlRenderer.kt
@@ -0,0 +1,51 @@
+package org.jetbrains.dokka.renderers
+
+import org.jetbrains.dokka.pages.ContentLink
+import org.jetbrains.dokka.pages.ContentNode
+import org.jetbrains.dokka.pages.PageNode
+import org.jetbrains.dokka.resolvers.LocationProvider
+
+open class HtmlRenderer(outputDir: String, fileWriter: FileWriter, locationProvider: LocationProvider): DefaultRenderer(outputDir, fileWriter, locationProvider) {
+
+ override fun buildComment(parts: List<ContentNode>): String = "<p>${super.buildComment(parts)}</p>"
+
+ override fun buildSymbol(parts: List<ContentNode>): String = "<code>${super.buildSymbol(parts)}</code>"
+
+ override fun buildHeader(level: Int, text: String): String = "<h$level>$text</h$level>\n"
+
+ override fun buildNewLine(): String = "<br/>"
+
+ override fun buildLink(text: String, address: String): String = "<a href=\"$address\">$text</a>"
+
+ override fun buildCode(code: String): String = "<code>$code</code>"
+
+ override fun buildNavigation(): String = "" // TODO implement
+
+ override fun buildGroup(children: List<ContentNode>): String = "<tr>\n" +
+ "<td>" + children.find { it is ContentLink }?.build() + "</td>\n" +
+ "<td>" + children.filterNot { it is ContentLink }.joinToString("\n") { it.build() } + "</td>\n" +
+ "</tr>\n"
+
+ override fun buildBlock(name: String, content: List<ContentNode>): String =
+ buildHeader(2, name) + "<table>\n" + content.joinToString("\n") { it.build() } + "</table>"
+
+ override fun renderPage(page: PageNode) {
+ val pageText = buildStartHtml(page) + buildPageContent(page) + buildEndHtml()
+ fileWriter.write(locationProvider.resolve(page), pageText)
+ }
+
+ protected open fun buildStartHtml(page: PageNode) = """<!DOCTYPE html>
+ |<html>
+ |<head>
+ |<title>${page.name}</title>
+ |</head>
+ |<body>
+ |""".trimMargin()
+
+ protected open fun buildEndHtml() =
+ """
+ |
+ |</body>
+ |</html>
+ """.trimMargin()
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/renderers/Renderer.kt b/core/src/main/kotlin/renderers/Renderer.kt
new file mode 100644
index 00000000..24e01cdb
--- /dev/null
+++ b/core/src/main/kotlin/renderers/Renderer.kt
@@ -0,0 +1,14 @@
+package org.jetbrains.dokka.renderers
+
+import org.jetbrains.dokka.pages.PageNode
+
+interface Renderer {
+ fun render(root: PageNode)
+}
+
+//class Renderers{
+// abstract class Renderer(resolvers: List[Resolver])
+// class HtmlRenderer(list)
+// fun toHtml() = {
+// new HtmlRenderer(a, b,c)
+//} \ No newline at end of file
diff --git a/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt b/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt
new file mode 100644
index 00000000..6120e65e
--- /dev/null
+++ b/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt
@@ -0,0 +1,80 @@
+package org.jetbrains.dokka.resolvers
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.htmlEscape
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.pages.*
+
+open class DefaultLocationProvider(private val pageGraphRoot: PageNode, val configuration: DokkaConfiguration, val extension: String): LocationProvider { // TODO: cache
+ override fun resolve(node: PageNode): String = pathTo(node) + extension
+
+ override fun resolve(dri: DRI, platforms: List<PlatformData>): String {
+ findInPageGraph(dri, platforms)?.let { return resolve(it) }
+ // Not found in PageGraph, that means it's an external link
+
+ val externalDocs = configuration.passesConfigurations
+ .filter { passConfig -> passConfig.targets.toSet() == platforms.toSet() } // TODO: change targets to something better?
+ .flatMap { it.externalDocumentationLinks }.map { it.packageListUrl }.distinct()
+
+ return ExternalLocationProvider.getLocation(dri, externalDocs)
+ }
+
+ protected open fun findInPageGraph(dri: DRI, platforms: List<PlatformData>): PageNode? = pageGraphRoot.dfs { it.dri == dri }
+
+ protected open fun pathTo(node: PageNode): String { // TODO: can be refactored probably, also we should think about root
+ fun parentPath(parent: PageNode?): String {
+ if(parent == null) return ""
+ val parts = parent.parent?.let(::parentPath) ?: ""
+ return if(parent is PackagePageNode) {"$parts/${parent.name}"} else { "$parts/${identifierToFilename(parent.name)}" }
+ }
+
+ return parentPath(node.parent) + "/${identifierToFilename(node.name)}" +
+ if (node.children.isEmpty()) {
+ ""
+ } else {
+ "/index"
+ }
+ }
+}
+
+fun DRI.toJavadocLocation(jdkVersion: Int): String { // TODO: classes without packages?
+ val packageLink = packageName?.replace(".", "/")
+ if (classNames == null) {
+ return "$packageLink/package-summary.html".htmlEscape()
+ }
+ val classLink = if (packageLink == null) { "$classNames.html" } else { "$packageLink/$classNames.html" }
+ if (callable == null) {
+ return classLink.htmlEscape()
+ }
+
+ val callableLink = "$classLink#${callable.name}" + when {
+ jdkVersion < 8 -> "(${callable.params.joinToString(", ")})"
+ jdkVersion < 10 -> "-${callable.params.joinToString("-")}-"
+ else -> "(${callable.params.joinToString(",")})"
+ }
+
+ return callableLink.htmlEscape()
+}
+
+fun DRI.toDokkaLocation(extension: String): String { // TODO: classes without packages?
+ if (classNames == null) {
+ return "$packageName/index$extension"
+ }
+ val classLink = if (packageName == null) { "" } else { "$packageName/" } +
+ classNames.split('.').joinToString("/", transform = ::identifierToFilename)
+
+ if (callable == null) {
+ return "$classLink/index$extension"
+ }
+
+ return "$classLink/${identifierToFilename(callable.name)}$extension"
+}
+
+private val reservedFilenames = setOf("index", "con", "aux", "lst", "prn", "nul", "eof", "inp", "out")
+
+private fun identifierToFilename(name: String): String {
+ if (name.isEmpty()) return "--root--"
+ val escaped = name.replace('<', '-').replace('>', '-')
+ val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() }
+ return if (lowercase in reservedFilenames) "--$lowercase--" else lowercase
+}
diff --git a/core/src/main/kotlin/resolvers/ExternalLocationProvider.kt b/core/src/main/kotlin/resolvers/ExternalLocationProvider.kt
new file mode 100644
index 00000000..e9181148
--- /dev/null
+++ b/core/src/main/kotlin/resolvers/ExternalLocationProvider.kt
@@ -0,0 +1,98 @@
+package org.jetbrains.dokka.resolvers
+
+import org.jetbrains.dokka.links.DRI
+import java.net.HttpURLConnection
+import java.net.URL
+import java.net.URLConnection
+
+object ExternalLocationProvider { // TODO: Refactor this!!!
+ private const val DOKKA_PARAM_PREFIX = "\$dokka."
+
+ private val cache: MutableMap<URL, LocationInfo> = mutableMapOf()
+
+ fun getLocation(dri: DRI, packageLocations: List<URL>): String {
+ val toResolve: MutableList<URL> = mutableListOf()
+ for(url in packageLocations){
+ val info = cache[url]
+ if(info == null) {
+ toResolve.add(url)
+ } else if(info.packages.contains(dri.packageName)) {
+ return getLink(dri, info)
+ }
+ }
+ // Not in cache, resolve packageLists
+ while (toResolve.isNotEmpty()){
+ val loc = toResolve.first().also { toResolve.remove(it) }
+ val locationInfo = loadPackageList(loc)
+ if(locationInfo.packages.contains(dri.packageName)) {
+ return getLink(dri, locationInfo)
+ }
+ }
+ return ""
+ }
+
+ private fun getLink(dri: DRI, locationInfo: LocationInfo): String = when(locationInfo.format) {
+ "javadoc" -> dri.toJavadocLocation(8)
+ "kotlin-website-html", "html" -> locationInfo.locations[dri.packageName + "." + dri.classNames] ?: dri.toDokkaLocation(".html")
+ "markdown" -> locationInfo.locations[dri.packageName + "." + dri.classNames] ?: dri.toDokkaLocation(".md")
+ // TODO: rework this
+ else -> throw RuntimeException("Unrecognized format")
+ }
+
+
+ private fun loadPackageList(url: URL): 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() ?: "javadoc"
+
+ val locations = paramsMap["location"].orEmpty()
+ .map { it.split("\u001f", limit = 2) }
+ .map { (key, value) -> key to value }
+ .toMap()
+
+ val info = LocationInfo(format, 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
+ }
+ }
+ data class LocationInfo(val format: String, val packages: Set<String>, val locations: Map<String, String>)
+
+}
diff --git a/core/src/main/kotlin/resolvers/LocationProvider.kt b/core/src/main/kotlin/resolvers/LocationProvider.kt
new file mode 100644
index 00000000..b62aa999
--- /dev/null
+++ b/core/src/main/kotlin/resolvers/LocationProvider.kt
@@ -0,0 +1,10 @@
+package org.jetbrains.dokka.resolvers
+
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.pages.PageNode
+import org.jetbrains.dokka.pages.PlatformData
+
+interface LocationProvider {
+ fun resolve(dri: DRI, platforms: List<PlatformData>): String
+ fun resolve(node: PageNode): String
+}
diff --git a/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt b/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt
new file mode 100644
index 00000000..13e9b0d0
--- /dev/null
+++ b/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt
@@ -0,0 +1,29 @@
+package org.jetbrains.dokka.transformers
+
+import org.jetbrains.dokka.DocumentationNode
+import org.jetbrains.dokka.DocumentationNodes
+import org.jetbrains.dokka.pages.ContentNode
+import org.jetbrains.dokka.pages.ContentSymbol
+import org.jetbrains.dokka.pages.ModulePageNode
+import org.jetbrains.dokka.pages.PageNode
+
+//class DefaultDocumentationToPageTransformer: DocumentationToPageTransformer {
+// override fun transform(d: DocumentationNode): PageNode {
+// assert(d is DocumentationNodes.Module) // TODO: remove this if it's true. Fix this it it's not
+// val rootPage = ModulePageNode(d.name, contentFor(d), null)
+//
+// // TODO
+//
+// return rootPage
+// }
+//
+// private fun contentFor(d: DocumentationNode): List<ContentNode> {
+// val symbol = ContentSymbol()
+// }
+// private fun moduleContent(d: DocumentationNodes.Module)
+//
+// private fun symbolFor(d: DocumentationNode): List<ContentNode> {
+//
+// }
+//
+//} \ No newline at end of file
diff --git a/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt b/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt
new file mode 100644
index 00000000..5a155dc1
--- /dev/null
+++ b/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt
@@ -0,0 +1,8 @@
+package org.jetbrains.dokka.transformers
+
+import org.jetbrains.dokka.DocumentationNode
+import org.jetbrains.dokka.pages.PageNode
+
+interface DocumentationToPageTransformer {
+ fun transform (d: DocumentationNode): PageNode
+} \ No newline at end of file