THRIFT-5571: add metadata map building to kotlin generator (#2584)

diff --git a/lib/kotlin/Makefile.am b/lib/kotlin/Makefile.am
index 6a99628..f57fc33 100644
--- a/lib/kotlin/Makefile.am
+++ b/lib/kotlin/Makefile.am
@@ -22,7 +22,7 @@
 all-local:
 	$(GRADLE) $(GRADLE_OPTS) assemble \
 		-Pthrift.version=$(PACKAGE_VERSION) \
-    -Pthrift.compiler=$(THRIFT) \
+		-Pthrift.compiler=$(THRIFT) \
 		--console=plain
 
 clean-local:
diff --git a/lib/kotlin/build.gradle.kts b/lib/kotlin/build.gradle.kts
new file mode 100644
index 0000000..6e944b0
--- /dev/null
+++ b/lib/kotlin/build.gradle.kts
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+plugins {
+    kotlin("jvm")
+    id("com.ncorti.ktfmt.gradle")
+}
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
+    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
+    // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-jdk8
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.6.1")
+    // https://mvnrepository.com/artifact/org.apache.thrift/libthrift
+    implementation("org.apache.thrift:libthrift:INCLUDED")
+    testImplementation(kotlin("test"))
+}
+
+tasks {
+    ktfmt {
+        kotlinLangStyle()
+    }
+
+    test {
+        useJUnitPlatform()
+    }
+
+    task<Exec>("compileThrift") {
+        val thriftBin = if (hasProperty("thrift.compiler")) {
+            file(property("thrift.compiler"))
+        } else {
+            project.rootDir.resolve("../../compiler/cpp/thrift")
+        }
+        val outputDir = layout.buildDirectory.dir("generated-sources")
+        doFirst {
+            mkdir(outputDir)
+        }
+        commandLine = listOf(
+            thriftBin.absolutePath,
+            "-gen",
+            "kotlin",
+            "-out",
+            outputDir.get().toString(),
+            layout.projectDirectory.file("src/test/resources/AnnotationTest.thrift").asFile.absolutePath
+        )
+        group = LifecycleBasePlugin.BUILD_GROUP
+    }
+
+    compileKotlin {
+        dependsOn("compileThrift")
+    }
+}
+
+sourceSets["main"].java {
+    srcDir(layout.buildDirectory.dir("generated-sources"))
+}
diff --git a/lib/kotlin/cross-test-client/build.gradle.kts b/lib/kotlin/cross-test-client/build.gradle.kts
index 3e67abf..e5f87dd 100644
--- a/lib/kotlin/cross-test-client/build.gradle.kts
+++ b/lib/kotlin/cross-test-client/build.gradle.kts
@@ -18,8 +18,8 @@
  */
 
 plugins {
-    kotlin("jvm") version "1.5.31"
-    id("com.ncorti.ktfmt.gradle") version "0.4.0"
+    kotlin("jvm")
+    id("com.ncorti.ktfmt.gradle")
     java
     application
 }
@@ -37,8 +37,6 @@
     implementation("org.apache.thrift:libthrift:INCLUDED")
     // https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
     implementation("ch.qos.logback:logback-classic:1.3.0-alpha14")
