Skip to content

Commit

Permalink
feature: support more types of keys in source
Browse files Browse the repository at this point in the history
  • Loading branch information
uchuhimo committed Dec 24, 2019
1 parent b64b12a commit d0ce0d7
Show file tree
Hide file tree
Showing 16 changed files with 107 additions and 10 deletions.
40 changes: 33 additions & 7 deletions konf-core/src/main/kotlin/com/uchuhimo/konf/source/Source.kt
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,9 @@ private fun <T : Any> TreeNode.castOrNull(source: Source, clazz: Class<T>): 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)) {
Expand Down Expand Up @@ -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<Any, Any>).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)
}
Expand Down Expand Up @@ -970,13 +981,28 @@ fun Any.asTree(): TreeNode =
@Suppress("UNCHECKED_CAST")
(ListSourceNode((this as List<Any>).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<Any, Any>).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<String, Any>).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<Any, Any>).map { (key, value) ->
key.toString() to value.asTree()
}.toMap().toMutableMap()))
}
else -> ValueSourceNode(this)
}
}
else -> ValueSourceNode(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ object ConfigForLoad : ConfigSpec("level1.level2") {
val sortedSet by required<SortedSet<Int>>()

val map by required<Map<String, Int>>()
val intMap by required<Map<Int, String>>()
val sortedMap by required<SortedMap<String, Int>>()
val listOfMap by required<List<Map<String, Int>>>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ object SourceLoadSpec : SubjectSpek<Config>({

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"))
Expand Down Expand Up @@ -345,6 +347,10 @@ private val loadContent = mapOf<String, Any>(
"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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ object SourceSpec : Spek({
on("load map with unsupported key type") {
it("should throw LoadException caused by UnsupportedMapKeyException") {
assertCausedBy<UnsupportedMapKeyException> {
load<Map<Int, String>>(mapOf(1 to "1"))
load<Map<Pair<Int, Int>, String>>(mapOf((1 to 1) to "1"))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions konf-core/src/test/resources/source/source.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@
"b": 2,
"c": 3
},
"intMap": {
"1": "a",
"2": "b",
"3": "c"
},
"sortedMap": {
"c": 3,
"b": 2,
Expand Down
3 changes: 3 additions & 0 deletions konf-core/src/test/resources/source/source.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions konf-hocon/src/test/resources/source/source.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
5 changes: 5 additions & 0 deletions konf-js/src/test/resources/source/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@
b: 2,
c: 3
},
intMap: {
1: "a",
2: "b",
3: "c"
},
sortedMap: {
c: 3,
b: 2,
Expand Down
1 change: 1 addition & 0 deletions konf-toml/src/test/resources/source/source.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }]]]
Expand Down
12 changes: 12 additions & 0 deletions konf-xml/src/test/resources/source/source.xml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,18 @@
<name>level1.level2.map.c</name>
<value>3</value>
</property>
<property>
<name>level1.level2.intMap.1</name>
<value>a</value>
</property>
<property>
<name>level1.level2.intMap.2</name>
<value>b</value>
</property>
<property>
<name>level1.level2.intMap.3</name>
<value>c</value>
</property>
<property>
<name>level1.level2.sortedMap.c</name>
<value>3</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ object YamlSourceLoadSpec : SubjectSpek<Config>({
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:
Expand All @@ -51,6 +51,26 @@ object YamlSourceLoadSpec : SubjectSpek<Config>({
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() }
}
}
}
})

Expand Down
4 changes: 4 additions & 0 deletions konf-yaml/src/test/resources/source/source.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit d0ce0d7

Please sign in to comment.