Implement inbound links in java layout html
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt
index 1a0763f..5c740eb 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt
@@ -2,8 +2,13 @@
 
 import org.jetbrains.dokka.DocumentationModule
 import org.jetbrains.dokka.ExternalDocumentationLinkResolver.Companion.DOKKA_PARAM_PREFIX
+import org.jetbrains.dokka.InboundExternalLinkResolutionService
 import org.jetbrains.dokka.NodeKind
 import org.jetbrains.dokka.PackageListService
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
+import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
+import org.jetbrains.kotlin.types.KotlinType
 
 class JavaLayoutHtmlPackageListService : PackageListService {
 
@@ -19,10 +24,91 @@
 
         return buildString {
             appendParam("format", "java-layout-html")
+            appendParam("mode", "kotlin")
             for (p in packages) {
                 appendln(p)
             }
         }
     }
 
+}
+
+class JavaLayoutHtmlInboundLinkResolutionService(private val paramMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
+    private fun getContainerPath(symbol: DeclarationDescriptor): String? {
+        return when (symbol) {
+            is PackageFragmentDescriptor -> symbol.fqName.asString() + "/"
+            is ClassifierDescriptor -> getContainerPath(symbol.findPackage()) + symbol.nameWithOuter() + ".html"
+            else -> null
+        }
+    }
+
+    private fun DeclarationDescriptor.findPackage(): PackageFragmentDescriptor =
+        generateSequence(this) { it.containingDeclaration }.filterIsInstance<PackageFragmentDescriptor>().first()
+
+    private fun ClassifierDescriptor.nameWithOuter(): String =
+        generateSequence(this) { it.containingDeclaration as? ClassifierDescriptor }
+            .toList().asReversed().joinToString(".") { it.name.asString() }
+
+    private fun getPagePath(symbol: DeclarationDescriptor): String? {
+        return when (symbol) {
+            is PackageFragmentDescriptor -> getContainerPath(symbol) + "package-summary.html"
+            is ClassifierDescriptor -> getContainerPath(symbol) + "#"
+            is FunctionDescriptor, is PropertyDescriptor -> getContainerPath(symbol.containingDeclaration!!) + "#" + symbol.signatureForAnchorUrlEncoded()
+            else -> null
+        }
+    }
+
+    private fun DeclarationDescriptor.signatureForAnchor(): String? {
+
+        fun ReceiverParameterDescriptor.extractReceiverName(): String {
+            var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!!
+            if (receiverClass.isCompanionObject()) {
+                receiverClass = receiverClass.containingDeclaration!!
+            } else if (receiverClass is TypeParameterDescriptor) {
+                val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor
+                if (upperBoundClass != null) {
+                    receiverClass = upperBoundClass
+                }
+            }
+
+            return receiverClass.name.asString()
+        }
+
+        fun KotlinType.qualifiedNameForSignature(): String {
+            val desc = constructor.declarationDescriptor
+            return desc?.fqNameUnsafe?.asString() ?: "<ERROR TYPE NAME>"
+        }
+
+        fun StringBuilder.appendReceiverAndCompanion(desc: CallableDescriptor) {
+            if (desc.containingDeclaration.isCompanionObject()) {
+                append("Companion.")
+            }
+            desc.extensionReceiverParameter?.let {
+                append("(")
+                append(it.extractReceiverName())
+                append(").")
+            }
+        }
+
+        return when(this) {
+            is FunctionDescriptor -> buildString {
+                appendReceiverAndCompanion(this@signatureForAnchor)
+                append(name.asString())
+                valueParameters.joinTo(this, prefix = "(", postfix = ")") {
+                    it.type.qualifiedNameForSignature()
+                }
+            }
+            is PropertyDescriptor -> buildString {
+                appendReceiverAndCompanion(this@signatureForAnchor)
+                append(name.asString())
+                append(":")
+                append(returnType?.qualifiedNameForSignature())
+            }
+            else -> null
+        }
+    }
+
+    private fun DeclarationDescriptor.signatureForAnchorUrlEncoded(): String? = signatureForAnchor()?.urlEncoded()
+
+    override fun getPath(symbol: DeclarationDescriptor) = getPagePath(symbol)
 }
\ No newline at end of file
diff --git a/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties b/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties
new file mode 100644
index 0000000..3b61eab
--- /dev/null
+++ b/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.JavaLayoutHtmlInboundLinkResolutionService
+description=Resolver for JavaLayoutHtml
\ No newline at end of file
diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
index 160b3cb..fae8f27 100644
--- a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
+++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
@@ -1,8 +1,10 @@
 package org.jetbrains.dokka.tests
 
