diff --git a/.github/workflows/build-on-ubuntu.yml b/.github/workflows/build-on-ubuntu.yml
index 788b891..55bb482 100644
--- a/.github/workflows/build-on-ubuntu.yml
+++ b/.github/workflows/build-on-ubuntu.yml
@@ -24,7 +24,7 @@ jobs:
# See: https://github.com/marketplace/actions/junit-report-action
- name: Publish Test Report
- uses: mikepenz/action-junit-report@v3.7.6
+ uses: mikepenz/action-junit-report@v4.0.3
if: always() # always run even if the previous step fails
with:
report_paths: '**/build/test-results/**/TEST-*.xml'
diff --git a/.github/workflows/build-on-windows.yml b/.github/workflows/build-on-windows.yml
index c910b20..ff947a6 100644
--- a/.github/workflows/build-on-windows.yml
+++ b/.github/workflows/build-on-windows.yml
@@ -18,8 +18,9 @@ jobs:
distribution: zulu
cache: gradle
+ # See: https://github.com/al-cheb/configure-pagefile-action
- name: Configure Pagefile
- uses: al-cheb/configure-pagefile-action@v1.2
+ uses: al-cheb/configure-pagefile-action@v1.3
- name: Build project and run tests
shell: cmd
@@ -28,7 +29,7 @@ jobs:
# See: https://github.com/marketplace/actions/junit-report-action
- name: Publish Test Report
- uses: mikepenz/action-junit-report@v3.7.6
+ uses: mikepenz/action-junit-report@v4.0.3
if: always() # always run even if the previous step fails
with:
report_paths: '**/build/test-results/**/TEST-*.xml'
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index d049e06..9bfa224 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -4,10 +4,10 @@
-
-
+
+
-
+
-
\ No newline at end of file
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3a87034..a3bc764 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,13 +1,14 @@
-
+
+
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 9df492c..d5e76bf 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023, TeamDev. All rights reserved.
+ * Copyright 2024, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -169,49 +169,74 @@ tasks.withType {
}
dependencies {
- implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
- implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jacksonVersion")
-
- @Suppress(
- "VulnerableLibrariesLocal", "RedundantSuppression" /*
- `artifactregistry-auth-common` has transitive dependency on Gson and Apache `commons-codec`.
-
- Gson from version `2.8.6` until `2.8.9` is vulnerable to Deserialization of Untrusted Data
- (https://devhub.checkmarx.com/cve-details/CVE-2022-25647/).
+ api("com.github.jk1:gradle-license-report:$licenseReportVersion")
+ dependOnAuthCommon()
+
+ listOf(
+ "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion",
+ "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jacksonVersion",
+ "com.github.jk1:gradle-license-report:$licenseReportVersion",
+ "com.google.guava:guava:$guavaVersion",
+ "com.google.protobuf:protobuf-gradle-plugin:$protobufPluginVersion",
+ "gradle.plugin.com.github.johnrengelman:shadow:${shadowVersion}",
+ "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$detektVersion",
+ "io.kotest:kotest-gradle-plugin:$kotestJvmPluginVersion",
+ // https://github.com/srikanth-lingala/zip4j
+ "net.lingala.zip4j:zip4j:2.10.0",
+ "net.ltgt.gradle:gradle-errorprone-plugin:${errorPronePluginVersion}",
+ "org.ajoberstar.grgit:grgit-core:${grGitVersion}",
+ "org.jetbrains.dokka:dokka-base:${dokkaVersion}",
+ "org.jetbrains.dokka:dokka-gradle-plugin:${dokkaVersion}",
+ "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion",
+ "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion",
+ "org.jetbrains.kotlinx:kover-gradle-plugin:$koverVersion"
+ ).forEach {
+ implementation(it)
+ }
+}
- Apache `commons-codec` before 1.13 is vulnerable to information exposure
- (https://devhub.checkmarx.com/cve-details/Cxeb68d52e-5509/).
+dependOnBuildSrcJar()
- We use Gson `2.10.1`and we force it in `forceProductionDependencies()`.
- We use `commons-code` with version `1.16.0`, forcing it in `forceProductionDependencies()`.
+/**
+ * Adds a dependency on a `buildSrc.jar`, iff:
+ * 1) the `src` folder is missing, and
+ * 2) `buildSrc.jar` is present in `buildSrc/` folder instead.
+ *
+ * This approach is used in the scope of integration testing.
+ */
+fun Project.dependOnBuildSrcJar() {
+ val srcFolder = this.rootDir.resolve("src")
+ val buildSrcJar = rootDir.resolve("buildSrc.jar")
+ if (!srcFolder.exists() && buildSrcJar.exists()) {
+ logger.info("Adding the pre-compiled 'buildSrc.jar' to 'implementation' dependencies.")
+ dependencies {
+ implementation(files("buildSrc.jar"))
+ }
+ }
+}
- So, we should be safe with the current version `artifactregistry-auth-common` until
- we migrate to a later version. */
- )
+/**
+ * Includes the `implementation` dependency on `artifactregistry-auth-common`,
+ * with the version defined in [googleAuthToolVersion].
+ *
+ * `artifactregistry-auth-common` has transitive dependency on Gson and Apache `commons-codec`.
+ * Gson from version `2.8.6` until `2.8.9` is vulnerable to Deserialization of Untrusted Data
+ * (https://devhub.checkmarx.com/cve-details/CVE-2022-25647/).
+ *
+ * Apache `commons-codec` before 1.13 is vulnerable to information exposure
+ * (https://devhub.checkmarx.com/cve-details/Cxeb68d52e-5509/).
+ *
+ * We use Gson `2.10.1` and we force it in `forceProductionDependencies()`.
+ * We use `commons-code` with version `1.16.0`, forcing it in `forceProductionDependencies()`.
+ *
+ * So, we should be safe with the current version `artifactregistry-auth-common` until
+ * we migrate to a later version.
+ */
+fun DependencyHandlerScope.dependOnAuthCommon() {
+ @Suppress("VulnerableLibrariesLocal", "RedundantSuppression")
implementation(
"com.google.cloud.artifactregistry:artifactregistry-auth-common:$googleAuthToolVersion"
) {
exclude(group = "com.google.guava")
}
-
- implementation("com.google.guava:guava:$guavaVersion")
- api("com.github.jk1:gradle-license-report:$licenseReportVersion")
- implementation("org.ajoberstar.grgit:grgit-core:${grGitVersion}")
- implementation("net.ltgt.gradle:gradle-errorprone-plugin:${errorPronePluginVersion}")
-
- // Add explicit dependency to avoid warning on different Kotlin runtime versions.
- implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
- implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
-
- implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$detektVersion")
- implementation("com.google.protobuf:protobuf-gradle-plugin:$protobufPluginVersion")
- implementation("org.jetbrains.dokka:dokka-gradle-plugin:${dokkaVersion}")
- implementation("org.jetbrains.dokka:dokka-base:${dokkaVersion}")
- implementation("gradle.plugin.com.github.johnrengelman:shadow:${shadowVersion}")
-
- // https://github.com/srikanth-lingala/zip4j
- implementation("net.lingala.zip4j:zip4j:2.10.0")
-
- implementation("io.kotest:kotest-gradle-plugin:$kotestJvmPluginVersion")
- implementation("org.jetbrains.kotlinx:kover-gradle-plugin:$koverVersion")
}
diff --git a/buildSrc/src/main/kotlin/DokkaExts.kt b/buildSrc/src/main/kotlin/DokkaExts.kt
index 9756dc3..feb9eb0 100644
--- a/buildSrc/src/main/kotlin/DokkaExts.kt
+++ b/buildSrc/src/main/kotlin/DokkaExts.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023, TeamDev. All rights reserved.
+ * Copyright 2024, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -143,6 +143,7 @@ fun TaskContainer.dokkaHtmlTask(): DokkaTask? = this.findByName("dokkaHtml") as
* Dokka can properly generate documentation for either Kotlin or Java depending on
* the configuration, but not both.
*/
+@Suppress("unused")
internal fun GradleDokkaSourceSetBuilder.onlyJavaSources(): FileCollection {
return sourceRoots.filter(File::isJavaSourceDirectory)
}
@@ -167,6 +168,19 @@ fun Project.dokkaKotlinJar(): TaskProvider = tasks.getOrCreate("dokkaKotlin
}
}
+/**
+ * Tells if this task belongs to the execution graph which contains publishing tasks.
+ *
+ * The task `"publishToMavenLocal"` is excluded from the check because it is a part of
+ * the local testing workflow.
+ */
+fun DokkaTask.isInPublishingGraph(): Boolean =
+ project.gradle.taskGraph.allTasks.any {
+ with(it.name) {
+ startsWith("publish") && !startsWith("publishToMavenLocal")
+ }
+ }
+
/**
* Locates or creates `dokkaJavaJar` task in this [Project].
*
@@ -182,3 +196,21 @@ fun Project.dokkaJavaJar(): TaskProvider = tasks.getOrCreate("dokkaJavaJar"
this@getOrCreate.dependsOn(dokkaTask)
}
}
+
+/**
+ * Disables Dokka and Javadoc tasks in this `Project`.
+ *
+ * This function could be useful to improve build speed when building subprojects containing
+ * test environments or integration test projects.
+ */
+@Suppress("unused")
+fun Project.disableDocumentationTasks() {
+ gradle.taskGraph.whenReady {
+ tasks.forEach { task ->
+ val lowercaseName = task.name.toLowerCase()
+ if (lowercaseName.contains("dokka") || lowercaseName.contains("javadoc")) {
+ task.enabled = false
+ }
+ }
+ }
+}
diff --git a/buildSrc/src/main/kotlin/dokka-for-java.gradle.kts b/buildSrc/src/main/kotlin/dokka-for-java.gradle.kts
index 0e5087a..49ac6d3 100644
--- a/buildSrc/src/main/kotlin/dokka-for-java.gradle.kts
+++ b/buildSrc/src/main/kotlin/dokka-for-java.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023, TeamDev. All rights reserved.
+ * Copyright 2024, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,4 +37,7 @@ dependencies {
tasks.withType().configureEach {
configureForJava()
+ onlyIf {
+ (it as DokkaTask).isInPublishingGraph()
+ }
}
diff --git a/buildSrc/src/main/kotlin/dokka-for-kotlin.gradle.kts b/buildSrc/src/main/kotlin/dokka-for-kotlin.gradle.kts
index 0b12a0d..9ed226e 100644
--- a/buildSrc/src/main/kotlin/dokka-for-kotlin.gradle.kts
+++ b/buildSrc/src/main/kotlin/dokka-for-kotlin.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023, TeamDev. All rights reserved.
+ * Copyright 2024, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,4 +36,7 @@ dependencies {
tasks.withType().configureEach {
configureForKotlin()
+ onlyIf {
+ (it as DokkaTask).isInPublishingGraph()
+ }
}
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Coroutines.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Coroutines.kt
new file mode 100644
index 0000000..3074c21
--- /dev/null
+++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Coroutines.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.internal.dependency
+
+/**
+ * Kotlin Coroutines.
+ *
+ * @see GitHub projecet
+ */
+@Suppress("unused")
+object Coroutines {
+ const val version = "1.6.4"
+ const val jdk8 = "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$version"
+ const val core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version"
+ const val bom = "org.jetbrains.kotlinx:kotlinx-coroutines-bom:$version"
+ const val coreJvm = "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:$version"
+}
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/GrpcKotlin.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/GrpcKotlin.kt
new file mode 100644
index 0000000..a2fb7ef
--- /dev/null
+++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/GrpcKotlin.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.internal.dependency
+
+/**
+ * gRPC-Kotlin/JVM.
+ *
+ * @see GitHub project
+ */
+@Suppress("unused")
+object GrpcKotlin {
+ const val version = "1.3.0"
+ const val stub = "io.grpc:grpc-kotlin-stub:$version"
+
+ object ProtocPlugin {
+ const val id = "grpckt"
+ const val artifact = "io.grpc:protoc-gen-grpc-kotlin:$version:jdk8@jar"
+ }
+}
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/IntelliJ.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/IntelliJ.kt
new file mode 100644
index 0000000..1062c9b
--- /dev/null
+++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/IntelliJ.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2023, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+@file:Suppress("ConstPropertyName")
+
+package io.spine.internal.dependency
+
+/**
+ * The components of the IntelliJ Platform.
+ *
+ * Make sure to add the `intellijReleases` and `jetBrainsCacheRedirector`
+ * repositories to your project. See `kotlin/Repositories.kt` for details.
+ */
+@Suppress("unused")
+object IntelliJ {
+
+ /**
+ * The version of the IntelliJ platform.
+ *
+ * This is the version used by Kotlin compiler `1.9.21`.
+ * Advance this version with caution because it may break the setup of
+ * IntelliJ platform standalone execution.
+ */
+ const val version = "213.7172.53"
+
+ object Platform {
+ private const val group = "com.jetbrains.intellij.platform"
+ const val core = "$group:core:$version"
+ const val util = "$group:util:$version"
+ const val coreImpl = "$group:core-impl:$version"
+ const val codeStyle = "$group:code-style:$version"
+ const val codeStyleImpl = "$group:code-style-impl:$version"
+ const val projectModel = "$group:project-model:$version"
+ const val projectModelImpl = "$group:project-model-impl:$version"
+ const val lang = "$group:lang:$version"
+ const val langImpl = "$group:lang-impl:$version"
+ const val ideImpl = "$group:ide-impl:$version"
+ const val ideCoreImpl = "$group:ide-core-impl:$version"
+ const val analysisImpl = "$group:analysis-impl:$version"
+ const val indexingImpl = "$group:indexing-impl:$version"
+ }
+
+ object Jsp {
+ private const val group = "com.jetbrains.intellij.jsp"
+ @Suppress("MemberNameEqualsClassName")
+ const val jsp = "$group:jsp:$version"
+ }
+
+ object Xml {
+ private const val group = "com.jetbrains.intellij.xml"
+ const val xmlPsiImpl = "$group:xml-psi-impl:$version"
+ }
+
+ object JavaPsi {
+ private const val group = "com.jetbrains.intellij.java"
+ const val api = "$group:java-psi:$version"
+ const val impl = "$group:java-psi-impl:$version"
+ }
+
+ object Java {
+ private const val group = "com.jetbrains.intellij.java"
+ @Suppress("MemberNameEqualsClassName")
+ const val java = "$group:java:$version"
+ const val impl = "$group:java-impl:$version"
+ }
+}
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt
index 8bfaa5c..1ef088c 100644
--- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt
+++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt
@@ -53,4 +53,11 @@ object Jackson {
// https://github.com/FasterXML/jackson-bom
const val bom = "com.fasterxml.jackson:jackson-bom:${version}"
+
+ // https://github.com/FasterXML/jackson-jr
+ object Junior {
+ const val version = Jackson.version
+ const val group = "com.fasterxml.jackson.jr"
+ const val objects = "$group:jackson-jr-objects:$version"
+ }
}
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotlin.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotlin.kt
index 320403b..ec7f338 100644
--- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotlin.kt
+++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotlin.kt
@@ -34,8 +34,8 @@ object Kotlin {
/**
* When changing the version, also change the version used in the `buildSrc/build.gradle.kts`.
*/
- @Suppress("MemberVisibilityCanBePrivate") // used directly from outside
- const val version = "1.9.20"
+ @Suppress("MemberVisibilityCanBePrivate") // used directly from the outside.
+ const val version = "1.9.23"
/**
* The version of the JetBrains annotations library, which is a transitive
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinX.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinX.kt
new file mode 100644
index 0000000..f10eb45
--- /dev/null
+++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinX.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.internal.dependency
+
+@Suppress("unused", "ConstPropertyName")
+object KotlinX {
+
+ const val group = "org.jetbrains.kotlinx"
+
+ object Coroutines {
+
+ // https://github.com/Kotlin/kotlinx.coroutines
+ const val version = "1.7.3"
+ const val core = "$group:kotlinx-coroutines-core:$version"
+ const val jdk8 = "$group:kotlinx-coroutines-jdk8:$version"
+ }
+}
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/ProtoData.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/ProtoData.kt
index 20a52d6..b127c2f 100644
--- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/ProtoData.kt
+++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/ProtoData.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023, TeamDev. All rights reserved.
+ * Copyright 2024, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@ package io.spine.internal.dependency
/**
* Dependencies on ProtoData modules.
*
- * In order to use locally published ProtoData version instead of the version from a public plugin
+ * To use a locally published ProtoData version instead of the version from a public plugin
* registry, set the `PROTODATA_VERSION` and/or the `PROTODATA_DF_VERSION` environment variables
* and stop the Gradle daemons so that Gradle observes the env change:
* ```
@@ -40,7 +40,7 @@ package io.spine.internal.dependency
* ./gradle build # Conduct the intended checks.
* ```
*
- * Then, in order to reset the console to run the usual versions again, remove the values of
+ * Then, to reset the console to run the usual versions again, remove the values of
* the environment variables and stop the daemon:
* ```
* export PROTODATA_VERSION=""
@@ -65,7 +65,7 @@ object ProtoData {
* The version of ProtoData dependencies.
*/
val version: String
- private const val fallbackVersion = "0.14.2"
+ private const val fallbackVersion = "0.20.7"
/**
* The distinct version of ProtoData used by other build tools.
@@ -74,19 +74,54 @@ object ProtoData {
* transitional dependencies, this is the version used to build the project itself.
*/
val dogfoodingVersion: String
- private const val fallbackDfVersion = "0.14.2"
+ private const val fallbackDfVersion = "0.20.7"
/**
* The artifact for the ProtoData Gradle plugin.
*/
val pluginLib: String
+ fun pluginLib(version: String): String =
+ "$group:gradle-plugin:$version"
+
+ fun api(version: String): String =
+ "$group:protodata-api:$version"
+
val api
- get() = "$group:protodata-api:$version"
+ get() = api(version)
+
+ @Deprecated("Use `backend` instead", ReplaceWith("backend"))
val compiler
- get() = "$group:protodata-compiler:$version"
+ get() = backend
+
+ val backend
+ get() = "$group:protodata-backend:$version"
+
+ val protocPlugin
+ get() = "$group:protodata-protoc:$version"
+
+ val gradleApi
+ get() = "$group:protodata-gradle-api:$version"
+
+ val cliApi
+ get() = "$group:protodata-cli-api:$version"
+
+ @Deprecated("Use `java()` instead", ReplaceWith("java(version)"))
+ fun codegenJava(version: String): String =
+ java(version)
+
+ fun java(version: String): String =
+ "$group:protodata-java:$version"
+
+ @Deprecated("Use `java` instead.", ReplaceWith("java"))
val codegenJava
- get() = "$group:protodata-codegen-java:$version"
+ get() = java(version)
+
+ val java
+ get() = java(version)
+
+ val fatCli
+ get() = "$group:protodata-fat-cli:$version"
/**
* An env variable storing a custom [version].
@@ -113,7 +148,7 @@ object ProtoData {
version = experimentVersion ?: fallbackVersion
dogfoodingVersion = experimentDfVersion ?: fallbackDfVersion
- pluginLib = "${group}:gradle-plugin:$version"
+ pluginLib = pluginLib(version)
println("""
❗ Running an experiment with ProtoData. ❗
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt
index 32c05ec..be80d2d 100644
--- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt
+++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt
@@ -33,7 +33,7 @@ package io.spine.internal.dependency
)
object Protobuf {
private const val group = "com.google.protobuf"
- const val version = "3.25.0"
+ const val version = "3.25.1"
/**
* The Java library containing proto definitions of Google Protobuf.
*/
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Spine.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Spine.kt
index 5ae9e95..e4093e7 100644
--- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Spine.kt
+++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Spine.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023, TeamDev. All rights reserved.
+ * Copyright 2024, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,17 +45,17 @@ object Spine {
*
* @see spine-base
*/
- const val base = "2.0.0-SNAPSHOT.192"
+ const val base = "2.0.0-SNAPSHOT.199"
/**
* The version of [Spine.reflect].
*
* @see spine-reflect
*/
- const val reflect = "2.0.0-SNAPSHOT.182"
+ const val reflect = "2.0.0-SNAPSHOT.183"
/**
- * The version of [Spine.logging].
+ * The version of [Spine.Logging].
*
* @see spine-logging
*/
@@ -75,7 +75,7 @@ object Spine {
* @see [Spine.CoreJava.server]
* @see core-java
*/
- const val core = "2.0.0-SNAPSHOT.173"
+ const val core = "2.0.0-SNAPSHOT.176"
/**
* The version of [Spine.modelCompiler].
@@ -89,14 +89,14 @@ object Spine {
*
* @see spine-mc-java
*/
- const val mcJava = "2.0.0-SNAPSHOT.172"
+ const val mcJava = "2.0.0-SNAPSHOT.205"
/**
* The version of [Spine.baseTypes].
*
* @see spine-base-types
*/
- const val baseTypes = "2.0.0-SNAPSHOT.125"
+ const val baseTypes = "2.0.0-SNAPSHOT.126"
/**
* The version of [Spine.time].
@@ -117,14 +117,14 @@ object Spine {
*
* @see spine-text
*/
- const val text = "2.0.0-SNAPSHOT.5"
+ const val text = "2.0.0-SNAPSHOT.6"
/**
* The version of [Spine.toolBase].
*
* @see spine-tool-base
*/
- const val toolBase = "2.0.0-SNAPSHOT.186"
+ const val toolBase = "2.0.0-SNAPSHOT.208"
/**
* The version of [Spine.javadocTools].
@@ -136,13 +136,6 @@ object Spine {
const val base = "$group:spine-base:${ArtifactVersion.base}"
- @Deprecated("Use `Logging.lib` instead.", ReplaceWith("Logging.lib"))
- const val logging = "$group:spine-logging:${ArtifactVersion.logging}"
- @Deprecated("Use `Logging.context` instead.", ReplaceWith("Logging.context"))
- const val loggingContext = "$group:spine-logging-context:${ArtifactVersion.logging}"
- @Deprecated("Use `Logging.backend` instead.", ReplaceWith("Logging.backend"))
- const val loggingBackend = "$group:spine-logging-backend:${ArtifactVersion.logging}"
-
const val reflect = "$group:spine-reflect:${ArtifactVersion.reflect}"
const val baseTypes = "$group:spine-base-types:${ArtifactVersion.baseTypes}"
const val time = "$group:spine-time:${ArtifactVersion.time}"
@@ -151,6 +144,8 @@ object Spine {
const val testlib = "$toolsGroup:spine-testlib:${ArtifactVersion.testlib}"
const val testUtilTime = "$toolsGroup:spine-testutil-time:${ArtifactVersion.time}"
+ const val psiJava = "$toolsGroup:spine-psi-java:${ArtifactVersion.toolBase}"
+ const val psiJavaBundle = "$toolsGroup:spine-psi-java-bundle:${ArtifactVersion.toolBase}"
const val toolBase = "$toolsGroup:spine-tool-base:${ArtifactVersion.toolBase}"
const val pluginBase = "$toolsGroup:spine-plugin-base:${ArtifactVersion.toolBase}"
const val pluginTestlib = "$toolsGroup:spine-plugin-testlib:${ArtifactVersion.toolBase}"
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Validation.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Validation.kt
index df8847b..ded15d8 100644
--- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Validation.kt
+++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Validation.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023, TeamDev. All rights reserved.
+ * Copyright 2024, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,11 +33,32 @@ package io.spine.internal.dependency
*/
@Suppress("unused", "ConstPropertyName")
object Validation {
- const val version = "2.0.0-SNAPSHOT.110"
+ /**
+ * The version of the Validation library artifacts.
+ */
+ const val version = "2.0.0-SNAPSHOT.132"
+
+ /**
+ * The distinct version of the Validation library used by build tools during
+ * the transition from a previous version when breaking API changes are introduced.
+ *
+ * When Validation is used both for building the project and as a part of the project's
+ * transitional dependencies, this is the version used to build the project itself to
+ * avoid errors caused by incompatible API changes.
+ */
+ const val dogfoodingVersion = "2.0.0-SNAPSHOT.132"
+
const val group = "io.spine.validation"
- const val runtime = "$group:spine-validation-java-runtime:$version"
- const val java = "$group:spine-validation-java:$version"
- const val javaBundle = "$group:spine-validation-java-bundle:$version"
- const val model = "$group:spine-validation-model:$version"
- const val config = "$group:spine-validation-configuration:$version"
+ private const val prefix = "spine-validation"
+
+ const val runtime = "$group:$prefix-java-runtime:$version"
+ const val java = "$group:$prefix-java:$version"
+
+ /** Obtains the artifact for the `java-bundle` artifact of the given version. */
+ fun javaBundle(version: String) = "$group:$prefix-java-bundle:$version"
+
+ val javaBundle = javaBundle(version)
+
+ const val model = "$group:$prefix-model:$version"
+ const val config = "$group:$prefix-configuration:$version"
}
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/Repositories.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/Repositories.kt
index 8c5015e..6d04ae7 100644
--- a/buildSrc/src/main/kotlin/io/spine/internal/gradle/Repositories.kt
+++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/Repositories.kt
@@ -38,6 +38,7 @@ import org.gradle.api.Project
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.kotlin.dsl.ScriptHandlerScope
+import org.gradle.kotlin.dsl.maven
/**
* Applies [standard][doApplyStandard] repositories to this [ScriptHandlerScope]
@@ -214,6 +215,12 @@ fun RepositoryHandler.spineArtifacts(): MavenArtifactRepository = maven {
}
}
+val RepositoryHandler.intellijReleases: MavenArtifactRepository
+ get() = maven("https://www.jetbrains.com/intellij-repository/releases")
+
+val RepositoryHandler.jetBrainsCacheRedirector: MavenArtifactRepository
+ get() = maven("https://cache-redirector.jetbrains.com/intellij-dependencies")
+
/**
* Applies repositories commonly used by Spine Event Engine projects.
*/
@@ -236,6 +243,9 @@ fun RepositoryHandler.standardToSpineSdk() {
}
}
+ intellijReleases
+ jetBrainsCacheRedirector
+
maven {
url = URI(Repos.sonatypeSnapshots)
}
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/protobuf/ProtoTaskExtensions.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/protobuf/ProtoTaskExtensions.kt
index a3bc6dc..0c4c8b4 100644
--- a/buildSrc/src/main/kotlin/io/spine/internal/gradle/protobuf/ProtoTaskExtensions.kt
+++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/protobuf/ProtoTaskExtensions.kt
@@ -29,6 +29,8 @@ package io.spine.internal.gradle.protobuf
import com.google.protobuf.gradle.GenerateProtoTask
import io.spine.internal.gradle.sourceSets
import java.io.File
+import java.nio.file.Files
+import java.nio.file.StandardOpenOption.TRUNCATE_EXISTING
import org.gradle.api.Project
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.SourceSet
@@ -112,23 +114,68 @@ fun GenerateProtoTask.setup() {
/**
* Tell `protoc` to generate descriptor set files under the project build dir.
+ *
+ * The name of the descriptor set file to be generated
+ * is made to be unique per project's Maven coordinates.
+ *
+ * As the last step of this task, writes a `desc.ref` file
+ * for the contextual source set, pointing to the generated descriptor set file.
+ * This is needed in order to allow other Spine libraries
+ * to locate and load the generated descriptor set files properly.
+ *
+ * Such a job is usually performed by Spine McJava plugin,
+ * however, it is not possible to use this plugin (or its code)
+ * in this repository due to cyclic dependencies.
*/
+@Suppress(
+ "TooGenericExceptionCaught" /* Handling all file-writing failures in the same way.*/)
private fun GenerateProtoTask.setupDescriptorSetFileCreation() {
// Tell `protoc` generate descriptor set file.
+ // The name of the generated file reflects project's Maven coordinates.
val ssn = sourceSet.name
generateDescriptorSet = true
val descriptorsDir = "${project.buildDir}/descriptors/${ssn}"
+ val descriptorName = project.descriptorSetName(sourceSet)
with(descriptorSetOptions) {
- path = "$descriptorsDir/known_types_${ssn}.desc"
+ path = "$descriptorsDir/$descriptorName"
includeImports = true
includeSourceInfo = true
}
+
// Make the descriptor set file included into the resources.
project.sourceSets.named(ssn) {
resources.srcDirs(descriptorsDir)
}
+
+ // Create a `desc.ref` in the same resource folder,
+ // with the name of the descriptor set file created above.
+ this.doLast {
+ val descRefFile = File(descriptorsDir, "desc.ref")
+ descRefFile.createNewFile()
+ try {
+ Files.write(descRefFile.toPath(), setOf(descriptorName), TRUNCATE_EXISTING)
+ } catch (e: Exception) {
+ project.logger.error("Error writing `${descRefFile.absolutePath}`.", e)
+ throw e
+ }
+ }
}
+/**
+ * Returns a name of the descriptor file for the given [sourceSet],
+ * reflecting the Maven coordinates of Gradle artifact, and the source set
+ * for which the descriptor set name is to be generated.
+ *
+ * The returned value is just a file name, and does not contain a file path.
+ */
+private fun Project.descriptorSetName(sourceSet: SourceSet) =
+ arrayOf(
+ group.toString(),
+ name,
+ sourceSet.name,
+ version.toString()
+ ).joinToString(separator = "_", postfix = ".desc")
+
/**
* Copies files from the [outputBaseDir][GenerateProtoTask.outputBaseDir] into
* a subdirectory of [generatedDir][Project.generatedDir] for
diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/Publications.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/Publications.kt
index be3bc0e..12c0563 100644
--- a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/Publications.kt
+++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/Publications.kt
@@ -80,13 +80,25 @@ internal sealed class PublicationHandler(
}
/**
- * Takes a group name and a version from the given [project] and assigns
- * them to this publication.
+ * Copies the attributes of Gradle [Project] to this [MavenPublication].
+ *
+ * The following project attributes are copied:
+ * * [group][Project.getGroup];
+ * * [version][Project.getVersion];
+ * * [description][Project.getDescription].
+ *
+ * Also, this function adds the [artifactPrefix][SpinePublishing.artifactPrefix] to
+ * the [artifactId][MavenPublication.setArtifactId] of this publication,
+ * if the prefix is not added yet.
*/
- protected fun MavenPublication.assignMavenCoordinates() {
+ protected fun MavenPublication.copyProjectAttributes() {
groupId = project.group.toString()
- artifactId = project.spinePublishing.artifactPrefix + artifactId
+ val prefix = project.spinePublishing.artifactPrefix
+ if (!artifactId.startsWith(prefix)) {
+ artifactId = prefix + artifactId
+ }
version = project.version.toString()
+ pom.description.set(project.description)
}
}
@@ -110,7 +122,7 @@ private fun RepositoryHandler.register(project: Project, repository: Repository)
/**
* A publication for a typical Java project.
*
- * In Gradle, in order to publish something somewhere one should create a publication.
+ * In Gradle, to publish something, one should create a publication.
* A publication has a name and consists of one or more artifacts plus information about
* those artifacts – the metadata.
*
@@ -142,7 +154,7 @@ internal class StandardJavaPublicationHandler(
val jars = project.artifacts(jarFlags)
val publications = project.publications
publications.create("mavenJava") {
- assignMavenCoordinates()
+ copyProjectAttributes()
specifyArtifacts(jars)
}
}
@@ -191,14 +203,14 @@ internal class StandardJavaPublicationHandler(
*
* Such publications should be treated differently than [StandardJavaPublicationHandler],
* which is created for a module. Instead, since the publications are already declared,
- * this class only [assigns maven coordinates][assignMavenCoordinates].
+ * this class only [assigns maven coordinates][copyProjectAttributes].
*
* A module which declares custom publications must be specified in
* the [SpinePublishing.modulesWithCustomPublishing] property.
*
* If a module with [publications] declared locally is not specified as one with custom publishing,
* it may cause a name clash between an artifact produced by the [standard][MavenPublication]
- * publication, and custom ones. In order to have both standard and custom publications,
+ * publication, and custom ones. To have both standard and custom publications,
* please specify custom artifact IDs or classifiers for each custom publication.
*/
internal class CustomPublicationHandler(project: Project, destinations: Set) :
@@ -206,7 +218,7 @@ internal class CustomPublicationHandler(project: Project, destinations: Set
diff --git a/config b/config
index f3d4902..bb06dc1 160000
--- a/config
+++ b/config
@@ -1 +1 @@
-Subproject commit f3d4902651973cd3f40f197f452b55e6f61de0f6
+Subproject commit bb06dc1f6d6e3f687a423d08c15c74ce58950849
diff --git a/dependencies.md b/dependencies.md
index 7fec6a2..a8d87f7 100644
--- a/dependencies.md
+++ b/dependencies.md
@@ -1,6 +1,6 @@
-# Dependencies of `io.spine:spine-reflect:2.0.0-SNAPSHOT.183`
+# Dependencies of `io.spine:spine-reflect:2.0.0-SNAPSHOT.184`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -30,15 +30,15 @@
* **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/)
* **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
-1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.25.0.
+1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.25.1.
* **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/)
* **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause)
-1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.25.0.
+1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.25.1.
* **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/)
* **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause)
-1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.25.0.
+1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.25.1.
* **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause)
1. **Group** : org.checkerframework. **Name** : checker-qual. **Version** : 3.40.0.
@@ -49,19 +49,19 @@
* **Project URL:** [https://github.com/JetBrains/java-annotations](https://github.com/JetBrains/java-annotations)
* **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt)
-1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-reflect. **Version** : 1.9.20.
+1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-reflect. **Version** : 1.9.23.
* **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/)
* **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
-1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 1.9.20.
+1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 1.9.23.
* **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/)
* **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
-1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 1.9.20.
+1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 1.9.23.
* **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/)
* **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
-1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk8. **Version** : 1.9.20.
+1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk8. **Version** : 1.9.23.
* **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/)
* **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
@@ -198,15 +198,15 @@
* **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/)
* **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
-1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.25.0.
+1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.25.1.
* **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/)
* **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause)
-1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.25.0.
+1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.25.1.
* **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/)
* **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause)
-1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.25.0.
+1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.25.1.
* **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause)
1. **Group** : com.google.truth. **Name** : truth. **Version** : 1.1.5.
@@ -592,7 +592,7 @@
* **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/)
* **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
-1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-reflect. **Version** : 1.9.20.
+1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-reflect. **Version** : 1.9.23.
* **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/)
* **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
@@ -620,16 +620,16 @@
* **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/)
* **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
-1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 1.9.20.
+1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 1.9.23.
* **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/)
* **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
-1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-common. **Version** : 1.9.20.**No license information found**
-1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 1.9.20.
+1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-common. **Version** : 1.9.23.**No license information found**
+1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 1.9.23.
* **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/)
* **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
-1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk8. **Version** : 1.9.20.
+1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk8. **Version** : 1.9.23.
* **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/)
* **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
@@ -756,4 +756,4 @@
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Sat Nov 25 11:39:44 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
\ No newline at end of file
+This report was generated on **Mon May 06 14:01:01 WEST 2024** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index b1624c4..c7d437b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/pom.xml b/pom.xml
index e67fcb0..68e86b1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject.
-->
io.spinereflect
-2.0.0-SNAPSHOT.183
+2.0.0-SNAPSHOT.1842015
@@ -32,25 +32,25 @@ all modules and does not describe the project structure per-subproject.
com.google.protobufprotobuf-java
- 3.25.0
+ 3.25.1compilecom.google.protobufprotobuf-java-util
- 3.25.0
+ 3.25.1compilecom.google.protobufprotobuf-kotlin
- 3.25.0
+ 3.25.1compileorg.jetbrains.kotlinkotlin-reflect
- 1.9.20
+ 1.9.23compile
diff --git a/src/main/kotlin/io/spine/reflect/CallerFinder.kt b/src/main/kotlin/io/spine/reflect/CallerFinder.kt
new file mode 100644
index 0000000..c626bf3
--- /dev/null
+++ b/src/main/kotlin/io/spine/reflect/CallerFinder.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+@file:JvmName("CallerFinder")
+
+package io.spine.reflect
+
+/**
+ * A helper object for determining callers of a specified class currently on the stack.
+ *
+ * @see
+ * Original Java code of Google Flogger
+ */
+public object CallerFinder {
+
+ private val STACK_GETTER by lazy {
+ createBestStackGetter()
+ }
+
+ /**
+ * Returns the stack trace element of the immediate caller of the specified class.
+ *
+ * @param target
+ * the target class whose callers we are looking for.
+ * @param skip
+ * the minimum number of calls known to have occurred between the first call to the
+ * target class and the point at which the specified throwable was created.
+ * If in doubt, specify zero here to avoid accidentally skipping past the caller.
+ * This is particularly important for code which might be used in Android, since you
+ * cannot know whether a tool such as Proguard has merged methods or classes and
+ * reduced the number of intermediate stack frames.
+ * @return the stack trace element representing the immediate caller of the specified class, or
+ * `null` if no caller was found (due to incorrect target, wrong skip count or
+ * use of JNI).
+ */
+ @JvmStatic
+ public fun findCallerOf(target: Class<*>?, skip: Int): StackTraceElement? {
+ checkSkipCount(skip)
+ return STACK_GETTER.callerOf(target!!, skip + 1)
+ }
+
+ /**
+ * Returns a synthetic stack trace starting at the immediate caller of the specified target.
+ *
+ * @param target
+ * the class who is the caller the returned stack trace will start at.
+ * @param maxDepth
+ * the maximum size of the returned stack (pass -1 for the complete stack).
+ * @param skip
+ * the minimum number of stack frames to skip before looking for callers.
+ * @return a synthetic stack trace starting at the immediate caller of the specified target, or
+ * the empty array if no caller was found (due to incorrect target, wrong skip count or
+ * use of JNI).
+ */
+ @JvmStatic
+ public fun stackForCallerOf(
+ target: Class<*>,
+ maxDepth: Int,
+ skip: Int
+ ): Array {
+ require((maxDepth > 0 || maxDepth == -1)) { "invalid maximum depth: $maxDepth." }
+ checkSkipCount(skip)
+ return STACK_GETTER.stackForCaller(target, maxDepth, skip + 1)
+ }
+
+ /**
+ * Returns the first available class implementing the [StackGetter] methods.
+ * The implementation returned is dependent on the current Java version.
+ */
+ private fun createBestStackGetter(): StackGetter {
+ return try {
+ StackWalkerStackGetter()
+ } catch (ignored: Throwable) {
+ // We may not be able to create `StackWalkerStackGetter` sometimes,
+ // for example, on Android. This is not a problem because we have
+ // `ThrowableStackGetter` as a fallback option.
+ ThrowableStackGetter()
+ }
+ }
+
+ private fun checkSkipCount(skip: Int) {
+ require(skip >= 0) { "skip can't be negative: $skip." }
+ }
+}
diff --git a/src/main/kotlin/io/spine/reflect/StackGetter.kt b/src/main/kotlin/io/spine/reflect/StackGetter.kt
new file mode 100644
index 0000000..6c5ff16
--- /dev/null
+++ b/src/main/kotlin/io/spine/reflect/StackGetter.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package io.spine.reflect
+
+/**
+ * Interface for finding call site information.
+ *
+ * @see
+ * Original Java code of Google Flogger
+ */
+internal interface StackGetter {
+ /**
+ * Returns the first caller of a method on the [target] class that is *not* a member of
+ * the `target` class.
+ *
+ * The caller is obtained by walking back on the stack.
+ *
+ * @param target
+ * the class to find the caller of.
+ * @param skipFrames
+ * skip this many frames before looking for the caller.
+ * This can be used for optimization.
+ * @return the first caller of the method or `null` if the `target` class
+ * cannot be found or is the last element of the stack.
+ */
+ fun callerOf(target: Class<*>, skipFrames: Int): StackTraceElement?
+
+ /**
+ * Returns up to `maxDepth` frames of the stack starting at the stack frame that
+ * is a caller of a method on `target` class but is *not* itself a method
+ * on `target` class.
+ *
+ * @param target
+ * the class to get the stack from.
+ * @param maxDepth
+ * the maximum depth of the stack to return.
+ * A value of -1 means to return the whole stack.
+ * @param skipFrames
+ * skip this many stack frames before looking for the target class.
+ * Used for optimization.
+ * @throws IllegalArgumentException
+ * if `maxDepth` is 0 or < -1 or `skipFrames` is < 0.
+ */
+ fun stackForCaller(
+ target: Class<*>,
+ maxDepth: Int,
+ skipFrames: Int
+ ): Array
+}
+
+internal fun checkMaxDepth(maxDepth: Int) {
+ require(maxDepth == -1 || maxDepth > 0) { "maxDepth must be > 0 or -1" }
+}
+
+internal fun checkSkipFrames(skipFrames: Int) {
+ require(skipFrames >= 0) { "skipFrames must be >= 0" }
+}
diff --git a/src/main/kotlin/io/spine/reflect/StackWalkerStackGetter.kt b/src/main/kotlin/io/spine/reflect/StackWalkerStackGetter.kt
new file mode 100644
index 0000000..1a85185
--- /dev/null
+++ b/src/main/kotlin/io/spine/reflect/StackWalkerStackGetter.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package io.spine.reflect
+
+import java.lang.StackWalker.Option.SHOW_REFLECT_FRAMES
+import java.lang.StackWalker.StackFrame
+import java.util.function.Predicate
+import java.util.stream.Stream
+import kotlin.Long.Companion.MAX_VALUE
+import kotlin.collections.toTypedArray
+import kotlin.streams.toList
+
+/**
+ * StackWalker based implementation of the [StackGetter] interface.
+ *
+ * @see
+ * Original Java code of Google Flogger
+ */
+internal class StackWalkerStackGetter : StackGetter {
+
+ init {
+ // Due to b/241269335, we check in constructor whether this implementation
+ // crashes in runtime, and CallerFinder should catch any Throwable caused.
+ @Suppress("UNUSED_VARIABLE")
+ val unused = callerOf(StackWalkerStackGetter::class.java, 0)
+ }
+
+ override fun callerOf(target: Class<*>, skipFrames: Int): StackTraceElement? {
+ checkSkipFrames(skipFrames)
+ return STACK_WALKER.walk { stream ->
+ filterStackTraceAfterTarget(isTargetClass(target), skipFrames, stream)
+ .findFirst()
+ .orElse(null)
+ }
+ }
+
+ override fun stackForCaller(
+ target: Class<*>,
+ maxDepth: Int,
+ skipFrames: Int
+ ): Array {
+ checkMaxDepth(maxDepth)
+ checkSkipFrames(skipFrames)
+ return STACK_WALKER.walk { stream ->
+ filterStackTraceAfterTarget(isTargetClass(target), skipFrames, stream)
+ .limit(if (maxDepth == -1) MAX_VALUE else maxDepth.toLong())
+ .toList()
+ .toTypedArray()
+ }
+ }
+
+ companion object {
+
+ private val STACK_WALKER: StackWalker = StackWalker.getInstance(SHOW_REFLECT_FRAMES)
+
+ private fun filterStackTraceAfterTarget(
+ isTargetClass: Predicate,
+ skipFrames: Int,
+ s: Stream
+ ): Stream {
+ // need to skip + 1 because of the call to the method this method is being called from.
+ return s.skip((skipFrames + 1).toLong())
+ // Skip all classes which don't match the name we are looking for.
+ .dropWhile(isTargetClass.negate())
+ // Then skip all which matches.
+ .dropWhile(isTargetClass)
+ .map { frame -> frame.toStackTraceElement() }
+ }
+ }
+}
+
+private fun isTargetClass(target: Class<*>): Predicate =
+ Predicate { frame -> (frame.className == target.name) }
diff --git a/src/main/kotlin/io/spine/reflect/ThrowableStackGetter.kt b/src/main/kotlin/io/spine/reflect/ThrowableStackGetter.kt
new file mode 100644
index 0000000..e6a2879
--- /dev/null
+++ b/src/main/kotlin/io/spine/reflect/ThrowableStackGetter.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package io.spine.reflect
+
+/**
+ * Default implementation of [StackGetter] using [Throwable.getStackTrace].
+ *
+ * @see
+ * Original Java code of Google Flogger
+ */
+@Suppress("ThrowingExceptionsWithoutMessageOrCause") // For obtaining current stacktrace.
+internal class ThrowableStackGetter : StackGetter {
+
+ override fun callerOf(target: Class<*>, skipFrames: Int): StackTraceElement? {
+ checkSkipFrames(skipFrames)
+ val stack = Throwable().stackTrace
+ val callerIndex = findCallerIndex(stack, target, skipFrames + 1)
+ if (callerIndex != -1) {
+ return stack[callerIndex]
+ }
+ return null
+ }
+
+ override fun stackForCaller(
+ target: Class<*>,
+ maxDepth: Int,
+ skipFrames: Int
+ ): Array {
+ checkMaxDepth(maxDepth)
+ checkSkipFrames(skipFrames)
+ val stack = Throwable().stackTrace
+ val callerIndex = findCallerIndex(stack, target, skipFrames + 1)
+ if (callerIndex == -1) {
+ return EMPTY_STACK_TRACE
+ }
+ var elementsToAdd = stack.size - callerIndex
+ if (maxDepth in 1..()
+
+ private fun findCallerIndex(
+ stack: Array,
+ target: Class<*>,
+ skipFrames: Int
+ ): Int {
+ var foundCaller = false
+ val targetClassName = target.name
+ for (frameIndex in skipFrames..
+ * Original Java code of Google Flogger
+ */
+internal abstract class AbstractStackGetterSpec(
+ private val stackGetter: StackGetter
+) {
+
+ @Test
+ fun `find the stack trace element of the immediate caller of the specified class`() {
+ // There are 2 internal methods (not including the log method itself)
+ // in our fake library.
+ val library = LoggerCode(skipCount = 2, stackGetter)
+ val code = UserCode(library)
+ code.invokeUserCode()
+ library.run {
+ caller shouldNotBe null
+ caller!!.className shouldBe UserCode::class.java.name
+ caller!!.methodName shouldBe "loggingMethod"
+ }
+ }
+
+ @Test
+ fun `return 'null' due to wrong skip count`() {
+ // If the minimum offset exceeds the number of internal methods, the find fails.
+ val library = LoggerCode(skipCount = 3, stackGetter)
+ val code = UserCode(library)
+ code.invokeUserCode()
+ library.caller shouldBe null
+ }
+}
diff --git a/src/test/kotlin/io/spine/reflect/CallerFinderSpec.kt b/src/test/kotlin/io/spine/reflect/CallerFinderSpec.kt
new file mode 100644
index 0000000..16e413b
--- /dev/null
+++ b/src/test/kotlin/io/spine/reflect/CallerFinderSpec.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.reflect
+
+import io.kotest.matchers.shouldBe
+import io.kotest.matchers.shouldNotBe
+import io.spine.reflect.given.AnybodyHome
+import io.spine.reflect.given.Elvis
+import io.spine.reflect.given.LoggerCode
+import io.spine.reflect.given.UserCode
+import org.junit.jupiter.api.DisplayName
+import org.junit.jupiter.api.Test
+
+/**
+ * Tests for [CallerFinder].
+ *
+ * @see
+ * Original Java code of Google Flogger
+ */
+@DisplayName("`CallerFinder` should")
+internal class CallerFinderSpec {
+
+ /**
+ * A sanity check if we ever discover a platform where the class name
+ * in the stack trace does not match [Class.getName] – this is never quite
+ * guaranteed by the JavaDoc in the JDK but is relied upon during log site analysis.
+ */
+ @Test
+ fun `use the class name that matches one in the stack trace`() {
+ // Simple case for a top-level named class.
+ Throwable().stackTrace[0].className shouldBe CallerFinderSpec::class.java.name
+
+ // Anonymous inner class.
+ val obj = object {
+ override fun toString(): String {
+ return Throwable().stackTrace[0].className
+ }
+ }
+
+ "$obj" shouldBe obj::class.java.name
+ }
+
+ @Test
+ fun `find the stack trace element of the immediate caller of the specified class`() {
+ // There are 2 internal methods (not including the log method itself)
+ // in our fake library.
+ val library = LoggerCode(skipCount = 2)
+ val code = UserCode(library)
+ code.invokeUserCode()
+ library.run {
+ caller shouldNotBe null
+ caller!!.className shouldBe UserCode::class.java.name
+ caller!!.methodName shouldBe "loggingMethod"
+ }
+ }
+
+ @Test
+ fun `return 'null' due to wrong skip count`() {
+ // If the minimum offset exceeds the number of internal methods, the find fails.
+ val library = LoggerCode(skipCount = 3)
+ val code = UserCode(library)
+ code.invokeUserCode()
+ library.caller shouldBe null
+ }
+
+ /**
+ * This is a test of obtaining the caller from inside a class that is
+ * interested in knowing the caller.
+ */
+ @Test
+ fun `obtain the caller of a class`() {
+ Elvis.sign() shouldBe this::class.java
+ AnybodyHome.call() shouldBe AnybodyHome::class.java
+ }
+
+ /**
+ * Please see [io.spine.reflect.given.LogContext] stub for details.
+ */
+ @Test
+ fun `obtain trimmed call stack for a class`() {
+ // We don't care about the skip count here since we call stub `LogContext` directly.
+ val library = LoggerCode(skipCount = 0)
+ val code = UserCode(library)
+ code.someMethod()
+
+ val stack = library.logContext.callerStack
+ stack shouldNotBe null
+ stack!!
+ // This is the element we want to be the caller.
+ stack[0].className shouldBe UserCode::class.java.name
+ // This means that logging internals are skipped in the stack trace.
+ stack[1].className shouldBe CallerFinderSpec::class.java.name
+ }
+}
diff --git a/src/test/kotlin/io/spine/reflect/StackWalkerStackGetterSpec.kt b/src/test/kotlin/io/spine/reflect/StackWalkerStackGetterSpec.kt
new file mode 100644
index 0000000..f747ced
--- /dev/null
+++ b/src/test/kotlin/io/spine/reflect/StackWalkerStackGetterSpec.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.reflect
+
+import org.junit.jupiter.api.DisplayName
+
+/**
+ * Tests for [StackWalkerStackGetter].
+ *
+ * @see
+ * Original Java code of Google Flogger
+ */
+@DisplayName("`StackWalkerStackGetter` should")
+internal class StackWalkerStackGetterSpec : AbstractStackGetterSpec(StackWalkerStackGetter())
diff --git a/src/test/kotlin/io/spine/reflect/ThrowableStackGetterSpec.kt b/src/test/kotlin/io/spine/reflect/ThrowableStackGetterSpec.kt
new file mode 100644
index 0000000..dee575b
--- /dev/null
+++ b/src/test/kotlin/io/spine/reflect/ThrowableStackGetterSpec.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.reflect
+
+import org.junit.jupiter.api.DisplayName
+
+/**
+ * Tests for [ThrowableStackGetter].
+ *
+ * @see
+ * Original Java code of Google Flogger
+ */
+@DisplayName("`ThrowableStackGetter` should")
+internal class ThrowableStackGetterSpec : AbstractStackGetterSpec(ThrowableStackGetter())
diff --git a/src/test/kotlin/io/spine/reflect/given/CallingElvis.kt b/src/test/kotlin/io/spine/reflect/given/CallingElvis.kt
new file mode 100644
index 0000000..dd919f8
--- /dev/null
+++ b/src/test/kotlin/io/spine/reflect/given/CallingElvis.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.reflect.given
+
+import io.spine.reflect.CallerFinder.findCallerOf
+
+object Elvis {
+
+ /**
+ * Obtains the caller of this class.
+ */
+ fun whoIsCalling(): Class<*>? {
+ val callingClass = findCallerOf(this::class.java, 0)?.let {
+ Class.forName(it.className)
+ }
+ return callingClass
+ }
+
+ fun sign(): Class<*>? {
+ return whoIsCalling()
+ }
+}
+
+object AnybodyHome {
+ fun call(): Class<*>? {
+ return Elvis.whoIsCalling()
+ }
+}
diff --git a/src/test/kotlin/io/spine/reflect/given/LogContext.kt b/src/test/kotlin/io/spine/reflect/given/LogContext.kt
new file mode 100644
index 0000000..8c46dbb
--- /dev/null
+++ b/src/test/kotlin/io/spine/reflect/given/LogContext.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.reflect.given
+
+import io.spine.reflect.CallerFinder.stackForCallerOf
+
+/**
+ * A stub mimicking the behavior of a real `LogContext` for the purpose of tests.
+ *
+ * Real [`LogContext`](https://github.com/SpineEventEngine/logging/blob/master/flogger/middleware/src/main/java/io/spine/logging/flogger/LogContext.java)
+ * class is the consumer of
+ * [CallerFinder.stackForCallerOf][io.spine.reflect.CallerFinder.stackForCallerOf] method.
+ *
+ * We recreate the behavior described in comments inside the real `LogContext.postProcess()` method
+ * to match the expected behavior.
+ */
+internal abstract class LogContext {
+
+ var message: String? = null
+ var callerStack: Array? = null
+
+ fun log(message: String) {
+ if (shouldLog()) {
+ this.message = message
+ }
+ }
+
+ private fun shouldLog(): Boolean {
+ val shouldLog = postProcess()
+ return shouldLog
+ }
+
+ protected open fun postProcess(): Boolean {
+ // Remember the call stack as if we get it in real `LogContext`.
+ // We pass `maxDepth` at maximum as the most demanding case.
+ callerStack = stackForCallerOf(LogContext::class.java, maxDepth = -1, skip = 1)
+ return true
+ }
+}
+
+internal open class ChildContext: LogContext() {
+
+ override fun postProcess(): Boolean {
+ val fromSuper = super.postProcess()
+ // Simulate overriding.
+ return fromSuper
+ }
+}
+
+internal class OtherChildContext: ChildContext() {
+ override fun postProcess(): Boolean {
+ val deeperWeGo = super.postProcess()
+ // Override yet more.
+ return deeperWeGo
+ }
+}
diff --git a/src/test/kotlin/io/spine/reflect/given/LoggerCode.kt b/src/test/kotlin/io/spine/reflect/given/LoggerCode.kt
new file mode 100644
index 0000000..5715db3
--- /dev/null
+++ b/src/test/kotlin/io/spine/reflect/given/LoggerCode.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.reflect.given
+
+import io.spine.reflect.CallerFinder
+import io.spine.reflect.StackGetter
+
+/**
+ * A fake class that emulates the logging library, which eventually
+ * calls the given [StackGetter], if any, or [CallerFinder].
+ */
+internal class LoggerCode(
+ private val skipCount: Int,
+ private val stackGetter: StackGetter? = null
+) {
+
+ var caller: StackTraceElement? = null
+
+ val logContext: LogContext = OtherChildContext()
+
+ fun logMethod() {
+ internalMethodOne()
+ }
+
+ private fun internalMethodOne() {
+ internalMethodTwo()
+ }
+
+ private fun internalMethodTwo() {
+ caller = if (stackGetter != null) {
+ stackGetter.callerOf(LoggerCode::class.java, skipCount)
+ } else {
+ CallerFinder.findCallerOf(LoggerCode::class.java, skipCount)
+ }
+ }
+}
diff --git a/src/test/kotlin/io/spine/reflect/given/UserCode.kt b/src/test/kotlin/io/spine/reflect/given/UserCode.kt
new file mode 100644
index 0000000..c77d121
--- /dev/null
+++ b/src/test/kotlin/io/spine/reflect/given/UserCode.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2024, TeamDev. All rights reserved.
+ *
+ * Licensed 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
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.reflect.given
+
+/**
+ * Fake class that emulates some code calling a log method.
+ */
+internal class UserCode(private val logger: LoggerCode) {
+
+ fun invokeUserCode() {
+ loggingMethod()
+ }
+
+ private fun loggingMethod() {
+ logger.logMethod()
+ }
+
+ /**
+ * Please see [LogContext] stub for details.
+ */
+ fun someMethod() {
+ val logContext = logger.logContext
+ logContext.log("INFO: at `someMethod()`")
+ }
+}
diff --git a/version.gradle.kts b/version.gradle.kts
index 52f72cb..3bbeef3 100644
--- a/version.gradle.kts
+++ b/version.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2023, TeamDev. All rights reserved.
+ * Copyright 2024, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,4 +24,4 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-val versionToPublish: String by extra("2.0.0-SNAPSHOT.183")
+val versionToPublish: String by extra("2.0.0-SNAPSHOT.184")