Skip to content

Commit c015675

Browse files
authored
Refactor assertion context (#37)
1 parent 39f1e94 commit c015675

23 files changed

+272
-226
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.github.optimumcode.json.schema
33
import io.github.optimumcode.json.pointer.JsonPointer
44
import io.github.optimumcode.json.schema.internal.AssertionWithPath
55
import io.github.optimumcode.json.schema.internal.DefaultAssertionContext
6+
import io.github.optimumcode.json.schema.internal.DefaultReferenceResolver
67
import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion
78
import io.github.optimumcode.json.schema.internal.RefId
89
import io.github.optimumcode.json.schema.internal.SchemaLoader
@@ -27,7 +28,7 @@ public class JsonSchema internal constructor(
2728
* All reported errors will be reported to [ErrorCollector.onError]
2829
*/
2930
public fun validate(value: JsonElement, errorCollector: ErrorCollector): Boolean {
30-
val context = DefaultAssertionContext(JsonPointer.ROOT, references)
31+
val context = DefaultAssertionContext(JsonPointer.ROOT, DefaultReferenceResolver(references))
3132
return assertion.validate(value, context, errorCollector)
3233
}
3334

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package io.github.optimumcode.json.schema.internal
2+
3+
import kotlin.jvm.JvmStatic
4+
import kotlin.reflect.KClass
5+
import kotlin.reflect.cast
6+
7+
internal interface AnnotationCollector {
8+
fun <T : Any> annotate(key: AnnotationKey<T>, value: T)
9+
fun <T : Any> annotated(key: AnnotationKey<T>): T?
10+
fun <T : Any> aggregatedAnnotation(key: AnnotationKey<T>): T?
11+
}
12+
13+
internal fun interface Aggregator<T : Any> {
14+
fun aggregate(a: T, b: T): T?
15+
}
16+
17+
internal class AnnotationKey<T : Any> private constructor(
18+
private val name: String,
19+
internal val type: KClass<T>,
20+
internal val aggregator: Aggregator<T>,
21+
) {
22+
override fun equals(other: Any?): Boolean {
23+
if (this === other) return true
24+
if (other == null || this::class != other::class) return false
25+
26+
other as AnnotationKey<*>
27+
28+
if (name != other.name) return false
29+
if (type != other.type) return false
30+
31+
return true
32+
}
33+
34+
override fun hashCode(): Int {
35+
var result = name.hashCode()
36+
result = 31 * result + type.hashCode()
37+
return result
38+
}
39+
40+
override fun toString(): String = "$name(${type.simpleName})"
41+
42+
companion object {
43+
internal val NOT_AGGREGATABLE: (Any, Any) -> Nothing? = { _, _ -> null }
44+
45+
private fun <T : Any> notAggragatable(): (T, T) -> T? = NOT_AGGREGATABLE
46+
47+
@JvmStatic
48+
inline fun <reified T : Any> create(name: String): AnnotationKey<T> = create(name, T::class)
49+
50+
@JvmStatic
51+
inline fun <reified T : Any> createAggregatable(name: String, noinline aggregator: (T, T) -> T): AnnotationKey<T> =
52+
createAggregatable(name, T::class, aggregator)
53+
54+
@JvmStatic
55+
fun <T : Any> create(name: String, type: KClass<T>): AnnotationKey<T> = AnnotationKey(name, type, notAggragatable())
56+
57+
@JvmStatic
58+
fun <T : Any> createAggregatable(
59+
name: String,
60+
type: KClass<T>,
61+
aggregator: (T, T) -> T,
62+
): AnnotationKey<T> = AnnotationKey(name, type, aggregator)
63+
}
64+
}
65+
66+
internal class DefaultAnnotationCollector : AnnotationCollector {
67+
private lateinit var _annotations: MutableMap<AnnotationKey<*>, Any>
68+
private lateinit var _aggregatedAnnotations: MutableMap<AnnotationKey<*>, Any>
69+
70+
override fun <T : Any> annotate(key: AnnotationKey<T>, value: T) {
71+
annotations()[key] = value
72+
}
73+
74+
override fun <T : Any> annotated(key: AnnotationKey<T>): T? {
75+
if (!::_annotations.isInitialized) {
76+
return null
77+
}
78+
return _annotations[key]?.let { key.type.cast(it) }
79+
}
80+
81+
override fun <T : Any> aggregatedAnnotation(key: AnnotationKey<T>): T? {
82+
if (!::_aggregatedAnnotations.isInitialized && !::_annotations.isInitialized) {
83+
return null
84+
}
85+
val currentLevelAnnotation: T? = annotated(key)
86+
if (!::_aggregatedAnnotations.isInitialized) {
87+
return currentLevelAnnotation
88+
}
89+
return _aggregatedAnnotations[key]?.let {
90+
val aggregatedAnnotation: T = key.type.cast(it)
91+
if (currentLevelAnnotation == null) {
92+
aggregatedAnnotation
93+
} else {
94+
key.aggregator.aggregate(currentLevelAnnotation, aggregatedAnnotation)
95+
}
96+
} ?: currentLevelAnnotation
97+
}
98+
99+
fun applyAnnotations() {
100+
if (::_annotations.isInitialized && _annotations.isNotEmpty()) {
101+
aggregateAnnotations(_annotations) { aggregatedAnnotations() }
102+
_annotations.clear()
103+
}
104+
}
105+
106+
fun resetAnnotations() {
107+
if (::_annotations.isInitialized && _annotations.isNotEmpty()) {
108+
_annotations.clear()
109+
}
110+
}
111+
112+
fun propagateToParent(parent: DefaultAnnotationCollector) {
113+
if (!::_aggregatedAnnotations.isInitialized) {
114+
return
115+
}
116+
aggregateAnnotations(_aggregatedAnnotations) { parent.aggregatedAnnotations() }
117+
}
118+
119+
private inline fun aggregateAnnotations(
120+
source: MutableMap<AnnotationKey<*>, Any>,
121+
destination: () -> MutableMap<AnnotationKey<*>, Any>,
122+
) {
123+
source.forEach { (key, value) ->
124+
if (key.aggregator === AnnotationKey.NOT_AGGREGATABLE) {
125+
return@forEach
126+
}
127+
val aggregatedAnnotations = destination()
128+
val oldValue: Any? = aggregatedAnnotations[key]
129+
if (oldValue != null) {
130+
// Probably there is a mistake in the architecture
131+
// Need to think on how to change that to avoid unchecked cast
132+
@Suppress("UNCHECKED_CAST")
133+
val aggregator: Aggregator<Any> = key.aggregator as Aggregator<Any>
134+
val aggregated = aggregator.aggregate(key.type.cast(oldValue), key.type.cast(value))
135+
if (aggregated != null) {
136+
aggregatedAnnotations[key] = aggregated
137+
}
138+
} else {
139+
aggregatedAnnotations[key] = value
140+
}
141+
}
142+
}
143+
144+
private fun annotations(): MutableMap<AnnotationKey<*>, Any> {
145+
if (!::_annotations.isInitialized) {
146+
_annotations = hashMapOf()
147+
}
148+
return _annotations
149+
}
150+
151+
private fun aggregatedAnnotations(): MutableMap<AnnotationKey<*>, Any> {
152+
if (!::_aggregatedAnnotations.isInitialized) {
153+
_aggregatedAnnotations = hashMapOf()
154+
}
155+
return _aggregatedAnnotations
156+
}
157+
}

0 commit comments

Comments
 (0)