Skip to content

Commit 7ebf5de

Browse files
authored
Merge pull request #141 from SpineEventEngine/new-field-api
New `Field` API
2 parents 2aeeef6 + 4846003 commit 7ebf5de

File tree

23 files changed

+215
-232
lines changed

23 files changed

+215
-232
lines changed

buildSrc/src/main/kotlin/io/spine/dependency/local/ArtifactVersion.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ object ArtifactVersion {
3737
*
3838
* @see <a href="https://github.com/SpineEventEngine/base">spine-base</a>
3939
*/
40-
const val base = "2.0.0-SNAPSHOT.215"
41-
const val baseForBuildScript = "2.0.0-SNAPSHOT.215"
40+
const val base = "2.0.0-SNAPSHOT.217"
41+
const val baseForBuildScript = "2.0.0-SNAPSHOT.217"
4242

4343
/**
4444
* The version of [Spine.reflect].
4545
*
4646
* @see <a href="https://github.com/SpineEventEngine/reflect">spine-reflect</a>
4747
*/
48-
const val reflect = "2.0.0-SNAPSHOT.190"
48+
const val reflect = "2.0.0-SNAPSHOT.191"
4949

5050
/**
5151
* The version of [Logging].

buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ package io.spine.dependency.local
3333
*/
3434
@Suppress("ConstPropertyName", "unused")
3535
object Logging {
36-
const val version = "2.0.0-SNAPSHOT.240"
36+
const val version = "2.0.0-SNAPSHOT.242"
3737
const val group = Spine.group
3838
const val lib = "$group:spine-logging:$version"
3939
const val libJvm = "$group:spine-logging-jvm:$version"

buildSrc/src/main/kotlin/io/spine/dependency/local/McJava.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ object McJava {
4747
/**
4848
* The version to be used for integration tests.
4949
*/
50-
const val version = "2.0.0-SNAPSHOT.248"
50+
const val version = "2.0.0-SNAPSHOT.251"
5151

5252
/**
5353
* The ID of the Gradle plugin.

buildSrc/src/main/kotlin/io/spine/dependency/local/ProtoData.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ object ProtoData {
7373
* The version of ProtoData dependencies.
7474
*/
7575
val version: String
76-
private const val fallbackVersion = "0.64.0"
76+
private const val fallbackVersion = "0.65.1"
7777

7878
/**
7979
* The distinct version of ProtoData used by other build tools.
@@ -82,7 +82,7 @@ object ProtoData {
8282
* transitional dependencies, this is the version used to build the project itself.
8383
*/
8484
val dogfoodingVersion: String
85-
private const val fallbackDfVersion = "0.64.0"
85+
private const val fallbackDfVersion = "0.61.8"
8686

8787
/**
8888
* The artifact for the ProtoData Gradle plugin.

buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ package io.spine.dependency.local
3434
@Suppress("ConstPropertyName", "unused")
3535
object ToolBase {
3636
const val group = Spine.toolsGroup
37-
const val version = "2.0.0-SNAPSHOT.233"
37+
const val version = "2.0.0-SNAPSHOT.234"
3838

3939
const val lib = "$group:spine-tool-base:$version"
4040
const val pluginBase = "$group:spine-plugin-base:$version"

config

dependencies.md

+17-121
Large diffs are not rendered by default.

java-tests/consumer/build.gradle.kts

+1-12
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,4 @@ dependencies {
6262
testImplementation(Kotest.assertions)
6363
}
6464

65-
// Uncomment the below block when remote debugging of code generation is needed.
66-
//
67-
//tasks.findByName("launchProtoData")?.apply {this as JavaExec
68-
// debugOptions {
69-
// // Set this option to `true` to enable remote debugging.
70-
// enabled.set(true)
71-
// port.set(5566)
72-
// server.set(true)
73-
// suspend.set(true)
74-
// }
75-
// System.err.println("Debug session for `:java-tests:consumer test` configured.")
76-
//}
65+
protoDataRemoteDebug(enabled = false)

java-tests/validation-gen/build.gradle.kts

+1-8
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,4 @@ dependencies {
3535
testImplementation(Logging.lib)
3636
}
3737

38-
tasks.findByName("launchTestProtoData")?.apply { this as JavaExec
39-
debugOptions {
40-
enabled.set(false) // Set this option to `true` to enable remote debugging.
41-
port.set(5566)
42-
server.set(true)
43-
suspend.set(true)
44-
}
45-
}
38+
testProtoDataRemoteDebug(enabled = false)

java/src/main/kotlin/io/spine/validation/java/DistributingGenerator.kt

+11-3
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ import com.google.common.collect.ImmutableList
3030
import com.google.common.reflect.TypeToken
3131
import com.squareup.javapoet.CodeBlock
3232
import io.spine.protodata.ast.Field
33+
import io.spine.protodata.ast.extractPrimitiveType
34+
import io.spine.protodata.ast.extractTypeName
3335
import io.spine.protodata.ast.isMap
34-
import io.spine.protodata.ast.messageOrEnumName
36+
import io.spine.protodata.ast.name
37+
import io.spine.protodata.ast.qualifiedName
3538
import io.spine.protodata.backend.SecureRandomString
3639
import io.spine.protodata.java.ClassOrEnumName
3740
import io.spine.protodata.java.Expression
@@ -101,11 +104,16 @@ internal class DistributingGenerator(
101104
}
102105

103106
private fun typeName(): ClassOrEnumName {
104-
val name = field.type.messageOrEnumName
107+
val name = field.type.extractTypeName()
105108
val typeName = if (name != null) {
106109
ctx.typeConvention.declarationFor(name).name
107110
} else {
108-
field.type.primitive.toClass()
111+
val primitiveType = field.type.extractPrimitiveType()
112+
check(primitiveType != null) {
113+
"The field `${field.qualifiedName}` is not of" +
114+
" a primitive type (`${field.type.name}`)."
115+
}
116+
primitiveType.toClass()
109117
}
110118
return typeName
111119
}

java/src/main/kotlin/io/spine/validation/java/PrimitiveTypeExtensions.kt

-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
package io.spine.validation.java
2828

2929
import com.google.protobuf.ByteString
30-
import io.spine.code.java.nameOfPackage
3130
import io.spine.protodata.ast.PrimitiveType
3231
import io.spine.protodata.ast.PrimitiveType.PT_UNKNOWN
3332
import io.spine.protodata.ast.PrimitiveType.TYPE_BOOL

java/src/main/kotlin/io/spine/validation/java/RequiredOneofGenerator.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
package io.spine.validation.java
2828

29-
import io.spine.protodata.ast.Field
29+
import io.spine.protodata.ast.Cardinality.CARDINALITY_SINGLE
3030
import io.spine.protodata.ast.OneofName
3131
import io.spine.protodata.java.Expression
3232
import io.spine.protodata.java.Literal
@@ -48,7 +48,7 @@ internal class RequiredOneofGenerator(
4848

4949
override fun condition(): Expression {
5050
val casePropertyName = "${name.value}_case"
51-
val pseudoField = ctx.msg.field(casePropertyName, Field.CardinalityCase.SINGLE)
51+
val pseudoField = ctx.msg.field(casePropertyName, CARDINALITY_SINGLE)
5252
val getter = pseudoField.getter
5353
val numberGetter = getter.chain("getNumber")
5454
return Literal("$numberGetter != 0")

java/src/main/kotlin/io/spine/validation/java/SimpleRuleGenerator.kt

+27-12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright 2023, TeamDev. All rights reserved.
2+
* Copyright 2024, TeamDev. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Redistribution and use in source and/or binary forms, with or without
1111
* modification, must retain the above copyright notice and the following
@@ -30,12 +30,15 @@ import com.squareup.javapoet.CodeBlock
3030
import io.spine.protodata.ast.Field
3131
import io.spine.protodata.ast.PrimitiveType.TYPE_BYTES
3232
import io.spine.protodata.ast.PrimitiveType.TYPE_STRING
33-
import io.spine.protodata.value.Value
33+
import io.spine.protodata.ast.Type
34+
import io.spine.protodata.ast.isList
35+
import io.spine.protodata.ast.isMap
3436
import io.spine.protodata.java.ClassName
3537
import io.spine.protodata.java.Expression
3638
import io.spine.protodata.java.Literal
3739
import io.spine.protodata.java.call
38-
import io.spine.protodata.ast.isRepeated
40+
import io.spine.protodata.value.Value
41+
import io.spine.string.shortly
3942
import io.spine.tools.java.codeBlock
4043
import io.spine.validation.ComparisonOperator.EQUAL
4144
import io.spine.validation.ComparisonOperator.GREATER_OR_EQUAL
@@ -48,6 +51,7 @@ import io.spine.validation.SimpleRule
4851
import io.spine.validation.SimpleRule.OperatorKindCase.CUSTOM_OPERATOR
4952
import io.spine.validation.SimpleRule.OperatorKindCase.OPERATOR
5053
import io.spine.validation.UnsetValue
54+
import io.spine.validation.extractType
5155
import kotlin.jvm.optionals.getOrNull
5256

5357
/**
@@ -103,7 +107,7 @@ internal open class SimpleRuleGenerator(ctx: GenerationContext) : CodeGenerator(
103107
private fun defaultFieldValue(): Value? = with(ctx) {
104108
val field = simpleRuleField
105109
return if (isElement) {
106-
UnsetValue.singular(field.type)
110+
UnsetValue.singular(field.directOrElementType())
107111
} else {
108112
UnsetValue.forField(field)
109113
}.getOrNull()
@@ -120,17 +124,19 @@ internal open class SimpleRuleGenerator(ctx: GenerationContext) : CodeGenerator(
120124
checkNotNull(otherValue) {
121125
"Expected the rule to specify `simple.other_value`, but was: $rule"
122126
}
123-
val type = field.type
127+
val type = field.directOrElementType()
124128
val signs = selectSigns()
125129
val compare = signs[rule.operator] ?: error(
126-
"Unsupported operation `${rule.operator}` for type `$type`."
130+
"Unsupported operation `${rule.operator}` for the type `$type`."
127131
)
128-
checkNotNull(ctx.fieldOrElement) { "There is no field value for rule: $rule" }
132+
checkNotNull(ctx.fieldOrElement) {
133+
"There is no field value for the rule: `${rule.shortly()}`."
134+
}
129135
return Literal(compare(ctx.fieldOrElement!!.toCode(), otherValue.toCode()))
130136
}
131137

132138
private fun fieldIsJavaObject(): Boolean =
133-
!field.isJavaPrimitive() || (field.isRepeated && !ctx.isElement)
139+
!field.refersToJavaPrimitive() || ((field.isList || field.isMap) && !ctx.isElement)
134140

135141
private fun selectSigns() = if (fieldIsJavaObject()) {
136142
OBJECT_COMPARISON_OPS
@@ -154,7 +160,7 @@ internal open class SimpleRuleGenerator(ctx: GenerationContext) : CodeGenerator(
154160
internal fun generatorForSimple(ctx: GenerationContext): CodeGenerator {
155161
val distribute = ctx.rule.simple.distribute
156162
val field = ctx.simpleRuleField
157-
return if (distribute && field.isRepeated) {
163+
return if (distribute && (field.isList || field.isMap)) {
158164
DistributingGenerator(ctx) {
159165
generatorForSingular(it)
160166
}
@@ -172,12 +178,21 @@ private fun generatorForSingular(ctx: GenerationContext): CodeGenerator {
172178
}
173179
}
174180

175-
private fun Field.isJavaPrimitive(): Boolean {
176-
if (!type.hasPrimitive()) {
181+
@Suppress("ReturnCount")
182+
private fun Field.refersToJavaPrimitive(): Boolean {
183+
if (type.isList) {
184+
return type.list.isPrimitive
185+
}
186+
if (type.isMap) {
187+
return type.map.valueType.isPrimitive
188+
}
189+
if (!type.isPrimitive) {
177190
return false
178191
}
179192
return when (type.primitive) {
180193
TYPE_STRING, TYPE_BYTES -> false
181194
else -> true
182195
}
183196
}
197+
198+
private fun Field.directOrElementType(): Type = type.extractType()

java/src/main/kotlin/io/spine/validation/java/ValidateGenerator.kt

+11-9
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,20 @@
2727
package io.spine.validation.java
2828

2929
import com.squareup.javapoet.CodeBlock
30-
import io.spine.protodata.ast.Field
31-
import io.spine.protodata.ast.isAny
30+
import io.spine.protodata.ast.Cardinality.CARDINALITY_LIST
31+
import io.spine.protodata.ast.name
32+
import io.spine.protodata.ast.qualifiedName
3233
import io.spine.protodata.java.Expression
3334
import io.spine.protodata.java.Literal
3435
import io.spine.protodata.java.MessageReference
3536
import io.spine.protodata.java.MethodCall
36-
import io.spine.protodata.ast.qualifiedName
3737
import io.spine.string.titleCase
3838
import io.spine.tools.java.codeBlock
3939
import io.spine.validate.ConstraintViolation
4040
import io.spine.validate.Validate
4141
import io.spine.validate.ValidationError
42+
import io.spine.validation.refersToAny
43+
import io.spine.validation.refersToMessage
4244
import java.util.*
4345

4446
/**
@@ -53,9 +55,9 @@ internal class ValidateGenerator(ctx: GenerationContext) : SimpleRuleGenerator(c
5355
init {
5456
val field = ctx.simpleRuleField
5557
val fieldType = field.type
56-
check(fieldType.hasMessage()) {
58+
check(fieldType.refersToMessage()) {
5759
"The `(validate)` option supports only `Message` types," +
58-
" but the field `${field.qualifiedName}` has the type `$fieldType`."
60+
" but the field `${field.qualifiedName}` has the type `${fieldType.name}`."
5961
}
6062
}
6163

@@ -69,7 +71,7 @@ internal class ValidateGenerator(ctx: GenerationContext) : SimpleRuleGenerator(c
6971
}
7072

7173
override fun prologue(): CodeBlock {
72-
return if (field.type.isAny) {
74+
return if (field.type.refersToAny()) {
7375
codeBlock {
7476
add(unpackAndValidate())
7577
add(wrapIntoError())
@@ -83,8 +85,8 @@ internal class ValidateGenerator(ctx: GenerationContext) : SimpleRuleGenerator(c
8385
* Generates the code obtaining an optional `ValidationError` by invoking Spine-generated
8486
* `.validate()` method of the validated field.
8587
*
86-
* NOTE: such an approach won't work for the types which code was not generated by Spine, such
87-
* as built-in Google Protobuf types.
88+
* NOTE: such an approach will not work for the types which code was not generated by Spine,
89+
* such as built-in Google Protobuf types.
8890
*
8991
* Sample output:
9092
* ```
@@ -161,7 +163,7 @@ internal class ValidateGenerator(ctx: GenerationContext) : SimpleRuleGenerator(c
161163
override fun createViolation(): CodeBlock {
162164
val validationError = MethodCall(validationErrorVar, "get")
163165
val violations = MessageReference(validationError.toCode())
164-
.field("constraint_violation", Field.CardinalityCase.LIST)
166+
.field("constraint_violation", CARDINALITY_LIST)
165167
.getter
166168
return error().createParentViolation(ctx, violations)
167169
}

model/src/main/java/io/spine/validation/DistinctPolicy.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
import io.spine.validation.event.SimpleRuleAdded;
4141

4242
import static io.spine.protobuf.AnyPacker.unpack;
43-
import static io.spine.protodata.ast.Fields.isRepeated;
43+
import static io.spine.protodata.ast.FieldTypeExtsKt.isSingular;
4444
import static io.spine.protodata.ast.TypeNames.getQualifiedName;
4545
import static io.spine.util.Exceptions.newIllegalStateException;
4646
import static io.spine.validation.EventFieldNames.OPTION_NAME;
@@ -54,7 +54,10 @@
5454
*/
5555
final class DistinctPolicy extends ValidationPolicy<FieldOptionDiscovered> {
5656

57-
@SuppressWarnings("DuplicateStringLiteralInspection") // Duplicates in generated code.
57+
@SuppressWarnings({
58+
"DuplicateStringLiteralInspection" /* Duplicates are in the generated code. */,
59+
"RedundantSuppression" /* Suppress warning until the code is generated. */
60+
})
5861
private static final String ERROR = "Collection must not contain duplicates.";
5962

6063
@Override
@@ -83,7 +86,7 @@ protected EitherOf2<RuleAdded, NoReaction> whenever(
8386

8487
private void checkCollection(FieldName fieldName, TypeName typeName, File file) {
8588
var field = findField(fieldName, typeName, file, this);
86-
if (!isRepeated(field)) {
89+
if (isSingular(field.getType())) {
8790
throw newIllegalStateException(
8891
"The field `%s.%s` is neither a `repeated` nor a `map` and " +
8992
"therefore cannot be `(distinct)`.",

model/src/main/java/io/spine/validation/RequiredPolicy.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,17 @@ final class RequiredPolicy extends ValidationPolicy<FieldExited> {
5050
@Override
5151
@React
5252
protected EitherOf2<RuleAdded, NoReaction> whenever(@External FieldExited event) {
53+
var declaringType = event.getType();
54+
var fieldName = event.getField();
5355
var id = FieldId.newBuilder()
54-
.setName(event.getField())
55-
.setType(event.getType())
56+
.setName(fieldName)
57+
.setType(declaringType)
5658
.build();
5759
var field = select(RequiredField.class).findById(id);
5860
if (field != null && field.getRequired()) {
59-
var declaration = findField(event.getField(), event.getType(), event.getFile(), this);
60-
return EitherOf2.withA(requiredRule(declaration, field));
61+
var declaration = findField(fieldName, declaringType, event.getFile(), this);
62+
var rule = requiredRule(declaration, field);
63+
return EitherOf2.withA(rule);
6164
}
6265
return ignore();
6366
}

0 commit comments

Comments
 (0)