+import org.jetbrains.dokka.*
 import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptor
-import org.jetbrains.dokka.NodeKind
 import org.junit.Test
+import java.io.File
+import java.net.URL
 
 class JavaLayoutHtmlFormatTest : JavaLayoutHtmlFormatTestCase() {
     override val formatDescriptor = JavaLayoutHtmlFormatDescriptor()
@@ -55,4 +57,49 @@
     fun constJava() {
         verifyNode("ConstJava.java", noStdlibLink = true)
     }
+
+    @Test
+    fun inboundLinksInKotlinMode() {
+        val root = "./testdata/format/java-layout-html"
+
+        val options = DocumentationOptions(
+            "",
+            "java-layout-html",
+            sourceLinks = listOf(),
+            generateIndexPages = false,
+            noStdlibLink = true,
+            apiVersion = null,
+            languageVersion = null,
+            perPackageOptions = listOf(PackageOptionsImpl("foo", suppress = true)),
+            externalDocumentationLinks =
+            listOf(
+                DokkaConfiguration.ExternalDocumentationLink.Builder(
+                    URL("file:///"),
+                    File(root, "inboundLinksTestPackageList").toURI().toURL()
+                ).build()
+            )
+        )
+
+
+        val sourcePath = "$root/inboundLinksInKotlinMode.kt"
+        val documentation = DocumentationModule("test")
+
+        appendDocumentation(
+            documentation,
+            contentRootFromPath(sourcePath),
+            contentRootFromPath("$root/inboundLinksInKotlinMode.Dep.kt"),
+            withJdk = false,
+            withKotlinRuntime = false,
+            options = options
+        )
+        documentation.prepareForGeneration(options)
+
+        verifyModelOutput(documentation, ".html", sourcePath) { model, output ->
+            buildPagesAndReadInto(
+                model,
+                model.members.single { it.name == "bar" }.members,
+                output
+            )
+        }
+    }
 }
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.Dep.kt b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.Dep.kt
new file mode 100644
index 0000000..610ebb2
--- /dev/null
+++ b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.Dep.kt
@@ -0,0 +1,23 @@
+package foo
+
+
+fun foobar() {
+
+}
+
+
+val v = 22
+
+class G {
+
+    fun oo() = ""
+
+    val og = 11
+
+    companion object {
+
+        fun dg() = "22"
+
+        val dv = 12
+    }
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html
new file mode 100644
index 0000000..7025fc7
--- /dev/null
+++ b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html
@@ -0,0 +1,80 @@
+<!-- File: /test/bar/X.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>X</h1>
+    <pre><span class="keyword">class </span><span class="identifier">X</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">bar.X</a></td>
+      </tr>
+    </table>
+    <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a>
+See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a>
+See <a href="file:/foo/G.html#">foo.G</a>
+See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a>
+See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a>
+See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a>
+See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+            <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a>
+See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a>
+See <a href="file:/foo/G.html#">foo.G</a>
+See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a>
+See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a>
+See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a>
+See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Functions</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><span class="identifier">String</span></td>
+          <td>
+            <div><code><a href="file:/foo/G.html#"><span class="identifier">G</span></a>.<a href="#%28foo.G%29.ext%28%29">ext</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">X</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+      <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a>
+See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a>
+See <a href="file:/foo/G.html#">foo.G</a>
+See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a>
+See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a>
+See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a>
+See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p>
+    </div>
+    <h2>Functions</h2>
+    <div id="(foo.G).ext()">
+      <h3>ext</h3>
+      <pre><span class="keyword">fun </span><a href="file:/foo/G.html#"><span class="identifier">G</span></a><span class="symbol">.</span><span class="identifier">ext</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt
new file mode 100644
index 0000000..6420d78
--- /dev/null
+++ b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt
@@ -0,0 +1,16 @@
+package bar
+
+/**
+ * See [foo.foobar]
+ * See [foo.v]
+ * See [foo.G]
+ * See [foo.G.oo]
+ * See [foo.G.og]
+ * See [foo.G.Companion.dg]
+ * See [foo.G.Companion.dv]
+ */
+class X {
+
+    fun (foo.G).ext() = this.oo()
+}
+
diff --git a/core/testdata/format/java-layout-html/inboundLinksTestPackageList b/core/testdata/format/java-layout-html/inboundLinksTestPackageList
new file mode 100644
index 0000000..64d25c3
--- /dev/null
+++ b/core/testdata/format/java-layout-html/inboundLinksTestPackageList
@@ -0,0 +1,3 @@
+$dokka.format:java-layout-html
+$dokka.mode:kotlin
+foo