Skip to content

Commit edfe862

Browse files
committed
Finished Query Builders
Finished Converter System Changed to experimental API to get correct KTypes for handling nullable arrays and collections, this is non negociable, it just doesn't work without
1 parent 4b07532 commit edfe862

File tree

64 files changed

+1090
-285
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1090
-285
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ dependencies {
3131

3232
compileKotlin {
3333
kotlinOptions.jvmTarget = "1.6"
34+
kotlinOptions.freeCompilerArgs += ["-Xuse-experimental=kotlin.ExperimentalStdlibApi"]
3435
}
3536
compileTestKotlin {
3637
kotlinOptions.jvmTarget = "1.6"

src/main/kotlin/com/papsign/kotlin/reflection/Reflection.kt

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import kotlin.reflect.*
55
import kotlin.reflect.full.createType
66
import kotlin.reflect.full.declaredMemberProperties
77
import kotlin.reflect.full.isSubclassOf
8-
import kotlin.reflect.full.withNullability
98
import kotlin.reflect.jvm.jvmErasure
109

1110
val unitKType = getKType<Unit>()
@@ -15,7 +14,8 @@ inline fun <reified T> isNullable(): Boolean {
1514
}
1615

1716
inline fun <reified T> getKType(): KType {
18-
return object : SuperTypeTokenHolder<T>() { }.getKTypeImpl().withNullability(isNullable<T>())
17+
return typeOf<T>()
18+
// return object : SuperTypeTokenHolder<T>() {}.getKTypeImpl().withNullability(isNullable<T>())
1919
}
2020

2121
// TODO: Lobby for a real kotlin ::type for types because the status quo sucks
@@ -24,7 +24,7 @@ inline fun <reified T> getKType(): KType {
2424
open class SuperTypeTokenHolder<T>
2525

2626
fun SuperTypeTokenHolder<*>.getKTypeImpl(): KType =
27-
javaClass.genericSuperclass.toKType().arguments.single().type!!
27+
javaClass.genericSuperclass.toKType().arguments.single().type!!
2828

2929
fun KClass<*>.toInvariantFlexibleProjection(arguments: List<KTypeProjection> = emptyList()): KTypeProjection {
3030
// TODO: there should be an API in kotlin-reflect which creates KType instances corresponding to flexible types
@@ -41,13 +41,15 @@ fun Type.toKTypeProjection(): KTypeProjection = when (this) {
4141
is Class<*> -> this.kotlin.toInvariantFlexibleProjection()
4242
is ParameterizedType -> {
4343
val erasure = (rawType as Class<*>).kotlin
44-
erasure.toInvariantFlexibleProjection((erasure.typeParameters.zip(actualTypeArguments).map { (parameter, argument) ->
45-
val projection = argument.toKTypeProjection()
46-
projection.takeIf {
47-
// Get rid of use-site projections on arguments, where the corresponding parameters already have a declaration-site projection
48-
parameter.variance == KVariance.INVARIANT || parameter.variance != projection.variance
49-
} ?: KTypeProjection.invariant(projection.type!!)
50-
}))
44+
erasure.toInvariantFlexibleProjection(
45+
(erasure.typeParameters.zip(actualTypeArguments).map { (parameter, argument) ->
46+
val projection = argument.toKTypeProjection()
47+
projection.takeIf {
48+
// Get rid of use-site projections on arguments, where the corresponding parameters already have a declaration-site projection
49+
parameter.variance == KVariance.INVARIANT || parameter.variance != projection.variance
50+
} ?: KTypeProjection.invariant(projection.type!!)
51+
})
52+
)
5153
}
5254
is WildcardType -> when {
5355
lowerBounds.isNotEmpty() -> KTypeProjection.contravariant(lowerBounds.single().toKType())
@@ -122,4 +124,4 @@ fun KType.getObjectSubtypes(): Set<KType> {
122124
// println(getKType<Array<Int?>?>())
123125
// println(getKType<Array<Array<String>>>())
124126
// println(getKType<Unit>())
125-
//}
127+
//}
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.papsign.ktor.openapigen.parameters
22

3-
import com.papsign.ktor.openapigen.parameters.parsers.generic.BuilderFactory
4-
53
interface ParameterStyle<S> where S: ParameterStyle<S>, S: Enum<S> {
64
val name: String
75
}

src/main/kotlin/com/papsign/ktor/openapigen/parameters/parsers/ObjectParameterParser.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package com.papsign.ktor.openapigen.parameters.parsers
22

33
import com.papsign.ktor.openapigen.parameters.QueryParamStyle
4-
import com.papsign.ktor.openapigen.parameters.parsers.deepobject.DeepBuilder
5-
import com.papsign.ktor.openapigen.parameters.parsers.deepobject.DeepBuilderFactory
4+
import com.papsign.ktor.openapigen.parameters.parsers.builders.query.deepobject.DeepBuilderFactory
65
import com.papsign.ktor.openapigen.parameters.util.ParameterInfo
76
import io.ktor.http.Parameters
87
import io.ktor.util.toMap

src/main/kotlin/com/papsign/ktor/openapigen/parameters/parsers/generic/Builder.kt renamed to src/main/kotlin/com/papsign/ktor/openapigen/parameters/parsers/builders/Builder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
package com.papsign.ktor.openapigen.parameters.parsers.generic
1+
package com.papsign.ktor.openapigen.parameters.parsers.builders
22

33
import com.papsign.ktor.openapigen.parameters.ParameterStyle
44

55
interface Builder<S> where S: ParameterStyle<S>, S: Enum<S> {
66
val style: S
7-
val exploded: Boolean
7+
val explode: Boolean
88
fun build(key: String, parameters: Map<String, List<String>>): Any?
99
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.papsign.ktor.openapigen.parameters.parsers.builders
2+
3+
import com.papsign.ktor.openapigen.parameters.ParameterStyle
4+
import kotlin.reflect.KType
5+
6+
interface BuilderFactory<out T: Builder<S>, S> where S: ParameterStyle<S>, S: Enum<S> {
7+
fun buildBuilder(type: KType, explode: Boolean): T?
8+
fun buildBuilderForced(type: KType, explode: Boolean): T = buildBuilder(type, explode) ?: error("No ${this.javaClass.declaringClass?.simpleName ?: this.javaClass.simpleName} Builder exists for type $type")
9+
}

src/main/kotlin/com/papsign/ktor/openapigen/parameters/parsers/generic/BuilderSelector.kt renamed to src/main/kotlin/com/papsign/ktor/openapigen/parameters/parsers/builders/BuilderSelector.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
package com.papsign.ktor.openapigen.parameters.parsers.generic
1+
package com.papsign.ktor.openapigen.parameters.parsers.builders
22

33
import kotlin.reflect.KType
44

55
interface BuilderSelector<out T: Builder<*>> {
6-
fun canHandle(type: KType): Boolean
6+
fun canHandle(type: KType, explode: Boolean): Boolean
77
fun create(type: KType, exploded: Boolean): T
88
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.papsign.ktor.openapigen.parameters.parsers.builders
2+
3+
import com.papsign.ktor.openapigen.parameters.ParameterStyle
4+
import kotlin.reflect.KType
5+
6+
open class BuilderSelectorFactory<out T: Builder<S>, S>(vararg val selectors: BuilderSelector<T>):
7+
BuilderFactory<T, S> where S: ParameterStyle<S>, S: Enum<S> {
8+
override fun buildBuilder(type: KType, explode: Boolean): T? {
9+
return selectors.find { it.canHandle(type, explode) }?.create(type, explode)
10+
}
11+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.papsign.ktor.openapigen.parameters.parsers.builders.query.deepobject
2+
3+
import com.papsign.kotlin.reflection.toKType
4+
import com.papsign.ktor.openapigen.parameters.parsers.builders.BuilderSelector
5+
import com.papsign.ktor.openapigen.parameters.util.ListToArray
6+
import kotlin.reflect.KType
7+
import kotlin.reflect.jvm.jvmErasure
8+
9+
class ArrayDeepBuilder(type: KType) : CollectionDeepBuilder(type) {
10+
11+
private val converter = ListToArray(type)
12+
13+
override fun transform(lst: List<Any?>): Any? {
14+
return converter.cvt(lst)
15+
}
16+
17+
companion object : BuilderSelector<ArrayDeepBuilder> {
18+
19+
override fun canHandle(type: KType, explode: Boolean): Boolean {
20+
return type.jvmErasure.java.isArray
21+
}
22+
23+
override fun create(type: KType, exploded: Boolean): ArrayDeepBuilder {
24+
return ArrayDeepBuilder(type)
25+
}
26+
}
27+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.papsign.ktor.openapigen.parameters.parsers.builders.query.deepobject
2+
3+
import com.papsign.ktor.openapigen.parameters.QueryParamStyle
4+
import com.papsign.ktor.openapigen.parameters.parsers.builders.Builder
5+
import com.papsign.ktor.openapigen.parameters.util.ListToArray
6+
import kotlin.reflect.KType
7+
8+
abstract class CollectionDeepBuilder(type: KType) : DeepBuilder {
9+
10+
private val contentType = ListToArray.arrayComponentKType(type)
11+
12+
private val builder: Builder<QueryParamStyle> by lazy {
13+
DeepBuilderFactory.buildBuilderForced(contentType, explode)
14+
}
15+
16+
abstract fun transform(lst: List<Any?>): Any?
17+
18+
override fun build(key: String, parameters: Map<String, List<String>>): Any? {
19+
val names = parameters.filterKeys { it != key && it.startsWith(key) }
20+
return transform(if (names.isEmpty()) {
21+
listOf()
22+
} else {
23+
val indices =
24+
names.entries.groupBy { (k, _) -> k.substring(key.length + 1, k.indexOf("]", key.length)).toInt() }
25+
indices.entries.fold(
26+
ArrayList<Any?>((0..(indices.keys.max() ?: 0)).map { null })
27+
) { acc, (idx, params) ->
28+
acc[idx] = builder.build("$key[$idx]", params.associate { (key, value) -> key to value })
29+
acc
30+
}
31+
})
32+
}
33+
}

0 commit comments

Comments
 (0)