Merge remote-tracking branch 'open/master' into os-merge-244167d

# Conflicts:
#	docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
#	docs/source/example-code/src/main/kotlin/net/corda/docs/LaunchSpaceshipFlow.kt
#	docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/tearoffs/TutorialTearOffs.kt
#	docs/source/running-a-node.rst
#	experimental/behave/build.gradle
#	experimental/behave/src/scenario/kotlin/net/corda/behave/scenarios/helpers/Substeps.kt
#	experimental/kryo-hook/build.gradle
#	experimental/quasar-hook/build.gradle
#	node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt
#	node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt
#	node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java
#	node/src/test/kotlin/net/corda/node/internal/NodeTest.kt
#	node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt
#	node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt
#	samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt
#	samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt
This commit is contained in:
Shams Asari
2018-07-04 18:38:34 +01:00
100 changed files with 713 additions and 582 deletions

View File

@ -35,6 +35,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
@ -42,6 +44,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
@ -50,7 +63,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 ->

View File

@ -237,6 +237,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,
@ -320,6 +350,7 @@ class ConfigParsingTest {
require(positive > 0) { "$positive is not positive" }
}
}
data class OldData(
@OldConfig("oldValue")
val newValue: String)