mirror of
https://github.com/corda/corda.git
synced 2024-12-23 14:52:29 +00:00
Custom config parser for more complex config structure (#3513)
* custom config parser for more complex config structure * address PR issues
This commit is contained in:
parent
d634cdcbac
commit
68d0826563
node-api/src
main/kotlin/net/corda/nodeapi/internal/config
test/kotlin/net/corda/nodeapi/internal/config
@ -25,6 +25,8 @@ import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createInstance
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
import kotlin.reflect.jvm.jvmErasure
|
||||
@ -32,6 +34,17 @@ import kotlin.reflect.jvm.jvmErasure
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
annotation class OldConfig(val value: String)
|
||||
|
||||
/**
|
||||
* This annotation can be used to provide ConfigParser for the class,
|
||||
* the [parseAs] method will use the provided parser instead of data class constructs to parse the object.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class CustomConfigParser(val parser: KClass<out ConfigParser<*>>)
|
||||
|
||||
interface ConfigParser<T> {
|
||||
fun parse(config: Config): T
|
||||
}
|
||||
|
||||
const val CUSTOM_NODE_PROPERTIES_ROOT = "custom"
|
||||
|
||||
// TODO Move other config parsing to use parseAs and remove this
|
||||
@ -40,7 +53,10 @@ operator fun <T : Any> Config.getValue(receiver: Any, metadata: KProperty<*>): T
|
||||
}
|
||||
|
||||
fun <T : Any> Config.parseAs(clazz: KClass<T>, onUnknownKeys: ((Set<String>, logger: Logger) -> Unit) = UnknownConfigKeysPolicy.FAIL::handle, nestedPath: String? = null): T {
|
||||
require(clazz.isData) { "Only Kotlin data classes can be parsed. Offending: ${clazz.qualifiedName}" }
|
||||
// Use custom parser if provided, instead of treating the object as data class.
|
||||
clazz.findAnnotation<CustomConfigParser>()?.let { return uncheckedCast(it.parser.createInstance().parse(this)) }
|
||||
|
||||
require(clazz.isData) { "Only Kotlin data classes or class annotated with CustomConfigParser can be parsed. Offending: ${clazz.qualifiedName}" }
|
||||
val constructor = clazz.primaryConstructor!!
|
||||
val parameters = constructor.parameters
|
||||
val parameterNames = parameters.flatMap { param ->
|
||||
|
@ -227,6 +227,36 @@ class ConfigParsingTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse with provided parser`() {
|
||||
val type1Config = mapOf("type" to "1", "value" to "type 1 value")
|
||||
val type2Config = mapOf("type" to "2", "value" to "type 2 value")
|
||||
|
||||
val configuration = config("values" to listOf(type1Config, type2Config))
|
||||
val objects = configuration.parseAs<TestObjects>()
|
||||
|
||||
assertThat(objects.values).containsExactly(TestObject.Type1("type 1 value"), TestObject.Type2("type 2 value"))
|
||||
}
|
||||
|
||||
class TestParser : ConfigParser<TestObject> {
|
||||
override fun parse(config: Config): TestObject {
|
||||
val type = config.getInt("type")
|
||||
return when (type) {
|
||||
1 -> config.parseAs<TestObject.Type1>(onUnknownKeys = UnknownConfigKeysPolicy.IGNORE::handle)
|
||||
2 -> config.parseAs<TestObject.Type2>(onUnknownKeys = UnknownConfigKeysPolicy.IGNORE::handle)
|
||||
else -> throw IllegalArgumentException("Unsupported Object type : '$type'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class TestObjects(val values: List<TestObject>)
|
||||
|
||||
@CustomConfigParser(TestParser::class)
|
||||
sealed class TestObject {
|
||||
data class Type1(val value: String) : TestObject()
|
||||
data class Type2(val value: String) : TestObject()
|
||||
}
|
||||
|
||||
private inline fun <reified S : SingleData<V>, reified L : ListData<V>, V : Any> testPropertyType(
|
||||
value1: V,
|
||||
value2: V,
|
||||
@ -310,6 +340,7 @@ class ConfigParsingTest {
|
||||
require(positive > 0) { "$positive is not positive" }
|
||||
}
|
||||
}
|
||||
|
||||
data class OldData(
|
||||
@OldConfig("oldValue")
|
||||
val newValue: String)
|
||||
|
Loading…
Reference in New Issue
Block a user