-    testImplementation("org.jetbrains.kotlin:kotlin-test")
-    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
 }
 
 tasks {
diff --git a/lib/kotlin/cross-test-client/src/main/kotlin/org/apache/thrift/test/TestClient.kt b/lib/kotlin/cross-test-client/src/main/kotlin/org/apache/thrift/test/TestClient.kt
index 10ca829..7597f2f 100644
--- a/lib/kotlin/cross-test-client/src/main/kotlin/org/apache/thrift/test/TestClient.kt
+++ b/lib/kotlin/cross-test-client/src/main/kotlin/org/apache/thrift/test/TestClient.kt
@@ -18,6 +18,9 @@
  */
 package org.apache.thrift.test
 
+import java.nio.ByteBuffer
+import kotlin.math.abs
+import kotlin.system.exitProcess
 import org.apache.http.impl.client.HttpClients
 import org.apache.thrift.TApplicationException
 import org.apache.thrift.TException
@@ -43,9 +46,6 @@
 import thrift.test.Xception2
 import thrift.test.Xtruct
 import thrift.test.Xtruct2
-import java.nio.ByteBuffer
-import kotlin.math.abs
-import kotlin.system.exitProcess
 
 /**
  * Test Java client for thrift. Essentially just a copy of the C++ version, this makes a variety of
@@ -739,13 +739,13 @@
                 val m1 = mm[4]!!
                 val m2 = mm[-4]!!
                 if (m1[1] != 1 ||
-                    m1[2] != 2 ||
-                    m1[3] != 3 ||
-                    m1[4] != 4 ||
-                    m2[-1] != -1 ||
-                    m2[-2] != -2 ||
-                    m2[-3] != -3 ||
-                    m2[-4] != -4
+                        m1[2] != 2 ||
+                        m1[3] != 3 ||
+                        m1[4] != 4 ||
+                        m2[-1] != -1 ||
+                        m2[-2] != -2 ||
+                        m2[-3] != -3 ||
+                        m2[-4] != -4
                 ) {
                     returnCode = returnCode or ERR_CONTAINERS
                     println("*** FAILURE ***\n")
@@ -810,12 +810,12 @@
                     val first_map = whoa[1L]!!
                     val second_map = whoa[2L]!!
                     if (first_map.size == 2 &&
-                        first_map.containsKey(Numberz.TWO) &&
-                        first_map.containsKey(Numberz.THREE) &&
-                        second_map.size == 1 &&
-                        second_map.containsKey(Numberz.SIX) &&
-                        insane == first_map[Numberz.TWO] &&
-                        insane == first_map[Numberz.THREE]
+                            first_map.containsKey(Numberz.TWO) &&
+                            first_map.containsKey(Numberz.THREE) &&
+                            second_map.size == 1 &&
+                            second_map.containsKey(Numberz.SIX) &&
+                            insane == first_map[Numberz.TWO] &&
+                            insane == first_map[Numberz.THREE]
                     ) {
                         val six = second_map[Numberz.SIX]!!
                         // Cannot use "new Insanity().equals(six)" because as of now,
@@ -901,8 +901,8 @@
             if (onewayElapsedMillis > 200) {
                 println(
                     "Oneway test took too long to execute failed: took " +
-                            onewayElapsedMillis +
-                            "ms"
+                        onewayElapsedMillis +
+                        "ms"
                 )
                 println(
                     "oneway calls are 'fire and forget' and therefore should not cause blocking."
@@ -1011,29 +1011,31 @@
             }
         }
         else -> {
-            val socket = if (ssl) {
-                TSSLTransportFactory.getClientSocket(host, port, socketTimeout)
-            } else {
-                println("using non-blocking socket $host:$port")
-                TNonblockingSocket(host, port, socketTimeout)
-            }
+            val socket =
+                if (ssl) {
+                    TSSLTransportFactory.getClientSocket(host, port, socketTimeout)
+                } else {
+                    println("using non-blocking socket $host:$port")
+                    TNonblockingSocket(host, port, socketTimeout)
+                }
             if (transport_type == "zlib") {
                 return TZlibTransport(socket)
             } else {
-                val wrapped = when (transport_type) {
-                    "buffered" -> {
-                        socket
+                val wrapped =
+                    when (transport_type) {
+                        "buffered" -> {
+                            socket
+                        }
+                        "framed" -> {
+                            TFramedTransport(socket)
+                        }
+                        "fastframed" -> {
+                            TFastFramedTransport(socket)
+                        }
+                        else -> {
+                            socket
+                        }
                     }
-                    "framed" -> {
-                        TFramedTransport(socket)
-                    }
-                    "fastframed" -> {
-                        TFastFramedTransport(socket)
-                    }
-                    else -> {
-                        socket
-                    }
-                }
                 return if (zlib) {
                     TZlibTransport(wrapped)
                 } else {
diff --git a/lib/kotlin/cross-test-server/build.gradle.kts b/lib/kotlin/cross-test-server/build.gradle.kts
index 6b20de1..8a654d9 100644
--- a/lib/kotlin/cross-test-server/build.gradle.kts
+++ b/lib/kotlin/cross-test-server/build.gradle.kts
@@ -18,8 +18,8 @@
  */
 
 plugins {
-    kotlin("jvm") version "1.5.31"
-    id("com.ncorti.ktfmt.gradle") version "0.4.0"
+    kotlin("jvm")
+    id("com.ncorti.ktfmt.gradle")
     java
     application
 }
@@ -37,8 +37,6 @@
     implementation("org.apache.thrift:libthrift:INCLUDED")
     // https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
     implementation("ch.qos.logback:logback-classic:1.3.0-alpha14")
-    testImplementation("org.jetbrains.kotlin:kotlin-test")
-    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
 }
 
 tasks {
diff --git a/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestServer.kt b/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestServer.kt
index b04548d..4b2bdff 100644
--- a/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestServer.kt
+++ b/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestServer.kt
@@ -143,7 +143,7 @@
         var zlib = false
         var transportType = "buffered"
         var protocolType = "binary"
-//        var serverType = "thread-pool"
+        //  var serverType = "thread-pool"
         var serverType = "nonblocking"
         val domainSocket = ""
         var stringLimit: Long = -1
@@ -308,11 +308,12 @@
             // Blocking servers
 
             // SSL socket
-            val tServerSocket: TServerSocket = if (ssl) {
-                TSSLTransportFactory.getServerSocket(port, 0)
-            } else {
-                TServerSocket(ServerSocketTransportArgs().port(port))
-            }
+            val tServerSocket: TServerSocket =
+                if (ssl) {
+                    TSSLTransportFactory.getServerSocket(port, 0)
+                } else {
+                    TServerSocket(ServerSocketTransportArgs().port(port))
+                }
             if (serverType == "simple") {
                 // Simple Server
                 val tServerArgs = TServer.Args(tServerSocket)
diff --git a/lib/kotlin/settings.gradle.kts b/lib/kotlin/settings.gradle.kts
index f412707..a6bdbbc 100644
--- a/lib/kotlin/settings.gradle.kts
+++ b/lib/kotlin/settings.gradle.kts
@@ -16,6 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+pluginManagement {
+    plugins {
+        kotlin("jvm") version "1.5.31"
+        id("com.ncorti.ktfmt.gradle") version "0.4.0"
+    }
+}
 
 rootProject.name = "libthrift-kotlin"
 
diff --git a/lib/kotlin/src/test/kotlin/org/apache/thrift/MetaDataTest.kt b/lib/kotlin/src/test/kotlin/org/apache/thrift/MetaDataTest.kt
new file mode 100644
index 0000000..e066bbe
--- /dev/null
+++ b/lib/kotlin/src/test/kotlin/org/apache/thrift/MetaDataTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.thrift
+
+import kotlin.test.assertEquals
+import org.apache.thrift.kotlin.annotation.test.Person
+import org.apache.thrift.meta_data.FieldMetaData
+import org.junit.jupiter.api.Test
+
+internal class MetaDataTest {
+    @Test
+    internal fun testAnnotation() {
+        val personMetadata = FieldMetaData.getStructMetaDataMap(Person::class.java)
+        assertEquals(3, personMetadata.size)
+        val idField = personMetadata[Person._Fields.ID]!!
+        assertEquals("id", idField.fieldName)
+        assertEquals(
+            mapOf(
+                "max" to "100000",
+                "min" to "1",
+            ),
+            idField.fieldAnnotations
+        )
+    }
+}
diff --git a/lib/kotlin/src/test/resources/AnnotationTest.thrift b/lib/kotlin/src/test/resources/AnnotationTest.thrift
new file mode 100644
index 0000000..0c4bca2
--- /dev/null
+++ b/lib/kotlin/src/test/resources/AnnotationTest.thrift
@@ -0,0 +1,7 @@
+namespace java org.apache.thrift.kotlin.annotation.test
+
+struct Person {
+  1: required i64 id (min="1", max="100000")
+  2: required string name
+  3: optional string phoneNumber
+}