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