At-like function for Java/Kotlin
authorGiles Payne <gilespayne@gmail.com>
Sat, 5 Jun 2021 11:46:09 +0000 (20:46 +0900)
committerGiles Payne <gilespayne@gmail.com>
Sun, 6 Jun 2021 11:09:13 +0000 (20:09 +0900)
CMakeLists.txt
cmake/android/android_gradle_projects.cmake
modules/core/misc/java/src/java/core+Mat.java
modules/core/misc/java/src/java/core+MatAt.kt [new file with mode: 0644]
modules/java/android_sdk/android_gradle_lib/build.gradle
modules/java/android_sdk/build.gradle.in
modules/java/generator/gen_java.py
platforms/android/build_sdk.py
samples/android/build.gradle.in

index 3911856f028467aaedce06981217d6634e08d46b..49abe017a5eecb2cfb2bb91c0eb70408e89b62e7 100644 (file)
@@ -467,6 +467,7 @@ OCV_OPTION(BUILD_ANDROID_SERVICE    "Build OpenCV Manager for Google Play" OFF I
 OCV_OPTION(BUILD_CUDA_STUBS         "Build CUDA modules stubs when no CUDA SDK" OFF  IF (NOT APPLE_FRAMEWORK) )
 OCV_OPTION(BUILD_JAVA               "Enable Java support"                         (ANDROID OR NOT CMAKE_CROSSCOMPILING)  IF (ANDROID OR (NOT APPLE_FRAMEWORK AND NOT WINRT)) )
 OCV_OPTION(BUILD_OBJC               "Enable Objective-C support"                  ON  IF APPLE_FRAMEWORK )
+OCV_OPTION(BUILD_KOTLIN_EXTENSIONS  "Build Kotlin extensions (Android)"           ON  IF ANDROID )
 
 # OpenCV installation options
 # ===================================================
index 2e34a20d97a7db5495336bd2812cc79c6a47ef7e..e54fef1a2d6f98e9992219189ac40bc1c602c297 100644 (file)
@@ -2,6 +2,17 @@
 set(ANDROID_GRADLE_PLUGIN_VERSION "3.2.1" CACHE STRING "Android Gradle Plugin version")
 message(STATUS "Android Gradle Plugin version: ${ANDROID_GRADLE_PLUGIN_VERSION}")
 
+set(KOTLIN_PLUGIN_VERSION "1.5.10" CACHE STRING "Kotlin Plugin version")
+message(STATUS "kotlin Plugin version: ${KOTLIN_GRADLE_PLUGIN_VERSION}")
+
+if(BUILD_KOTLIN_EXTENSIONS)
+  set(KOTLIN_PLUGIN_DECLARATION "apply plugin: 'kotlin-android'" CACHE STRING "Kotlin Plugin version")
+  set(KOTLIN_STD_LIB "implementation 'org.jetbrains.kotlin:kotlin-stdlib:${KOTLIN_PLUGIN_VERSION}'" CACHE STRING "Kotlin Standard Library dependency")
+else()
+  set(KOTLIN_PLUGIN_DECLARATION "" CACHE STRING "Kotlin Plugin version")
+  set(KOTLIN_STD_LIB "" CACHE STRING "Kotlin Standard Library dependency")
+endif()
+
 set(GRADLE_VERSION "5.6.4" CACHE STRING "Gradle version")
 message(STATUS "Gradle version: ${GRADLE_VERSION}")
 
index 641d9f8ae8435ef50b0a6a740654596b4d1a8c60..5fcc72773873170ced80788153d33f3e13a1821e 100644 (file)
@@ -1128,6 +1128,458 @@ public class Mat {
         return cols();
     }
 
+    // javadoc:Mat::at(clazz, row, col)
+    @SuppressWarnings("unchecked")
+    public <T> Atable<T> at(Class<T> clazz, int row, int col) {
+        if (clazz == Byte.class || clazz == byte.class) {
+            return (Atable<T>)new AtableByte(this, row, col);
+        } else if (clazz == Double.class || clazz == double.class) {
+            return (Atable<T>)new AtableDouble(this, row, col);
+        } else if (clazz == Float.class || clazz == float.class) {
+            return (Atable<T>)new AtableFloat(this, row, col);
+        } else if (clazz == Integer.class || clazz == int.class) {
+            return (Atable<T>)new AtableInteger(this, row, col);
+        } else if (clazz == Short.class || clazz == short.class) {
+            return (Atable<T>)new AtableShort(this, row, col);
+        } else {
+            throw new RuntimeException("Unsupported class type");
+        }
+    }
+
+    // javadoc:Mat::at(clazz, idx)
+    @SuppressWarnings("unchecked")
+    public <T> Atable<T> at(Class<T> clazz, int[] idx) {
+        if (clazz == Byte.class || clazz == byte.class) {
+            return (Atable<T>)new AtableByte(this, idx);
+        } else if (clazz == Double.class || clazz == double.class) {
+            return (Atable<T>)new AtableDouble(this, idx);
+        } else if (clazz == Float.class || clazz == float.class) {
+            return (Atable<T>)new AtableFloat(this, idx);
+        } else if (clazz == Integer.class || clazz == int.class) {
+            return (Atable<T>)new AtableInteger(this, idx);
+        } else if (clazz == Short.class || clazz == short.class) {
+            return (Atable<T>)new AtableShort(this, idx);
+        } else {
+            throw new RuntimeException("Unsupported class parameter");
+        }
+    }
+
+    public static class Tuple2<T> {
+        public Tuple2(T _0, T _1) {
+            this._0 = _0;
+            this._1 = _1;
+        }
+
+        public T get_0() {
+            return _0;
+        }
+
+        public T get_1() {
+            return _1;
+        }
+
+        private final T _0;
+        private final T _1;
+    }
+
+    public static class Tuple3<T> {
+        public Tuple3(T _0, T _1, T _2) {
+            this._0 = _0;
+            this._1 = _1;
+            this._2 = _2;
+        }
+
+        public T get_0() {
+            return _0;
+        }
+
+        public T get_1() {
+            return _1;
+        }
+
+        public T get_2() {
+            return _2;
+        }
+
+        private final T _0;
+        private final T _1;
+        private final T _2;
+    }
+
+    public static class Tuple4<T> {
+        public Tuple4(T _0, T _1, T _2, T _3) {
+            this._0 = _0;
+            this._1 = _1;
+            this._2 = _2;
+            this._3 = _3;
+        }
+
+        public T get_0() {
+            return _0;
+        }
+
+        public T get_1() {
+            return _1;
+        }
+
+        public T get_2() {
+            return _2;
+        }
+
+        public T get_3() {
+            return _3;
+        }
+
+        private final T _0;
+        private final T _1;
+        private final T _2;
+        private final T _3;
+    }
+
+    public interface Atable<T> {
+        T getV();
+        void setV(T v);
+        Tuple2<T> getV2c();
+        void setV2c(Tuple2<T> v);
+        Tuple3<T> getV3c();
+        void setV3c(Tuple3<T> v);
+        Tuple4<T> getV4c();
+        void setV4c(Tuple4<T> v);
+    }
+
+    private static class AtableBase {
+
+        protected AtableBase(Mat mat, int row, int col) {
+            this.mat = mat;
+            indices = new int[2];
+            indices[0] = row;
+            indices[1] = col;
+        }
+
+        protected AtableBase(Mat mat, int[] indices) {
+            this.mat = mat;
+            this.indices = indices;
+        }
+
+        protected final Mat mat;
+        protected final int[] indices;
+    }
+
+    private static class AtableByte extends AtableBase implements Atable<Byte> {
+
+        public AtableByte(Mat mat, int row, int col) {
+            super(mat, row, col);
+        }
+
+        public AtableByte(Mat mat, int[] indices) {
+            super(mat, indices);
+        }
+
+        @Override
+        public Byte getV() {
+            byte[] data = new byte[1];
+            mat.get(indices, data);
+            return data[0];
+        }
+
+        @Override
+        public void setV(Byte v) {
+            byte[] data = new byte[] { v };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple2<Byte> getV2c() {
+            byte[] data = new byte[2];
+            mat.get(indices, data);
+            return new Tuple2<Byte>(data[0], data[1]);
+        }
+
+        @Override
+        public void setV2c(Tuple2<Byte> v) {
+            byte[] data = new byte[] { v._0, v._1 };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple3<Byte> getV3c() {
+            byte[] data = new byte[3];
+            mat.get(indices, data);
+            return new Tuple3<Byte>(data[0], data[1], data[2]);
+        }
+
+        @Override
+        public void setV3c(Tuple3<Byte> v) {
+            byte[] data = new byte[] { v._0, v._1, v._2 };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple4<Byte> getV4c() {
+            byte[] data = new byte[4];
+            mat.get(indices, data);
+            return new Tuple4<Byte>(data[0], data[1], data[2], data[3]);
+        }
+
+        @Override
+        public void setV4c(Tuple4<Byte> v) {
+            byte[] data = new byte[] { v._0, v._1, v._2, v._3 };
+            mat.put(indices, data);
+        }
+    }
+
+    private static class AtableDouble extends AtableBase implements Atable<Double> {
+
+        public AtableDouble(Mat mat, int row, int col) {
+            super(mat, row, col);
+        }
+
+        public AtableDouble(Mat mat, int[] indices) {
+            super(mat, indices);
+        }
+
+        @Override
+        public Double getV() {
+            double[] data = new double[1];
+            mat.get(indices, data);
+            return data[0];
+        }
+
+        @Override
+        public void setV(Double v) {
+            double[] data = new double[] { v };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple2<Double> getV2c() {
+            double[] data = new double[2];
+            mat.get(indices, data);
+            return new Tuple2<Double>(data[0], data[1]);
+        }
+
+        @Override
+        public void setV2c(Tuple2<Double> v) {
+            double[] data = new double[] { v._0, v._1 };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple3<Double> getV3c() {
+            double[] data = new double[3];
+            mat.get(indices, data);
+            return new Tuple3<Double>(data[0], data[1], data[2]);
+        }
+
+        @Override
+        public void setV3c(Tuple3<Double> v) {
+            double[] data = new double[] { v._0, v._1, v._2 };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple4<Double> getV4c() {
+            double[] data = new double[4];
+            mat.get(indices, data);
+            return new Tuple4<Double>(data[0], data[1], data[2], data[3]);
+        }
+
+        @Override
+        public void setV4c(Tuple4<Double> v) {
+            double[] data = new double[] { v._0, v._1, v._2, v._3 };
+            mat.put(indices, data);
+        }
+    }
+
+    private static class AtableFloat extends AtableBase implements Atable<Float> {
+
+        public AtableFloat(Mat mat, int row, int col) {
+            super(mat, row, col);
+        }
+
+        public AtableFloat(Mat mat, int[] indices) {
+            super(mat, indices);
+        }
+
+        @Override
+        public Float getV() {
+            float[] data = new float[1];
+            mat.get(indices, data);
+            return data[0];
+        }
+
+        @Override
+        public void setV(Float v) {
+            float[] data = new float[] { v };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple2<Float> getV2c() {
+            float[] data = new float[2];
+            mat.get(indices, data);
+            return new Tuple2<Float>(data[0], data[1]);
+        }
+
+        @Override
+        public void setV2c(Tuple2<Float> v) {
+            float[] data = new float[] { v._0, v._1 };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple3<Float> getV3c() {
+            float[] data = new float[3];
+            mat.get(indices, data);
+            return new Tuple3<Float>(data[0], data[1], data[2]);
+        }
+
+        @Override
+        public void setV3c(Tuple3<Float> v) {
+            float[] data = new float[] { v._0, v._1, v._2 };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple4<Float> getV4c() {
+            float[] data = new float[4];
+            mat.get(indices, data);
+            return new Tuple4<Float>(data[0], data[1], data[2], data[3]);
+        }
+
+        @Override
+        public void setV4c(Tuple4<Float> v) {
+            double[] data = new double[] { v._0, v._1, v._2, v._3 };
+            mat.put(indices, data);
+        }
+    }
+
+    private static class AtableInteger extends AtableBase implements Atable<Integer> {
+
+        public AtableInteger(Mat mat, int row, int col) {
+            super(mat, row, col);
+        }
+
+        public AtableInteger(Mat mat, int[] indices) {
+            super(mat, indices);
+        }
+
+        @Override
+        public Integer getV() {
+            int[] data = new int[1];
+            mat.get(indices, data);
+            return data[0];
+        }
+
+        @Override
+        public void setV(Integer v) {
+            int[] data = new int[] { v };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple2<Integer> getV2c() {
+            int[] data = new int[2];
+            mat.get(indices, data);
+            return new Tuple2<Integer>(data[0], data[1]);
+        }
+
+        @Override
+        public void setV2c(Tuple2<Integer> v) {
+            int[] data = new int[] { v._0, v._1 };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple3<Integer> getV3c() {
+            int[] data = new int[3];
+            mat.get(indices, data);
+            return new Tuple3<Integer>(data[0], data[1], data[2]);
+        }
+
+        @Override
+        public void setV3c(Tuple3<Integer> v) {
+            int[] data = new int[] { v._0, v._1, v._2 };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple4<Integer> getV4c() {
+            int[] data = new int[4];
+            mat.get(indices, data);
+            return new Tuple4<Integer>(data[0], data[1], data[2], data[3]);
+        }
+
+        @Override
+        public void setV4c(Tuple4<Integer> v) {
+            int[] data = new int[] { v._0, v._1, v._2, v._3 };
+            mat.put(indices, data);
+        }
+    }
+
+    private static class AtableShort extends AtableBase implements Atable<Short> {
+
+        public AtableShort(Mat mat, int row, int col) {
+            super(mat, row, col);
+        }
+
+        public AtableShort(Mat mat, int[] indices) {
+            super(mat, indices);
+        }
+
+        @Override
+        public Short getV() {
+            short[] data = new short[1];
+            mat.get(indices, data);
+            return data[0];
+        }
+
+        @Override
+        public void setV(Short v) {
+            short[] data = new short[] { v };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple2<Short> getV2c() {
+            short[] data = new short[2];
+            mat.get(indices, data);
+            return new Tuple2<Short>(data[0], data[1]);
+        }
+
+        @Override
+        public void setV2c(Tuple2<Short> v) {
+            short[] data = new short[] { v._0, v._1 };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple3<Short> getV3c() {
+            short[] data = new short[3];
+            mat.get(indices, data);
+            return new Tuple3<Short>(data[0], data[1], data[2]);
+        }
+
+        @Override
+        public void setV3c(Tuple3<Short> v) {
+            short[] data = new short[] { v._0, v._1, v._2 };
+            mat.put(indices, data);
+        }
+
+        @Override
+        public Tuple4<Short> getV4c() {
+            short[] data = new short[4];
+            mat.get(indices, data);
+            return new Tuple4<Short>(data[0], data[1], data[2], data[3]);
+        }
+
+        @Override
+        public void setV4c(Tuple4<Short> v) {
+            short[] data = new short[] { v._0, v._1, v._2, v._3 };
+            mat.put(indices, data);
+        }
+    }
+
     // javadoc:Mat::getNativeObjAddr()
     public long getNativeObjAddr() {
         return nativeObj;
diff --git a/modules/core/misc/java/src/java/core+MatAt.kt b/modules/core/misc/java/src/java/core+MatAt.kt
new file mode 100644 (file)
index 0000000..f48a3de
--- /dev/null
@@ -0,0 +1,99 @@
+package org.opencv.core
+
+import org.opencv.core.Mat.*
+import java.lang.RuntimeException
+
+/***
+ *  Example use:
+ *
+ *  val (b, g, r) = mat.at<UByte>(50, 50).v3c
+ *  mat.at<UByte>(50, 50).val = T3(245u, 113u, 34u)
+ *
+ */
+@Suppress("UNCHECKED_CAST")
+inline fun <reified T> Mat.at(row: Int, col: Int) : Atable<T> =
+    when (T::class) {
+        Byte::class, Double::class, Float::class, Int::class, Short::class -> this.at(
+            T::class.java,
+            row,
+            col
+        )
+        UByte::class -> AtableUByte(this, row, col) as Atable<T>
+        else -> throw RuntimeException("Unsupported class type")
+    }
+
+@Suppress("UNCHECKED_CAST")
+inline fun <reified T> Mat.at(idx: IntArray) : Atable<T> =
+    when (T::class) {
+        Byte::class, Double::class, Float::class, Int::class, Short::class -> this.at(
+            T::class.java,
+            idx
+        )
+        UByte::class -> AtableUByte(this, idx) as Atable<T>
+        else -> throw RuntimeException("Unsupported class type")
+    }
+
+class AtableUByte(val mat: Mat, val indices: IntArray): Atable<UByte> {
+
+    constructor(mat: Mat, row: Int, col: Int) : this(mat, intArrayOf(row, col))
+
+    override fun getV(): UByte {
+        val data = ByteArray(1)
+        mat[indices, data]
+        return data[0].toUByte()
+    }
+
+    override fun setV(v: UByte) {
+        val data = byteArrayOf(v.toByte())
+        mat.put(indices, data)
+    }
+
+    override fun getV2c(): Tuple2<UByte> {
+        val data = ByteArray(2)
+        mat[indices, data]
+        return Tuple2(data[0].toUByte(), data[1].toUByte())
+    }
+
+    override fun setV2c(v: Tuple2<UByte>) {
+        val data = byteArrayOf(v._0.toByte(), v._1.toByte())
+        mat.put(indices, data)
+    }
+
+    override fun getV3c(): Tuple3<UByte> {
+        val data = ByteArray(3)
+        mat[indices, data]
+        return Tuple3(data[0].toUByte(), data[1].toUByte(), data[2].toUByte())
+    }
+
+    override fun setV3c(v: Tuple3<UByte>) {
+        val data = byteArrayOf(v._0.toByte(), v._1.toByte(), v._2.toByte())
+        mat.put(indices, data)
+    }
+
+    override fun getV4c(): Tuple4<UByte> {
+        val data = ByteArray(4)
+        mat[indices, data]
+        return Tuple4(data[0].toUByte(), data[1].toUByte(), data[2].toUByte(), data[3].toUByte())
+    }
+
+    override fun setV4c(v: Tuple4<UByte>) {
+        val data = byteArrayOf(v._0.toByte(), v._1.toByte(), v._2.toByte(), v._3.toByte())
+        mat.put(indices, data)
+    }
+}
+
+operator fun <T> Tuple2<T>.component1(): T = this._0
+operator fun <T> Tuple2<T>.component2(): T = this._1
+
+operator fun <T> Tuple3<T>.component1(): T = this._0
+operator fun <T> Tuple3<T>.component2(): T = this._1
+operator fun <T> Tuple3<T>.component3(): T = this._2
+
+operator fun <T> Tuple4<T>.component1(): T = this._0
+operator fun <T> Tuple4<T>.component2(): T = this._1
+operator fun <T> Tuple4<T>.component3(): T = this._2
+operator fun <T> Tuple4<T>.component4(): T = this._3
+
+fun <T> T2(_0: T, _1: T) : Tuple2<T> = Tuple2(_0, _1)
+fun <T> T3(_0: T, _1: T, _2: T) : Tuple3<T> = Tuple3(_0, _1, _2)
+fun <T> T4(_0: T, _1: T, _2: T, _3: T) : Tuple4<T> = Tuple4(_0, _1, _2, _3)
index 8f969a0a864a5051fe2ac1f1b568901801bb7222..3966c9def6cd3f41f98315ed760b616b9d1e8e30 100644 (file)
@@ -1,4 +1,5 @@
 apply plugin: 'com.android.library'
+@KOTLIN_PLUGIN_DECLARATION@
 
 def openCVersionName = "@OPENCV_VERSION@"
 def openCVersionCode = ((@OPENCV_VERSION_MAJOR@ * 100 + @OPENCV_VERSION_MINOR@) * 100 + @OPENCV_VERSION_PATCH@) * 10 + 0
index 4ff5cd86be23a0882ee504e3a5babd8d991abf56..3ac66b3c79c3e7c1c273abcc8ca2b36130ac3af8 100644 (file)
@@ -89,6 +89,7 @@
 //
 
 apply plugin: 'com.android.library'
+@KOTLIN_PLUGIN_DECLARATION@
 
 def openCVersionName = "@OPENCV_VERSION@"
 def openCVersionCode = ((@OPENCV_VERSION_MAJOR@ * 100 + @OPENCV_VERSION_MINOR@) * 100 + @OPENCV_VERSION_PATCH@) * 10 + 0
index e41117558a26c69e1516e8950a7bbdbf37765990..6019ca340d2530e8c768b0fcf73ac7452115204c 100755 (executable)
@@ -1236,13 +1236,13 @@ JNIEXPORT void JNICALL Java_org_opencv_%(module)s_%(j_cls)s_delete
 def copy_java_files(java_files_dir, java_base_path, default_package_path='org/opencv/'):
     global total_files, updated_files
     java_files = []
-    re_filter = re.compile(r'^.+\.(java|aidl)(.in)?$')
+    re_filter = re.compile(r'^.+\.(java|aidl|kt)(.in)?$')
     for root, dirnames, filenames in os.walk(java_files_dir):
        java_files += [os.path.join(root, filename) for filename in filenames if re_filter.match(filename)]
     java_files = [f.replace('\\', '/') for f in java_files]
 
     re_package = re.compile(r'^package +(.+);')
-    re_prefix = re.compile(r'^.+[\+/]([^\+]+).(java|aidl)(.in)?$')
+    re_prefix = re.compile(r'^.+[\+/]([^\+]+).(java|aidl|kt)(.in)?$')
     for java_file in java_files:
         src = checkFileRemap(java_file)
         with open(src, 'r') as f:
index 7d898e4c366bc7197f117c9a3ebbb7599f4af893..88cb5ff87f26c6443f374813ffb62f905d9b669b 100755 (executable)
@@ -159,6 +159,7 @@ class Builder:
         self.debug_info = True if config.debug_info else False
         self.no_samples_build = True if config.no_samples_build else False
         self.opencl = True if config.opencl else False
+        self.no_kotlin = True if config.no_kotlin else False
 
     def get_cmake(self):
         if not self.config.use_android_buildtools and check_executable(['cmake', '--version']):
@@ -219,6 +220,7 @@ class Builder:
             CMAKE_TOOLCHAIN_FILE=self.get_toolchain_file(),
             INSTALL_CREATE_DISTRIB="ON",
             WITH_OPENCL="OFF",
+            BUILD_KOTLIN_EXTENSIONS="ON",
             WITH_IPP=("ON" if abi.haveIPP() else "OFF"),
             WITH_TBB="ON",
             BUILD_EXAMPLES="OFF",
@@ -240,6 +242,9 @@ class Builder:
         if self.opencl:
             cmake_vars['WITH_OPENCL'] = "ON"
 
+        if self.no_kotlin:
+            cmake_vars['BUILD_KOTLIN_EXTENSIONS'] = "OFF"
+
         if self.config.modules_list is not None:
             cmd.append("-DBUILD_LIST='%s'" % self.config.modules_list)
 
@@ -359,6 +364,7 @@ if __name__ == "__main__":
     parser.add_argument('--debug_info', action="store_true", help="Build with debug information (useful for Release mode: BUILD_WITH_DEBUG_INFO=ON)")
     parser.add_argument('--no_samples_build', action="store_true", help="Do not build samples (speeds up build)")
     parser.add_argument('--opencl', action="store_true", help="Enable OpenCL support")
+    parser.add_argument('--no_kotlin', action="store_true", help="Disable Kotlin extensions")
     args = parser.parse_args()
 
     log.basicConfig(format='%(message)s', level=log.DEBUG)
index e89eb911be7d13f09ae402f9df679ad21311bdd7..934a1a69d0db18b5b9752e565485607976a23e44 100644 (file)
@@ -8,6 +8,7 @@ buildscript {
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:@ANDROID_GRADLE_PLUGIN_VERSION@'
+        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:@KOTLIN_PLUGIN_VERSION@'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files