Skip to content

Commit 95871df

Browse files
committed
Optimize error transformation
1 parent 5a35a24 commit 95871df

File tree

7 files changed

+57
-43
lines changed

7 files changed

+57
-43
lines changed

src/commonMain/kotlin/io/github/optimumcode/json/schema/OutputCollector.kt

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,29 @@ import io.github.optimumcode.json.schema.ValidationOutput.Verbose
66

77
internal typealias OutputErrorTransformer<T> = OutputCollector<T>.(ValidationError) -> ValidationError?
88

9+
private val NO_TRANSFORMATION: OutputErrorTransformer<*> = { it }
10+
911
public sealed class OutputCollector<T> private constructor(
10-
protected open val parent: OutputCollector<T>? = null,
11-
protected val transformer: OutputErrorTransformer<T> = { it },
12+
parent: OutputCollector<T>? = null,
13+
transformer: OutputErrorTransformer<T> = NO_TRANSFORMATION,
1214
) : ErrorCollector {
1315
public abstract val output: T
16+
private val transformerFunc: OutputErrorTransformer<T> =
17+
parent?.let { p ->
18+
when {
19+
transformer === NO_TRANSFORMATION && p.transformerFunc === NO_TRANSFORMATION
20+
-> NO_TRANSFORMATION
21+
transformer === NO_TRANSFORMATION
22+
-> p.transformerFunc
23+
p.transformerFunc === NO_TRANSFORMATION
24+
-> transformer
25+
else -> {
26+
{ err ->
27+
transformer(err)?.let { p.transformError(it) }
28+
}
29+
}
30+
}
31+
} ?: transformer
1432

1533
internal abstract fun updateLocation(path: JsonPointer): OutputCollector<T>
1634

@@ -29,17 +47,7 @@ public sealed class OutputCollector<T> private constructor(
2947
reportErrors()
3048
}
3149

32-
internal fun transformError(error: ValidationError): ValidationError? {
33-
return transformer(error)?.let {
34-
parent.let { p ->
35-
if (p == null) {
36-
it
37-
} else {
38-
p.transformError(it)
39-
}
40-
}
41-
}
42-
}
50+
protected fun transformError(error: ValidationError): ValidationError? = transformerFunc(error)
4351

4452
internal data object Empty : OutputCollector<Nothing>() {
4553
override val output: Nothing
@@ -58,8 +66,8 @@ public sealed class OutputCollector<T> private constructor(
5866

5967
internal class DelegateOutputCollector(
6068
private val errorCollector: ErrorCollector,
61-
override val parent: DelegateOutputCollector? = null,
62-
transformer: OutputErrorTransformer<Nothing> = { it },
69+
private val parent: DelegateOutputCollector? = null,
70+
transformer: OutputErrorTransformer<Nothing> = NO_TRANSFORMATION,
6371
) : OutputCollector<Nothing>(parent, transformer) {
6472
private val reportedErrors = mutableListOf<ValidationError>()
6573

@@ -85,12 +93,12 @@ public sealed class OutputCollector<T> private constructor(
8593
?: reportedErrors.forEach(errorCollector::onError)
8694
}
8795

88-
override fun childCollector(): OutputCollector<Nothing> = DelegateOutputCollector(errorCollector, this, transformer)
96+
override fun childCollector(): OutputCollector<Nothing> = DelegateOutputCollector(errorCollector, this)
8997
}
9098

9199
public class Flag private constructor(
92-
override val parent: Flag? = null,
93-
transformer: OutputErrorTransformer<ValidationOutput.Flag> = { it },
100+
private val parent: Flag? = null,
101+
transformer: OutputErrorTransformer<ValidationOutput.Flag> = NO_TRANSFORMATION,
94102
) : OutputCollector<ValidationOutput.Flag>(parent, transformer) {
95103
private var valid: Boolean = true
96104
override val output: ValidationOutput.Flag
@@ -128,8 +136,8 @@ public sealed class OutputCollector<T> private constructor(
128136
public class Detailed private constructor(
129137
private val location: JsonPointer = JsonPointer.ROOT,
130138
private val keywordLocation: JsonPointer = JsonPointer.ROOT,
131-
override val parent: Detailed? = null,
132-
transformer: OutputErrorTransformer<ValidationOutput.Detailed> = { it },
139+
private val parent: Detailed? = null,
140+
transformer: OutputErrorTransformer<ValidationOutput.Detailed> = NO_TRANSFORMATION,
133141
) : OutputCollector<ValidationOutput.Detailed>(parent, transformer) {
134142
private val errors: MutableList<ValidationOutput.Detailed> = mutableListOf()
135143

@@ -189,8 +197,8 @@ public sealed class OutputCollector<T> private constructor(
189197
public class Verbose private constructor(
190198
private val location: JsonPointer = JsonPointer.ROOT,
191199
private val keywordLocation: JsonPointer = JsonPointer.ROOT,
192-
override val parent: Verbose? = null,
193-
transformer: OutputErrorTransformer<ValidationOutput.Verbose> = { it },
200+
private val parent: Verbose? = null,
201+
transformer: OutputErrorTransformer<ValidationOutput.Verbose> = NO_TRANSFORMATION,
194202
) : OutputCollector<ValidationOutput.Verbose>(parent, transformer) {
195203
private val errors: MutableList<ValidationOutput.Verbose> = mutableListOf()
196204

src/commonMain/kotlin/io/github/optimumcode/json/schema/ValidationError.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,10 @@ public data class ValidationError(
2626
/**
2727
* The absolute path to triggered assertion if the $ref was used
2828
*/
29-
val absoluteLocation: Uri? = null,
29+
val absoluteLocation: AbsoluteLocation? = null,
30+
)
31+
32+
public data class AbsoluteLocation(
33+
val uri: Uri,
34+
val path: JsonPointer,
3035
)

src/commonMain/kotlin/io/github/optimumcode/json/schema/ValidationOutput.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.github.optimumcode.json.schema
22

3-
import com.eygraber.uri.Uri
43
import io.github.optimumcode.json.pointer.JsonPointer
54
import kotlin.jvm.JvmField
65

@@ -21,7 +20,7 @@ public sealed class ValidationOutput private constructor(
2120
valid: Boolean,
2221
public val keywordLocation: JsonPointer,
2322
public val instanceLocation: JsonPointer,
24-
public val absoluteKeywordLocation: Uri? = null,
23+
public val absoluteKeywordLocation: AbsoluteLocation? = null,
2524
public val error: String? = null,
2625
public val errors: List<Detailed> = emptyList(),
2726
) : ValidationOutput(valid)
@@ -30,7 +29,7 @@ public sealed class ValidationOutput private constructor(
3029
valid: Boolean,
3130
public val keywordLocation: JsonPointer,
3231
public val instanceLocation: JsonPointer,
33-
public val absoluteKeywordLocation: Uri? = null,
32+
public val absoluteKeywordLocation: AbsoluteLocation? = null,
3433
public val error: String? = null,
3534
public val errors: List<Verbose> = emptyList(),
3635
public val annotations: List<Verbose> = emptyList(),

src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/RecursiveRefSchemaAssertion.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.github.optimumcode.json.schema.internal
33
import io.github.optimumcode.json.pointer.JsonPointer
44
import io.github.optimumcode.json.pointer.plus
55
import io.github.optimumcode.json.pointer.relative
6+
import io.github.optimumcode.json.schema.AbsoluteLocation
67
import io.github.optimumcode.json.schema.OutputCollector
78
import kotlinx.serialization.json.JsonElement
89

@@ -23,9 +24,7 @@ internal class RecursiveRefSchemaAssertion(
2324
it.copy(
2425
schemaPath = basePath + relativePath,
2526
absoluteLocation =
26-
it.absoluteLocation ?: absoluteLocation.buildUpon()
27-
.encodedFragment(it.schemaPath.toString())
28-
.build(),
27+
it.absoluteLocation ?: AbsoluteLocation(absoluteLocation, it.schemaPath),
2928
)
3029
}.use {
3130
refAssertion.validate(

src/commonMain/kotlin/io/github/optimumcode/json/schema/internal/RefSchemaAssertion.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.eygraber.uri.Uri
44
import io.github.optimumcode.json.pointer.JsonPointer
55
import io.github.optimumcode.json.pointer.plus
66
import io.github.optimumcode.json.pointer.relative
7+
import io.github.optimumcode.json.schema.AbsoluteLocation
78
import io.github.optimumcode.json.schema.OutputCollector
89
import kotlinx.serialization.json.JsonElement
910

@@ -30,9 +31,7 @@ internal class RefSchemaAssertion(
3031
it.copy(
3132
schemaPath = basePath + refIdPath.relative(it.schemaPath),
3233
absoluteLocation =
33-
it.absoluteLocation ?: refAbsolutePath.buildUpon()
34-
.encodedFragment(it.schemaPath.toString())
35-
.build(),
34+
it.absoluteLocation ?: AbsoluteLocation(refAbsolutePath, it.schemaPath),
3635
)
3736
}.use {
3837
refAssertion.validate(

src/commonTest/kotlin/io/github/optimumcode/json/schema/assertions/ref/JsonSchemaRefValidationTest.kt

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package io.github.optimumcode.json.schema.assertions.ref
22

33
import com.eygraber.uri.Uri
44
import io.github.optimumcode.json.pointer.JsonPointer
5+
import io.github.optimumcode.json.schema.AbsoluteLocation
56
import io.github.optimumcode.json.schema.JsonSchema
67
import io.github.optimumcode.json.schema.ValidationError
78
import io.github.optimumcode.json.schema.base.KEY
@@ -60,9 +61,7 @@ class JsonSchemaRefValidationTest : FunSpec() {
6061
objectPath = JsonPointer("/size"),
6162
message = "-1 must be greater or equal to 0",
6263
absoluteLocation =
63-
Uri.parse(
64-
"#/definitions/positiveInteger/minimum",
65-
),
64+
AbsoluteLocation(Uri.EMPTY, JsonPointer("/definitions/positiveInteger/minimum")),
6665
),
6766
)
6867
}
@@ -129,9 +128,7 @@ class JsonSchemaRefValidationTest : FunSpec() {
129128
objectPath = JsonPointer("/other/size"),
130129
message = "-1 must be greater or equal to 0",
131130
absoluteLocation =
132-
Uri.parse(
133-
"#/definitions/positiveInteger/minimum",
134-
),
131+
AbsoluteLocation(Uri.EMPTY, JsonPointer("/definitions/positiveInteger/minimum")),
135132
),
136133
)
137134
}
@@ -172,9 +169,7 @@ class JsonSchemaRefValidationTest : FunSpec() {
172169
objectPath = JsonPointer("/size"),
173170
message = "element is not a integer",
174171
absoluteLocation =
175-
Uri.parse(
176-
"#/definitions/A/definitions/B/type",
177-
),
172+
AbsoluteLocation(Uri.EMPTY, JsonPointer("/definitions/A/definitions/B/type")),
178173
),
179174
)
180175
}

src/commonTest/kotlin/io/github/optimumcode/json/schema/base/JsonSchemaLoaderTest.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package io.github.optimumcode.json.schema.base
22

33
import com.eygraber.uri.Uri
44
import io.github.optimumcode.json.pointer.JsonPointer
5+
import io.github.optimumcode.json.schema.AbsoluteLocation
56
import io.github.optimumcode.json.schema.ErrorCollector
67
import io.github.optimumcode.json.schema.FormatBehavior.ANNOTATION_AND_ASSERTION
78
import io.github.optimumcode.json.schema.FormatValidationResult
@@ -74,7 +75,11 @@ class JsonSchemaLoaderTest : FunSpec() {
7475
schemaPath = JsonPointer("/properties/anotherName/\$ref/type"),
7576
objectPath = JsonPointer("/anotherName"),
7677
message = "element is not a string",
77-
absoluteLocation = Uri.parse("https://test.com#/properties/name/type"),
78+
absoluteLocation =
79+
AbsoluteLocation(
80+
Uri.parse("https://test.com"),
81+
JsonPointer("/properties/name/type"),
82+
),
7883
),
7984
)
8085
}
@@ -350,7 +355,11 @@ class JsonSchemaLoaderTest : FunSpec() {
350355
schemaPath = JsonPointer("/properties/foobar/\$ref/type"),
351356
objectPath = JsonPointer("/foobar"),
352357
message = "element is not a integer",
353-
absoluteLocation = Uri.parse("myproject/enums/foo#/type"),
358+
absoluteLocation =
359+
AbsoluteLocation(
360+
Uri.parse("myproject/enums/foo"),
361+
JsonPointer("/type"),
362+
),
354363
),
355364
)
356365
}

0 commit comments

Comments
 (0)