Data.kt
package com.hexagonkt.core
import kotlin.reflect.KProperty1
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @receiver .
* @param keys .
* @return .
*/
inline fun <reified T : Any> Map<*, *>.getPath(vararg keys: Any): T? {
val mappedKeys = keys.map {
when (it) {
is KProperty1<*, *> -> it.name
else -> it
}
}
return mappedKeys
.dropLast(1)
.fold(this) { result, element ->
when (val value = result[element]) {
is Map<*, *> -> value
is Collection<*> -> value.mapIndexed { ii, item -> ii to item }.toMap()
else -> emptyMap<Any, Any>()
}
}[mappedKeys.last()] as? T
}
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @receiver .
* @param name .
* @return .
*/
inline fun <reified T : Any> Map<*, *>.requirePath(vararg name: Any): T =
this.getPath(*name) ?: error("$name required key not found")
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @param fields .
* @return .
*/
fun <T : Any> fieldsMapOf(vararg fields: Pair<KProperty1<T, *>, *>): Map<String, *> =
fields.associate { it.first.name to it.second }
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @param fields .
* @return .
*/
fun <T : Any> fieldsMapOfNotNull(vararg fields: Pair<KProperty1<T, *>, *>): Map<String, *> =
fieldsMapOf(*fields).filterValues { it != null }
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @param pairs .
* @return .
*/
fun <K : Any> mapOfNotNull(vararg pairs: Pair<K, *>): Map<K, *> =
mapOf(*pairs).filterValues { it != null }
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @receiver .
* @param name .
* @return .
*/
fun <K, V> Map<K, V>.require(name: K): V =
this[name] ?: error("$name required key not found")
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @param T .
* @param key .
* @return .
*/
inline operator fun <reified T : Any> Map<*, *>.get(key: KProperty1<*, *>): T? =
this[key.name] as? T
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @param T .
* @param key .
* @param default .
* @return .
*/
inline fun <reified T : Any> Map<*, *>.getOrDefault(key: KProperty1<*, *>, default: T): T =
this[key] ?: default
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* Mermaid test:
* ```mermaid
* graph LR
* A --> B
* ```
*
* @param mapA .
* @param mapB .
* @return .
*/
fun merge(mapA: Map<*, *>, mapB: Map<*, *>): Map<*, *> =
(mapA.entries + mapB.entries)
.groupBy { it.key }
.mapValues { (_, v) -> v.map { it.value } }
.mapValues { (_, v) ->
val isCollection = v.all { it is Collection<*> }
val isMap = v.all { it is Map<*, *> }
when {
isCollection -> v.map { it as Collection<*> }.reduce { a, b -> a + b }
isMap -> v.map { it as Map<*, *> }.reduce { a, b -> merge(a, b) }
else -> v.last()
}
}
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @param maps .
* @return .
*/
fun merge(maps: Collection<Map<*, *>>): Map<*, *> =
maps.reduce { a, b -> merge(a, b) }
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @receiver .
* @return .
*/
fun <K, V> Map<K, Collection<V>>.pairs(): Collection<Pair<K, V>> =
flatMap { (k, v) -> v.map { k to it } }
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @receiver .
* @return .
*/
fun <K, V> Map<K, V?>.filterNotEmpty(): Map<K, V> =
this.filterValues(::notEmpty).mapValues { (_, v) -> v ?: fail }
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @receiver .
* @return .
*/
fun <V> Collection<V?>.filterNotEmpty(): Collection<V> =
this.filter(::notEmpty).map { it ?: fail }
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @receiver .
* @return .
*/
fun Map<*, *>.filterNotEmptyRecursive(): Map<*, *> =
mapValues { (_, v) ->
when (v) {
is Collection<*> -> v.filterNotEmptyRecursive()
is Map<*, *> -> v.filterNotEmptyRecursive()
else -> v
}
}
.filterNotEmpty()
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @receiver .
* @return .
*/
fun Collection<*>.filterNotEmptyRecursive(): Collection<*> =
map {
when (it) {
is Collection<*> -> it.filterNotEmptyRecursive()
is Map<*, *> -> it.filterNotEmptyRecursive()
else -> it
}
}
.filterNotEmpty()
/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
* @param value .
* @return .
*/
fun <V> notEmpty(value: V?): Boolean {
return when (value) {
null -> false
is Collection<*> -> value.isNotEmpty()
is Map<*, *> -> value.isNotEmpty()
else -> true
}
}
fun Map<*, *>.getInt(key: KProperty1<*, *>): Int? =
get(key)
fun Map<*, *>.getLong(key: KProperty1<*, *>): Long? =
get(key)
fun Map<*, *>.getFloat(key: KProperty1<*, *>): Float? =
get(key)
fun Map<*, *>.getDouble(key: KProperty1<*, *>): Double? =
get(key)
fun Map<*, *>.getBoolean(key: KProperty1<*, *>): Boolean? =
get(key)
fun Map<*, *>.getString(key: KProperty1<*, *>): String? =
get(key)
fun Map<*, *>.getList(key: KProperty1<*, *>): Collection<*>? =
get(key)
fun Map<*, *>.getMap(key: KProperty1<*, *>): Map<String, *>? =
get(key)
fun Map<*, *>.getInts(key: KProperty1<*, *>): Collection<Int>? =
get(key)
fun Map<*, *>.getLongs(key: KProperty1<*, *>): Collection<Long>? =
get(key)
fun Map<*, *>.getFloats(key: KProperty1<*, *>): Collection<Float>? =
get(key)
fun Map<*, *>.getDoubles(key: KProperty1<*, *>): Collection<Double>? =
get(key)
fun Map<*, *>.getBooleans(key: KProperty1<*, *>): Collection<Boolean>? =
get(key)
fun Map<*, *>.getStrings(key: KProperty1<*, *>): Collection<String>? =
get(key)
fun Map<*, *>.getLists(key: KProperty1<*, *>): Collection<List<*>>? =
get(key)
fun Map<*, *>.getMaps(key: KProperty1<*, *>): Collection<Map<String, *>>? =
get(key)
fun Map<*, *>.getListOrEmpty(key: KProperty1<*, *>): Collection<*> =
getList(key) ?: emptyList<Any>()
fun Map<*, *>.getMapOrEmpty(key: KProperty1<*, *>): Map<String, *> =
getMap(key) ?: emptyMap<String, Any>()
fun Map<*, *>.getIntsOrEmpty(key: KProperty1<*, *>): Collection<Int> =
getInts(key) ?: emptyList()
fun Map<*, *>.getLongsOrEmpty(key: KProperty1<*, *>): Collection<Long> =
getLongs(key) ?: emptyList()
fun Map<*, *>.getFloatsOrEmpty(key: KProperty1<*, *>): Collection<Float> =
getFloats(key) ?: emptyList()
fun Map<*, *>.getDoublesOrEmpty(key: KProperty1<*, *>): Collection<Double> =
getDoubles(key) ?: emptyList()
fun Map<*, *>.getBooleansOrEmpty(key: KProperty1<*, *>): Collection<Boolean> =
getBooleans(key) ?: emptyList()
fun Map<*, *>.getStringsOrEmpty(key: KProperty1<*, *>): Collection<String> =
getStrings(key) ?: emptyList()
fun Map<*, *>.getListsOrEmpty(key: KProperty1<*, *>): Collection<Collection<*>> =
getLists(key) ?: emptyList()
fun Map<*, *>.getMapsOrEmpty(key: KProperty1<*, *>): Collection<Map<String, *>> =
getMaps(key) ?: emptyList()
inline fun <reified T : Any> Map<*, *>.requireKey(key: KProperty1<*, *>): T =
get(key)
?: error("'${key.name}' key not found, or wrong type (must be ${T::class.qualifiedName})")
fun Map<*, *>.requireInt(key: KProperty1<*, *>): Int =
requireKey(key)
fun Map<*, *>.requireLong(key: KProperty1<*, *>): Long =
requireKey(key)
fun Map<*, *>.requireFloat(key: KProperty1<*, *>): Float =
requireKey(key)
fun Map<*, *>.requireDouble(key: KProperty1<*, *>): Double =
requireKey(key)
fun Map<*, *>.requireBoolean(key: KProperty1<*, *>): Boolean =
requireKey(key)
fun Map<*, *>.requireString(key: KProperty1<*, *>): String =
requireKey(key)
fun Map<*, *>.requireList(key: KProperty1<*, *>): Collection<*> =
requireKey(key)
fun Map<*, *>.requireMap(key: KProperty1<*, *>): Map<String, *> =
requireKey(key)
fun Map<*, *>.requireInts(key: KProperty1<*, *>): List<Int> =
requireKey(key)
fun Map<*, *>.requireLongs(key: KProperty1<*, *>): Collection<Long> =
requireKey(key)
fun Map<*, *>.requireFloats(key: KProperty1<*, *>): Collection<Float> =
requireKey(key)
fun Map<*, *>.requireDoubles(key: KProperty1<*, *>): Collection<Double> =
requireKey(key)
fun Map<*, *>.requireBooleans(key: KProperty1<*, *>): Collection<Boolean> =
requireKey(key)
fun Map<*, *>.requireStrings(key: KProperty1<*, *>): Collection<String> =
requireKey(key)
fun Map<*, *>.requireLists(key: KProperty1<*, *>): Collection<Collection<*>> =
requireKey(key)
fun Map<*, *>.requireMaps(key: KProperty1<*, *>): Collection<Map<String, *>> =
requireKey(key)