@@ -11,13 +11,13 @@ import io.ktor.http.Parameters
1111import java.math.BigDecimal
1212import java.math.BigInteger
1313import java.text.SimpleDateFormat
14- import java.time.Instant
1514import java.util.*
1615import kotlin.reflect.KFunction
1716import kotlin.reflect.KParameter
1817import kotlin.reflect.KType
1918import kotlin.reflect.full.findAnnotation
2019import kotlin.reflect.full.isSubclassOf
20+ import kotlin.reflect.full.isSubtypeOf
2121import kotlin.reflect.full.primaryConstructor
2222import kotlin.reflect.jvm.jvmErasure
2323
@@ -27,7 +27,7 @@ interface ParameterHandler<T> {
2727 val translator: OpenAPIPathSegmentTranslator
2828}
2929
30- object UnitParameterHandler: ParameterHandler<Unit> {
30+ object UnitParameterHandler : ParameterHandler<Unit> {
3131 override val translator: OpenAPIPathSegmentTranslator = NoTranslation
3232 override fun parse (parameters : Parameters ) = Unit
3333}
@@ -54,12 +54,6 @@ interface QueryParameterParser : ParameterParser {
5454 val explode: Boolean
5555}
5656
57- class PrimitiveParameterParser <T >(override val key : String , val parse : (String? ) -> T ) : ParameterParser {
58- override fun parse (parameters : Parameters ): T {
59- return parse(parameters[key])
60- }
61- }
62-
6357private fun <T > genPathParseFunc (key : String , style : PathParamStyle , parse : (String? ) -> T ): (String? ) -> T {
6458 return when (style) {
6559 PathParamStyle .simple -> parse
@@ -68,24 +62,24 @@ private fun <T> genPathParseFunc(key: String, style: PathParamStyle, parse: (Str
6862 }
6963}
7064
71- class PrimitivePathParameterParserWrapper <T >(
72- val parser : PrimitiveParameterParser <T >,
65+ class PrimitivePathParameterParser <T >(
66+ override val key : String ,
67+ val parse : (String? ) -> T ,
7368 override val style : PathParamStyle ,
7469 override val explode : Boolean
7570) : PathParameterParser {
76- override val key: String
77- get() = parser.key
7871 override val translation: PathParameterTranslation = PathParameterTranslation (key, style, explode)
7972
80- private val parseFunc = genPathParseFunc(key, style, parser. parse)
73+ private val parseFunc = genPathParseFunc(key, style, parse)
8174
8275 override fun parse (parameters : Parameters ): Any? {
8376 return parseFunc(parameters[key])
8477 }
8578}
8679
87- class PrimitiveQueryParameterParserWrapper <T >(
88- val parser : PrimitiveParameterParser <T >,
80+ class PrimitiveQueryParameterParser <T >(
81+ override val key : String ,
82+ val parse : (String? ) -> T ,
8983 style : QueryParamStyle ,
9084 override val explode : Boolean
9185) : QueryParameterParser {
@@ -94,21 +88,22 @@ class PrimitiveQueryParameterParserWrapper<T>(
9488 log.warn(" Using non-form style for primitive type, it is undefined in the OpenAPI standard, reverting to form style" )
9589 }
9690
97- override val key: String
98- get() = parser.key
9991 override val style: QueryParamStyle = QueryParamStyle .form
10092 override val translation: QueryParameterTranslation = QueryParameterTranslation (key, this .style, explode)
10193 override fun parse (parameters : Parameters ): Any? {
102- return parser. parse(parameters[key])
94+ return parse(parameters[key])
10395 }
10496}
10597
106- class EnumQueryParameterParser (info : ParameterInfo , val enumMap : Map <String , * >, val nullable : Boolean ): QueryParameterParser {
98+ class EnumQueryParameterParser (info : ParameterInfo , val enumMap : Map <String , * >, val nullable : Boolean ) :
99+ QueryParameterParser {
107100 override val key: String = info.key
101+
108102 init {
109103 if (info.queryAnnotation?.style != QueryParamStyle .form)
110104 log.warn(" Using non-form style for enum type, it is undefined in the OpenAPI standard, reverting to form style" )
111105 }
106+
112107 override val style: QueryParamStyle = QueryParamStyle .form
113108 override val explode: Boolean = info.queryAnnotation!! .explode
114109 override val translation: QueryParameterTranslation = QueryParameterTranslation (key, style, explode)
@@ -118,10 +113,11 @@ class EnumQueryParameterParser(info: ParameterInfo, val enumMap: Map<String, *>,
118113 }
119114}
120115
121- class EnumPathParameterParser (info : ParameterInfo , val enumMap : Map <String , * >, val nullable : Boolean ): PathParameterParser {
116+ class EnumPathParameterParser (info : ParameterInfo , val enumMap : Map <String , * >, val nullable : Boolean ) :
117+ PathParameterParser {
122118 override val key: String = info.key
123119 override val style: PathParamStyle = info.pathAnnotation!! .style
124- override val explode: Boolean = info.queryAnnotation !! .explode
120+ override val explode: Boolean = info.pathAnnotation !! .explode
125121 override val translation: PathParameterTranslation = PathParameterTranslation (key, style, explode)
126122
127123 private fun parse (parameter : String? ): Any? {
@@ -135,37 +131,81 @@ class EnumPathParameterParser(info: ParameterInfo, val enumMap: Map<String, *>,
135131 }
136132}
137133
138- // class CollectionPathParameterParser<T, A>(info: ParameterInfo, val cvt: (List<T>)->A): PathParameterParser {
139- // override val key: String = info.key
140- // override val style: PathParamStyle = info.pathAnnotation!!.style
141- // override val explode: Boolean = info.queryAnnotation !!.explode
142- // override val translation: PathParameterTranslation = PathParameterTranslation(key, style, explode)
143- //
144- // private fun parse(parameter: String?): Any? {
145- // return parameter?.let { enumMap[it] }
146- // }
147- //
148- // private val parseFunc = when (style) {
149- // PathParamStyle.simple -> {
150- //
151- // }
152- // PathParamStyle.label -> {
153- //
154- // }
155- // PathParamStyle.matrix -> {
156- //
157- // }
158- // }
159- //
160- // override fun parse(parameters: Parameters): Any? {
161- // return parseFunc(parameters[key])
162- // }
163- // }
134+ class CollectionPathParameterParser <T , A >(info : ParameterInfo , type : KType , val cvt : (List <T >? ) -> A ) :
135+ PathParameterParser {
136+ override val key : String = info.key
137+ override val style : PathParamStyle = info.pathAnnotation !! .style
138+ override val explode : Boolean = info.pathAnnotation !! . explode
139+ override val translation : PathParameterTranslation = PathParameterTranslation (key, style, explode)
140+
141+ private val typeParser =
142+ primitiveParsers[type] as (( String? ) -> T ) ? ? : error( " Non-primitive Arrays aren't yet supported " )
143+ private val parseFunc : ( String? ) -> A = when (style) {
144+ PathParamStyle .simple -> ({ str : String? -> cvt(str?.split( ' , ' )?.map(typeParser)) })
145+ PathParamStyle .label -> {
146+ if (explode) {
147+ ({ str : String? -> cvt(str?.split( ' . ' )?.drop( 1 )?.map(typeParser)) })
148+ } else {
149+ ({ str : String? -> cvt(str?.removePrefix( " . " )?.split( ' , ' )?.map(typeParser)) })
150+ }
151+ }
152+ PathParamStyle .matrix -> {
153+ if (explode) {
154+ ({ str : String? -> cvt(str?.split( " ; $key = " )?.drop( 1 )?.map(typeParser)) })
155+ } else {
156+ ({ str : String? -> cvt(str?.removePrefix( " ; $key = " )?.split( ' , ' )?.map(typeParser)) })
157+ }
158+ }
159+ }
164160
161+ override fun parse (parameters : Parameters ): Any? {
162+ return parseFunc(parameters[key])
163+ }
164+ }
165165
166+ class CollectionQueryParameterParser <T , A >(info : ParameterInfo , type : KType , val cvt : (List <T >? ) -> A ) :
167+ QueryParameterParser {
168+ override val key: String = info.key
169+ override val style: QueryParamStyle = info.queryAnnotation!! .style
170+ override val explode: Boolean = info.queryAnnotation!! .explode
171+ override val translation: QueryParameterTranslation = QueryParameterTranslation (key, style, explode)
166172
167- inline fun <reified T > primitive (noinline cvt : (String? ) -> T ): Pair <KType , (String ) - > PrimitiveParameterParser <T >> {
168- return getKType<T >() to { key -> PrimitiveParameterParser (key, cvt) }
173+ private val typeParser =
174+ primitiveParsers[type] as ((String? ) -> T )? ? : error(" Non-primitive Arrays aren't yet supported" )
175+ private val explodedParse = ({ parameters: Parameters -> cvt(parameters.getAll(key)?.map(typeParser)) })
176+ private val parseFunc: (Parameters ) -> A = when (style) {
177+ QueryParamStyle .form -> {
178+ if (explode) {
179+ explodedParse
180+ } else {
181+ ({ parameters: Parameters -> cvt(parameters[key]?.split(' ,' )?.map(typeParser)) })
182+ }
183+ }
184+ QueryParamStyle .pipeDelimited -> {
185+ if (explode) {
186+ explodedParse
187+ } else {
188+ ({ parameters: Parameters -> cvt(parameters[key]?.split(' |' )?.map(typeParser)) })
189+ }
190+ }
191+ QueryParamStyle .spaceDelimited -> {
192+ if (explode) {
193+ explodedParse
194+ } else {
195+ ({ parameters: Parameters -> cvt(parameters[key]?.split(' ' )?.map(typeParser)) })
196+ }
197+ }
198+ QueryParamStyle .deepObject -> error(" Deep Objects are not supported for Arrays" )
199+ }
200+
201+ override fun parse (parameters : Parameters ): Any? {
202+ return parseFunc(parameters)
203+ }
204+ }
205+
206+
207+ inline fun <reified T > primitive (noinline cvt : (String? ) -> T ): Pair <KType , (String ?) - > T > {
208+ return getKType<T >() to cvt
169209}
170210
171211private val dateFormat = SimpleDateFormat ()
@@ -185,10 +225,11 @@ val primitiveParsers = mapOf(
185225 primitive { it?.toDoubleOrNull() },
186226 primitive { it?.toBoolean() ? : false },
187227 primitive { it?.toBoolean() },
188- primitive { it?.toLongOrNull()?.let (::Date ) ? : it?.let (dateFormat::parse) ? : Date () },
189- primitive { it?.toLongOrNull()?.let (::Date ) ? : it?.let (dateFormat::parse) },
190- primitive { it?.toLongOrNull()?.let (Instant ::ofEpochMilli) ? : it?.let (Instant ::parse) ? : Instant .now() },
191- primitive { it?.toLongOrNull()?.let (Instant ::ofEpochMilli) ? : it?.let (Instant ::parse) },
228+ // removed temporarily because behavior may not be standard or expected
229+ // primitive { it?.toLongOrNull()?.let(::Date) ?: it?.let(dateFormat::parse) ?: Date() },
230+ // primitive { it?.toLongOrNull()?.let(::Date) ?: it?.let(dateFormat::parse) },
231+ // primitive { it?.toLongOrNull()?.let(Instant::ofEpochMilli) ?: it?.let(Instant::parse) ?: Instant.now() },
232+ // primitive { it?.toLongOrNull()?.let(Instant::ofEpochMilli) ?: it?.let(Instant::parse) },
192233 primitive {
193234 it?.let {
194235 try {
@@ -220,7 +261,7 @@ class ModularParameterHander<T>(val parsers: Map<KParameter, ParameterParser>, v
220261 )
221262
222263 override fun parse (parameters : Parameters ): T {
223- return constructor .callBy(parsers.mapValues { it.value.parse(parameters) })
264+ return constructor .callBy(parsers.mapValues { it.value.parse(parameters)?. also { println (it:: class ) } })
224265 }
225266}
226267
@@ -235,17 +276,21 @@ inline fun <reified T : Any> buildParameterHandler(): ParameterHandler<T> {
235276 val info = ParameterInfo (key, param)
236277 primitiveParsers[type]?.let {
237278 if (info.pathAnnotation != null ) {
238- PrimitivePathParameterParserWrapper (it( key) , info.pathAnnotation.style, info.pathAnnotation.explode)
279+ PrimitivePathParameterParser ( key, it , info.pathAnnotation.style, info.pathAnnotation.explode)
239280 } else {
240- PrimitiveQueryParameterParserWrapper (it( key) , info.queryAnnotation!! .style, info.queryAnnotation.explode)
281+ PrimitiveQueryParameterParser ( key, it , info.queryAnnotation!! .style, info.queryAnnotation.explode)
241282 }
242283 } ? : makeParameterParser(type, info)
243284 }
244285 return ModularParameterHander (parsers, constructor )
245286}
246287
247288
248- data class ParameterInfo (val key : String , val pathAnnotation : PathParam ? = null , val queryAnnotation : QueryParam ? = null ) {
289+ data class ParameterInfo (
290+ val key : String ,
291+ val pathAnnotation : PathParam ? = null ,
292+ val queryAnnotation : QueryParam ? = null
293+ ) {
249294 constructor (key: String , parameter: KParameter ) : this (
250295 key,
251296 parameter.findAnnotation<PathParam >(),
@@ -259,7 +304,7 @@ fun makeParameterParser(type: KType, info: ParameterInfo): ParameterParser {
259304 val jclazz = clazz.java
260305 return when {
261306 jclazz.isEnum -> makeEnumParameterParser(type, info)
262- clazz.isSubclassOf(List ::class ) || (jclazz. let { it.isArray && ! it.componentType.isPrimitive }) -> {
307+ clazz.isSubclassOf(List ::class ) -> {
263308 makeListParameterParser(type, info)
264309 }
265310 jclazz.isArray -> makeArrayParameterParser(type, info)
@@ -270,20 +315,77 @@ fun makeParameterParser(type: KType, info: ParameterInfo): ParameterParser {
270315
271316private fun makeEnumParameterParser (type : KType , info : ParameterInfo ): ParameterParser {
272317 return if (info.pathAnnotation != null ) {
273- EnumPathParameterParser (info, type.jvmErasure.java.enumConstants.associateBy { (it as Enum <* >).name }, type.isMarkedNullable)
318+ EnumPathParameterParser (
319+ info,
320+ type.jvmErasure.java.enumConstants.associateBy { (it as Enum <* >).name },
321+ type.isMarkedNullable
322+ )
274323 } else {
275- EnumQueryParameterParser (info, type.jvmErasure.java.enumConstants.associateBy { (it as Enum <* >).name }, type.isMarkedNullable)
324+ EnumQueryParameterParser (
325+ info,
326+ type.jvmErasure.java.enumConstants.associateBy { (it as Enum <* >).name },
327+ type.isMarkedNullable
328+ )
276329 }
277330}
278331
279332private fun makeListParameterParser (type : KType , info : ParameterInfo ): ParameterParser {
280333 val contentType = type.arguments[0 ].type!!
281- return TODO (" Implement $type " )
334+ return if (info.pathAnnotation != null ) {
335+ CollectionPathParameterParser <Any ?, Any ?>(info, contentType) { it }
336+ } else {
337+ CollectionQueryParameterParser <Any ?, Any ?>(info, contentType) { it }
338+ }
339+ }
340+
341+ inline fun <reified T > primCVT (noinline cvt : (List <T >) -> Any ): Pair <KType , (List <Any ?>? ) -> Any? > {
342+ return getKType<T >() to ({ lst ->
343+ lst?.let {
344+ cvt(
345+ it as List <T >
346+ )
347+ }
348+ })
282349}
283350
351+ val primCVT = mapOf (
352+ primCVT<Long > { it.toLongArray() },
353+ primCVT<Int > { it.toIntArray() },
354+ primCVT<Float > { it.toFloatArray() },
355+ primCVT<Double > { it.toDoubleArray() },
356+ primCVT<Boolean > { it.toBooleanArray() }
357+ )
358+
359+ /* *
360+ * you may think it is redundant but it is not. Maybe the nullable types are useless though.
361+ */
362+ val arrCVT = mapOf (
363+ primCVT<Long > { it.toTypedArray() },
364+ primCVT<Long ?> { it.toTypedArray() },
365+ primCVT<Int > { it.toTypedArray() },
366+ primCVT<Int ?> { it.toTypedArray() },
367+ primCVT<Float > { it.toTypedArray() },
368+ primCVT<Float ?> { it.toTypedArray() },
369+ primCVT<Double > { it.toTypedArray() },
370+ primCVT<Double ?> { it.toTypedArray() },
371+ primCVT<Boolean > { it.toTypedArray() },
372+ primCVT<Boolean ?> { it.toTypedArray() }
373+ )
374+
375+
284376private fun makeArrayParameterParser (type : KType , info : ParameterInfo ): ParameterParser {
285377 val contentType = type.jvmErasure.java.componentType.toKType()
286- return TODO (" Implement $type " )
378+
379+ val cvt = if (type.toString().startsWith(" kotlin.Array" )) {
380+ arrCVT[contentType] ? : ({ lst -> lst?.toTypedArray() })
381+ } else {
382+ primCVT[contentType] ? : error(" Arrays with primitive type $contentType are not supported" )
383+ }
384+ return if (info.pathAnnotation != null ) {
385+ CollectionPathParameterParser <Any ?, Any ?>(info, contentType) { cvt(it) }
386+ } else {
387+ CollectionQueryParameterParser <Any ?, Any ?>(info, contentType) { cvt(it) }
388+ }
287389}
288390
289391private fun makeObjectParameterParser (type : KType , info : ParameterInfo ): ParameterParser {
0 commit comments