@@ -4,68 +4,98 @@ import com.papsign.kotlin.reflection.getKType
44import com.papsign.ktor.openapigen.classLogger
55import com.papsign.ktor.openapigen.modules.ModuleProvider
66import com.papsign.ktor.openapigen.modules.ofClass
7- import java.lang.Error
8- import java.lang.Exception
9- import kotlin.reflect.*
10- import kotlin.reflect.full.functions
11- import kotlin.reflect.full.instanceParameter
12- import kotlin.reflect.full.memberProperties
7+ import kotlin.reflect.KClass
8+ import kotlin.reflect.KFunction
9+ import kotlin.reflect.KProperty1
10+ import kotlin.reflect.KType
11+ import kotlin.reflect.full.*
1312import kotlin.reflect.jvm.jvmErasure
1413
1514class ValidationHandler <T >(type : KType , context : ModuleProvider <* >) {
1615 private val log = classLogger()
1716
1817 private val transformFun: ((T ) -> T )?
18+
1919 init {
20- val validators = context.ofClass<Validator <Any , Annotation >>()
21- val eligibleProperties = try { type.jvmErasure.memberProperties } catch (e: Error ) { listOf<KProperty1 <T , * >>() }
22- val targets = eligibleProperties.associateWith { prop ->
23- val annotations = prop.annotations.map { it.annotationClass }.toSet()
24- val handler = ValidationHandler <Any ?>(prop.returnType, context)
25- val appliedValidators = validators.filter {
26- val applied = (annotations as Set <KClass <Annotation >>).contains(it.annotationClass)
27- if (applied && ! it.isTypeSupported(prop.returnType.jvmErasure)) {
28- log.error(" Validator ${it::class .simpleName} does not support type ${prop.returnType} of $prop and will be ignored" )
29- false
30- } else {
31- applied
32- }
33- }.associateWith {
34- prop.annotations.filter { annot -> it.annotationClass.isInstance(annot) }
20+ val nonNullType = type.withNullability(false )
21+ when {
22+ nonNullType.isSubtypeOf(arrayErasure) -> {
23+ val arrtype = type.arguments[0 ].type!!
24+ val handler = ValidationHandler <Any ?>(arrtype, context)
25+ transformFun = if (handler.isUseful()) {
26+ { t: T -> if (t != null ) (t as Array <Any ?>).map { handler.handle(it) }.toTypedArray() as T else t }
27+ } else null
3528 }
36- val validations = appliedValidators.flatMap { (valid, annots) ->
37- annots.map { annot ->
38- log.trace(" Applying validator ${valid::class } with $annot on $prop " );
39- { t: Any? -> valid.validate(t, annot) }
29+ nonNullType.isSubtypeOf(iterableErasure) -> {
30+ val listtype = type.arguments[0 ].type!!
31+ val handler = ValidationHandler <Any ?>(listtype, context)
32+ transformFun = when {
33+ nonNullType.isSupertypeOf(listErasure) -> {
34+ log.error(" Supertypes of List are not yet supported, ignoring $type " )
35+ null
36+ }
37+ handler.isUseful() -> {
38+ { t: T -> if (t != null ) (t as Iterable <Any ?>).map { handler.handle(it) } as T else t }
39+ }
40+ else -> null
4041 }
4142 }
42- if (validations.isNotEmpty()) {
43- if (handler.isUseful()) {
44- { t: Any? -> validations.fold(handler.handle(t)) { v, op -> op(v) } }
45- } else {
46- { t: Any? -> validations.fold(t) { v, op -> op(v) } }
43+ else -> {
44+ val validators = context.ofClass<Validator <Any , Annotation >>()
45+ val eligibleProperties = try {
46+ type.jvmErasure.memberProperties
47+ } catch (e: Error ) {
48+ listOf<KProperty1 <T , * >>()
4749 }
48- } else {
49- if (handler.isUseful()) {
50- handler::handle
51- } else {
52- null
50+ val targets = eligibleProperties.associateWith { prop ->
51+ val annotations = prop.annotations.map { it.annotationClass }.toSet()
52+ val handler = ValidationHandler <Any ?>(prop.returnType, context)
53+ val appliedValidators = validators.filter {
54+ val applied = (annotations as Set <KClass <Annotation >>).contains(it.annotationClass)
55+ if (applied && ! it.isTypeSupported(prop.returnType.jvmErasure)) {
56+ log.error(" Validator ${it::class .simpleName} does not support type ${prop.returnType} of $prop and will be ignored" )
57+ false
58+ } else {
59+ applied
60+ }
61+ }.associateWith {
62+ prop.annotations.filter { annot -> it.annotationClass.isInstance(annot) }
63+ }
64+ val validations = appliedValidators.flatMap { (valid, annots) ->
65+ annots.map { annot ->
66+ log.trace(" Applying validator ${valid::class .simpleName} with @${annot.annotationClass.simpleName} on $prop " );
67+ { t: Any? -> valid.validate(t, annot) }
68+ }
69+ }
70+ if (validations.isNotEmpty()) {
71+ if (handler.isUseful()) {
72+ { t: Any? -> validations.fold(handler.handle(t)) { v, op -> op(v) } }
73+ } else {
74+ { t: Any? -> validations.fold(t) { v, op -> op(v) } }
75+ }
76+ } else {
77+ if (handler.isUseful()) {
78+ handler::handle
79+ } else {
80+ null
81+ }
82+ }
83+ }.filterValues { it != null } as Map <KProperty1 <T , * >, (Any? ) -> Any? >
84+ transformFun = when {
85+ targets.isEmpty() -> null
86+ type.jvmErasure.isData -> {
87+ val copy = type.jvmErasure.functions.find { it.name == " copy" } as KFunction <T >
88+ val propMap = copy.parameters.associateBy { it.name }
89+ val mapped = targets.map { (prop, fn) -> Pair (propMap[prop.name]!! , Pair (prop, fn)) }.associate { it }
90+ val instanceParam = copy.instanceParameter!!
91+ { t: T -> if (t != null ) copy.callBy(mapOf (instanceParam to t) + mapped.mapValues { it.value.second(it.value.first.get(t)) }) else t }
92+ }
93+ else -> {
94+ log.error(" Validators are only supported on data classes, tried on: $type " )
95+ null
96+ }
5397 }
5498 }
55- }.filterValues { it != null } as Map <KProperty1 <T , * >, (Any? ) -> Any? >
56- transformFun = when {
57- targets.isEmpty() -> null
58- type.jvmErasure.isData -> {
59- val copy = type.jvmErasure.functions.find { it.name == " copy" } as KFunction <T >
60- val propMap = copy.parameters.associateBy { it.name }
61- val mapped = targets.map { (prop, fn) -> Pair (propMap[prop.name]!! , Pair (prop, fn)) }.associate { it }
62- val instanceParam = copy.instanceParameter!!
63- { t: T -> if (t != null ) copy.callBy(mapOf (instanceParam to t) + mapped.mapValues { it.value.second(it.value.first.get(t)) }) else t }
64- }
65- else -> {
66- log.error(" Validators are only supported on data classes, tried on: $type " )
67- null
68- }
6999 }
70100 }
71101
@@ -83,3 +113,7 @@ class ValidationHandler<T>(type: KType, context: ModuleProvider<*>) {
83113 }
84114 }
85115}
116+
117+ val arrayErasure = getKType<Array <* >>().jvmErasure.starProjectedType
118+ val iterableErasure = getKType<Iterable <* >>().jvmErasure.starProjectedType
119+ val listErasure = getKType<List <* >>().jvmErasure.starProjectedType
0 commit comments