From d0ce0d7bc92aee45cc9345b6611357ff63846466 Mon Sep 17 00:00:00 2001 From: uchuhimo Date: Tue, 24 Dec 2019 18:36:56 +0800 Subject: [PATCH] feature: support more types of keys in source --- .../kotlin/com/uchuhimo/konf/source/Source.kt | 40 +++++++++++++++---- .../uchuhimo/konf/source/SourceException.kt | 2 +- .../com/uchuhimo/konf/source/ConfigForLoad.kt | 1 + .../konf/source/MergedSourceLoadSpec.kt | 6 +++ .../uchuhimo/konf/source/SourceLoadSpec.kt | 6 +++ .../com/uchuhimo/konf/source/SourceSpec.kt | 2 +- .../konf/source/base/FlatSourceLoadSpec.kt | 3 ++ .../konf/source/base/MapSourceLoadSpec.kt | 4 ++ .../src/test/resources/source/source.json | 5 +++ .../test/resources/source/source.properties | 3 ++ .../src/test/resources/source/source.conf | 1 + konf-js/src/test/resources/source/source.js | 5 +++ .../src/test/resources/source/source.toml | 1 + konf-xml/src/test/resources/source/source.xml | 12 ++++++ .../konf/source/yaml/YamlSourceLoadSpec.kt | 22 +++++++++- .../src/test/resources/source/source.yaml | 4 ++ 16 files changed, 107 insertions(+), 10 deletions(-) diff --git a/konf-core/src/main/kotlin/com/uchuhimo/konf/source/Source.kt b/konf-core/src/main/kotlin/com/uchuhimo/konf/source/Source.kt index 20096fa8..87c4b716 100644 --- a/konf-core/src/main/kotlin/com/uchuhimo/konf/source/Source.kt +++ b/konf-core/src/main/kotlin/com/uchuhimo/konf/source/Source.kt @@ -800,6 +800,9 @@ private fun TreeNode.castOrNull(source: Source, clazz: Class): T? { } } +private val promotedFromStringTypes = promoteMap.getValue(String::class).map { it.first } +private val promotedFromStringMap = promoteMap.getValue(String::class).toMap() + private fun TreeNode.toValue(source: Source, type: JavaType, mapper: ObjectMapper): Any { if (this is ValueNode && type == TypeFactory.defaultInstance().constructType(value::class.java)) { @@ -874,6 +877,14 @@ private fun TreeNode.toValue(source: Source, type: JavaType, mapper: ObjectMappe value.toValue(source, type.contentType, mapper) }) } + } else if (type.keyType.rawClass.kotlin in promotedFromStringTypes) { + val promoteFunc = promotedFromStringMap.getValue(type.keyType.rawClass.kotlin) + @Suppress("UNCHECKED_CAST") + return (implOf(type.rawClass).getDeclaredConstructor().newInstance() as MutableMap).apply { + putAll(this@toValue.toMap(source).map { (key, value) -> + promoteFunc(key, source)!! to value.toValue(source, type.contentType, mapper) + }) + } } else { throw UnsupportedMapKeyException(type.keyType.rawClass) } @@ -970,13 +981,28 @@ fun Any.asTree(): TreeNode = @Suppress("UNCHECKED_CAST") (ListSourceNode((this as List).map { it.asTree() })) is Map<*, *> -> { - if (this.size != 0 && this.keys.toList()[0] !is String && this.keys.toList()[0] !is Int) { - ValueSourceNode(this) - } else { - @Suppress("UNCHECKED_CAST") - (ContainerNode((this as Map).map { (key, value) -> - key.toString() to value.asTree() - }.toMap().toMutableMap())) + when { + this.size == 0 -> ContainerNode(mutableMapOf()) + this.iterator().next().key is String -> { + @Suppress("UNCHECKED_CAST") + (ContainerNode((this as Map).mapValues { (_, value) -> + value.asTree() + }.toMutableMap())) + } + this.iterator().next().key!!::class in listOf( + Char::class, + Byte::class, + Short::class, + Int::class, + Long::class, + BigInteger::class + ) -> { + @Suppress("UNCHECKED_CAST") + (ContainerNode((this as Map).map { (key, value) -> + key.toString() to value.asTree() + }.toMap().toMutableMap())) + } + else -> ValueSourceNode(this) } } else -> ValueSourceNode(this) diff --git a/konf-core/src/main/kotlin/com/uchuhimo/konf/source/SourceException.kt b/konf-core/src/main/kotlin/com/uchuhimo/konf/source/SourceException.kt index afd67d85..412b281e 100644 --- a/konf-core/src/main/kotlin/com/uchuhimo/konf/source/SourceException.kt +++ b/konf-core/src/main/kotlin/com/uchuhimo/konf/source/SourceException.kt @@ -77,7 +77,7 @@ class ObjectMappingException(source: String, clazz: Class<*>, cause: Throwable) * Exception indicates that value of specified class is unsupported as key of map. */ class UnsupportedMapKeyException(val clazz: Class<*>) : SourceException( - "cannot support map with ${clazz.simpleName} key, only support string key") + "cannot support map with ${clazz.simpleName} key") /** * Exception indicates failure to load specified path. diff --git a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/ConfigForLoad.kt b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/ConfigForLoad.kt index cf16eb28..3c64733d 100644 --- a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/ConfigForLoad.kt +++ b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/ConfigForLoad.kt @@ -94,6 +94,7 @@ object ConfigForLoad : ConfigSpec("level1.level2") { val sortedSet by required>() val map by required>() + val intMap by required>() val sortedMap by required>() val listOfMap by required>>() diff --git a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/MergedSourceLoadSpec.kt b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/MergedSourceLoadSpec.kt index 8bafc8ca..7105325c 100644 --- a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/MergedSourceLoadSpec.kt +++ b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/MergedSourceLoadSpec.kt @@ -97,6 +97,9 @@ private val facadeContent = mapOf("level1" to mapOf("level2" to "map" to mapOf( "a" to 1, "c" to 3), + "intMap" to mapOf( + 1 to "a", + 3 to "c"), "sortedMap" to mapOf( "c" to 3, "a" to 1 @@ -194,6 +197,9 @@ private val fallbackContent = mapOf("level1" to mapOf("level2" to "map" to mapOf( "b" to 2, "c" to 3), + "intMap" to mapOf( + 2 to "b", + 3 to "c"), "sortedMap" to mapOf( "b" to 2, "a" to 1 diff --git a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/SourceLoadSpec.kt b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/SourceLoadSpec.kt index 6ec816c9..10e15c74 100644 --- a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/SourceLoadSpec.kt +++ b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/SourceLoadSpec.kt @@ -158,6 +158,8 @@ object SourceLoadSpec : SubjectSpek({ assertThat(subject[ConfigForLoad.map], equalTo(mapOf("a" to 1, "b" to 2, "c" to 3))) + assertThat(subject[ConfigForLoad.intMap], + equalTo(mapOf(1 to "a", 2 to "b", 3 to "c"))) assertThat(subject[ConfigForLoad.sortedMap], equalTo(sortedMapOf("a" to 1, "b" to 2, "c" to 3))) assertThat(subject[ConfigForLoad.sortedMap].firstKey(), equalTo("a")) @@ -345,6 +347,10 @@ private val loadContent = mapOf( "a" to 1, "b" to 2, "c" to 3), + "intMap" to mapOf( + 1 to "a", + 2 to "b", + 3 to "c"), "sortedMap" to mapOf( "c" to 3, "b" to 2, diff --git a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/SourceSpec.kt b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/SourceSpec.kt index c4a3834f..0a6a7787 100644 --- a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/SourceSpec.kt +++ b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/SourceSpec.kt @@ -506,7 +506,7 @@ object SourceSpec : Spek({ on("load map with unsupported key type") { it("should throw LoadException caused by UnsupportedMapKeyException") { assertCausedBy { - load>(mapOf(1 to "1")) + load, String>>(mapOf((1 to 1) to "1")) } } } diff --git a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/base/FlatSourceLoadSpec.kt b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/base/FlatSourceLoadSpec.kt index 7915dbc7..20952fa3 100644 --- a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/base/FlatSourceLoadSpec.kt +++ b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/base/FlatSourceLoadSpec.kt @@ -134,6 +134,9 @@ private val loadContent = mapOf( "map.a" to "1", "map.b" to "2", "map.c" to "3", + "intMap.1" to "a", + "intMap.2" to "b", + "intMap.3" to "c", "sortedMap.c" to "3", "sortedMap.b" to "2", "sortedMap.a" to "1", diff --git a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/base/MapSourceLoadSpec.kt b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/base/MapSourceLoadSpec.kt index eecb2829..d6667643 100644 --- a/konf-core/src/test/kotlin/com/uchuhimo/konf/source/base/MapSourceLoadSpec.kt +++ b/konf-core/src/test/kotlin/com/uchuhimo/konf/source/base/MapSourceLoadSpec.kt @@ -141,6 +141,10 @@ private val loadContent = mapOf("level1" to mapOf("level2" to "a" to 1, "b" to 2, "c" to 3), + "intMap" to mapOf( + 1 to "a", + 2 to "b", + 3 to "c"), "sortedMap" to mapOf( "c" to 3, "b" to 2, diff --git a/konf-core/src/test/resources/source/source.json b/konf-core/src/test/resources/source/source.json index 42a1d8d9..0b821cd7 100644 --- a/konf-core/src/test/resources/source/source.json +++ b/konf-core/src/test/resources/source/source.json @@ -127,6 +127,11 @@ "b": 2, "c": 3 }, + "intMap": { + "1": "a", + "2": "b", + "3": "c" + }, "sortedMap": { "c": 3, "b": 2, diff --git a/konf-core/src/test/resources/source/source.properties b/konf-core/src/test/resources/source/source.properties index 4277b2d3..64530e8b 100644 --- a/konf-core/src/test/resources/source/source.properties +++ b/konf-core/src/test/resources/source/source.properties @@ -35,6 +35,9 @@ level1.level2.sortedSet=2,1,1,3 level1.level2.map.a=1 level1.level2.map.b=2 level1.level2.map.c=3 +level1.level2.intMap.1=a +level1.level2.intMap.2=b +level1.level2.intMap.3=c level1.level2.sortedMap.c=3 level1.level2.sortedMap.b=2 level1.level2.sortedMap.a=1 diff --git a/konf-hocon/src/test/resources/source/source.conf b/konf-hocon/src/test/resources/source/source.conf index b97b7314..0eab7fc1 100644 --- a/konf-hocon/src/test/resources/source/source.conf +++ b/konf-hocon/src/test/resources/source/source.conf @@ -60,6 +60,7 @@ level1 { sortedSet = [2, 1, 1, 3] map = {a = 1, b = 2, c = 3} + intMap = {1 = a, 2 = b, 3 = c} sortedMap = {c = 3, b = 2, a = 1} listOfMap = [ {a = 1, b = 2} diff --git a/konf-js/src/test/resources/source/source.js b/konf-js/src/test/resources/source/source.js index 6136bc69..6fd962c3 100644 --- a/konf-js/src/test/resources/source/source.js +++ b/konf-js/src/test/resources/source/source.js @@ -127,6 +127,11 @@ b: 2, c: 3 }, + intMap: { + 1: "a", + 2: "b", + 3: "c" + }, sortedMap: { c: 3, b: 2, diff --git a/konf-toml/src/test/resources/source/source.toml b/konf-toml/src/test/resources/source/source.toml index fe12dd13..5e71cce3 100644 --- a/konf-toml/src/test/resources/source/source.toml +++ b/konf-toml/src/test/resources/source/source.toml @@ -41,6 +41,7 @@ set = [1, 2, 1] sortedSet = [2, 1, 1, 3] map = { a = 1, b = 2, c = 3 } +intMap = { 1 = "a", 2 = "b", 3 = "c" } sortedMap = { c = 3, b = 2, a = 1 } nested = [[[{ a = 1 }]]] diff --git a/konf-xml/src/test/resources/source/source.xml b/konf-xml/src/test/resources/source/source.xml index 1b7086e7..f7c9d74e 100644 --- a/konf-xml/src/test/resources/source/source.xml +++ b/konf-xml/src/test/resources/source/source.xml @@ -148,6 +148,18 @@ level1.level2.map.c 3 + + level1.level2.intMap.1 + a + + + level1.level2.intMap.2 + b + + + level1.level2.intMap.3 + c + level1.level2.sortedMap.c 3 diff --git a/konf-yaml/src/test/kotlin/com/uchuhimo/konf/source/yaml/YamlSourceLoadSpec.kt b/konf-yaml/src/test/kotlin/com/uchuhimo/konf/source/yaml/YamlSourceLoadSpec.kt index 98429c4b..30853c46 100644 --- a/konf-yaml/src/test/kotlin/com/uchuhimo/konf/source/yaml/YamlSourceLoadSpec.kt +++ b/konf-yaml/src/test/kotlin/com/uchuhimo/konf/source/yaml/YamlSourceLoadSpec.kt @@ -41,7 +41,7 @@ object YamlSourceLoadSpec : SubjectSpek({ itBehavesLike(SourceLoadSpec) given("a config") { - on("load a YAML with a numeric key") { + on("load a YAML with an int key") { val config = Config().from.yaml.string(""" tree: 1: @@ -51,6 +51,26 @@ object YamlSourceLoadSpec : SubjectSpek({ assertTrue { config.at("tree.1.myVal").toValue() } } } + on("load a YAML with a long key") { + val config = Config().from.yaml.string(""" + tree: + 2147483648: + myVal: true + """.trimIndent()) + it("should treat it as a string key") { + assertTrue { config.at("tree.2147483648.myVal").toValue() } + } + } + on("load a YAML with a BigInteger key") { + val config = Config().from.yaml.string(""" + tree: + 9223372036854775808: + myVal: true + """.trimIndent()) + it("should treat it as a string key") { + assertTrue { config.at("tree.9223372036854775808.myVal").toValue() } + } + } } }) diff --git a/konf-yaml/src/test/resources/source/source.yaml b/konf-yaml/src/test/resources/source/source.yaml index 631fbd27..c9238f32 100644 --- a/konf-yaml/src/test/resources/source/source.yaml +++ b/konf-yaml/src/test/resources/source/source.yaml @@ -66,6 +66,10 @@ level1: a: 1 b: 2 c: 3 + intMap: + 1: a + 2: b + 3: c sortedMap: { c: 3, b: 2, a: 1 } listOfMap: - a: 1