Skip to content

Commit

Permalink
✨ Support vararg and default parameters for filter runtime (#560)
Browse files Browse the repository at this point in the history
  • Loading branch information
devkanro authored Nov 24, 2023
1 parent 9948963 commit b0df615
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.bybutter.sisyphus.reflect

import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.KType
import kotlin.reflect.full.extensionReceiverParameter
import kotlin.reflect.full.valueParameters

fun KFunction<*>.resolveArguments(args: List<Any?>): Map<KParameter, Any?>? {
val requiredParameters = mutableListOf<KParameter>()
val optionalParameters = mutableListOf<KParameter>()
val result = mutableMapOf<KParameter, Any?>()
var index = 0

extensionReceiverParameter?.let {
requiredParameters += it
}

valueParameters.forEach {
if (it.isOptional || it.isVararg) {
optionalParameters += it
} else {
requiredParameters += optionalParameters
optionalParameters.clear()
requiredParameters += it
}
}

for (it in requiredParameters) {
if (index >= args.size) return null

if (!it.accept(args[index])) {
return null
}

result[it] = args[index]
index++
}

for (it in optionalParameters) {
if (index >= args.size) break

if (it.isVararg) {
val vararg = mutableListOf<Any?>()
while (index < args.size) {
if (!it.accept(args[index])) {
continue
}
vararg += args[index]
index++
}
val varargCollection = vararg as java.util.Collection<Any?>
result[it] =
varargCollection.toArray(
java.lang.reflect.Array.newInstance(
(it.type.arguments.first().type!!.classifier as KClass<*>).java,
0,
) as Array<*>,
)
} else {
if (!it.accept(args[index])) {
break
}
result[it] = args[index]
index++
}
}

return result
}

private fun KParameter.accept(value: Any?): Boolean =
if (isVararg) {
type.arguments.first().type?.accept(value)!!
} else {
type.accept(value)
}

private fun KType.accept(value: Any?): Boolean {
if (value == null) {
return this.isMarkedNullable
}
return (this.classifier as KClass<*>).isInstance(value)
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package com.bybutter.sisyphus.middleware.jdbc.support.proto.filter

import com.bybutter.sisyphus.reflect.resolveArguments
import java.lang.reflect.InvocationTargetException
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.full.extensionReceiverParameter
import kotlin.reflect.full.instanceParameter
import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.valueParameters
import kotlin.reflect.jvm.javaMethod

open class FilterRuntime(private val std: FilterStandardLibrary = FilterStandardLibrary()) {
Expand All @@ -26,9 +23,11 @@ open class FilterRuntime(private val std: FilterStandardLibrary = FilterStandard
): Any? {
return invokeOrDefault(function, arguments) {
throw NoSuchMethodException(
"Can't find function '$function(${arguments.joinToString(
", ",
) { it?.javaClass?.canonicalName ?: "null" }})' in filter standard library.",
"Can't find function '$function(${
arguments.joinToString(
", ",
) { it?.javaClass?.canonicalName ?: "null" }
})' in filter standard library.",
)
}
}
Expand All @@ -38,42 +37,27 @@ open class FilterRuntime(private val std: FilterStandardLibrary = FilterStandard
arguments: List<Any?>,
block: () -> Any?,
): Any? {
val func =
memberFunctions[function]?.firstOrNull {
it.compatibleWith(arguments)
val (func, args) =
memberFunctions[function]?.firstNotNullOfOrNull { func ->
func.resolveArguments(arguments)?.let {
func to it
}
} ?: return block()

return try {
if (func.instanceParameter == null) {
func.call(*arguments.toTypedArray())
func.callBy(args)
} else {
func.call(std, *arguments.toTypedArray())
func.callBy(
args.toMutableMap().apply {
this[func.instanceParameter!!] = std
},
)
}
} catch (ex: InvocationTargetException) {
throw ex.cause ?: ex
} catch (e: Exception) {
throw e
}
}

private fun KFunction<*>.compatibleWith(arguments: List<Any?>): Boolean {
return compatibleWith(
listOfNotNull(this.extensionReceiverParameter) + this.valueParameters,
arguments,
)
}

private fun KFunction<*>.compatibleWith(
parameters: List<KParameter>,
arguments: List<Any?>,
): Boolean {
if (parameters.size != arguments.size) return false
for ((index, parameter) in parameters.withIndex()) {
val type = arguments[index]?.javaClass
if (type == null && parameter.type.isMarkedNullable) continue
if (type == null) return false
if (!(parameter.type.classifier as KClass<*>).isInstance(arguments[index])) return false
}

return true
}
}

0 comments on commit b0df615

Please sign in to comment.