Data.kt

  1. package com.hexagonkt.core

  2. import kotlin.reflect.KProperty1

  3. /**
  4.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  5.  *
  6.  * @receiver .
  7.  * @param keys .
  8.  * @return .
  9.  */
  10. inline fun <reified T : Any> Map<*, *>.getPath(vararg keys: Any): T? {
  11.     val mappedKeys = keys.map {
  12.         when (it) {
  13.             is KProperty1<*, *> -> it.name
  14.             else -> it
  15.         }
  16.     }

  17.     return mappedKeys
  18.         .dropLast(1)
  19.         .fold(this) { result, element ->
  20.             when (val value = result[element]) {
  21.                 is Map<*, *> -> value
  22.                 is Collection<*> -> value.mapIndexed { ii, item -> ii to item }.toMap()
  23.                 else -> emptyMap<Any, Any>()
  24.             }
  25.         }[mappedKeys.last()] as? T
  26. }

  27. /**
  28.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  29.  *
  30.  * @receiver .
  31.  * @param name .
  32.  * @return .
  33.  */
  34. inline fun <reified T : Any> Map<*, *>.requirePath(vararg name: Any): T =
  35.     this.getPath(*name) ?: error("$name required key not found")

  36. /**
  37.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  38.  *
  39.  * @param fields .
  40.  * @return .
  41.  */
  42. fun <T : Any> fieldsMapOf(vararg fields: Pair<KProperty1<T, *>, *>): Map<String, *> =
  43.     fields.associate { it.first.name to it.second }

  44. /**
  45.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  46.  *
  47.  * @param fields .
  48.  * @return .
  49.  */
  50. fun <T : Any> fieldsMapOfNotNull(vararg fields: Pair<KProperty1<T, *>, *>): Map<String, *> =
  51.     fieldsMapOf(*fields).filterValues { it != null }

  52. /**
  53.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  54.  *
  55.  * @param pairs .
  56.  * @return .
  57.  */
  58. fun <K : Any> mapOfNotNull(vararg pairs: Pair<K, *>): Map<K, *> =
  59.     mapOf(*pairs).filterValues { it != null }

  60. /**
  61.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  62.  *
  63.  * @receiver .
  64.  * @param name .
  65.  * @return .
  66.  */
  67. fun <K, V> Map<K, V>.require(name: K): V =
  68.     this[name] ?: error("$name required key not found")

  69. /**
  70.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  71.  *
  72.  * @param T .
  73.  * @param key .
  74.  * @return .
  75.  */
  76. inline operator fun <reified T : Any> Map<*, *>.get(key: KProperty1<*, *>): T? =
  77.     this[key.name] as? T

  78. /**
  79.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  80.  *
  81.  * @param T .
  82.  * @param key .
  83.  * @param default .
  84.  * @return .
  85.  */
  86. inline fun <reified T : Any> Map<*, *>.getOrDefault(key: KProperty1<*, *>, default: T): T =
  87.     this[key] ?: default

  88. /**
  89.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  90.  *
  91.  * Mermaid test:
  92.  * ```mermaid
  93.  * graph LR
  94.  *   A --> B
  95.  * ```
  96.  *
  97.  * @param mapA .
  98.  * @param mapB .
  99.  * @return .
  100.  */
  101. fun merge(mapA: Map<*, *>, mapB: Map<*, *>): Map<*, *> =
  102.     (mapA.entries + mapB.entries)
  103.         .groupBy { it.key }
  104.         .mapValues { (_, v) -> v.map { it.value } }
  105.         .mapValues { (_, v) ->
  106.             val isCollection = v.all { it is Collection<*> }
  107.             val isMap = v.all { it is Map<*, *> }
  108.             when {
  109.                 isCollection -> v.map { it as Collection<*> }.reduce { a, b -> a + b }
  110.                 isMap -> v.map { it as Map<*, *> }.reduce { a, b -> merge(a, b) }
  111.                 else -> v.last()
  112.             }
  113.         }

  114. /**
  115.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  116.  *
  117.  * @param maps .
  118.  * @return .
  119.  */
  120. fun merge(maps: Collection<Map<*, *>>): Map<*, *> =
  121.     maps.reduce { a, b -> merge(a, b) }

  122. /**
  123.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  124.  *
  125.  * @receiver .
  126.  * @return .
  127.  */
  128. fun <K, V> Map<K, Collection<V>>.pairs(): Collection<Pair<K, V>> =
  129.     flatMap { (k, v) -> v.map { k to it } }

  130. /**
  131.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  132.  *
  133.  * @receiver .
  134.  * @return .
  135.  */
  136. fun <K, V> Map<K, V?>.filterNotEmpty(): Map<K, V> =
  137.     this.filterValues(::notEmpty).mapValues { (_, v) -> v ?: fail }

  138. /**
  139.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  140.  *
  141.  * @receiver .
  142.  * @return .
  143.  */
  144. fun <V> Collection<V?>.filterNotEmpty(): Collection<V> =
  145.     this.filter(::notEmpty).map { it ?: fail }

  146. /**
  147.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  148.  *
  149.  * @receiver .
  150.  * @return .
  151.  */
  152. fun Map<*, *>.filterNotEmptyRecursive(): Map<*, *> =
  153.     mapValues { (_, v) ->
  154.         when (v) {
  155.             is Collection<*> -> v.filterNotEmptyRecursive()
  156.             is Map<*, *> -> v.filterNotEmptyRecursive()
  157.             else -> v
  158.         }
  159.     }
  160.         .filterNotEmpty()

  161. /**
  162.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  163.  *
  164.  * @receiver .
  165.  * @return .
  166.  */
  167. fun Collection<*>.filterNotEmptyRecursive(): Collection<*> =
  168.     map {
  169.         when (it) {
  170.             is Collection<*> -> it.filterNotEmptyRecursive()
  171.             is Map<*, *> -> it.filterNotEmptyRecursive()
  172.             else -> it
  173.         }
  174.     }
  175.         .filterNotEmpty()

  176. /**
  177.  * [TODO](https://github.com/hexagontk/hexagon/issues/271).
  178.  *
  179.  * @param value .
  180.  * @return .
  181.  */
  182. fun <V> notEmpty(value: V?): Boolean {
  183.     return when (value) {
  184.         null -> false
  185.         is Collection<*> -> value.isNotEmpty()
  186.         is Map<*, *> -> value.isNotEmpty()
  187.         else -> true
  188.     }
  189. }

  190. fun Map<*, *>.getInt(key: KProperty1<*, *>): Int? =
  191.     get(key)

  192. fun Map<*, *>.getLong(key: KProperty1<*, *>): Long? =
  193.     get(key)

  194. fun Map<*, *>.getFloat(key: KProperty1<*, *>): Float? =
  195.     get(key)

  196. fun Map<*, *>.getDouble(key: KProperty1<*, *>): Double? =
  197.     get(key)

  198. fun Map<*, *>.getBoolean(key: KProperty1<*, *>): Boolean? =
  199.     get(key)

  200. fun Map<*, *>.getString(key: KProperty1<*, *>): String? =
  201.     get(key)

  202. fun Map<*, *>.getList(key: KProperty1<*, *>): Collection<*>? =
  203.     get(key)

  204. fun Map<*, *>.getMap(key: KProperty1<*, *>): Map<String, *>? =
  205.     get(key)

  206. fun Map<*, *>.getInts(key: KProperty1<*, *>): Collection<Int>? =
  207.     get(key)

  208. fun Map<*, *>.getLongs(key: KProperty1<*, *>): Collection<Long>? =
  209.     get(key)

  210. fun Map<*, *>.getFloats(key: KProperty1<*, *>): Collection<Float>? =
  211.     get(key)

  212. fun Map<*, *>.getDoubles(key: KProperty1<*, *>): Collection<Double>? =
  213.     get(key)

  214. fun Map<*, *>.getBooleans(key: KProperty1<*, *>): Collection<Boolean>? =
  215.     get(key)

  216. fun Map<*, *>.getStrings(key: KProperty1<*, *>): Collection<String>? =
  217.     get(key)

  218. fun Map<*, *>.getLists(key: KProperty1<*, *>): Collection<List<*>>? =
  219.     get(key)

  220. fun Map<*, *>.getMaps(key: KProperty1<*, *>): Collection<Map<String, *>>? =
  221.     get(key)

  222. fun Map<*, *>.getListOrEmpty(key: KProperty1<*, *>): Collection<*> =
  223.     getList(key) ?: emptyList<Any>()

  224. fun Map<*, *>.getMapOrEmpty(key: KProperty1<*, *>): Map<String, *> =
  225.     getMap(key) ?: emptyMap<String, Any>()

  226. fun Map<*, *>.getIntsOrEmpty(key: KProperty1<*, *>): Collection<Int> =
  227.     getInts(key) ?: emptyList()

  228. fun Map<*, *>.getLongsOrEmpty(key: KProperty1<*, *>): Collection<Long> =
  229.     getLongs(key) ?: emptyList()

  230. fun Map<*, *>.getFloatsOrEmpty(key: KProperty1<*, *>): Collection<Float> =
  231.     getFloats(key) ?: emptyList()

  232. fun Map<*, *>.getDoublesOrEmpty(key: KProperty1<*, *>): Collection<Double> =
  233.     getDoubles(key) ?: emptyList()

  234. fun Map<*, *>.getBooleansOrEmpty(key: KProperty1<*, *>): Collection<Boolean> =
  235.     getBooleans(key) ?: emptyList()

  236. fun Map<*, *>.getStringsOrEmpty(key: KProperty1<*, *>): Collection<String> =
  237.     getStrings(key) ?: emptyList()

  238. fun Map<*, *>.getListsOrEmpty(key: KProperty1<*, *>): Collection<Collection<*>> =
  239.     getLists(key) ?: emptyList()

  240. fun Map<*, *>.getMapsOrEmpty(key: KProperty1<*, *>): Collection<Map<String, *>> =
  241.     getMaps(key) ?: emptyList()

  242. inline fun <reified T : Any> Map<*, *>.requireKey(key: KProperty1<*, *>): T =
  243.     get(key)
  244.         ?: error("'${key.name}' key not found, or wrong type (must be ${T::class.qualifiedName})")

  245. fun Map<*, *>.requireInt(key: KProperty1<*, *>): Int =
  246.     requireKey(key)

  247. fun Map<*, *>.requireLong(key: KProperty1<*, *>): Long =
  248.     requireKey(key)

  249. fun Map<*, *>.requireFloat(key: KProperty1<*, *>): Float =
  250.     requireKey(key)

  251. fun Map<*, *>.requireDouble(key: KProperty1<*, *>): Double =
  252.     requireKey(key)

  253. fun Map<*, *>.requireBoolean(key: KProperty1<*, *>): Boolean =
  254.     requireKey(key)

  255. fun Map<*, *>.requireString(key: KProperty1<*, *>): String =
  256.     requireKey(key)

  257. fun Map<*, *>.requireList(key: KProperty1<*, *>): Collection<*> =
  258.     requireKey(key)

  259. fun Map<*, *>.requireMap(key: KProperty1<*, *>): Map<String, *> =
  260.     requireKey(key)

  261. fun Map<*, *>.requireInts(key: KProperty1<*, *>): List<Int> =
  262.     requireKey(key)

  263. fun Map<*, *>.requireLongs(key: KProperty1<*, *>): Collection<Long> =
  264.     requireKey(key)

  265. fun Map<*, *>.requireFloats(key: KProperty1<*, *>): Collection<Float> =
  266.     requireKey(key)

  267. fun Map<*, *>.requireDoubles(key: KProperty1<*, *>): Collection<Double> =
  268.     requireKey(key)

  269. fun Map<*, *>.requireBooleans(key: KProperty1<*, *>): Collection<Boolean> =
  270.     requireKey(key)

  271. fun Map<*, *>.requireStrings(key: KProperty1<*, *>): Collection<String> =
  272.     requireKey(key)

  273. fun Map<*, *>.requireLists(key: KProperty1<*, *>): Collection<Collection<*>> =
  274.     requireKey(key)

  275. fun Map<*, *>.requireMaps(key: KProperty1<*, *>): Collection<Map<String, *>> =
  276.     requireKey(key)