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