diff --git a/.agents/tasks/archive/rewrite-external-messages-section.md b/.agents/tasks/archive/rewrite-external-messages-section.md new file mode 100644 index 0000000000..4196ae4af3 --- /dev/null +++ b/.agents/tasks/archive/rewrite-external-messages-section.md @@ -0,0 +1,22 @@ +# Task: Rewrite the "External messages" section of the documentation +- New name for the section: "Using Validators" +- Purpose: explain how to use validators in code, including how to create and apply them to proto messages. +- Target outcome: a reader can create a validator for a proto message, apply it to an external + message and to a local message likewise. +- Cases for using validators + - External messages: the message the modification of source code is not possible (e.g. messages in libraries). + - Validation of local messages that require computation or custom logic that cannot be expressed in proto options. +- Describe that multiple validations can be applied to the same message type. + +## Describe creating a validator +- Implement `io.spine.validation.MessageValidator`. +- Describe automatic discovered of validators using the `ServiceLoader` mechanism. +- Mention using `AutoService` to generate the service provider configuration file. +- Mention that it is possible to add a validator to `ValidatorRegistry` explicitly during + application startup or configuration. This is an alternative to service discovery approach. +- Describe the requirement for a public no-args constructor. + +## Keep the `implement-an-external-validator.md` page +- Rename the file to `implement-a-validator.md`. +- Remove using the `@Validator` annotation from the example, and instead show using `@AutoService`. +- Add the page to `sidenav.yml`. diff --git a/.agents/tasks/third-party-messages-plan.md b/.agents/tasks/archive/third-party-messages-plan.md similarity index 85% rename from .agents/tasks/third-party-messages-plan.md rename to .agents/tasks/archive/third-party-messages-plan.md index 690a7f7ec9..6af2326679 100644 --- a/.agents/tasks/third-party-messages-plan.md +++ b/.agents/tasks/archive/third-party-messages-plan.md @@ -12,7 +12,7 @@ validating local messages. ## Placement - Placement of the page: after “Built-in options”, before “Custom validation”. -- Hugo section (minimal change): add the page under `docs/content/docs/validation/02-concepts/`. +- Hugo section (minimal change): add the page under `docs/content/docs/validation/03-built-in-options/`. If the site navigation later gains an “Advanced topics” section, the page can move there. ## Planned content @@ -39,12 +39,9 @@ validating local messages. - Return `List`. - Use `FieldViolation` (and other available violation types) to point at a field path and value. - Mention that the runtime converts `DetectedViolation` into `ConstraintViolation`/`ValidationError`. -- Constraints and guardrails: - - Exactly one validator per external message type (duplicate is an error). - - Validators for local messages are prohibited (use options/codegen instead). - Example walkthrough (short, copy-pastable): - - Implement `EarphonesValidator` (from `:tests:validator`) and show how it affects a local message - that contains an `Earphones` field. + - Implement `TimestampValidator` (from `:jvm-runtime`) and show how it affects a local message + that contains a `Timestamp` field. ## Source references to anchor the docs @@ -52,7 +49,7 @@ validating local messages. - `jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt` - `jvm-runtime/src/main/kotlin/io/spine/validation/Validator.kt` - Example implementation: - - `tests/validator/src/main/kotlin/io/spine/tools/validation/test/EarphonesValidator.kt` + - `jvm-runtime/src/main/kotlin/io/spine/validation/TimestampValidator.kt` ## Output diff --git a/.agents/tasks/archive/using-validator-registry-section.md b/.agents/tasks/archive/using-validator-registry-section.md new file mode 100644 index 0000000000..db3077cceb --- /dev/null +++ b/.agents/tasks/archive/using-validator-registry-section.md @@ -0,0 +1,22 @@ +# Task: create a page about `ValidatorRegistry` and how to use it + +## Main idea +In simple cases the programmer using the Validation library does not need to interact +with `ValidatorRegistry` directly, as the library discovers and applies validators automatically. + +However, in some cases it may be necessary to interact with the registry directly, +for example, to add a validator explicitly or to query the registry for registered validators. + +## Features +- Discovery and loading validators using the `ServiceLoader` mechanism. +- Setting `validator` placeholder in the reported violation messages. +- Adding validators. +- Quering and removal of registered validators. + +## Edge case +- Overwriting validation by automatically registered validators by removing them from the + registry and adding new validators. + +## Placement +- Add the page after "Implement a validator" page. +- Update `sidenav.yml` accodringly. diff --git a/.agents/tasks/archive/validator-registry.md b/.agents/tasks/archive/validator-registry.md new file mode 100644 index 0000000000..689c08af20 --- /dev/null +++ b/.agents/tasks/archive/validator-registry.md @@ -0,0 +1,25 @@ +# Task: Implement a registry for custom validators of Protobuf messages + +This task involves creating a registry system that allows for the dynamic registration and retrieval +of custom validators for Protobuf messages. +The registry should support adding new validators at runtime and efficiently look up validators +based on message types. + +## Goals +- Design a flexible and efficient registry for custom validators implementing +the `MessageValidator` interface. + +## Target class +- `io.spine.validation.ValidatorRegistry` + +## Module +- `jvm-runtime` + +## Features +- Ability to add custom validators for specific Protobuf message types. +- Support several validators per type. +- Ability to remove validators from the registry for a given type. +- Ability to clear the whole registry. +- Load validators from the classpath using Java's ServiceLoader mechanism. +- API for validating a message by looking up its type in the registry and + applying all associated validators. diff --git a/build.gradle.kts b/build.gradle.kts index 95f5f83778..366eb7fcad 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -85,7 +85,6 @@ spinePublishing { "java", "java-bundle", "jvm-runtime", - "ksp", ) modulesWithCustomPublishing = setOf( "java-bundle", diff --git a/buildSrc/src/main/kotlin/fat-jar.gradle.kts b/buildSrc/src/main/kotlin/fat-jar.gradle.kts index 67a00b45ec..482d4bb774 100644 --- a/buildSrc/src/main/kotlin/fat-jar.gradle.kts +++ b/buildSrc/src/main/kotlin/fat-jar.gradle.kts @@ -92,8 +92,8 @@ tasks.shadowJar { "resources/com/pty4j/**", // Protobuf files. - "google/**", - "spine/**", + "google/**/*.proto", + "spine/**/*.proto", "src/**", // Java source code files of the package `org.osgi`. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt index 5743f9fcf4..83566850ba 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt @@ -36,7 +36,7 @@ object Validation { /** * The version of the Validation library artifacts. */ - const val version = "2.0.0-SNAPSHOT.395" + const val version = "2.0.0-SNAPSHOT.400" /** * The last version of Validation compatible with ProtoData. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/test/JUnit.kt b/buildSrc/src/main/kotlin/io/spine/dependency/test/JUnit.kt index 6e9021fb0c..a4962175ae 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/test/JUnit.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/test/JUnit.kt @@ -33,7 +33,7 @@ import io.spine.dependency.DependencyWithBom @Suppress("unused", "ConstPropertyName") object JUnit : DependencyWithBom() { - override val version = "6.0.0" + override val version = "6.0.3" override val group: String = "org.junit" /** diff --git a/dependencies.md b/dependencies.md index 93172c0ca6..5eb1cfa7c1 100644 --- a/dependencies.md +++ b/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1061,7 +1061,7 @@ * **Project URL:** [http://jspecify.org/](http://jspecify.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -1069,27 +1069,27 @@ * **Project URL:** [https://junit-pioneer.org/](https://junit-pioneer.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -1139,14 +1139,14 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:13 WET 2026** using +This report was generated on **Mon Mar 09 16:52:42 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1703,11 +1703,19 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.3. + * **Project URL:** [https://junit.org/](https://junit.org/) + * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) + 1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.3. + * **Project URL:** [https://junit.org/](https://junit.org/) + * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) + +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -1715,6 +1723,10 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.3. + * **Project URL:** [https://junit.org/](https://junit.org/) + * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) + 1. **Group** : org.opentest4j. **Name** : opentest4j. **Version** : 1.3.0. * **Project URL:** [https://github.com/ota4j-team/opentest4j](https://github.com/ota4j-team/opentest4j) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -1731,28 +1743,28 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:12 WET 2026** using +This report was generated on **Mon Mar 09 16:52:40 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-docs:2.0.0-SNAPSHOT.396` +# Dependencies of `io.spine.tools:validation-docs:2.0.0-SNAPSHOT.401` ## Runtime ## Compile, tests, and tooling The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Mon Feb 23 18:35:33 WET 2026** using +This report was generated on **Fri Mar 06 18:16:01 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -2763,7 +2775,7 @@ This report was generated on **Mon Feb 23 18:35:33 WET 2026** using * **Project URL:** [http://jspecify.org/](http://jspecify.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -2771,27 +2783,27 @@ This report was generated on **Mon Feb 23 18:35:33 WET 2026** using * **Project URL:** [https://junit-pioneer.org/](https://junit-pioneer.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -2841,14 +2853,14 @@ This report was generated on **Mon Feb 23 18:35:33 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:13 WET 2026** using +This report was generated on **Mon Mar 09 16:52:42 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -2924,10 +2936,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.36.0. * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -3191,10 +3199,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.errorprone. **Name** : error_prone_annotation. **Version** : 2.36.0. * **Project URL:** [https://errorprone.info/error_prone_annotation](https://errorprone.info/error_prone_annotation) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -3285,976 +3289,14 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 2.1.2. - * **Project URL:** [http://www.github.com/sksamuel/aedile](http://www.github.com/sksamuel/aedile) - * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : com.soywiz.korlibs.korte. **Name** : korte-jvm. **Version** : 4.0.10. - * **Project URL:** [https://github.com/korlibs/korge-next](https://github.com/korlibs/korge-next) - * **License:** [MIT](https://raw.githubusercontent.com/korlibs/korge-next/master/korge/LICENSE.txt) - -1. **Group** : commons-codec. **Name** : commons-codec. **Version** : 1.16.0. - * **Project URL:** [https://commons.apache.org/proper/commons-codec/](https://commons.apache.org/proper/commons-codec/) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : dev.drewhamilton.poko. **Name** : poko-annotations. **Version** : 0.17.1. - * **Project URL:** [https://github.com/drewhamilton/Poko](https://github.com/drewhamilton/Poko) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : dev.drewhamilton.poko. **Name** : poko-annotations-jvm. **Version** : 0.17.1. - * **Project URL:** [https://github.com/drewhamilton/Poko](https://github.com/drewhamilton/Poko) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.github.davidburstrom.contester. **Name** : contester-breakpoint. **Version** : 0.2.0. - * **Project URL:** [https://github.com/davidburstrom/contester](https://github.com/davidburstrom/contester) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.github.detekt.sarif4k. **Name** : sarif4k. **Version** : 0.6.0. - * **Project URL:** [https://detekt.github.io/detekt](https://detekt.github.io/detekt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.github.detekt.sarif4k. **Name** : sarif4k-jvm. **Version** : 0.6.0. - * **Project URL:** [https://detekt.github.io/detekt](https://detekt.github.io/detekt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.github.eisop. **Name** : dataflow-errorprone. **Version** : 3.41.0-eisop1. - * **Project URL:** [https://eisop.github.io/](https://eisop.github.io/) - * **License:** [GNU General Public License, version 2 (GPL2), with the classpath exception](http://www.gnu.org/software/classpath/license.html) - -1. **Group** : io.github.java-diff-utils. **Name** : java-diff-utils. **Version** : 4.12. - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-api. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-cli. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-core. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-metrics. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-parser. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-psi-utils. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-report-html. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-report-md. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-report-sarif. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-report-txt. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-report-xml. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-rules. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-rules-complexity. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-rules-coroutines. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-rules-documentation. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-rules-empty. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-rules-errorprone. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-rules-exceptions. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-rules-naming. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-rules-performance. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-rules-style. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-tooling. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-utils. **Version** : 1.23.8. - * **Project URL:** [https://detekt.dev](https://detekt.dev) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.76.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.76.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.76.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.76.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.76.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.grpc. **Name** : grpc-kotlin-stub. **Version** : 1.4.1. - * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.76.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.76.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.76.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.kotest. **Name** : kotest-assertions-core. **Version** : 6.0.4. - * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) - * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.kotest. **Name** : kotest-assertions-core-jvm. **Version** : 6.0.4. - * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) - * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.kotest. **Name** : kotest-assertions-shared. **Version** : 6.0.4. - * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) - * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.kotest. **Name** : kotest-assertions-shared-jvm. **Version** : 6.0.4. - * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) - * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.kotest. **Name** : kotest-common. **Version** : 6.0.4. - * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) - * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.kotest. **Name** : kotest-common-jvm. **Version** : 6.0.4. - * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) - * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : io.opentelemetry. **Name** : opentelemetry-api. **Version** : 1.41.0. - * **Project URL:** [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.opentelemetry. **Name** : opentelemetry-context. **Version** : 1.41.0. - * **Project URL:** [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : io.perfmark. **Name** : perfmark-api. **Version** : 0.27.0. - * **Project URL:** [https://github.com/perfmark/perfmark](https://github.com/perfmark/perfmark) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - -1. **Group** : javax.annotation. **Name** : javax.annotation-api. **Version** : 1.3.2. - * **Project URL:** [http://jcp.org/en/jsr/detail?id=250](http://jcp.org/en/jsr/detail?id=250) - * **License:** [CDDL + GPLv2 with classpath exception](https://github.com/javaee/javax.annotation/blob/master/LICENSE) - -1. **Group** : javax.inject. **Name** : javax.inject. **Version** : 1. - * **Project URL:** [http://code.google.com/p/atinject/](http://code.google.com/p/atinject/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : junit. **Name** : junit. **Version** : 4.13.2. - * **Project URL:** [http://junit.org](http://junit.org) - * **License:** [Eclipse Public License 1.0](http://www.eclipse.org/legal/epl-v10.html) - -1. **Group** : net.sf.saxon. **Name** : Saxon-HE. **Version** : 12.5. - * **Project URL:** [http://www.saxonica.com/](http://www.saxonica.com/) - * **License:** [Mozilla Public License Version 2.0](http://www.mozilla.org/MPL/2.0/) - -1. **Group** : net.sourceforge.pmd. **Name** : pmd-ant. **Version** : 7.12.0. - * **License:** [BSD-style](http://pmd.sourceforge.net/license.html) - -1. **Group** : net.sourceforge.pmd. **Name** : pmd-core. **Version** : 7.12.0. - * **License:** [BSD-style](http://pmd.sourceforge.net/license.html) - -1. **Group** : net.sourceforge.pmd. **Name** : pmd-java. **Version** : 7.12.0. - * **License:** [BSD-style](http://pmd.sourceforge.net/license.html) - -1. **Group** : org.antlr. **Name** : antlr4-runtime. **Version** : 4.9.3. - * **Project URL:** [http://www.antlr.org](http://www.antlr.org) - * **License:** [The BSD License](http://www.antlr.org/license.html) - -1. **Group** : org.apache.commons. **Name** : commons-lang3. **Version** : 3.17.0. - * **Project URL:** [https://commons.apache.org/proper/commons-lang/](https://commons.apache.org/proper/commons-lang/) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.apache.httpcomponents.client5. **Name** : httpclient5. **Version** : 5.1.3. - * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.apache.httpcomponents.core5. **Name** : httpcore5. **Version** : 5.1.3. - * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.apache.httpcomponents.core5. **Name** : httpcore5-h2. **Version** : 5.1.3. - * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.apiguardian. **Name** : apiguardian-api. **Version** : 1.1.2. - * **Project URL:** [https://github.com/apiguardian-team/apiguardian](https://github.com/apiguardian-team/apiguardian) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.bouncycastle. **Name** : bcpg-jdk18on. **Version** : 1.80. - * **Project URL:** [https://www.bouncycastle.org/download/bouncy-castle-java/](https://www.bouncycastle.org/download/bouncy-castle-java/) - * **License:** [Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - * **License:** [Bouncy Castle Licence](https://www.bouncycastle.org/licence.html) - -1. **Group** : org.bouncycastle. **Name** : bcpkix-jdk18on. **Version** : 1.80. - * **Project URL:** [https://www.bouncycastle.org/download/bouncy-castle-java/](https://www.bouncycastle.org/download/bouncy-castle-java/) - * **License:** [Bouncy Castle Licence](https://www.bouncycastle.org/licence.html) - -1. **Group** : org.bouncycastle. **Name** : bcprov-jdk18on. **Version** : 1.80. - * **Project URL:** [https://www.bouncycastle.org/download/bouncy-castle-java/](https://www.bouncycastle.org/download/bouncy-castle-java/) - * **License:** [Bouncy Castle Licence](https://www.bouncycastle.org/licence.html) - -1. **Group** : org.bouncycastle. **Name** : bcutil-jdk18on. **Version** : 1.80. - * **Project URL:** [https://www.bouncycastle.org/download/bouncy-castle-java/](https://www.bouncycastle.org/download/bouncy-castle-java/) - * **License:** [Bouncy Castle Licence](https://www.bouncycastle.org/licence.html) - -1. **Group** : org.checkerframework. **Name** : checker-compat-qual. **Version** : 2.5.3. - * **Project URL:** [https://checkerframework.org](https://checkerframework.org) - * **License:** [GNU General Public License, version 2 (GPL2), with the classpath exception](http://www.gnu.org/software/classpath/license.html) - * **License:** [The MIT License](http://opensource.org/licenses/MIT) - -1. **Group** : org.checkerframework. **Name** : checker-qual. **Version** : 3.40.0. - * **Project URL:** [https://checkerframework.org/](https://checkerframework.org/) - * **License:** [The MIT License](http://opensource.org/licenses/MIT) - -1. **Group** : org.codehaus.mojo. **Name** : animal-sniffer-annotations. **Version** : 1.21. - * **License:** [MIT license](http://www.opensource.org/licenses/mit-license.php) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.codehaus.woodstox. **Name** : stax2-api. **Version** : 4.2.2. - * **Project URL:** [http://github.com/FasterXML/stax2-api](http://github.com/FasterXML/stax2-api) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The BSD 2-Clause License](http://www.opensource.org/licenses/bsd-license.php) - -1. **Group** : org.freemarker. **Name** : freemarker. **Version** : 2.3.32. - * **Project URL:** [https://freemarker.apache.org/](https://freemarker.apache.org/) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.functionaljava. **Name** : functionaljava. **Version** : 4.8. - * **Project URL:** [http://functionaljava.org/](http://functionaljava.org/) - * **License:** [The BSD3 License](https://github.com/functionaljava/functionaljava/blob/master/etc/LICENCE) - -1. **Group** : org.hamcrest. **Name** : hamcrest. **Version** : 3.0. - * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) - * **License:** [BSD-3-Clause](https://raw.githubusercontent.com/hamcrest/JavaHamcrest/master/LICENSE) - -1. **Group** : org.hamcrest. **Name** : hamcrest-core. **Version** : 3.0. - * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) - * **License:** [BSD-3-Clause](https://raw.githubusercontent.com/hamcrest/JavaHamcrest/master/LICENSE) - -1. **Group** : org.jacoco. **Name** : org.jacoco.agent. **Version** : 0.8.14. - * **License:** [EPL-2.0](https://www.eclipse.org/legal/epl-2.0/) - -1. **Group** : org.jacoco. **Name** : org.jacoco.ant. **Version** : 0.8.14. - * **License:** [EPL-2.0](https://www.eclipse.org/legal/epl-2.0/) - -1. **Group** : org.jacoco. **Name** : org.jacoco.core. **Version** : 0.8.14. - * **License:** [EPL-2.0](https://www.eclipse.org/legal/epl-2.0/) - -1. **Group** : org.jacoco. **Name** : org.jacoco.report. **Version** : 0.8.14. - * **License:** [EPL-2.0](https://www.eclipse.org/legal/epl-2.0/) - -1. **Group** : org.jcommander. **Name** : jcommander. **Version** : 1.85. - * **Project URL:** [https://jcommander.org](https://jcommander.org) - * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 26.0.2. - * **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. **Name** : markdown. **Version** : 0.7.3. - * **Project URL:** [https://github.com/JetBrains/markdown](https://github.com/JetBrains/markdown) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains. **Name** : markdown-jvm. **Version** : 0.7.3. - * **Project URL:** [https://github.com/JetBrains/markdown](https://github.com/JetBrains/markdown) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.dokka. **Name** : analysis-kotlin-symbols. **Version** : 2.1.0. - * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.dokka. **Name** : analysis-markdown. **Version** : 2.1.0. - * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.dokka. **Name** : dokka-base. **Version** : 2.1.0. - * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.dokka. **Name** : dokka-core. **Version** : 2.1.0. - * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.dokka. **Name** : javadoc-plugin. **Version** : 2.1.0. - * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.dokka. **Name** : kotlin-as-java-plugin. **Version** : 2.1.0. - * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.dokka. **Name** : templating-plugin. **Version** : 2.1.0. - * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.intellij.deps. **Name** : trove4j. **Version** : 1.0.20200330. - * **Project URL:** [https://github.com/JetBrains/intellij-deps-trove4j](https://github.com/JetBrains/intellij-deps-trove4j) - * **License:** [GNU LESSER GENERAL PUBLIC LICENSE 2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html) - -1. **Group** : org.jetbrains.kotlin. **Name** : abi-tools. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : abi-tools-api. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-api. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-impl. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-compiler-embeddable. **Version** : 2.0.21. - * **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-compiler-embeddable. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-compiler-runner. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-daemon-client. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-daemon-embeddable. **Version** : 2.0.21. - * **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-daemon-embeddable. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-klib-commonizer-embeddable. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-metadata-jvm. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-reflect. **Version** : 2.0.21. - * **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** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-script-runtime. **Version** : 2.0.21. - * **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-script-runtime. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-common. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-compiler-embeddable. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-compiler-impl-embeddable. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-jvm. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.0.21. - * **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** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-common. **Version** : 2.0.21. - * **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** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 2.0.21. - * **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** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk8. **Version** : 2.0.21. - * **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** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : swift-export-embeddable. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : atomicfu. **Version** : 0.23.1. - * **Project URL:** [https://github.com/Kotlin/kotlinx.atomicfu](https://github.com/Kotlin/kotlinx.atomicfu) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : atomicfu. **Version** : 0.29.0. - * **Project URL:** [https://github.com/Kotlin/kotlinx.atomicfu](https://github.com/Kotlin/kotlinx.atomicfu) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : atomicfu-jvm. **Version** : 0.29.0. - * **Project URL:** [https://github.com/Kotlin/kotlinx.atomicfu](https://github.com/Kotlin/kotlinx.atomicfu) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-bom. **Version** : 1.10.2. - * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-core. **Version** : 1.10.2. - * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-core-jvm. **Version** : 1.10.2. - * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-core-jvm. **Version** : 1.6.4. - * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-jdk8. **Version** : 1.10.2. - * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-test. **Version** : 1.10.2. - * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-test-jvm. **Version** : 1.10.2. - * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-datetime. **Version** : 0.7.1. - * **Project URL:** [https://github.com/Kotlin/kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-datetime-jvm. **Version** : 0.7.1. - * **Project URL:** [https://github.com/Kotlin/kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-html-jvm. **Version** : 0.8.1. - * **Project URL:** [https://github.com/Kotlin/kotlinx.html](https://github.com/Kotlin/kotlinx.html) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-html-jvm. **Version** : 0.9.1. - * **Project URL:** [https://github.com/Kotlin/kotlinx.html](https://github.com/Kotlin/kotlinx.html) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-bom. **Version** : 1.7.3. - * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core. **Version** : 1.4.1. - * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core. **Version** : 1.7.3. - * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core-jvm. **Version** : 1.4.1. - * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core-jvm. **Version** : 1.7.3. - * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-json. **Version** : 1.4.1. - * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-json-jvm. **Version** : 1.4.1. - * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jsoup. **Name** : jsoup. **Version** : 1.16.1. - * **Project URL:** [https://jsoup.org/](https://jsoup.org/) - * **License:** [The MIT License](https://jsoup.org/license) - -1. **Group** : org.jspecify. **Name** : jspecify. **Version** : 1.0.0. - * **Project URL:** [http://jspecify.org/](http://jspecify.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. - * **Project URL:** [https://junit.org/](https://junit.org/) - * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) - -1. **Group** : org.junit-pioneer. **Name** : junit-pioneer. **Version** : 2.3.0. - * **Project URL:** [https://junit-pioneer.org/](https://junit-pioneer.org/) - * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) - -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. - * **Project URL:** [https://junit.org/](https://junit.org/) - * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) - -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.0. - * **Project URL:** [https://junit.org/](https://junit.org/) - * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) - -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. - * **Project URL:** [https://junit.org/](https://junit.org/) - * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) - -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. - * **Project URL:** [https://junit.org/](https://junit.org/) - * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) - -1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.0. - * **Project URL:** [https://junit.org/](https://junit.org/) - * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) - -1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.0. - * **Project URL:** [https://junit.org/](https://junit.org/) - * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) - -1. **Group** : org.opentest4j. **Name** : opentest4j. **Version** : 1.3.0. - * **Project URL:** [https://github.com/ota4j-team/opentest4j](https://github.com/ota4j-team/opentest4j) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.ow2.asm. **Name** : asm. **Version** : 9.6. - * **Project URL:** [http://asm.ow2.io/](http://asm.ow2.io/) - * **License:** [BSD-3-Clause](https://asm.ow2.io/license.html) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.ow2.asm. **Name** : asm-commons. **Version** : 9.6. - * **Project URL:** [http://asm.ow2.io/](http://asm.ow2.io/) - * **License:** [BSD-3-Clause](https://asm.ow2.io/license.html) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.ow2.asm. **Name** : asm-tree. **Version** : 9.6. - * **Project URL:** [http://asm.ow2.io/](http://asm.ow2.io/) - * **License:** [BSD-3-Clause](https://asm.ow2.io/license.html) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.pcollections. **Name** : pcollections. **Version** : 4.0.1. - * **Project URL:** [https://github.com/hrldcpr/pcollections](https://github.com/hrldcpr/pcollections) - * **License:** [The MIT License](https://opensource.org/licenses/mit-license.php) - -1. **Group** : org.pcollections. **Name** : pcollections. **Version** : 4.0.2. - * **Project URL:** [https://github.com/hrldcpr/pcollections](https://github.com/hrldcpr/pcollections) - * **License:** [The MIT License](https://opensource.org/licenses/mit-license.php) - -1. **Group** : org.slf4j. **Name** : jul-to-slf4j. **Version** : 1.7.36. - * **Project URL:** [http://www.slf4j.org](http://www.slf4j.org) - * **License:** [MIT License](http://www.opensource.org/licenses/mit-license.php) - -1. **Group** : org.snakeyaml. **Name** : snakeyaml-engine. **Version** : 2.7. - * **Project URL:** [https://bitbucket.org/snakeyaml/snakeyaml-engine](https://bitbucket.org/snakeyaml/snakeyaml-engine) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.xmlresolver. **Name** : xmlresolver. **Version** : 5.2.2. - * **Project URL:** [https://github.com/xmlresolver/xmlresolver](https://github.com/xmlresolver/xmlresolver) - * **License:** [Apache License version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - -1. **Group** : org.yaml. **Name** : snakeyaml. **Version** : 2.4. - * **Project URL:** [https://bitbucket.org/snakeyaml/snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - - -The dependencies distributed under several licenses, are used according their commercial-use-friendly license. - -This report was generated on **Fri Feb 27 20:33:13 WET 2026** 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). - - - - -# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.400` - -## Runtime -1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. - * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 13.0. - * **Project URL:** [http://www.jetbrains.org](http://www.jetbrains.org) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-bom. **Version** : 1.10.2. - * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jspecify. **Name** : jspecify. **Version** : 1.0.0. - * **Project URL:** [http://jspecify.org/](http://jspecify.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -## Compile, tests, and tooling -1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. - * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 13.0. - * **Project URL:** [http://www.jetbrains.org](http://www.jetbrains.org) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-bom. **Version** : 1.10.2. - * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jspecify. **Name** : jspecify. **Version** : 1.0.0. - * **Project URL:** [http://jspecify.org/](http://jspecify.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - - -The dependencies distributed under several licenses, are used according their commercial-use-friendly license. - -This report was generated on **Fri Feb 27 20:33:11 WET 2026** 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). - - - - -# Dependencies of `io.spine:validation-jvm-runtime:2.0.0-SNAPSHOT.400` - -## Runtime -1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. - * **Project URL:** [http://findbugs.sourceforge.net/](http://findbugs.sourceforge.net/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.13.0. - * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.36.0. - * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.3. - * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.guava. **Name** : guava. **Version** : 33.5.0-jre. - * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.j2objc. **Name** : j2objc-annotations. **Version** : 2.8. - * **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** : 4.33.2. - * **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** : 4.33.2. - * **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** : 4.33.2. - * **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** : org.jetbrains. **Name** : annotations. **Version** : 26.0.2. - * **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-bom. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-reflect. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.2.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-bom. **Version** : 1.10.2. - * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jspecify. **Name** : jspecify. **Version** : 1.0.0. - * **Project URL:** [http://jspecify.org/](http://jspecify.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -## Compile, tests, and tooling -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. - * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.20. - * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.20.0. - * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.20.0. - * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.20.0. - * **Project URL:** [https://github.com/FasterXML/jackson-dataformat-xml](https://github.com/FasterXML/jackson-dataformat-xml) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.20.0. - * **Project URL:** [https://github.com/FasterXML/jackson-module-kotlin](https://github.com/FasterXML/jackson-module-kotlin) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.fasterxml.woodstox. **Name** : woodstox-core. **Version** : 7.1.1. - * **Project URL:** [https://github.com/FasterXML/woodstox](https://github.com/FasterXML/woodstox) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.github.ben-manes.caffeine. **Name** : caffeine. **Version** : 2.9.3. - * **Project URL:** [https://github.com/ben-manes/caffeine](https://github.com/ben-manes/caffeine) - * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.github.ben-manes.caffeine. **Name** : caffeine. **Version** : 3.0.5. - * **Project URL:** [https://github.com/ben-manes/caffeine](https://github.com/ben-manes/caffeine) - * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.github.kevinstern. **Name** : software-and-algorithms. **Version** : 1.0. - * **Project URL:** [https://www.github.com/KevinStern/software-and-algorithms](https://www.github.com/KevinStern/software-and-algorithms) - * **License:** [MIT License](http://www.opensource.org/licenses/mit-license.php) - -1. **Group** : com.github.oowekyala.ooxml. **Name** : nice-xml-messages. **Version** : 3.1. - * **Project URL:** [https://github.com/oowekyala/nice-xml-messages](https://github.com/oowekyala/nice-xml-messages) - * **License:** [MIT License](https://github.com/oowekyala/nice-xml-messages/tree/master/LICENSE) - -1. **Group** : com.google.auto. **Name** : auto-common. **Version** : 1.2.2. - * **Project URL:** [https://github.com/google/auto/tree/main/common](https://github.com/google/auto/tree/main/common) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.auto.service. **Name** : auto-service. **Version** : 1.1.1. - * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. - * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.auto.value. **Name** : auto-value-annotations. **Version** : 1.10.2. - * **Project URL:** [https://github.com/google/auto/tree/main/value](https://github.com/google/auto/tree/main/value) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. - * **Project URL:** [http://findbugs.sourceforge.net/](http://findbugs.sourceforge.net/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.13.0. - * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.errorprone. **Name** : error_prone_annotation. **Version** : 2.36.0. - * **Project URL:** [https://errorprone.info/error_prone_annotation](https://errorprone.info/error_prone_annotation) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.36.0. - * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.errorprone. **Name** : error_prone_check_api. **Version** : 2.36.0. - * **Project URL:** [https://errorprone.info/error_prone_check_api](https://errorprone.info/error_prone_check_api) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.errorprone. **Name** : error_prone_core. **Version** : 2.36.0. - * **Project URL:** [https://errorprone.info/error_prone_core](https://errorprone.info/error_prone_core) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.errorprone. **Name** : error_prone_type_annotations. **Version** : 2.36.0. - * **Project URL:** [https://errorprone.info/error_prone_type_annotations](https://errorprone.info/error_prone_type_annotations) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.errorprone. **Name** : javac. **Version** : 9+181-r4173-1. - * **Project URL:** [https://github.com/google/error-prone-javac](https://github.com/google/error-prone-javac) - * **License:** [GNU General Public License, version 2, with the Classpath Exception](http://openjdk.java.net/legal/gplv2+ce.html) - -1. **Group** : com.google.flogger. **Name** : flogger. **Version** : 0.7.4. - * **Project URL:** [https://github.com/google/flogger](https://github.com/google/flogger) - * **License:** [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.flogger. **Name** : flogger-system-backend. **Version** : 0.7.4. - * **Project URL:** [https://github.com/google/flogger](https://github.com/google/flogger) - * **License:** [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.googlejavaformat. **Name** : google-java-format. **Version** : 1.19.1. - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.3. - * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.guava. **Name** : guava. **Version** : 33.5.0-jre. - * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 33.5.0-jre. - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.j2objc. **Name** : j2objc-annotations. **Version** : 2.8. - * **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** : 4.33.2. - * **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** : 4.33.2. - * **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** : 4.33.2. - * **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** : protoc. **Version** : 4.33.2. - * **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) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.truth. **Name** : truth. **Version** : 1.4.4. - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.truth.extensions. **Name** : truth-java8-extension. **Version** : 1.4.4. - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.truth.extensions. **Name** : truth-liteproto-extension. **Version** : 1.4.4. - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.truth.extensions. **Name** : truth-proto-extension. **Version** : 1.4.4. - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 2.1.2. + * **Project URL:** [http://www.github.com/sksamuel/aedile](http://www.github.com/sksamuel/aedile) + * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) 1. **Group** : com.soywiz.korlibs.korte. **Name** : korte-jvm. **Version** : 4.0.10. * **Project URL:** [https://github.com/korlibs/korge-next](https://github.com/korlibs/korge-next) * **License:** [MIT](https://raw.githubusercontent.com/korlibs/korge-next/master/korge/LICENSE.txt) -1. **Group** : com.squareup. **Name** : kotlinpoet. **Version** : 2.2.0. - * **Project URL:** [https://github.com/square/kotlinpoet](https://github.com/square/kotlinpoet) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.squareup. **Name** : kotlinpoet-jvm. **Version** : 2.2.0. - * **Project URL:** [https://github.com/square/kotlinpoet](https://github.com/square/kotlinpoet) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : commons-codec. **Name** : commons-codec. **Version** : 1.16.0. * **Project URL:** [https://commons.apache.org/proper/commons-codec/](https://commons.apache.org/proper/commons-codec/) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -4267,10 +3309,6 @@ This report was generated on **Fri Feb 27 20:33:11 WET 2026** using * **Project URL:** [https://github.com/drewhamilton/Poko](https://github.com/drewhamilton/Poko) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : dev.zacsweers.autoservice. **Name** : auto-service-ksp. **Version** : 1.2.0. - * **Project URL:** [https://github.com/ZacSweers/auto-service-ksp](https://github.com/ZacSweers/auto-service-ksp) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : io.github.davidburstrom.contester. **Name** : contester-breakpoint. **Version** : 0.2.0. * **Project URL:** [https://github.com/davidburstrom/contester](https://github.com/davidburstrom/contester) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -4382,6 +3420,42 @@ This report was generated on **Fri Feb 27 20:33:11 WET 2026** using * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.76.0. + * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) + * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) + +1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.76.0. + * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) + * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) + +1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.76.0. + * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) + * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) + +1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.76.0. + * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) + * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) + +1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.76.0. + * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) + * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) + +1. **Group** : io.grpc. **Name** : grpc-kotlin-stub. **Version** : 1.4.1. + * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) + * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) + +1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.76.0. + * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) + * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) + +1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.76.0. + * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) + * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) + +1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.76.0. + * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) + * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) + 1. **Group** : io.kotest. **Name** : kotest-assertions-core. **Version** : 6.0.4. * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) @@ -4414,6 +3488,14 @@ This report was generated on **Fri Feb 27 20:33:11 WET 2026** using * **Project URL:** [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : io.perfmark. **Name** : perfmark-api. **Version** : 0.27.0. + * **Project URL:** [https://github.com/perfmark/perfmark](https://github.com/perfmark/perfmark) + * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) + +1. **Group** : javax.annotation. **Name** : javax.annotation-api. **Version** : 1.3.2. + * **Project URL:** [http://jcp.org/en/jsr/detail?id=250](http://jcp.org/en/jsr/detail?id=250) + * **License:** [CDDL + GPLv2 with classpath exception](https://github.com/javaee/javax.annotation/blob/master/LICENSE) + 1. **Group** : javax.inject. **Name** : javax.inject. **Version** : 1. * **Project URL:** [http://code.google.com/p/atinject/](http://code.google.com/p/atinject/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -4482,6 +3564,10 @@ This report was generated on **Fri Feb 27 20:33:11 WET 2026** using * **Project URL:** [https://checkerframework.org/](https://checkerframework.org/) * **License:** [The MIT License](http://opensource.org/licenses/MIT) +1. **Group** : org.codehaus.mojo. **Name** : animal-sniffer-annotations. **Version** : 1.21. + * **License:** [MIT license](http://www.opensource.org/licenses/mit-license.php) + * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + 1. **Group** : org.codehaus.woodstox. **Name** : stax2-api. **Version** : 4.2.2. * **Project URL:** [http://github.com/FasterXML/stax2-api](http://github.com/FasterXML/stax2-api) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -4491,6 +3577,10 @@ This report was generated on **Fri Feb 27 20:33:11 WET 2026** using * **Project URL:** [https://freemarker.apache.org/](https://freemarker.apache.org/) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : org.functionaljava. **Name** : functionaljava. **Version** : 4.8. + * **Project URL:** [http://functionaljava.org/](http://functionaljava.org/) + * **License:** [The BSD3 License](https://github.com/functionaljava/functionaljava/blob/master/etc/LICENCE) + 1. **Group** : org.hamcrest. **Name** : hamcrest. **Version** : 3.0. * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) * **License:** [BSD-3-Clause](https://raw.githubusercontent.com/hamcrest/JavaHamcrest/master/LICENSE) @@ -4771,7 +3861,7 @@ This report was generated on **Fri Feb 27 20:33:11 WET 2026** using * **Project URL:** [http://jspecify.org/](http://jspecify.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -4779,27 +3869,27 @@ This report was generated on **Fri Feb 27 20:33:11 WET 2026** using * **Project URL:** [https://junit-pioneer.org/](https://junit-pioneer.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -4842,23 +3932,77 @@ This report was generated on **Fri Feb 27 20:33:11 WET 2026** using * **Project URL:** [https://github.com/xmlresolver/xmlresolver](https://github.com/xmlresolver/xmlresolver) * **License:** [Apache License version 2.0](https://www.apache.org/licenses/LICENSE-2.0) +1. **Group** : org.yaml. **Name** : snakeyaml. **Version** : 2.4. + * **Project URL:** [https://bitbucket.org/snakeyaml/snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:13 WET 2026** using +This report was generated on **Mon Mar 09 16:52:42 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-ksp:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.401` ## Runtime -1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. - * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 13.0. + * **Project URL:** [http://www.jetbrains.org](http://www.jetbrains.org) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.2.21. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.2.21. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-bom. **Version** : 1.10.2. + * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jspecify. **Name** : jspecify. **Version** : 1.0.0. + * **Project URL:** [http://jspecify.org/](http://jspecify.org/) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +## Compile, tests, and tooling +1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 13.0. + * **Project URL:** [http://www.jetbrains.org](http://www.jetbrains.org) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.2.21. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.2.21. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-bom. **Version** : 1.10.2. + * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jspecify. **Name** : jspecify. **Version** : 1.0.0. + * **Project URL:** [http://jspecify.org/](http://jspecify.org/) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + + +The dependencies distributed under several licenses, are used according their commercial-use-friendly license. + +This report was generated on **Mon Mar 09 16:52:39 WET 2026** 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). + + + + +# Dependencies of `io.spine:validation-jvm-runtime:2.0.0-SNAPSHOT.401` + +## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. * **Project URL:** [http://findbugs.sourceforge.net/](http://findbugs.sourceforge.net/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -4867,10 +4011,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.36.0. * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -4927,14 +4067,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) ## Compile, tests, and tooling -1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. - * **Project URL:** [http://nanoxml.cyberelf.be/](http://nanoxml.cyberelf.be/) - * **License:** [The zlib/libpng License](http://www.opensource.org/licenses/zlib-license.html) - -1. **Group** : com.fasterxml. **Name** : aalto-xml. **Version** : 1.3.0. - * **Project URL:** [https://github.com/FasterXML/aalto-xml](https://github.com/FasterXML/aalto-xml) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -4989,6 +4121,10 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/google/auto/tree/main/common](https://github.com/google/auto/tree/main/common) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : com.google.auto.service. **Name** : auto-service. **Version** : 1.1.1. + * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + 1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5009,18 +4145,10 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-aa-embeddable. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.errorprone. **Name** : error_prone_annotation. **Version** : 2.36.0. * **Project URL:** [https://errorprone.info/error_prone_annotation](https://errorprone.info/error_prone_annotation) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5103,12 +4231,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using 1. **Group** : com.google.truth.extensions. **Name** : truth-proto-extension. **Version** : 1.4.4. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.jetbrains.intellij.platform. **Name** : util. **Version** : 213.7172.53.**No license information found** -1. **Group** : com.jetbrains.intellij.platform. **Name** : util-base. **Version** : 213.7172.53.**No license information found** -1. **Group** : com.jetbrains.intellij.platform. **Name** : util-class-loader. **Version** : 213.7172.53.**No license information found** -1. **Group** : com.jetbrains.intellij.platform. **Name** : util-rt. **Version** : 213.7172.53.**No license information found** -1. **Group** : com.jetbrains.intellij.platform. **Name** : util-text-matching. **Version** : 213.7172.53.**No license information found** -1. **Group** : com.jetbrains.intellij.platform. **Name** : util-xml-dom. **Version** : 213.7172.53.**No license information found** 1. **Group** : com.soywiz.korlibs.korte. **Name** : korte-jvm. **Version** : 4.0.10. * **Project URL:** [https://github.com/korlibs/korge-next](https://github.com/korlibs/korge-next) * **License:** [MIT](https://raw.githubusercontent.com/korlibs/korge-next/master/korge/LICENSE.txt) @@ -5121,14 +4243,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/square/kotlinpoet](https://github.com/square/kotlinpoet) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.squareup.okio. **Name** : okio. **Version** : 3.6.0. - * **Project URL:** [https://github.com/square/okio/](https://github.com/square/okio/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.squareup.okio. **Name** : okio-jvm. **Version** : 3.6.0. - * **Project URL:** [https://github.com/square/okio/](https://github.com/square/okio/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : commons-codec. **Name** : commons-codec. **Version** : 1.16.0. * **Project URL:** [https://commons.apache.org/proper/commons-codec/](https://commons.apache.org/proper/commons-codec/) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5145,18 +4259,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/ZacSweers/auto-service-ksp](https://github.com/ZacSweers/auto-service-ksp) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : dev.zacsweers.kctfork. **Name** : core. **Version** : 0.7.1. - * **Project URL:** [https://github.com/zacsweers/kotlin-compile-testing](https://github.com/zacsweers/kotlin-compile-testing) - * **License:** [Mozilla Public License 2.0](https://www.mozilla.org/en-US/MPL/2.0/) - -1. **Group** : dev.zacsweers.kctfork. **Name** : ksp. **Version** : 0.7.1. - * **Project URL:** [https://github.com/zacsweers/kotlin-compile-testing](https://github.com/zacsweers/kotlin-compile-testing) - * **License:** [Mozilla Public License 2.0](https://www.mozilla.org/en-US/MPL/2.0/) - -1. **Group** : io.github.classgraph. **Name** : classgraph. **Version** : 4.8.179. - * **Project URL:** [https://github.com/classgraph/classgraph](https://github.com/classgraph/classgraph) - * **License:** [The MIT License (MIT)](http://opensource.org/licenses/MIT) - 1. **Group** : io.github.davidburstrom.contester. **Name** : contester-breakpoint. **Version** : 0.2.0. * **Project URL:** [https://github.com/davidburstrom/contester](https://github.com/davidburstrom/contester) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5325,10 +4427,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [http://www.antlr.org](http://www.antlr.org) * **License:** [The BSD License](http://www.antlr.org/license.html) -1. **Group** : org.apache.commons. **Name** : commons-compress. **Version** : 1.21. - * **Project URL:** [https://commons.apache.org/proper/commons-compress/](https://commons.apache.org/proper/commons-compress/) - * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.apache.commons. **Name** : commons-lang3. **Version** : 3.17.0. * **Project URL:** [https://commons.apache.org/proper/commons-lang/](https://commons.apache.org/proper/commons-lang/) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5342,14 +4440,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using 1. **Group** : org.apache.httpcomponents.core5. **Name** : httpcore5-h2. **Version** : 5.1.3. * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.apache.logging.log4j. **Name** : log4j-api. **Version** : 2.20.0. - * **Project URL:** [https://www.apache.org/](https://www.apache.org/) - * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.apache.logging.log4j. **Name** : log4j-core. **Version** : 2.20.0. - * **Project URL:** [https://www.apache.org/](https://www.apache.org/) - * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.apiguardian. **Name** : apiguardian-api. **Version** : 1.1.2. * **Project URL:** [https://github.com/apiguardian-team/apiguardian](https://github.com/apiguardian-team/apiguardian) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5380,11 +4470,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://checkerframework.org/](https://checkerframework.org/) * **License:** [The MIT License](http://opensource.org/licenses/MIT) -1. **Group** : org.codehaus.woodstox. **Name** : stax2-api. **Version** : 4.2. - * **Project URL:** [http://github.com/FasterXML/stax2-api](http://github.com/FasterXML/stax2-api) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The BSD License](http://www.opensource.org/licenses/bsd-license.php) - 1. **Group** : org.codehaus.woodstox. **Name** : stax2-api. **Version** : 4.2.2. * **Project URL:** [http://github.com/FasterXML/stax2-api](http://github.com/FasterXML/stax2-api) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5422,10 +4507,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **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. **Name** : annotations-java5. **Version** : 20.1.0. - * **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. **Name** : markdown. **Version** : 0.7.3. * **Project URL:** [https://github.com/JetBrains/markdown](https://github.com/JetBrains/markdown) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5462,26 +4543,10 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.intellij.deps. **Name** : jdom. **Version** : 2.0.6.**No license information found** -1. **Group** : org.jetbrains.intellij.deps. **Name** : log4j. **Version** : 1.2.17.2. - * **Project URL:** [http://logging.apache.org/log4j/1.2/](http://logging.apache.org/log4j/1.2/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.intellij.deps. **Name** : trove4j. **Version** : 1.0.20200330. * **Project URL:** [https://github.com/JetBrains/intellij-deps-trove4j](https://github.com/JetBrains/intellij-deps-trove4j) * **License:** [GNU LESSER GENERAL PUBLIC LICENSE 2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html) -1. **Group** : org.jetbrains.intellij.deps.fastutil. **Name** : intellij-deps-fastutil. **Version** : 8.5.4-9.**No license information found** -1. **Group** : org.jetbrains.intellij.deps.jna. **Name** : jna. **Version** : 5.9.0.26. - * **Project URL:** [https://github.com/JetBrains/intellij-deps-jna](https://github.com/JetBrains/intellij-deps-jna) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [LGPL-2.1-or-later](https://www.gnu.org/licenses/old-licenses/lgpl-2.1) - -1. **Group** : org.jetbrains.intellij.deps.jna. **Name** : jna-platform. **Version** : 5.9.0.26. - * **Project URL:** [https://github.com/JetBrains/intellij-deps-jna](https://github.com/JetBrains/intellij-deps-jna) - * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [LGPL-2.1-or-later](https://www.gnu.org/licenses/old-licenses/lgpl-2.1) - 1. **Group** : org.jetbrains.kotlin. **Name** : abi-tools. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5490,14 +4555,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-annotation-processing-compiler. **Version** : 2.1.10. - * **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-annotation-processing-embeddable. **Version** : 2.1.10. - * **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-bom. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5702,7 +4759,7 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [http://jspecify.org/](http://jspecify.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -5710,34 +4767,30 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://junit-pioneer.org/](https://junit-pioneer.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.lz4. **Name** : lz4-pure-java. **Version** : 1.8.0. - * **Project URL:** [https://github.com/lz4/lz4-java](https://github.com/lz4/lz4-java) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.opentest4j. **Name** : opentest4j. **Version** : 1.3.0. * **Project URL:** [https://github.com/ota4j-team/opentest4j](https://github.com/ota4j-team/opentest4j) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5777,18 +4830,17 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/xmlresolver/xmlresolver](https://github.com/xmlresolver/xmlresolver) * **License:** [Apache License version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : oro. **Name** : oro. **Version** : 2.0.8.**No license information found** The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:13 WET 2026** using +This report was generated on **Mon Mar 09 16:52:42 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -5864,10 +4916,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.41.0. * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -6105,10 +5153,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/google/gson/gson](https://github.com/google/gson/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.41.0. * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -6351,15 +5395,15 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [http://jspecify.org/](http://jspecify.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -6379,14 +5423,14 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:12 WET 2026** using +This report was generated on **Mon Mar 09 16:52:40 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -6897,14 +5941,14 @@ This report was generated on **Fri Feb 27 20:33:12 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:13 WET 2026** using +This report was generated on **Mon Mar 09 16:52:42 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -6980,10 +6024,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.41.0. * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -7588,14 +6628,14 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:12 WET 2026** using +This report was generated on **Mon Mar 09 16:52:42 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -8169,7 +7209,7 @@ This report was generated on **Fri Feb 27 20:33:12 WET 2026** using * **Project URL:** [http://jspecify.org/](http://jspecify.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -8177,27 +7217,27 @@ This report was generated on **Fri Feb 27 20:33:12 WET 2026** using * **Project URL:** [https://junit-pioneer.org/](https://junit-pioneer.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -8217,14 +7257,14 @@ This report was generated on **Fri Feb 27 20:33:12 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:13 WET 2026** using +This report was generated on **Mon Mar 09 16:52:42 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -8841,7 +7881,7 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [http://jspecify.org/](http://jspecify.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -8849,27 +7889,27 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://junit-pioneer.org/](https://junit-pioneer.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -8889,14 +7929,14 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:13 WET 2026** using +This report was generated on **Mon Mar 09 16:52:42 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -8972,10 +8012,6 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.41.0. * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9225,6 +8261,10 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.26.1. + * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + 1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.28.0. * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9245,10 +8285,18 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://github.com/google/flogger](https://github.com/google/flogger) * **License:** [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.2. + * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + 1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.3. * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : com.google.guava. **Name** : guava. **Version** : 33.2.1-jre. + * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + 1. **Group** : com.google.guava. **Name** : guava. **Version** : 33.5.0-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9304,6 +8352,18 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [http://www.github.com/sksamuel/aedile](http://www.github.com/sksamuel/aedile) * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) +1. **Group** : com.squareup. **Name** : kotlinpoet. **Version** : 2.2.0. + * **Project URL:** [https://github.com/square/kotlinpoet](https://github.com/square/kotlinpoet) + * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.squareup. **Name** : kotlinpoet-jvm. **Version** : 2.2.0. + * **Project URL:** [https://github.com/square/kotlinpoet](https://github.com/square/kotlinpoet) + * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : dev.zacsweers.autoservice. **Name** : auto-service-ksp. **Version** : 1.2.0. + * **Project URL:** [https://github.com/ZacSweers/auto-service-ksp](https://github.com/ZacSweers/auto-service-ksp) + * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + 1. **Group** : io.github.java-diff-utils. **Name** : java-diff-utils. **Version** : 4.12. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9424,6 +8484,10 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://checkerframework.org/](https://checkerframework.org/) * **License:** [The MIT License](http://opensource.org/licenses/MIT) +1. **Group** : org.checkerframework. **Name** : checker-qual. **Version** : 3.42.0. + * **Project URL:** [https://checkerframework.org/](https://checkerframework.org/) + * **License:** [The MIT License](http://opensource.org/licenses/MIT) + 1. **Group** : org.checkerframework. **Name** : checker-qual. **Version** : 3.43.0. * **Project URL:** [https://checkerframework.org/](https://checkerframework.org/) * **License:** [The MIT License](http://opensource.org/licenses/MIT) @@ -9495,6 +8559,10 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **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** : 2.1.21. + * **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** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9519,6 +8587,10 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.1.21. + * **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** : 2.2.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9599,7 +8671,7 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [http://jspecify.org/](http://jspecify.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -9607,27 +8679,27 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using * **Project URL:** [https://junit-pioneer.org/](https://junit-pioneer.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -9647,14 +8719,14 @@ This report was generated on **Fri Feb 27 20:33:13 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:12 WET 2026** using +This report was generated on **Mon Mar 09 16:52:42 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -9924,14 +8996,14 @@ This report was generated on **Fri Feb 27 20:33:12 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:12 WET 2026** using +This report was generated on **Mon Mar 09 16:52:41 WET 2026** 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). -# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.400` +# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.401` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -10032,10 +9104,6 @@ This report was generated on **Fri Feb 27 20:33:12 WET 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. - * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) - * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.auto.value. **Name** : auto-value-annotations. **Version** : 1.11.0. * **Project URL:** [https://github.com/google/auto/tree/main/value](https://github.com/google/auto/tree/main/value) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -10048,10 +9116,6 @@ This report was generated on **Fri Feb 27 20:33:12 WET 2026** using * **Project URL:** [https://github.com/google/gson/gson](https://github.com/google/gson/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.41.0. * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -10238,7 +9302,7 @@ This report was generated on **Fri Feb 27 20:33:12 WET 2026** using * **Project URL:** [http://jspecify.org/](http://jspecify.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -10246,27 +9310,27 @@ This report was generated on **Fri Feb 27 20:33:12 WET 2026** using * **Project URL:** [https://junit-pioneer.org/](https://junit-pioneer.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.0. +1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.3. * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -10282,6 +9346,6 @@ This report was generated on **Fri Feb 27 20:33:12 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Feb 27 20:33:12 WET 2026** using +This report was generated on **Mon Mar 09 16:52:40 WET 2026** 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/docs/_examples b/docs/_examples index 57934013cb..5ba3d2a518 160000 --- a/docs/_examples +++ b/docs/_examples @@ -1 +1 @@ -Subproject commit 57934013cb7910c95f7926114d6c97c7be7329cd +Subproject commit 5ba3d2a518adb53f4e58b7858c26ba21f2a0df16 diff --git a/docs/content/docs/validation/01-getting-started/adding-to-build.md b/docs/content/docs/validation/01-getting-started/adding-to-build.md index 06205c6dea..273b6c0164 100644 --- a/docs/content/docs/validation/01-getting-started/adding-to-build.md +++ b/docs/content/docs/validation/01-getting-started/adding-to-build.md @@ -82,7 +82,7 @@ Add the Validation plugin to the build. ```kotlin plugins { module - id("io.spine.validation") version "2.0.0-SNAPSHOT.400" + id("io.spine.validation") version "2.0.0-SNAPSHOT.401" } ``` diff --git a/docs/content/docs/validation/02-concepts/_index.md b/docs/content/docs/validation/02-concepts/_index.md index 54c4ec69e0..09d8628ea5 100644 --- a/docs/content/docs/validation/02-concepts/_index.md +++ b/docs/content/docs/validation/02-concepts/_index.md @@ -105,6 +105,6 @@ See [Custom validation](../08-custom-validation/) for the workflow and a referen - Customize and format messages: [Working with error messages](error-messages.md). - [Built-in options](../03-built-in-options/) -- [Validating third-party messages](../04-third-party-messages/) +- [Using validators](../04-validators/) - Add custom validation options: [Custom validation](../08-custom-validation/). diff --git a/docs/content/docs/validation/02-concepts/error-messages.md b/docs/content/docs/validation/02-concepts/error-messages.md index ba00d250bd..4a78f893b1 100644 --- a/docs/content/docs/validation/02-concepts/error-messages.md +++ b/docs/content/docs/validation/02-concepts/error-messages.md @@ -136,7 +136,7 @@ Recommended actions: [Options overview](options-overview.md). - Explore the built-in options: [Built-in options](../03-built-in-options/). -- Learn how to validate message types from third-party libraries: - [Validating third-party messages](../04-third-party-messages/). +- Learn how to validate messages with custom code: + [Using validators](../04-validators/). - If built-in options are not enough, define your own constraints and messages: [Custom validation](../08-custom-validation/). diff --git a/docs/content/docs/validation/03-built-in-options/_index.md b/docs/content/docs/validation/03-built-in-options/_index.md index 0559f82d32..04356e24de 100644 --- a/docs/content/docs/validation/03-built-in-options/_index.md +++ b/docs/content/docs/validation/03-built-in-options/_index.md @@ -46,5 +46,5 @@ on GitHub: [spine/options.proto](https://github.com/SpineEventEngine/base-librar - [Options for `oneof` fields](oneof-fields.md) - [Message-level options](message-level-options.md) - [Options for `repeated` and `map` fields](repeated-and-map-fields.md) -- [Validating third-party messages](../04-third-party-messages/) +- [Using validators](../04-validators/) - [Custom validation](../08-custom-validation/) diff --git a/docs/content/docs/validation/04-third-party-messages/_index.md b/docs/content/docs/validation/04-third-party-messages/_index.md deleted file mode 100644 index 18665689ab..0000000000 --- a/docs/content/docs/validation/04-third-party-messages/_index.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Validating third-party messages -description: How to validate message types generated outside your build. -headline: Documentation ---- - -# Validating third-party messages - -## What’s next - -- [Custom validation](../08-custom-validation/) -- [Architecture](../09-developers-guide/architecture.md) diff --git a/docs/content/docs/validation/04-validators/_index.md b/docs/content/docs/validation/04-validators/_index.md new file mode 100644 index 0000000000..541e027f90 --- /dev/null +++ b/docs/content/docs/validation/04-validators/_index.md @@ -0,0 +1,111 @@ +--- +title: MessageValidator overview +description: How to validate messages with custom code using `MessageValidator`. +headline: Documentation +--- + +# Overview of `MessageValidator` + +{{% note-block class="lead" %}} +Spine Validation enforces most constraints via **code generation** from `.proto` options. +Sometimes this is not enough, or not possible. +{{% /note-block %}} + +Use validators when you need **custom logic in code**: + +- validate **external** message types whose `.proto` files you cannot change (e.g. `google.protobuf.Timestamp`); +- validate **local** messages when the rule requires computation and cannot be expressed as proto options. + +Validators are implemented via `io.spine.validation.MessageValidator` and executed by +`io.spine.validation.ValidatorRegistry`. + +## When to use validators + +Prefer `.proto` options when you can: + +1. Use [built-in options](../03-built-in-options/). +2. If built-ins are not enough, implement [custom validation options](../08-custom-validation/). + +Use `MessageValidator` when: + +- You cannot modify the `.proto` source of a message type (external messages). +- You need checks that depend on multiple fields, computations, or library calls (local messages). + +## Create a validator + +To validate a message type `M`: + +1. Implement `io.spine.validation.MessageValidator`. +2. Make the validator discoverable via Java `ServiceLoader` (recommended), or register it in + `ValidatorRegistry` explicitly at application startup. +3. Ensure the class has a public no-args constructor (Kotlin/Java default constructors work). + +### ServiceLoader discovery (recommended) + +`ValidatorRegistry` loads implementations of `MessageValidator` from the classpath via +`ServiceLoader`. +On the JVM, the easiest way to generate the required `META-INF/services/...` entry is to annotate +your validator with `@AutoService(MessageValidator::class)`: + +```kotlin +import com.google.auto.service.AutoService +import io.spine.validation.DetectedViolation +import io.spine.validation.MessageValidator + +@AutoService(MessageValidator::class) +public class MeetingValidator : MessageValidator { + override fun validate(message: Meeting): List = emptyList() +} +``` + +{{% note-block class="warning" %}} +For AutoService to work, you'll also need to update your build. +Please see the [documentation of the library][auto-service] for details. +{{% /note-block %}} + + +### Explicit registration (alternative) + +If you prefer not to rely on classpath discovery, add validators during application startup: + +```kotlin +ValidatorRegistry.add(Meeting::class, MeetingValidator()) +``` + +## Apply a validator + +Validators are executed when the generated validation code is invoked. +In practice, this happens in three common ways: + +1. **Validate a message directly via the registry**: + + ```kotlin + val violations = ValidatorRegistry.validate(message) + ``` + Please note that this approach does not apply any checks generated from `.proto` options, + only registered validators. + +2. **Build a local message of the corresponding type**. + When you call `M.Builder.build()`, the generated validation runs for `M`: it applies checks + produced from `.proto` options (if any) and executes all validators registered for `M` via + `ValidatorRegistry`. + +3. **Validate nested message fields** by marking them with `(validate) = true`. + When the enclosing message is validated (for example, during the enclosing builder’s + `build()`), Spine Validation also validates the nested values of those fields. This nested + validation runs both the nested message’s generated constraints (if any) and any validators + registered for the nested message type — including external message types. + +## Multiple validators per message type + +You can register more than one validator for the same message type. +When the message is validated, Spine Validation applies all registered validators and reports +their violations together. + +## What’s next +- [Implement a validator](implement-a-validator.md) +- [Using `ValidatorRegistry`](validator-registry.md) +- [Custom validation](../08-custom-validation/) +- [Architecture](../09-developers-guide/architecture.md) + +[auto-service]: https://github.com/google/auto/tree/main/service diff --git a/docs/content/docs/validation/04-validators/implement-a-validator.md b/docs/content/docs/validation/04-validators/implement-a-validator.md new file mode 100644 index 0000000000..44b65dfb17 --- /dev/null +++ b/docs/content/docs/validation/04-validators/implement-a-validator.md @@ -0,0 +1,138 @@ +--- +title: Implement a validator +description: How to validate messages with custom logic using `MessageValidator`. +headline: Documentation +--- + +# Implement a validator + +To validate a Protobuf message type `M` with custom logic: + +1. Implement `io.spine.validation.MessageValidator`. +2. Make the implementation discoverable via Java `ServiceLoader` (recommended), or register it in + `ValidatorRegistry` explicitly. +3. Ensure the class has a public no-args constructor. + +Keep validators stateless and cheap to construct. + +## Reference implementation: `TimestampValidator` + +Let's review the `MessageValidator` implementation on the example of +`io.spine.validation.TimestampValidator` from the Validation JVM runtime. + +It validates `com.google.protobuf.Timestamp` and reports violations for invalid +`seconds` and `nanos` values. + +### Service discovery + +The validator is a regular `MessageValidator` implementation and is discoverable via +`ServiceLoader`. +To generate the required service provider configuration automatically, annotate it with +`@AutoService(MessageValidator::class)`: + +```kotlin +import com.google.auto.service.AutoService +import io.spine.validation.MessageValidator + +@AutoService(MessageValidator::class) +public class TimestampValidator : MessageValidator { + // ... +} +``` + +### Validation logic + +The core logic is intentionally small: it first delegates to `Timestamps.isValid(message)` and, +if invalid, adds a field-specific violation for each invalid field (`seconds` and/or `nanos`). +For range checks, it relies on `Timestamps.MIN_VALUE` and `Timestamps.MAX_VALUE`. + +```kotlin +import com.google.auto.service.AutoService +import com.google.protobuf.Timestamp +import com.google.protobuf.util.Timestamps +import com.google.protobuf.util.Timestamps.MAX_VALUE +import com.google.protobuf.util.Timestamps.MIN_VALUE +import io.spine.validation.DetectedViolation +import io.spine.validation.MessageValidator + +@AutoService(MessageValidator::class) +public class TimestampValidator : MessageValidator { + + override fun validate(message: Timestamp): List { + if (Timestamps.isValid(message)) { + return emptyList() + } + val violations = mutableListOf() + if (message.seconds < MIN_VALUE.seconds || + message.seconds > MAX_VALUE.seconds) { + violations.add(invalidSeconds(message.seconds)) + } + if (message.nanos !in 0..MAX_VALUE.nanos) { + violations.add(invalidNanos(message.nanos)) + } + return violations + } +} +``` + +### Reporting violations with placeholders + +`TimestampValidator` reports errors via `FieldViolation`, providing: + +- `fieldPath` — which field is invalid (for example, `"seconds"`), +- `fieldValue` — the actual invalid value, and +- `message` — a `TemplateString` with placeholders and a placeholder-to-value map. + +The message is defined as a template (via `withPlaceholders`) and populated by specifying +values in `placeholderValue`. This keeps error messages machine-friendly and allows consistent +formatting, logging, and customization. + +When violations are converted to regular `ConstraintViolation`s, Spine Validation also populates +the `validator` placeholder with the fully qualified class name of the validator. + +Below is the helper that creates a violation for invalid `seconds` +(the `invalidNanos()` function is similar): + +```kotlin +private fun invalidSeconds(seconds: Long): FieldViolation = FieldViolation( + message = templateString { + withPlaceholders = + "The ${FIELD_PATH.value} value is out of range" + + " (${RANGE_VALUE.value}): $seconds." + placeholderValue.put(FIELD_PATH.value, "seconds") + placeholderValue.put(RANGE_VALUE.value, + "${MIN_VALUE.seconds}..${MAX_VALUE.seconds}") + + }, + fieldPath = fieldPath { + fieldName.add("seconds") + }, + fieldValue = seconds +) +``` + +## Walkthrough: validate a nested message field + +To validate a message nested inside another message, mark the field with `(validate) = true`. +This applies both generated constraints (if any) and validators registered for the nested type. + +Suppose your local model uses `Timestamp`: + +```proto +import "google/protobuf/timestamp.proto"; +import "spine/options.proto"; + +message Meeting { + google.protobuf.Timestamp starts_at = 1 [(validate) = true]; +} +``` + +Once you add a validator for `Timestamp`, validation of `Meeting` +reports a violation for the `starts_at` field if the timestamp is invalid pointing +to the nested field in error (for example, to `starts_at.seconds`). + +## What’s next + +- [Using `ValidatorRegistry`](validator-registry.md) +- [Custom validation](../08-custom-validation/) +- [Architecture](../09-developers-guide/architecture.md) diff --git a/docs/content/docs/validation/04-validators/validator-registry.md b/docs/content/docs/validation/04-validators/validator-registry.md new file mode 100644 index 0000000000..94fbe0d6af --- /dev/null +++ b/docs/content/docs/validation/04-validators/validator-registry.md @@ -0,0 +1,272 @@ +--- +title: Using `ValidatorRegistry` +description: How to register, query, and apply `MessageValidator`s explicitly via `ValidatorRegistry`. +headline: Documentation +--- + +# Using `ValidatorRegistry` + +In most cases, you don’t need to interact with `io.spine.validation.ValidatorRegistry` directly. +Spine Validation discovers validators automatically and applies them as part of the generated +validation API. + +Use `ValidatorRegistry` directly when you need to: + +- register a validator explicitly, for example, at application startup; +- inspect which validators are registered for a message type; +- remove or replace validators, for example, to override ones discovered automatically; +- validate a message using validators only. + +{{% note-block class="note" %}} +`ValidatorRegistry` uses Java `ServiceLoader` to discover validators when the object is first +initialized. +{{% /note-block %}} + +## Validate a message + +There are two common ways to validate a message when you have validators: + +1. `ValidatorRegistry.validate(message)` — applies **validators only**. +2. `message.validate()` — applies **generated checks** (from `.proto` options) and also applies + all validators registered for that message type. + +{{% note-block class="note" %}} +`ValidatorRegistry.validate(message)` is useful when you need to run custom logic independently +from generated checks, or when you validate external message types that do not have a generated +`validate()` method. +{{% /note-block %}} + +{{< code-tabs langs="Kotlin, Java">}} + +{{< code-tab lang="Kotlin" >}} +```kotlin +import com.google.protobuf.Timestamp +import io.spine.validation.ValidatorRegistry + +val timestamp: Timestamp = // ... + +// Applies validators only. +val violations = ValidatorRegistry.validate(timestamp) + +// Applies generated checks (if any) and validators registered for the type. +val error = myMessage.validate() +``` +{{< /code-tab >}} + +{{< code-tab lang="Java" >}} +```java +import com.google.protobuf.Timestamp; +import io.spine.validation.ValidatorRegistry; + +Timestamp timestamp = /* ... */; + +// Applies validators only. +var violations = ValidatorRegistry.validate(timestamp); + +// Applies generated checks (if any) and validators registered for the type. +var error = myMessage.validate(); +``` +{{< /code-tab >}} + +{{< /code-tabs >}} + +## Add a validator + +To register a validator explicitly, call `ValidatorRegistry.add()` with: + +- the message type the validator applies to, and +- the validator instance. + +{{< code-tabs langs="Kotlin, Java">}} + +{{< code-tab lang="Kotlin" >}} +```kotlin +import com.google.protobuf.Timestamp +import io.spine.validation.TimestampValidator +import io.spine.validation.ValidatorRegistry + +ValidatorRegistry.add(Timestamp::class, TimestampValidator()) +``` +{{< /code-tab >}} + +{{< code-tab lang="Java" >}} +```java +import com.google.protobuf.Timestamp; +import io.spine.validation.TimestampValidator; +import io.spine.validation.ValidatorRegistry; + +ValidatorRegistry.add(Timestamp.class, new TimestampValidator()); +``` +{{< /code-tab >}} + +{{< /code-tabs >}} + +## Query registered validators + +To obtain the currently registered validators for a message type, call `ValidatorRegistry.get()`. + +{{< code-tabs langs="Kotlin, Java">}} + +{{< code-tab lang="Kotlin" >}} +```kotlin +import com.google.protobuf.Timestamp +import io.spine.validation.ValidatorRegistry + +val validators = ValidatorRegistry.get(Timestamp::class) +``` +{{< /code-tab >}} + +{{< code-tab lang="Java" >}} +```java +import com.google.protobuf.Timestamp; +import io.spine.validation.ValidatorRegistry; + +var validators = ValidatorRegistry.get(Timestamp.class); +``` +{{< /code-tab >}} + +{{< /code-tabs >}} + +## Remove and replace validators + +To remove all validators registered for a message type, call `ValidatorRegistry.remove()`. + +This is also the simplest way to **override automatically discovered validators**: remove +validators for the message type and then add the desired ones. + +{{< code-tabs langs="Kotlin, Java">}} + +{{< code-tab lang="Kotlin" >}} +```kotlin +import com.google.protobuf.Timestamp +import io.spine.validation.ValidatorRegistry + +ValidatorRegistry.remove(Timestamp::class) +ValidatorRegistry.add(Timestamp::class, MyTimestampValidator()) +``` +{{< /code-tab >}} + +{{< code-tab lang="Java" >}} +```java +import com.google.protobuf.Timestamp; +import io.spine.validation.ValidatorRegistry; + +ValidatorRegistry.remove(Timestamp.class); +ValidatorRegistry.add(Timestamp.class, new MyTimestampValidator()); +``` +{{< /code-tab >}} + +{{< /code-tabs >}} + +## The `${validator}` placeholder + +When `ValidatorRegistry` converts validator-reported violations into `ConstraintViolation`s, +it automatically populates the `validator` placeholder with the validator’s fully qualified +class name. + +If your error message template references `${validator}`, you can format it after validation: + +{{< code-tabs langs="Kotlin, Java">}} + +{{< code-tab lang="Kotlin" >}} +```kotlin +import com.google.protobuf.Timestamp +import io.spine.validation.DetectedViolation +import io.spine.validation.MessageValidator +import io.spine.validation.MessageViolation +import io.spine.validation.ValidatorRegistry +import io.spine.validation.templateString + +class MyTimestampValidator : MessageValidator { + override fun validate(message: Timestamp): List = + listOf( + MessageViolation( + templateString { + withPlaceholders = "Rejected by `\${validator}`." + } + ) + ) +} + +ValidatorRegistry.add(Timestamp::class, MyTimestampValidator()) + +val timestamp = Timestamp.newBuilder().setNanos(-1).build() +val violation = ValidatorRegistry.validate(timestamp).single() + +val placeholder = ValidatorRegistry.VALIDATOR_PLACEHOLDER +val validatorClass = violation.message.placeholderValueMap[placeholder] + +// If the template references `${validator}`, it will be substituted here. +val text = violation.message.format() +``` +{{< /code-tab >}} + +{{< code-tab lang="Java" >}} +```java +import com.google.protobuf.Timestamp; +import io.spine.validation.DetectedViolation; +import io.spine.validation.MessageValidator; +import io.spine.validation.MessageViolation; +import io.spine.validation.TemplateString; +import io.spine.validation.TemplateStrings; +import io.spine.validation.ValidatorRegistry; +import java.util.List; + +final class MyTimestampValidator implements MessageValidator { + @Override + public List validate(Timestamp message) { + return List.of( + new MessageViolation( + TemplateString.newBuilder() + .setWithPlaceholders("Rejected by ${validator}.") + .build() + ) + ); + } +} + +ValidatorRegistry.add(Timestamp.class, new MyTimestampValidator()); + +var timestamp = Timestamp.newBuilder().setNanos(-1).build(); +var violation = ValidatorRegistry.validate(timestamp).get(0); + +var placeholder = ValidatorRegistry.VALIDATOR_PLACEHOLDER; +var validatorClass = violation.getMessage().getPlaceholderValueMap().get(placeholder); + +// If the template references `${validator}`, it will be substituted here. +var text = TemplateStrings.format(violation.getMessage()); +``` +{{< /code-tab >}} + +{{< /code-tabs >}} + +{{% note-block class="note" %}} +The `validator` entry is always added to `placeholder_value`, even if the template does not +reference `${validator}`. +{{% /note-block %}} + +## Clear the registry + +To remove all validators for all message types, call `ValidatorRegistry.clear()`. + +This API is typically useful in tests to ensure isolation between test cases. + +{{< code-tabs langs="Kotlin, Java">}} + +{{< code-tab lang="Kotlin" >}} +```kotlin +import io.spine.validation.ValidatorRegistry + +ValidatorRegistry.clear() +``` +{{< /code-tab >}} + +{{< code-tab lang="Java" >}} +```java +import io.spine.validation.ValidatorRegistry; + +ValidatorRegistry.clear(); +``` +{{< /code-tab >}} + +{{< /code-tabs >}} diff --git a/docs/content/docs/validation/08-custom-validation/_index.md b/docs/content/docs/validation/08-custom-validation/_index.md index 681741c310..d207d6a7b6 100644 --- a/docs/content/docs/validation/08-custom-validation/_index.md +++ b/docs/content/docs/validation/08-custom-validation/_index.md @@ -25,7 +25,7 @@ Below is a workflow diagram for a typical option: ## What’s next -- [Validating third-party messages](../04-third-party-messages/) +- [Using validators](../04-validators/) - Learn where this plugs in: [Architecture](../09-developers-guide/architecture.md). Take a look at the `:tests:extensions` module that contains a full example of diff --git a/docs/content/docs/validation/_index.md b/docs/content/docs/validation/_index.md index 3d075a52b3..befa11289e 100644 --- a/docs/content/docs/validation/_index.md +++ b/docs/content/docs/validation/_index.md @@ -17,6 +17,6 @@ options, and runs those checks automatically when you build messages. ## Deeper topics - [Built-in options](03-built-in-options/) -- [Validating third-party messages](04-third-party-messages/) +- [Using validators](04-validators/) - How it works: [Architecture](09-developers-guide/architecture.md) - Extension points: [Custom validation](08-custom-validation/) diff --git a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml index 6c910b386a..ac1887e642 100644 --- a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml +++ b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml @@ -48,8 +48,15 @@ file_path: 03-built-in-options/message-level-options - page: "Options for `repeated` and `map` fields" file_path: 03-built-in-options/repeated-and-map-fields - - page: Validating third-party messages - file_path: 04-third-party-messages + - page: Using validators + key: 04-validators + children: + - page: Overview of `MessageValidator` + file_path: 04-validators + - page: Implement a validator + file_path: 04-validators/implement-a-validator + - page: Using `ValidatorRegistry` + file_path: 04-validators/validator-registry - page: Custom validation file_path: 08-custom-validation - page: Developer’s guide diff --git a/java/build.gradle.kts b/java/build.gradle.kts index b168a3a23f..4caf66950c 100644 --- a/java/build.gradle.kts +++ b/java/build.gradle.kts @@ -1,7 +1,5 @@ -import io.spine.dependency.local.Compiler - /* - * Copyright 2024, TeamDev. All rights reserved. + * Copyright 2026, 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. @@ -26,6 +24,8 @@ import io.spine.dependency.local.Compiler * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import io.spine.dependency.local.Compiler + plugins { module `build-proto-model` @@ -33,7 +33,6 @@ plugins { dependencies { api(Compiler.jvm) - api(project(":ksp")) api(project(":context")) api(project(":jvm-runtime")) } diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationPlugin.kt b/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationPlugin.kt index 4187a6ce1c..f595934f52 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationPlugin.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationPlugin.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025, TeamDev. All rights reserved. + * Copyright 2026, 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. @@ -26,14 +26,9 @@ package io.spine.tools.validation.java -import io.spine.tools.compiler.jvm.ClassName -import io.spine.tools.validation.java.generate.MessageClass -import io.spine.tools.validation.java.generate.ValidatorClass -import io.spine.tools.validation.java.setonce.SetOnceRenderer import io.spine.tools.validation.ValidationPlugin -import io.spine.tools.validation.ksp.DiscoveredValidators -import java.io.File -import java.util.* +import io.spine.tools.validation.java.setonce.SetOnceRenderer +import java.util.ServiceLoader /** * An implementation of [ValidationPlugin] for Java language. @@ -52,7 +47,7 @@ import java.util.* @Suppress("unused") // Accessed via reflection. public open class JavaValidationPlugin : ValidationPlugin( renderers = listOf( - JavaValidationRenderer(customOptions.map { it.generator }, customValidators), + JavaValidationRenderer(customOptions.map { it.generator }), SetOnceRenderer() ), views = customOptions.flatMap { it.view }.toSet(), @@ -66,30 +61,3 @@ private val customOptions: List by lazy { ServiceLoader.load(ValidationOption::class.java) .filterNotNull() } - -/** - * Dynamically discovered instances of custom - * [MessageValidator][io.spine.validation.MessageValidator]s. - * - * Note that the KSP module is responsible for the actual discovering of the message validators. - * The discovered validators are written to a text file in the KSP task output. - * This property loads the validators from that file. - */ -private val customValidators: Map by lazy { - val workingDir = System.getProperty("user.dir") - val kspOutput = File("$workingDir/$KSP_GENERATED_RESOURCES") - val messageValidators = DiscoveredValidators.resolve(kspOutput) - if (!messageValidators.exists()) { - return@lazy emptyMap() - } - - messageValidators.readLines().associate { - val (message, validator) = it.split(":") - ClassName.guess(message) to ClassName.guess(validator) - } -} - -/** - * The default location to which the KSP task puts the generated output. - */ -private const val KSP_GENERATED_RESOURCES = "build/generated/ksp/main" diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationRenderer.kt b/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationRenderer.kt index e9db326278..a84375da81 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationRenderer.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationRenderer.kt @@ -28,7 +28,6 @@ package io.spine.tools.validation.java import com.google.protobuf.Message import com.intellij.psi.PsiJavaFile -import io.spine.string.ti import io.spine.tools.code.Java import io.spine.tools.compiler.ast.MessageType import io.spine.tools.compiler.jvm.JavaValueConverter @@ -39,12 +38,9 @@ import io.spine.tools.compiler.jvm.render.findClass import io.spine.tools.compiler.jvm.render.findMessageTypes import io.spine.tools.compiler.render.SourceFile import io.spine.tools.compiler.render.SourceFileSet -import io.spine.tools.validation.java.generate.MessageClass import io.spine.tools.validation.java.generate.MessageValidationCode import io.spine.tools.validation.java.generate.OptionGenerator import io.spine.tools.validation.java.generate.ValidationCodeInjector -import io.spine.tools.validation.java.generate.ValidatorClass -import io.spine.tools.validation.java.generate.ValidatorGenerator import io.spine.tools.validation.java.generate.option.ChoiceGenerator import io.spine.tools.validation.java.generate.option.DistinctGenerator import io.spine.tools.validation.java.generate.option.GoesGenerator @@ -64,15 +60,11 @@ import io.spine.tools.validation.java.generate.option.bound.RangeGenerator * even if the message does not have any declared constraints. */ internal class JavaValidationRenderer( - customGenerators: List, - private val validators: Map + customGenerators: List ) : JavaRenderer() { private val codeInjector = ValidationCodeInjector() private val querying = this@JavaValidationRenderer - private val validatorGenerator by lazy { - ValidatorGenerator(validators, typeSystem) - } private val optionGenerators by lazy { (buildInGenerators() + customGenerators) .onEach { it.inject(querying) } @@ -87,7 +79,6 @@ internal class JavaValidationRenderer( findMessageTypes() .forEach { message -> - checkDoesNotHaveValidator(message) val code = generateCode(message) val file = sources.javaFileOf(message) file.render(code) @@ -123,10 +114,9 @@ internal class JavaValidationRenderer( private fun generateCode(message: MessageType): MessageValidationCode { val fieldOptions = optionGenerators.flatMap { it.codeFor(message.name) } - val validatorFields = validatorGenerator.codeFor(message) val messageCode = MessageValidationCode( message = message.javaClassName(typeSystem), - constraints = fieldOptions.map { it.constraint } + validatorFields, + constraints = fieldOptions.map { it.constraint }, fields = fieldOptions.flatMap { it.fields }, methods = fieldOptions.flatMap { it.methods } ) @@ -139,21 +129,4 @@ internal class JavaValidationRenderer( codeInjector.inject(code, messageClass) overwrite(psiFile.text) } - - /** - * Ensures that the given compilation [message] does not have an assigned validator. - * - * Local messages are prohibited from having validators. - */ - private fun checkDoesNotHaveValidator(message: MessageType) { - val javaClass = message.javaClassName(typeSystem) - val validator = validators[javaClass] - check(validator == null) { - """ - The validator `$validator` cannot be used to validate the `$javaClass` messages. - Validators can be used only for external message types, which are not generated locally. - Use built-in or custom validation options to declare constraints for the local messages. - """.ti() - } - } } diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/generate/ValidationCodeInjector.kt b/java/src/main/kotlin/io/spine/tools/validation/java/generate/ValidationCodeInjector.kt index 5238978b40..805dcd00d0 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/generate/ValidationCodeInjector.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/generate/ValidationCodeInjector.kt @@ -144,19 +144,43 @@ private fun MessagePsiClass.declareValidateMethod(constraints: List) addLast(psiMethod) } -private fun validateMethodBody(constraints: List): String = - if (constraints.isEmpty()) { - """ - // This message does not have any validation constraints. - return java.util.Optional.empty(); +private fun validateMethodBody(constraints: List): String { + val constraintViolation = ConstraintViolation::class.java.canonicalName + + val violationsDecl = + if (constraints.isEmpty()) { + """ + // No constraints declared in the message. There could be validators, though. + var $violations = io.spine.validation.ValidatorRegistry.validate(this); + + """.trimIndent() + } else { + """ + var $violations = new java.util.ArrayList<$constraintViolation>(); + """.trimIndent() - } else { - val constraintViolation = ConstraintViolation::class.java.canonicalName + } + + val addingViolations = + if (constraints.isEmpty()) + // We validated the message already under `violationsDecl` above. + "" + else { + """ + + ${constraints.joinByLines()} + + var thisByRegistry = io.spine.validation.ValidatorRegistry.validate(this); + if (!thisByRegistry.isEmpty()) { + $violations.addAll(thisByRegistry); + } + + + """.trimIndent() + } + + val returnBlock = """ - var $violations = new java.util.ArrayList<$constraintViolation>(); - - ${constraints.joinByLines()} - if (!$violations.isEmpty()) { var error = $validationError.newBuilder() .addAllConstraintViolation($violations) @@ -166,7 +190,9 @@ private fun validateMethodBody(constraints: List): String = return java.util.Optional.empty(); } """.trimIndent() - } + + return "$violationsDecl$addingViolations$returnBlock" +} /** * Adds declarations of the given [fields] to this [MessagePsiClass]. diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/ValidateGenerator.kt b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/ValidateGenerator.kt index 3a7da7a1f7..759eb77e19 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/ValidateGenerator.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/ValidateGenerator.kt @@ -39,6 +39,7 @@ import io.spine.tools.compiler.jvm.JavaValueConverter import io.spine.tools.compiler.jvm.ReadVar import io.spine.tools.compiler.jvm.field import io.spine.tools.compiler.jvm.getDefaultInstance +import io.spine.tools.validation.ValidateField import io.spine.tools.validation.java.expression.AnyClass import io.spine.tools.validation.java.expression.AnyPackerClass import io.spine.tools.validation.java.expression.EmptyFieldCheck @@ -55,7 +56,7 @@ import io.spine.tools.validation.java.generate.SingleOptionCode import io.spine.tools.validation.java.generate.ValidateScope.parentName import io.spine.tools.validation.java.generate.ValidateScope.parentPath import io.spine.tools.validation.java.generate.ValidateScope.violations -import io.spine.tools.validation.ValidateField +import org.intellij.lang.annotations.Language /** * The generator for `(validate)` option. @@ -153,26 +154,78 @@ private class GenerateValidate( * @param isAny Must be `true` if the provided [message] is [com.google.protobuf.Any]. * In this case, the method will do the unpacking in the first place. */ - @Suppress("MaxLineLength") // For better readability of the rendered conditions. + @Suppress( + "MaxLineLength", "NewClassNamingConvention", "PackageVisibleField", "EmptyClass", + "LongMethod" + ) // To support highlighting and layout of the generated code blocks. private fun validate(message: Expression, isAny: Boolean): CodeBlock { + @Language("java") + val isNotDefault = + if ((field.isMap || field.isList) && !isAny) + // Avoid having unnecessary comparison with an empty list or map. + // The validation goes over an element inside a `for()` loop. + // + // The `null` value avoids the need for the `if()` clause with the comparison. + // + // Do the comparison for `Any` `element` with `Any.getDefaultInstance()` + // so that we can unpack a real message of interest. + // + null + else + if (isAny) + "$message != ${AnyClass.getDefaultInstance()}" + else + "!${field.hasDefaultValue()}" + + @Language("java") val isValidatable = if (isAny) - "$message != ${AnyClass.getDefaultInstance()} &&" + - " $KnownTypesClass.instance().contains($TypeUrlClass.ofEnclosed($message)) &&" + - " $AnyPackerClass.unpack($message) instanceof $ValidatableMessageClass validatable" + " $KnownTypesClass.instance().contains($TypeUrlClass.ofEnclosed($message)) &&" + + " unpacked instanceof $ValidatableMessageClass validatable" else - "!${field.hasDefaultValue()} &&" + - " (($MessageClass) $message) instanceof $ValidatableMessageClass validatable" - return CodeBlock( - """ - if ($isValidatable) { + " (($MessageClass) $message) instanceof $ValidatableMessageClass validatable" + + @Language("java") + val validationBlock = + if (isAny) + """ var fieldPath = ${parentPath.resolve(field.name)}; var typeName = ${parentName.orElse(declaringType)}; - validatable.validate(fieldPath, typeName) - .map($ValidationErrorClass::getConstraintViolationList) - .ifPresent($violations::addAll); - } - """.trimIndent() - ) + var unpacked = $AnyPackerClass.unpack($message); + if ($isValidatable) { + validatable.validate(fieldPath, typeName) + .map($ValidationErrorClass::getConstraintViolationList) + .ifPresent($violations::addAll); + } + var byRegistry = io.spine.validation.ValidatorRegistry.validate(unpacked, fieldPath, typeName); + if (!byRegistry.isEmpty()) { + $violations.addAll(byRegistry); + } + """.trimIndent() + else + """ + var fieldPath = ${parentPath.resolve(field.name)}; + var typeName = ${parentName.orElse(declaringType)}; + if ($isValidatable) { + validatable.validate(fieldPath, typeName) + .map($ValidationErrorClass::getConstraintViolationList) + .ifPresent($violations::addAll); + } + var byRegistry = io.spine.validation.ValidatorRegistry.validate($message, fieldPath, typeName); + if (!byRegistry.isEmpty()) { + $violations.addAll(byRegistry); + } + """.trimIndent() + return if (isNotDefault == null) { + CodeBlock(validationBlock) + } else { + CodeBlock( + """ + if ($isNotDefault) { + $validationBlock + } + """.trimIndent() + ) + } } } diff --git a/jvm-runtime/build.gradle.kts b/jvm-runtime/build.gradle.kts index 2614f4bd11..2c2166cc06 100644 --- a/jvm-runtime/build.gradle.kts +++ b/jvm-runtime/build.gradle.kts @@ -29,7 +29,6 @@ import io.spine.dependency.lib.AutoService import io.spine.dependency.lib.AutoServiceKsp import io.spine.dependency.local.Base -import io.spine.dependency.local.Logging import io.spine.dependency.local.TestLib import io.spine.gradle.publish.SpinePublishing import io.spine.gradle.publish.StandardJavaPublicationHandler diff --git a/jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt b/jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt index 2b534fe50d..d9b6398464 100644 --- a/jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt +++ b/jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt @@ -59,8 +59,8 @@ import io.spine.annotation.SPI * * The Validation library provides a mechanism that allows validating of * the external messages, **which are used for fields within local messages**. - * Implement this interface and annotate the implementing class with - * the [@Validator][Validator] annotation, specifying the message type to validate. + * Implement this interface and ensure the implementation is discoverable + * by [ServiceLoader][java.util.ServiceLoader]. * * For each field of type [M] within any local message, the library will invoke * the [MessageValidator.validate] method when validating the local message. @@ -74,7 +74,6 @@ import io.spine.annotation.SPI * An example of the validator declaration for the `Earphones` message: * * ```kotlin - * @Validator(Earphones::class) * public class EarphonesValidator : MessageValidator { * public override fun validate(message: Earphones): List { * return emptyList() // Always valid. diff --git a/jvm-runtime/src/main/kotlin/io/spine/validation/TimestampValidator.kt b/jvm-runtime/src/main/kotlin/io/spine/validation/TimestampValidator.kt new file mode 100644 index 0000000000..22780df192 --- /dev/null +++ b/jvm-runtime/src/main/kotlin/io/spine/validation/TimestampValidator.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2026, 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 + * + * https://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.validation + +import com.google.auto.service.AutoService +import com.google.protobuf.Timestamp +import com.google.protobuf.util.Timestamps +import com.google.protobuf.util.Timestamps.MAX_VALUE +import com.google.protobuf.util.Timestamps.MIN_VALUE +import io.spine.base.fieldPath +import io.spine.validation.RuntimeErrorPlaceholder.FIELD_PATH +import io.spine.validation.RuntimeErrorPlaceholder.RANGE_VALUE + +/** + * Validates [Timestamp] messages. + * + * Uses [Timestamps.MIN_VALUE] and [Timestamps.MAX_VALUE] to ensure + * the fields of the timestamp are valid. + */ +@AutoService(MessageValidator::class) +public class TimestampValidator : MessageValidator { + + override fun validate(message: Timestamp): List { + if (Timestamps.isValid(message)) { + return emptyList() + } + val violations = mutableListOf() + if (message.seconds < MIN_VALUE.seconds || + message.seconds > MAX_VALUE.seconds) { + violations.add(invalidSeconds(message.seconds)) + } + if (message.nanos !in 0..MAX_VALUE.nanos) { + violations.add(invalidNanos(message.nanos)) + } + return violations + } +} + +/** + * Creates a violation for invalid seconds. + */ +private fun invalidSeconds(seconds: Long): FieldViolation = FieldViolation( + message = templateString { + withPlaceholders = + "The ${FIELD_PATH.value} value is out of range" + + " (${RANGE_VALUE.value}): $seconds." + placeholderValue.put(FIELD_PATH.value, "seconds") + placeholderValue.put(RANGE_VALUE.value, + "${MIN_VALUE.seconds}..${MAX_VALUE.seconds}") + + }, + fieldPath = fieldPath { + fieldName.add("seconds") + }, + fieldValue = seconds +) + +/** + * Creates a violation for invalid nanos. + */ +private fun invalidNanos(nanos: Int): FieldViolation = FieldViolation( + message = templateString { + withPlaceholders = "The ${FIELD_PATH.value} value is out of range" + + ": (${RANGE_VALUE.value})$nanos." + placeholderValue.put(FIELD_PATH.value, "nanos") + placeholderValue.put(RANGE_VALUE.value, "0..${MAX_VALUE.nanos}") + }, + fieldPath = fieldPath { + fieldName.add("nanos") + }, + fieldValue = nanos +) diff --git a/jvm-runtime/src/main/kotlin/io/spine/validation/Validator.kt b/jvm-runtime/src/main/kotlin/io/spine/validation/Validator.kt index ca2057295a..853cb7b0c7 100644 --- a/jvm-runtime/src/main/kotlin/io/spine/validation/Validator.kt +++ b/jvm-runtime/src/main/kotlin/io/spine/validation/Validator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025, TeamDev. All rights reserved. + * Copyright 2026, 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. @@ -35,15 +35,12 @@ import kotlin.reflect.KClass * Applying this annotation to an implementation of [MessageValidator] * makes the class visible to the validation library. * - * Note that the following requirements are imposed to the marked class: - * - * 1. The class must implement the [MessageValidator] interface. - * 2. The class must have a `public`, no-args constructor. - * 3. The class cannot be `inner`, but nested classes are allowed. - * 4. The message type of [Validator.value] and [MessageValidator] must match. + * @deprecated Use [MessageValidator] directly. The message type will be + * obtained via reflection. */ +@Deprecated("Use `MessageValidator` directly.") @Target(AnnotationTarget.CLASS) -@Retention(AnnotationRetention.SOURCE) +@Retention(AnnotationRetention.RUNTIME) public annotation class Validator( /** diff --git a/jvm-runtime/src/main/kotlin/io/spine/validation/ValidatorRegistry.kt b/jvm-runtime/src/main/kotlin/io/spine/validation/ValidatorRegistry.kt new file mode 100644 index 0000000000..524c45372d --- /dev/null +++ b/jvm-runtime/src/main/kotlin/io/spine/validation/ValidatorRegistry.kt @@ -0,0 +1,254 @@ +/* + * Copyright 2026, 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 + * + * https://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.validation + +import com.google.common.collect.Sets.newConcurrentHashSet +import com.google.common.reflect.TypeToken +import com.google.errorprone.annotations.ThreadSafe +import com.google.protobuf.Message +import io.spine.annotation.VisibleForTesting +import io.spine.base.FieldPath +import io.spine.protobuf.TypeConverter +import io.spine.type.TypeName +import java.lang.reflect.ParameterizedType +import java.util.* +import java.util.concurrent.ConcurrentHashMap +import kotlin.reflect.KClass +import org.checkerframework.checker.signature.qual.FullyQualifiedName +import com.google.protobuf.Any as ProtoAny + +/** + * A registry for custom validators of Protobuf messages. + * + * This registry allows for dynamic registration and retrieval of custom validators + * implementing the [MessageValidator] interface. + * + * It supports several validators per message type and provides an API for validating + * messages by applying all associated validators. + * + * The registry also automatically loads validators from the classpath using + * the [ServiceLoader] mechanism. + */ +@ThreadSafe +public object ValidatorRegistry { + + /** + * The name of a key in the placeholder entry for a class name of a validator. + * + * The placeholder value is automatically populated with the fully qualified class name + * of the validator during [validation][validate]. + * + * @see TemplateString + */ + public const val VALIDATOR_PLACEHOLDER: String = "validator" + + /** + * Maps a fully qualified Kotlin class name of a message to a list of validators. + */ + private val validators: MutableMap<@FullyQualifiedName String, + MutableSet>> = ConcurrentHashMap() + + init { + loadFromServiceLoader() + } + + /** + * Loads validators from the classpath using [ServiceLoader]. + */ + @VisibleForTesting + internal fun loadFromServiceLoader() { + val loader = ServiceLoader.load(MessageValidator::class.java) + loader.forEach { validator -> + @Suppress("UNCHECKED_CAST") + val casted = validator as MessageValidator + val messageType = casted.messageClass() + add(messageType, casted) + } + } + + /** + * Adds a custom validator for the specific Protobuf message type. + * + * @param cls The class of the message to validate. + * @param validator The validator to add. + */ + @JvmStatic + public fun add(cls: KClass, validator: MessageValidator) { + synchronized(this) { + validators.compute(cls.qualifiedName!!) { _, currentSet -> + val set = currentSet ?: newConcurrentHashSet() + set.add(validator) + set + } + } + } + + /** + * Adds a custom validator for the specific Protobuf message type. + * + * @param cls The class of the message to validate. + * @param validator The validator to add. + */ + @JvmStatic + public fun add(cls: Class, validator: MessageValidator) { + add(cls.kotlin, validator) + } + + /** + * Removes all validators for the given message type. + * + * @param cls The class of the message for which to remove validators. + */ + @JvmStatic + public fun remove(cls: KClass) { + synchronized(this) { + validators.remove(cls.qualifiedName) + } + } + + /** + * Removes all validators for the given message type. + * + * @param cls The class of the message for which to remove validators. + */ + @JvmStatic + public fun remove(cls: Class) { + remove(cls.kotlin) + } + + /** + * Obtains the validators for the given message type. + * + * @param cls The class of the message for which to get validators. + * @return The set of validators for the given message type, + * or an empty set if no validators are registered. + */ + @JvmStatic + public fun get(cls: KClass): Set> { + val registered = validators[cls.qualifiedName!!] ?: return emptySet() + @Suppress("UNCHECKED_CAST") + return Collections.unmodifiableSet(registered as Set>) + } + + /** + * Obtains the validators for the given message type. + * + * @param cls The class of the message for which to get validators. + * @return The set of validators for the given message type, + * or an empty set if no validators are registered. + */ + @JvmStatic + public fun get(cls: Class): Set> { + return get(cls.kotlin) + } + + /** + * Clears all registered validators. + */ + @JvmStatic + public fun clear() { + synchronized(this) { + validators.clear() + } + } + + /** + * Validates the given [message] by looking up its type in the registry + * and applying all associated validators. + * + * @param message The message to validate. + * @param parentPath The path to the field where the validation occurred. + * If empty, it means that the validation occurred at the top-level. + * @param parentName The name of the message type where the validation occurred. + * If null, it means that the validation occurred at the top-level. + * @return The list of detected violations, or an empty list if no violations were found. + */ + @JvmStatic + public fun validate( + message: Message, + parentPath: FieldPath, + parentName: TypeName? + ): List { + val cls = message::class.qualifiedName!! + val associatedValidators = validators[cls]?.toSet() ?: return emptyList() + val violations = mutableListOf>() + + associatedValidators.forEach { validator -> + val validatorClass = validator::class.qualifiedName ?: "UnknownValidator" + @Suppress("UNCHECKED_CAST") + val casted = validator as MessageValidator + for (violation in casted.validate(message)) { + violations.add(validatorClass to violation) + } + } + + val result = violations.map { (k, v) -> + constraintViolation { + this.message = v.message + .toBuilder() + .putPlaceholderValue(VALIDATOR_PLACEHOLDER, k) + .build() + typeName = if (parentName != null) + parentName.value + else + TypeName.of(message).value + fieldPath = if (v.fieldPath != null) { + parentPath.toBuilder() + .addAllFieldName(v.fieldPath.fieldNameList) + .build() + } else { + parentPath + } + fieldValue = v.fieldValue?.let { TypeConverter.toAny(it) } + ?: ProtoAny.getDefaultInstance() + } + } + return result + } + + /** + * Validates the given [message] by applying all associated validators. + */ + @JvmStatic + public fun validate(message: Message): List = + validate(message, parentPath = FieldPath.getDefaultInstance(), parentName = null) +} + +/** + * Obtains the type of the message validated by this [MessageValidator]. + */ +@VisibleForTesting +internal fun MessageValidator.messageClass(): KClass { + val typeToken = TypeToken.of(this::class.java) + val supertype = typeToken.getSupertype(MessageValidator::class.java) + val messageType = supertype.type.let { + val parameterized = it as? ParameterizedType + parameterized?.actualTypeArguments?.get(0) + } + @Suppress("UNCHECKED_CAST") // The cast is ensured by the type parameter `M`. + return (messageType as Class).kotlin +} diff --git a/jvm-runtime/src/main/resources/spine/validation/message-validators b/jvm-runtime/src/main/resources/spine/validation/message-validators new file mode 100644 index 0000000000..02dcfea90b --- /dev/null +++ b/jvm-runtime/src/main/resources/spine/validation/message-validators @@ -0,0 +1 @@ +com.google.protobuf.Timestamp:io.spine.validation.TimestampValidator diff --git a/jvm-runtime/src/test/java/io/spine/validation/ValidatorRegistryJavaTest.java b/jvm-runtime/src/test/java/io/spine/validation/ValidatorRegistryJavaTest.java new file mode 100644 index 0000000000..3118cbe320 --- /dev/null +++ b/jvm-runtime/src/test/java/io/spine/validation/ValidatorRegistryJavaTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2026, 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 + * + * https://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.validation; + +import com.google.protobuf.Timestamp; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static com.google.common.truth.Truth.assertThat; + +@DisplayName("`ValidatorRegistry` Java-class API should") +class ValidatorRegistryJavaTest { + + @BeforeEach + void setUp() { + ValidatorRegistry.clear(); + } + + @Test + @DisplayName("allow adding a validator using a Java class") + void allowAdding() { + var validator = new TimestampValidator(); + ValidatorRegistry.add(Timestamp.class, validator); + + var validators = ValidatorRegistry.get(Timestamp.class); + assertThat(validators).containsExactly(validator); + } + + @Test + @DisplayName("allow removing a validator using a Java class") + void allowRemoving() { + TimestampValidator validator = new TimestampValidator(); + ValidatorRegistry.add(Timestamp.class, validator); + ValidatorRegistry.remove(Timestamp.class); + + var validators = ValidatorRegistry.get(Timestamp.class); + assertThat(validators).isEmpty(); + } + + @Test + @DisplayName("allow querying validators using a Java class") + void allowQuerying() { + var validator = new TimestampValidator(); + ValidatorRegistry.add(Timestamp.class, validator); + + var validators = ValidatorRegistry.get(Timestamp.class); + assertThat(validators).containsExactly(validator); + } +} diff --git a/jvm-runtime/src/test/kotlin/io/spine/validation/TimestampValidatorSpec.kt b/jvm-runtime/src/test/kotlin/io/spine/validation/TimestampValidatorSpec.kt new file mode 100644 index 0000000000..c5e3b9d353 --- /dev/null +++ b/jvm-runtime/src/test/kotlin/io/spine/validation/TimestampValidatorSpec.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2026, 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 + * + * https://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.validation + +import com.google.protobuf.timestamp +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("`TimestampValidator` should") +internal class TimestampValidatorSpec { + + private val validator = TimestampValidator() + + @Test + fun `validate a valid timestamp`() { + val timestamp = timestamp { + seconds = 123456789 + nanos = 123 + } + validator.validate(timestamp).shouldBeEmpty() + } + + @Test + fun `detect an invalid timestamp with seconds out of range`() { + val secondsValue = -62135596801L // One second before the minimum. + val timestamp = timestamp { + seconds = secondsValue + } + val violations = validator.validate(timestamp) + violations shouldHaveSize 1 + val violation = violations[0] as FieldViolation + violation.message.withPlaceholders shouldBe + "The field.path value is out of range (range.value): $secondsValue." + violation.message.placeholderValueMap shouldBe mapOf( + "field.path" to "seconds", + "range.value" to "-62135596800..253402300799" + ) + violation.fieldPath!!.fieldNameList shouldBe listOf("seconds") + violation.fieldValue shouldBe secondsValue + } + + @Test + fun `detect an invalid timestamp with nanos out of range`() { + val nanosValue = 1_000_000_000 + val timestamp = timestamp { + nanos = nanosValue + } + val violations = validator.validate(timestamp) + violations shouldHaveSize 1 + val violation = violations[0] as FieldViolation + violation.message.withPlaceholders shouldBe + "The field.path value is out of range: (range.value)$nanosValue." + violation.message.placeholderValueMap shouldBe mapOf( + "field.path" to "nanos", + "range.value" to "0..999999999" + ) + violation.fieldPath!!.fieldNameList shouldBe listOf("nanos") + violation.fieldValue shouldBe nanosValue + } + + @Test + fun `detect both seconds and nanos out of range`() { + val secondsValue = -62135596801L + val nanosValue = 1_000_000_000 + val timestamp = timestamp { + seconds = secondsValue + nanos = nanosValue + } + val violations = validator.validate(timestamp) + violations shouldHaveSize 2 + + val secondsViolation = violations.find { + it.fieldPath?.fieldNameList == listOf("seconds") + } as FieldViolation + secondsViolation.message.withPlaceholders shouldBe + "The field.path value is out of range (range.value): $secondsValue." + secondsViolation.message.placeholderValueMap shouldBe mapOf( + "field.path" to "seconds", + "range.value" to "-62135596800..253402300799" + ) + secondsViolation.fieldValue shouldBe secondsValue + + val nanosViolation = violations.find { + it.fieldPath?.fieldNameList == listOf("nanos") + } as FieldViolation + nanosViolation.message.withPlaceholders shouldBe + "The field.path value is out of range: (range.value)$nanosValue." + nanosViolation.message.placeholderValueMap shouldBe mapOf( + "field.path" to "nanos", + "range.value" to "0..999999999" + ) + nanosViolation.fieldValue shouldBe nanosValue + } +} diff --git a/jvm-runtime/src/test/kotlin/io/spine/validation/ValidatorRegistrySpec.kt b/jvm-runtime/src/test/kotlin/io/spine/validation/ValidatorRegistrySpec.kt new file mode 100644 index 0000000000..8c9a2da8e1 --- /dev/null +++ b/jvm-runtime/src/test/kotlin/io/spine/validation/ValidatorRegistrySpec.kt @@ -0,0 +1,212 @@ +/* + * Copyright 2026, 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 + * + * https://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.validation + +import com.google.protobuf.Timestamp +import com.google.protobuf.timestamp +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import java.util.ServiceLoader +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow + +@DisplayName("`ValidatorRegistry` should") +internal class ValidatorRegistrySpec { + + @BeforeEach + fun setUp() { + ValidatorRegistry.clear() + } + + @Test + fun `allow adding and removing validators`() { + val validator = TimestampValidator() + ValidatorRegistry.add(Timestamp::class, validator) + + val invalidTimestamp = timestamp { seconds = -100000000000L } + ValidatorRegistry.validate(invalidTimestamp) shouldHaveSize 1 + + ValidatorRegistry.remove(Timestamp::class) + ValidatorRegistry.validate(invalidTimestamp).shouldBeEmpty() + } + + @Test + fun `query registered validators`() { + val validator1 = TimestampValidator() + val validator2 = AlwaysInvalidTimestampValidator() + + ValidatorRegistry.add(Timestamp::class, validator1) + ValidatorRegistry.add(Timestamp::class, validator2) + + val validators = ValidatorRegistry.get(Timestamp::class) + validators shouldContainExactly setOf(validator1, validator2) + + ValidatorRegistry.remove(Timestamp::class) + ValidatorRegistry.get(Timestamp::class).shouldBeEmpty() + } + + @Test + fun `allow adding and removing validators using Java classes`() { + val validator = TimestampValidator() + ValidatorRegistry.add(Timestamp::class.java, validator) + + val invalidTimestamp = timestamp { seconds = -100000000000L } + ValidatorRegistry.validate(invalidTimestamp) shouldHaveSize 1 + + ValidatorRegistry.remove(Timestamp::class.java) + ValidatorRegistry.validate(invalidTimestamp).shouldBeEmpty() + } + + @Test + fun `query registered validators using Java classes`() { + val validator1 = TimestampValidator() + val validator2 = AlwaysInvalidTimestampValidator() + + ValidatorRegistry.add(Timestamp::class.java, validator1) + ValidatorRegistry.add(Timestamp::class.java, validator2) + + val validators = ValidatorRegistry.get(Timestamp::class.java) + validators shouldContainExactly setOf(validator1, validator2) + + ValidatorRegistry.remove(Timestamp::class.java) + ValidatorRegistry.get(Timestamp::class.java).shouldBeEmpty() + } + + @Test + fun `query registered validators using both Kotlin and Java classes`() { + val validator = TimestampValidator() + + ValidatorRegistry.add(Timestamp::class, validator) + ValidatorRegistry.get(Timestamp::class.java) shouldContainExactly setOf(validator) + + ValidatorRegistry.remove(Timestamp::class.java) + ValidatorRegistry.get(Timestamp::class).shouldBeEmpty() + + ValidatorRegistry.add(Timestamp::class.java, validator) + ValidatorRegistry.get(Timestamp::class) shouldContainExactly setOf(validator) + } + + @Test + fun `support multiple validators per type`() { + val validator1 = TimestampValidator() + val validator2 = AlwaysInvalidTimestampValidator() + + ValidatorRegistry.add(Timestamp::class, validator1) + ValidatorRegistry.add(Timestamp::class, validator2) + + val validTimestamp = timestamp { seconds = 100 } + val violations = ValidatorRegistry.validate(validTimestamp) + + violations shouldHaveSize 1 + violations[0].message.withPlaceholders shouldBe "Always invalid" + } + + @Test + fun `return 'ConstraintViolation' objects`() { + val validator = AlwaysInvalidTimestampValidator() + ValidatorRegistry.add(Timestamp::class, validator) + + val timestamp = timestamp { seconds = 100 } + val violations = ValidatorRegistry.validate(timestamp) + + violations shouldHaveSize 1 + val violation = violations[0] + violation.message.withPlaceholders shouldBe "Always invalid" + violation.typeName shouldBe "google.protobuf.Timestamp" + } + + @Test + fun `clear the whole registry`() { + ValidatorRegistry.add(Timestamp::class, TimestampValidator()) + ValidatorRegistry.clear() + + val invalidTimestamp = timestamp { seconds = -100000000000L } + ValidatorRegistry.validate(invalidTimestamp).shouldBeEmpty() + } + + @Test + fun `load validators from the classpath using 'ServiceLoader'`() { + // We need to trigger the init block of the object if it hasn't been triggered yet. + // But since it's an object, it's lazy. + // In our case, we cleared it in `setUp`. + + // Re-adding what `ServiceLoader` should find. + val loader = ServiceLoader.load(MessageValidator::class.java) + val hasTimestampValidator = loader.any { it is TimestampValidator } + + if (hasTimestampValidator) { + // If AutoService worked during this test run (it might not if it's not a full build), + // we can re-load or just check if it's there after manual trigger. + ValidatorRegistry.clear() + ValidatorRegistry.loadFromServiceLoader() + + val invalidTimestamp = timestamp { nanos = -1 } + ValidatorRegistry.validate(invalidTimestamp) shouldHaveSize 1 + } + } + + @Test + fun `obtain the message type for a validator as a generic argument`() { + val type = TimestampValidator().messageClass() + type shouldBe Timestamp::class + } + + @Test + fun `be thread-safe`() { + val threadCount = 10 + val iterations = 1000 + val executor = Executors.newFixedThreadPool(threadCount) + + assertDoesNotThrow { + repeat(iterations) { + executor.execute { + ValidatorRegistry.add(Timestamp::class, object : MessageValidator { + override fun validate(message: Timestamp): List = + emptyList() + }) + ValidatorRegistry.get(Timestamp::class) + ValidatorRegistry.validate(Timestamp.getDefaultInstance()) + } + } + + executor.shutdown() + executor.awaitTermination(1, TimeUnit.MINUTES) + } + } +} + +private class AlwaysInvalidTimestampValidator : MessageValidator { + override fun validate(message: Timestamp): List { + return listOf(MessageViolation(templateString { withPlaceholders = "Always invalid" })) + } +} diff --git a/ksp/build.gradle.kts b/ksp/build.gradle.kts deleted file mode 100644 index 4a8de206e0..0000000000 --- a/ksp/build.gradle.kts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2025, 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 - * - * https://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. - */ - -import io.spine.dependency.artifact -import io.spine.dependency.build.Ksp -import io.spine.dependency.lib.AutoService -import io.spine.dependency.lib.AutoServiceKsp -import io.spine.dependency.lib.IntelliJ -import io.spine.dependency.local.Base -import io.spine.dependency.local.Logging -import io.spine.dependency.test.KotlinCompileTesting - -plugins { - id("com.google.devtools.ksp") - module -} - -dependencies { - ksp(AutoServiceKsp.processor) - - implementation(AutoService.annotations) - implementation(Ksp.artifact { symbolProcessingApi }) - implementation(Base.annotations) - implementation(Base.lib) - implementation(project(":jvm-runtime")) - - testImplementation(IntelliJ.Platform.util) - ?.because("We need `com.intellij.util.lang.JavaVersion`.") - testImplementation(KotlinCompileTesting.libKsp) - testImplementation(Logging.testLib) - ?.because("We need `tapConsole`.") -} diff --git a/ksp/src/main/kotlin/io/spine/tools/validation/ksp/DiscoveredValidators.kt b/ksp/src/main/kotlin/io/spine/tools/validation/ksp/DiscoveredValidators.kt deleted file mode 100644 index 3bbd135f11..0000000000 --- a/ksp/src/main/kotlin/io/spine/tools/validation/ksp/DiscoveredValidators.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2025, 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 - * - * https://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.tools.validation.ksp - -import io.spine.annotation.Internal -import java.io.File - -/** - * Holds a path to a file with the discovered validators. - * - * The KSP processor generates a resource file using this path. - * Then, the Java codegen plugin picks up this file. - */ -@Internal -public object DiscoveredValidators { - - /** - * The path to the file with the discovered message validators. - * - * The path is relative to the output directory of the KSP processor. - */ - public const val RESOURCES_LOCATION: String = "spine/validation/message-validators" - - /** - * Resolves the path to the file containing discovered message validators. - * - * @param kspOutputDirectory The path to the KSP output. - */ - public fun resolve(kspOutputDirectory: File): File = kspOutputDirectory - .resolve("resources") - .resolve(RESOURCES_LOCATION) -} diff --git a/ksp/src/main/kotlin/io/spine/tools/validation/ksp/ValidatorProcessor.kt b/ksp/src/main/kotlin/io/spine/tools/validation/ksp/ValidatorProcessor.kt deleted file mode 100644 index 8250c7aba6..0000000000 --- a/ksp/src/main/kotlin/io/spine/tools/validation/ksp/ValidatorProcessor.kt +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2025, 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 - * - * https://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.tools.validation.ksp - -import com.google.devtools.ksp.getClassDeclarationByName -import com.google.devtools.ksp.getConstructors -import com.google.devtools.ksp.isPublic -import com.google.devtools.ksp.processing.CodeGenerator -import com.google.devtools.ksp.processing.Dependencies -import com.google.devtools.ksp.processing.Resolver -import com.google.devtools.ksp.processing.SymbolProcessor -import com.google.devtools.ksp.symbol.KSAnnotated -import com.google.devtools.ksp.symbol.KSClassDeclaration -import com.google.devtools.ksp.symbol.KSType -import com.google.devtools.ksp.symbol.KSTypeReference -import com.google.devtools.ksp.symbol.Modifier -import io.spine.string.qualified -import io.spine.string.qualifiedClassName -import io.spine.string.simply -import io.spine.validation.MessageValidator -import io.spine.validation.Validator - -private typealias MessageDeclaration = KSClassDeclaration -private typealias ValidatorDeclaration = KSClassDeclaration - -/** - * Discovers classes annotated with the [@Validator][Validator] annotation. - * - * The processor verifies that the annotation is applied correctly. - * Then, the discovered validators are written to - * the [DiscoveredValidators.RESOURCES_LOCATION] file. - */ -internal class ValidatorProcessor(codeGenerator: CodeGenerator) : SymbolProcessor { - - /** - * Already discovered validators. - * - * Contains the mapping of the message class to the validator class declaration. - * - * The same validator may be discovered several times because - * KSP can perform several rounds of the code analysis. - * This map is used to prevent outputting the same validator twice - * and to enforce the message type has exactly one validator. - */ - private val alreadyDiscovered = mutableMapOf() - - /** - * The output file that contains the discovered validators. - * - * Each line represents a single mapping: - * - * `${MESSAGE_CLASS_FQN}:${VALIDATOR_CLASS_FQN}`. - */ - private val output = codeGenerator.createNewFileByPath( - dependencies = Dependencies(aggregating = true), - path = DiscoveredValidators.RESOURCES_LOCATION, - extensionName = "" - ).writer() - - override fun process(resolver: Resolver): List { - - // The resolved `KSType` of the `MessageValidator` interface is needed - // only for verifying the annotation is applied correctly. - val messageValidatorInterface = resolver - .getClassDeclarationByName>()!! - .asStarProjectedType() - - val annotatedValidators = resolver - .getSymbolsWithAnnotation(ValidatorAnnotation.qualifiedName!!) - .filterIsInstance() - .onEach { it.checkApplicability(messageValidatorInterface) } - - val newlyDiscovered = annotatedValidators - .map { validator -> - val message = validator.validatedMessage(messageValidatorInterface) - message to validator - } - .filterNot { (message, validator) -> - when (val previous = alreadyDiscovered[message]) { - - // This validator was already discovered. - validator -> true - - // The `message` does not have an assigned validator yet. - null -> { - alreadyDiscovered[message] = validator - false - } - - // The `message` already has another validator assigned. - else -> message.reportDuplicateValidator(validator, previous) - } - } - - output.use { writer -> - newlyDiscovered.forEach { (message, validator) -> - val validatorFQN = validator.qualifiedName?.asString()!! - val messageFQN = message.qualifiedName?.asString()!! - writer.appendLine("$messageFQN:$validatorFQN") - } - } - - return emptyList() - } -} - -/** - * Checks if this [MessageValidator] implementation satisfies requirements - * of the [Validator] annotation. - * - * The method ensures the following: - * - * 1. This class is not `inner`. - * 2. It implements [MessageValidator] interface. - * 3. It has a public, no-args constructor. - * - * @param messageValidator The type of the [MessageValidator] interface. - */ -private fun ValidatorDeclaration.checkApplicability(messageValidator: KSType) { - val className = qualifiedName?.asString() - val validator = simply() - check(!modifiers.contains(Modifier.INNER)) { - """ - The `$className` class cannot be marked with the `@$validator` annotation. - This annotation is not applicable to the `inner` classes. - Please consider making the class nested or top-level. - """.trimIndent() - } - check(messageValidator.isAssignableFrom(asStarProjectedType())) { - """ - The `$className` class cannot be marked with the `@$validator` annotation. - This annotation requires the target class to implement the `${qualified>()}` interface. - """.trimIndent() - } - check(hasPublicNoArgConstructor()) { - """ - The `$className` class cannot be marked with the `@$validator` annotation. - This annotation requires the target class to have a public, no-args constructor. - """.trimIndent() - } -} - -private fun ValidatorDeclaration.hasPublicNoArgConstructor(): Boolean = - getConstructors() - .any { it.isPublic() && it.parameters.isEmpty() } - -/** - * Returns the message type this [ValidatorDeclaration] is declared for. - * - * Also, the method makes sure that the message type of the [Validator] annotation - * and [MessageValidator] interface match. - * - * @param messageValidator The type of the [MessageValidator] interface. - */ -private fun ValidatorDeclaration.validatedMessage(messageValidator: KSType): KSClassDeclaration { - - val annotation = annotations.first { it.shortName.asString() == ValidatorAnnotation.simpleName } - val annotationArg = annotation.arguments - .first { it.name?.asString() == VALIDATOR_ARGUMENT_NAME } - .value - - // The argument value can be a `KSType` or a `KSTypeReference`. - // The latter must be resolved. - val annotationMessage = when (annotationArg) { - is KSType -> annotationArg - is KSTypeReference -> annotationArg.resolve() - else -> error( - """ - `${simply()}` cannot parse the argument parameter of the `@${annotation.shortName.asString()}` annotation. - Unexpected KSP type of the argument value: `${annotationArg?.qualifiedClassName}`. - The argument value: `$annotationArg`. - """.trimIndent() - ) - } - - val interfaceMessage = interfaceMessage(messageValidator.declaration as KSClassDeclaration)!! - check(annotationMessage == interfaceMessage) { - """ - The `@${annotation.shortName.asString()}` annotation is applied to incompatible `${qualifiedName?.asString()}` validator. - The validated message type of the annotation and the validator must match. - The message type specified for the annotation: `${annotationMessage.declaration.qualifiedName?.asString()}`. - The message type specified for the validator: `${interfaceMessage.declaration.qualifiedName?.asString()}`. - """.trimIndent() - } - - return annotationMessage.declaration as KSClassDeclaration -} - -/** - * Walks the inheritance tree of this [ValidatorDeclaration] and, if it implements - * the generic interface [messageValidator], returns its single type‐argument. - */ -@Suppress("ReturnCount") // This is a recursive function. -private fun ValidatorDeclaration.interfaceMessage( - messageValidator: KSClassDeclaration, - visited: MutableSet = mutableSetOf() -): KSType? { - - // Prevents cycles. - if (!visited.add(this)) { - return null - } - - for (superRef: KSTypeReference in superTypes) { - val superType = superRef.resolve() - val superDecl = superType.declaration as? KSClassDeclaration ?: continue - - if (superDecl.qualifiedName?.asString() == messageValidator.qualifiedName?.asString()) { - return superType.arguments.first().type?.resolve() - } - - superDecl.interfaceMessage(messageValidator, visited) - ?.let { return it } - } - - return null -} - -private fun MessageDeclaration.reportDuplicateValidator( - newValidator: KSClassDeclaration, - oldValidator: KSClassDeclaration -): Nothing = error(""" - Cannot register the `${newValidator.qualifiedName?.asString()}` validator. - The message type `${qualifiedName?.asString()}` is already validated by the `${oldValidator.qualifiedName?.asString()}` validator. - Only one validator is allowed per message type. -""".trimIndent()) - -/** - * The name of the [Validator.value] property. - */ -private const val VALIDATOR_ARGUMENT_NAME = "value" - -/** - * The class of the [Validator] annotation. - */ -private val ValidatorAnnotation = Validator::class diff --git a/ksp/src/test/kotlin/io/spine/tools/validation/ksp/ValidatorCompilationTest.kt b/ksp/src/test/kotlin/io/spine/tools/validation/ksp/ValidatorCompilationTest.kt deleted file mode 100644 index 0c8c047fd0..0000000000 --- a/ksp/src/test/kotlin/io/spine/tools/validation/ksp/ValidatorCompilationTest.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2025, 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 - * - * https://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.tools.validation.ksp - -import com.google.protobuf.Message -import com.intellij.util.lang.JavaVersion -import com.tschuchort.compiletesting.JvmCompilationResult -import com.tschuchort.compiletesting.KotlinCompilation -import com.tschuchort.compiletesting.SourceFile -import com.tschuchort.compiletesting.SourceFile.Companion.kotlin -import com.tschuchort.compiletesting.configureKsp -import io.spine.logging.testing.ConsoleTap -import io.spine.logging.testing.tapConsole -import io.spine.validation.Validator -import java.io.File -import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.BeforeEach - -/** - * Abstract base for tests checking discovering of - * the [@Validator][Validator] annotation. - */ -@OptIn(ExperimentalCompilerApi::class) -internal sealed class ValidatorCompilationTest { - - companion object { - - /** - * Suppress excessive console output produced by [KotlinCompilation.compile]. - * - * @see Related issue - * @see KotlinCompilation.compileSilently - */ - @BeforeAll - @JvmStatic - fun redirectStreams() { - ConsoleTap.install() - } - } - - protected lateinit var compilation: KotlinCompilation - - @BeforeEach - fun prepareCompilation() { - val dependencyJars = setOf( - Validator::class.java, // The annotation to discover. - Message::class.java, // Protobuf. - ).map { it.classpathFile() } - compilation = KotlinCompilation() - compilation.apply { - jvmTarget = JavaVersion.current().toFeatureString() - javaPackagePrefix = "io.spine.validation.java.ksp" - classpaths = classpaths + dependencyJars - messageOutputStream = System.out - configureKsp (useKsp2 = true) { - symbolProcessorProviders += ValidatorProcessorProvider() - } - } - } - - /** - * Performs compilation with redirected console output. - * - * @see io.spine.logging.testing.ConsoleTap.install - * @see tapConsole - */ - @OptIn(ExperimentalCompilerApi::class) - fun KotlinCompilation.compileSilently(): JvmCompilationResult { - var result: JvmCompilationResult? = null - tapConsole { - result = compile() - } - return result!! - } -} - -/** - * Obtains the path to the classpath element which contains the receiver class. - */ -private fun Class<*>.classpathFile(): File = File(protectionDomain.codeSource.location.path) - -/** - * The package used to define Java and Kotlin files. - */ -private const val PACKAGE_DIR = "io/spine/validation/java/ksp/test" - -/** - * Creates an instance of [SourceFile] with the Kotlin file containing the class - * with the specified name and contents. - */ -internal fun kotlinFile(simpleClassName: String, contents: String): SourceFile = kotlin( - name = "$PACKAGE_DIR/${simpleClassName}.kt", - contents = contents, - trimIndent = true -) diff --git a/ksp/src/test/kotlin/io/spine/tools/validation/ksp/ValidatorProcessorKotlinSpec.kt b/ksp/src/test/kotlin/io/spine/tools/validation/ksp/ValidatorProcessorKotlinSpec.kt deleted file mode 100644 index 774b2cd601..0000000000 --- a/ksp/src/test/kotlin/io/spine/tools/validation/ksp/ValidatorProcessorKotlinSpec.kt +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2025, 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 - * - * https://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.tools.validation.ksp - -import com.tschuchort.compiletesting.KotlinCompilation.ExitCode.INTERNAL_ERROR -import com.tschuchort.compiletesting.KotlinCompilation.ExitCode.OK -import com.tschuchort.compiletesting.kspSourcesDir -import io.kotest.matchers.shouldBe -import io.kotest.matchers.string.shouldContain -import io.spine.string.qualified -import io.spine.validation.MessageValidator -import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test - -@OptIn(ExperimentalCompilerApi::class) -@DisplayName("`ValidatorProcessor` in Kotlin should") -internal class ValidatorProcessorKotlinSpec : ValidatorCompilationTest() { - - @Nested inner class - Discover { - - @Test - fun `a top level validator`() = assertDiscovered("TimestampValidator") { - """ - @Validator(Timestamp::class) - public class TimestampValidator : MessageValidator { - public override fun validate(message: Timestamp): List { - return emptyList() // Always valid. - } - } - """.trimIndent() - } - - @Test - fun `a nested validator`() = assertDiscovered("Outer.TimestampValidator") { - """ - public class Outer { - @Validator(Timestamp::class) - public class TimestampValidator : MessageValidator { - public override fun validate(message: Timestamp): List { - return emptyList() // Always valid. - } - } - } - """.trimIndent() - } - - private fun assertDiscovered(validator: String, declaration: () -> String) { - val sourceFile = kotlinFile("TimestampValidator", """ - package $VALIDATOR_PACKAGE - - $IMPORTS - - ${declaration()} - """.trimIndent() - ) - - compilation.sources = listOf(sourceFile) - val result = compilation.compileSilently() - result.exitCode shouldBe OK - - val validators = DiscoveredValidators.resolve(compilation.kspSourcesDir) - with(validators) { - exists() shouldBe true - readText() shouldBe "$TIMESTAMP_CLASS:$VALIDATOR_PACKAGE.$validator\n" - } - } - } - - @Nested inner class - RejectValidator { - - @Test - fun `declared as 'inner' class`() = assertRejects( - """ - public class Outer { - @Validator(Timestamp::class) - public inner class TimestampValidator : MessageValidator { - public override fun validate(message: Timestamp): List { - return emptyList() // Always valid. - } - } - } - """.trimIndent() - ) { error -> - error shouldContain "$VALIDATOR_PACKAGE.Outer.TimestampValidator" - error shouldContain "This annotation is not applicable to the `inner` classes." - } - - @Test - fun `not implementing 'MessageValidator' interface`() = assertRejects( - """ - @Validator(Timestamp::class) - public class TimestampValidator { - public fun validate(message: Timestamp): List { - return emptyList() // Always valid. - } - } - """.trimIndent() - ) { error -> - error shouldContain "$VALIDATOR_PACKAGE.TimestampValidator" - error shouldContain "requires the target class to implement" + - " the `${qualified>()}`" - } - - @Test - fun `not having a public, no-args constructor`() = assertRejects( - """ - @Validator(Timestamp::class) - public class TimestampValidator private constructor() : MessageValidator { - public fun validate(message: Timestamp): List { - return emptyList() // Always valid. - } - } - """.trimIndent() - ) { error -> - error shouldContain "$VALIDATOR_PACKAGE.TimestampValidator" - error shouldContain "requires the target class to have a public, no-args constructor" - } - - @Test - fun `having different message type for annotation and interface`() = assertRejects( - """ - @Validator(Timestamp::class) - public class DurationValidator : MessageValidator { - public override fun validate(message: Duration): List { - return emptyList() // Always valid. - } - } - """.trimIndent() - ) { error -> - error shouldContain "$VALIDATOR_PACKAGE.DurationValidator" - error shouldContain TIMESTAMP_CLASS - error shouldContain DURATION_CLASS - error shouldContain "message type of the annotation and the validator must match" - } - - @Test - fun `validating the same message twice`() = assertRejects( - """ - @Validator(Timestamp::class) - public class TimestampValidator : MessageValidator { - public override fun validate(message: Timestamp): List { - return emptyList() // Always valid. - } - } - - @Validator(Timestamp::class) - public class TimestampValidator2 : MessageValidator { - public override fun validate(message: Timestamp): List { - return emptyList() // Always valid. - } - } - """.trimIndent() - ) { error -> - error shouldContain TIMESTAMP_CLASS - error shouldContain "$VALIDATOR_PACKAGE.TimestampValidator" - error shouldContain "$VALIDATOR_PACKAGE.TimestampValidator2" - error shouldContain "Only one validator is allowed per message type" - } - - private fun assertRejects(declaration: String, errorMessageAssertions: (String) -> Unit) { - val sourceFile = kotlinFile("TimestampValidator", """ - package $VALIDATOR_PACKAGE - - $IMPORTS - - $declaration - """.trimIndent() - ) - - compilation.sources = listOf(sourceFile) - val result = compilation.compileSilently() - - result.exitCode shouldBe INTERNAL_ERROR - errorMessageAssertions(result.messages) - } - } -} - -private const val TIMESTAMP_CLASS = "com.google.protobuf.Timestamp" -private const val DURATION_CLASS = "com.google.protobuf.Duration" -private const val VALIDATOR_PACKAGE = "io.spine.validation.java.ksp.test" -private val IMPORTS = """ - import io.spine.validation.DetectedViolation - import io.spine.validation.MessageValidator - import io.spine.validation.Validator - import $TIMESTAMP_CLASS - import $DURATION_CLASS - """.trimIndent() diff --git a/pom.xml b/pom.xml index 84c66423bc..afc054ebd4 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.tools validation -2.0.0-SNAPSHOT.400 +2.0.0-SNAPSHOT.401 2015 @@ -47,12 +47,6 @@ all modules and does not describe the project structure per-subproject. 4.33.2 compile - - io.spine - spine-annotations - 2.0.0-SNAPSHOT.384 - compile - io.spine spine-base @@ -68,7 +62,7 @@ all modules and does not describe the project structure per-subproject. io.spine spine-validation-jvm-runtime - 2.0.0-SNAPSHOT.395 + 2.0.0-SNAPSHOT.400 compile @@ -149,18 +143,6 @@ all modules and does not describe the project structure per-subproject. 1.4.4 test - - com.jetbrains.intellij.platform - util - 213.7172.53 - test - - - dev.zacsweers.kctfork - ksp - 0.7.1 - test - io.kotest kotest-assertions-core @@ -200,7 +182,7 @@ all modules and does not describe the project structure per-subproject. org.junit junit-bom - 6.0.0 + 6.0.3 test @@ -212,19 +194,19 @@ all modules and does not describe the project structure per-subproject. org.junit.jupiter junit-jupiter-api - 6.0.0 + 6.0.3 test org.junit.jupiter junit-jupiter-engine - 6.0.0 + 6.0.3 test org.junit.jupiter junit-jupiter-params - 6.0.0 + 6.0.3 test @@ -307,7 +289,7 @@ all modules and does not describe the project structure per-subproject. io.spine.tools validation-java-bundle - 2.0.0-SNAPSHOT.395 + 2.0.0-SNAPSHOT.400 net.sourceforge.pmd diff --git a/settings.gradle.kts b/settings.gradle.kts index 83cf6afdf1..95030fa624 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -40,7 +40,6 @@ include( "java", "jvm-runtime", "java-bundle", - ":ksp", ":tests", ":tests:extensions", ":tests:consumer", diff --git a/tests/validating/src/test/kotlin/io/spine/test/options/ValidateITest.kt b/tests/validating/src/test/kotlin/io/spine/test/options/ValidateITest.kt index c5b8756ed6..f4fa362096 100644 --- a/tests/validating/src/test/kotlin/io/spine/test/options/ValidateITest.kt +++ b/tests/validating/src/test/kotlin/io/spine/test/options/ValidateITest.kt @@ -35,6 +35,7 @@ import io.spine.test.tools.validate.inDepthValidatedMaps import io.spine.test.tools.validate.inDepthValidatedMessage import io.spine.test.tools.validate.inDepthValidatedRepeated import io.spine.test.tools.validate.personName +import io.spine.type.UnknownTypeException import io.spine.validation.ValidationException import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested @@ -134,8 +135,8 @@ internal class ValidateITest { } @Test - fun `accept any unknown enclosed message`() { - assertDoesNotThrow { + fun `reject any unknown enclosed message`() { + assertThrows { inDepthValidatedMessage { any = unknownAny } @@ -250,8 +251,8 @@ internal class ValidateITest { } @Test - fun `accept unknown enclosed messages`() { - assertDoesNotThrow { + fun `reject unknown enclosed messages`() { + assertThrows { inDepthValidatedRepeated { any.addAll( listOf(unknownAny, unknownAny, unknownAny) @@ -405,7 +406,7 @@ internal class ValidateITest { @Test fun `accept unknown enclosed message values`() { - assertDoesNotThrow { + assertThrows { inDepthValidatedMaps { any.putAll( mapOf( diff --git a/tests/validator/build.gradle.kts b/tests/validator/build.gradle.kts index 77c03fa0c6..a4c13ae8b2 100644 --- a/tests/validator/build.gradle.kts +++ b/tests/validator/build.gradle.kts @@ -1,3 +1,4 @@ +import io.spine.dependency.lib.AutoServiceKsp import io.spine.gradle.report.license.LicenseReporter /* @@ -40,7 +41,7 @@ plugins { LicenseReporter.generateReportIn(project) dependencies { - ksp(project(":ksp")) + ksp(AutoServiceKsp.processor) spineCompiler(project(":java")) implementation(project(":java")) implementation(project(":tests:validator-dependency")) diff --git a/tests/validator/src/main/kotlin/io/spine/tools/validation/test/EarphonesValidator.kt b/tests/validator/src/main/kotlin/io/spine/tools/validation/test/EarphonesValidator.kt index e29e189315..f275101f53 100644 --- a/tests/validator/src/main/kotlin/io/spine/tools/validation/test/EarphonesValidator.kt +++ b/tests/validator/src/main/kotlin/io/spine/tools/validation/test/EarphonesValidator.kt @@ -26,6 +26,7 @@ package io.spine.tools.validation.test +import com.google.auto.service.AutoService import com.google.common.annotations.VisibleForTesting import io.spine.base.FieldPath import io.spine.base.fieldPath @@ -34,14 +35,14 @@ import io.spine.validation.DetectedViolation import io.spine.validation.FieldViolation import io.spine.validation.MessageValidator import io.spine.validation.TemplateString -import io.spine.validation.Validator +import io.spine.validation.ValidatorRegistry import io.spine.validation.templateString /** * Validates [Earphones] messages, treating all instances as invalid * except for [ValidEarphones]. */ -@Validator(Earphones::class) +@AutoService(MessageValidator::class) public class EarphonesValidator : MessageValidator { public override fun validate(message: Earphones): List { @@ -79,6 +80,10 @@ public class EarphonesValidator : MessageValidator { */ public val message: TemplateString = templateString { withPlaceholders = "Price is too high." + placeholderValue.put( + ValidatorRegistry.VALIDATOR_PLACEHOLDER, + EarphonesValidator::class.qualifiedName!! + ) } /** diff --git a/tests/validator/src/main/kotlin/io/spine/tools/validation/test/TheOnlyTimeValid.kt b/tests/validator/src/main/kotlin/io/spine/tools/validation/test/TheOnlyTimeValid.kt index 691b19078c..21921a2312 100644 --- a/tests/validator/src/main/kotlin/io/spine/tools/validation/test/TheOnlyTimeValid.kt +++ b/tests/validator/src/main/kotlin/io/spine/tools/validation/test/TheOnlyTimeValid.kt @@ -26,6 +26,7 @@ package io.spine.tools.validation.test +import com.google.auto.service.AutoService import com.google.common.annotations.VisibleForTesting import com.google.protobuf.Timestamp import com.google.protobuf.util.Timestamps @@ -35,18 +36,18 @@ import io.spine.validation.DetectedViolation import io.spine.validation.FieldViolation import io.spine.validation.MessageValidator import io.spine.validation.TemplateString -import io.spine.validation.Validator +import io.spine.validation.ValidatorRegistry import io.spine.validation.templateString /** * Validates [com.google.protobuf.Timestamp] messages, treating all instances as invalid * except for [ValidTimestamp]. */ -@Validator(Timestamp::class) +@AutoService(MessageValidator::class) public class TheOnlyTimeValid : MessageValidator { public override fun validate(message: Timestamp): List { - if (message === ValidTimestamp) { + if (message == ValidTimestamp) { return emptyList() } @@ -76,6 +77,10 @@ public class TheOnlyTimeValid : MessageValidator { */ public val message: TemplateString = templateString { withPlaceholders = "Invalid timestamp." + placeholderValue.put( + ValidatorRegistry.VALIDATOR_PLACEHOLDER, + TheOnlyTimeValid::class.qualifiedName!! + ) } /** diff --git a/tests/validator/src/main/proto/spine/validation/test/dependency_message.proto b/tests/validator/src/main/proto/spine/validation/test/dependency_message.proto index e009bdbcdb..41616c3a07 100644 --- a/tests/validator/src/main/proto/spine/validation/test/dependency_message.proto +++ b/tests/validator/src/main/proto/spine/validation/test/dependency_message.proto @@ -39,17 +39,17 @@ import "spine/validation/test/earphones.proto"; // Tests a singular field of an external message type from dependencies. message SingularDependencyMessage { - Earphones value = 1; + Earphones value = 1 [(validate) = true]; } // Tests a repeated field of an external message type from dependencies. message RepeatedDependencyMessage { - repeated Earphones value = 1; + repeated Earphones value = 1 [(validate) = true]; } // Tests a map field of an external message type from dependencies. message MappedDependencyMessage { - map value = 1; + map value = 1 [(validate) = true]; } diff --git a/ksp/src/main/kotlin/io/spine/tools/validation/ksp/ValidatorProcessorProvider.kt b/tests/validator/src/main/proto/spine/validation/test/packed_fields.proto similarity index 60% rename from ksp/src/main/kotlin/io/spine/tools/validation/ksp/ValidatorProcessorProvider.kt rename to tests/validator/src/main/proto/spine/validation/test/packed_fields.proto index 3a0f5f59b8..be21244e78 100644 --- a/ksp/src/main/kotlin/io/spine/tools/validation/ksp/ValidatorProcessorProvider.kt +++ b/tests/validator/src/main/proto/spine/validation/test/packed_fields.proto @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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 @@ -23,22 +23,27 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +syntax = "proto3"; -package io.spine.tools.validation.ksp +package spine.validation.test; -import com.google.auto.service.AutoService -import com.google.devtools.ksp.processing.SymbolProcessor -import com.google.devtools.ksp.processing.SymbolProcessorEnvironment -import com.google.devtools.ksp.processing.SymbolProcessorProvider +import "spine/options.proto"; -/** - * Provides [ValidatorProcessor] that discovers classes annotated with - * the [@Validator][io.spine.validation.Validator] annotation. - */ -@AutoService(SymbolProcessorProvider::class) -public class ValidatorProcessorProvider : SymbolProcessorProvider { +option (type_url_prefix) = "type.spine.io"; +option java_package = "io.spine.tools.validation.test"; +option java_outer_classname = "PackedFieldsProto"; +option java_multiple_files = true; + +import "google/protobuf/any.proto"; + +message PackedFields { + + // Tests a singular field of well-known message type packed into `Any`. + google.protobuf.Any singular = 1 [(validate) = true]; + + // Tests a repeated field of well-known message type packed into `Any`. + repeated google.protobuf.Any repeated = 2 [(validate) = true]; - override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { - return ValidatorProcessor(environment.codeGenerator) - } + // Tests a map field of well-known message type packed into `Any`. + map mapped = 3 [(validate) = true]; } diff --git a/tests/validator/src/main/proto/spine/validation/test/well_known_message.proto b/tests/validator/src/main/proto/spine/validation/test/well_known_message.proto index 8d90458d11..4fbbb2f20b 100644 --- a/tests/validator/src/main/proto/spine/validation/test/well_known_message.proto +++ b/tests/validator/src/main/proto/spine/validation/test/well_known_message.proto @@ -39,17 +39,17 @@ import "google/protobuf/timestamp.proto"; // Tests a singular field of well-known message type. message SingularWellKnownMessage { - google.protobuf.Timestamp value = 1; + google.protobuf.Timestamp value = 1 [(validate) = true]; } // Tests a repeated field of well-known message type. message RepeatedWellKnownMessage { - repeated google.protobuf.Timestamp value = 1; + repeated google.protobuf.Timestamp value = 1 [(validate) = true]; } // Tests a map field of well-known message type. message MappedWellKnownMessage { - map value = 1; + map value = 1 [(validate) = true]; } diff --git a/tests/validator/src/test/kotlin/io/spine/tools/validation/test/TimestampValidatorSpec.kt b/tests/validator/src/test/kotlin/io/spine/tools/validation/test/ExternalMessageValidatorTest.kt similarity index 95% rename from tests/validator/src/test/kotlin/io/spine/tools/validation/test/TimestampValidatorSpec.kt rename to tests/validator/src/test/kotlin/io/spine/tools/validation/test/ExternalMessageValidatorTest.kt index feee5156a0..faa3e46c38 100644 --- a/tests/validator/src/test/kotlin/io/spine/tools/validation/test/TimestampValidatorSpec.kt +++ b/tests/validator/src/test/kotlin/io/spine/tools/validation/test/ExternalMessageValidatorTest.kt @@ -42,8 +42,12 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertThrows -@DisplayName("`TimestampValidator` should") -class TimestampValidatorSpec { +/** + * This test suite verifies that a custom message validator [TheOnlyTimeValid] + * works when validating messages containing [Timestamp] fields. + */ +@DisplayName("A validator for an external message should") +internal class ExternalMessageValidatorTest { @Nested inner class `prohibit invalid instances` { diff --git a/tests/validator/src/test/kotlin/io/spine/tools/validation/test/PackedMessageValidatorTest.kt b/tests/validator/src/test/kotlin/io/spine/tools/validation/test/PackedMessageValidatorTest.kt new file mode 100644 index 0000000000..05144a528f --- /dev/null +++ b/tests/validator/src/test/kotlin/io/spine/tools/validation/test/PackedMessageValidatorTest.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2026, 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 + * + * https://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.tools.validation.test + +import com.google.protobuf.util.Timestamps +import io.spine.protobuf.pack +import io.spine.validation.ValidationException +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.assertThrows + +@DisplayName("A validator for messages packed into `Any` should") +internal class PackedMessageValidatorTest { + + /** + * This is going to be invalid because of [TheOnlyTimeValid] validator + * available in the classpath. + */ + private val invalid = Timestamps.now().pack() + + private val valid = TheOnlyTimeValid.ValidTimestamp.pack() + + @Test + fun `validate a singular field`() { + assertThrows { + packedFields { + singular = invalid + } + } + assertDoesNotThrow { + packedFields { + singular = valid + } + } + } + + @Test + fun `validate a repeated field`() { + assertThrows { + packedFields { + repeated.addAll(listOf(valid, valid, invalid)) + } + } + assertDoesNotThrow { + packedFields { + repeated.addAll(listOf(valid, valid, valid)) + } + } + } + + @Test + fun `validate a map field`() { + assertThrows { + packedFields { + mapped.put("1", valid) + mapped.put("2", valid) + mapped.put("3", invalid) + } + } + assertDoesNotThrow { + packedFields { + mapped.put("1", valid) + mapped.put("2", valid) + mapped.put("3", valid) + } + } + } +} diff --git a/tests/validator/src/test/kotlin/io/spine/tools/validation/test/EarphonesValidatorSpec.kt b/tests/validator/src/test/kotlin/io/spine/tools/validation/test/ValidatorTest.kt similarity index 96% rename from tests/validator/src/test/kotlin/io/spine/tools/validation/test/EarphonesValidatorSpec.kt rename to tests/validator/src/test/kotlin/io/spine/tools/validation/test/ValidatorTest.kt index bf9e912ca7..32476e41c6 100644 --- a/tests/validator/src/test/kotlin/io/spine/tools/validation/test/EarphonesValidatorSpec.kt +++ b/tests/validator/src/test/kotlin/io/spine/tools/validation/test/ValidatorTest.kt @@ -41,8 +41,12 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertThrows -@DisplayName("`EarphonesValidator` should") -class EarphonesValidatorSpec { +/** + * This test suite verifies that a custom message validator [EarphonesValidator] + * works when validating messages containing [Earphones] fields. + */ +@DisplayName("A message validator should") +class ValidatorTest { @Nested inner class `prohibit invalid instances` { diff --git a/version.gradle.kts b/version.gradle.kts index 197951b0e1..8d4217a3c5 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -29,4 +29,4 @@ * * For Spine-based dependencies please see [io.spine.dependency.local.Spine]. */ -val validationVersion by extra("2.0.0-SNAPSHOT.400") +val validationVersion by extra("2.0.0-SNAPSHOT.401")