Skip to content

Commit

Permalink
Merge pull request #618 from hexagonkt/develop
Browse files Browse the repository at this point in the history
Minor improvements
  • Loading branch information
jaguililla authored Apr 15, 2023
2 parents b77cefe + af81b06 commit 4dc8a3f
Show file tree
Hide file tree
Showing 101 changed files with 1,119 additions and 2,581 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ plugins {
id("org.jetbrains.dokka") version("1.8.10")
id("com.github.jk1.dependency-license-report") version("2.1")
id("org.jetbrains.kotlinx.binary-compatibility-validator") version("0.13.0")
id("org.graalvm.buildtools.native") version("0.9.21") apply(false)
id("io.gitlab.arturbosch.detekt") version("1.22.0") apply(false)
id("me.champeau.jmh") version("0.7.0") apply(false)
}
Expand Down
4 changes: 0 additions & 4 deletions core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ application), you can import it with the following code:
# Package com.hexagonkt.core
JVM information and other useful utilities.

## Classpath URLs in native images
To use the 'classpath' URL scheme on GraalVM native images, the `native-image` command requires to
add the Classpath handler manually with the `--enable-url-protocols=classpath` parameter.

# Package com.hexagonkt.core.logging
Provides a logging management capabilities abstracting the application from logging libraries.

Expand Down
1 change: 1 addition & 0 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {
apply(from = "../gradle/kotlin.gradle")
apply(from = "../gradle/publish.gradle")
apply(from = "../gradle/dokka.gradle")
apply(from = "../gradle/native.gradle")

description = "Hexagon core utilities. Includes logging helpers."

Expand Down
282 changes: 265 additions & 17 deletions core/src/main/kotlin/com/hexagonkt/core/Data.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import kotlin.reflect.KProperty1
* @param keys .
* @return .
*/
@Suppress("UNCHECKED_CAST")
inline fun <reified T : Any> Map<*, *>.keys(vararg keys: Any): T? {

inline fun <reified T : Any> Map<*, *>.getPath(vararg keys: Any): T? {
val mappedKeys = keys.map {
when (it) {
is KProperty1<*, *> -> it.name
Expand All @@ -22,34 +20,23 @@ inline fun <reified T : Any> Map<*, *>.keys(vararg keys: Any): T? {
return mappedKeys
.dropLast(1)
.fold(this) { result, element ->
val r = result as Map<Any, Any>
when (val value = r[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/hexagonkt/hexagon/issues/271).
*
* @receiver .
* @param keys .
* @return .
*/
inline operator fun <reified T : Any> Map<*, *>.invoke(vararg keys: Any): T? =
keys(*keys)

/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
*
* @receiver .
* @param name .
* @return .
*/
inline fun <reified T : Any> Map<*, *>.requireKeys(vararg name: Any): T =
this.keys(*name) ?: error("$name required key not found")
inline fun <reified T : Any> Map<*, *>.requirePath(vararg name: Any): T =
this.getPath(*name) ?: error("$name required key not found")

/**
* [TODO](https://github.com/hexagonkt/hexagon/issues/271).
Expand Down Expand Up @@ -87,3 +74,264 @@ fun <K : Any> mapOfNotNull(vararg pairs: Pair<K, *>): Map<K, *> =
*/
fun <K, V> Map<K, V>.require(name: K): V =
this[name] ?: error("$name required key not found")

/**
* [TODO](https://github.com/hexagonkt/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/hexagonkt/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/hexagonkt/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/hexagonkt/hexagon/issues/271).
*
* @param maps .
* @return .
*/
fun merge(maps: Collection<Map<*, *>>): Map<*, *> =
maps.reduce { a, b -> merge(a, b) }

/**
* [TODO](https://github.com/hexagonkt/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/hexagonkt/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/hexagonkt/hexagon/issues/271).
*
* @receiver .
* @return .
*/
fun <V> Collection<V?>.filterNotEmpty(): Collection<V> =
this.filter(::notEmpty).map { it ?: fail }

/**
* [TODO](https://github.com/hexagonkt/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/hexagonkt/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/hexagonkt/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)
8 changes: 3 additions & 5 deletions core/src/main/kotlin/com/hexagonkt/core/Exceptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ fun Throwable.filterStackTrace(prefix: String): Array<out StackTraceElement> =
*/
fun Throwable.toText(prefix: String = ""): String =
"${this.javaClass.name}: ${this.message}" +
this.filterStackTrace(prefix).joinToString(eol, eol) { "\tat $it" } +
if (this.cause == null)
""
else
"${eol}Caused by: " + (this.cause as Throwable).toText(prefix)
this.filterStackTrace(prefix).joinToString(eol, eol) { "\tat $it" } +
if (this.cause == null) ""
else "${eol}Caused by: " + (this.cause as Throwable).toText(prefix)
3 changes: 0 additions & 3 deletions core/src/main/kotlin/com/hexagonkt/core/Jvm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import kotlin.reflect.KClass

/**
* Object with utilities to gather information about the running JVM.
*
* TODO Add JVM exception handler to add information on known exceptions. I.e: Classpath handler not
* registered with information on how to fix it (call `ClasspathHandler.registerHandler()`)
*/
object Jvm {
/** Default timezone. TODO Defining this lazily fails in macOS */
Expand Down
1 change: 0 additions & 1 deletion core/src/main/kotlin/com/hexagonkt/core/Strings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ fun <T : Any> String.parse(type: KClass<T>): T =
* @param type .
* @return .
*/
@Suppress("UNCHECKED_CAST") // All allowed types are checked at runtime
fun <T : Any> String?.parseOrNull(type: KClass<T>): T? =
this?.let {
require(type in parsedClasses) { "Unsupported type: ${type.qualifiedName}" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ internal class ClasspathHandlerProviderTest {
val e = assertFailsWith<MalformedURLException> {
assert(URL("unknown:data").readText().lines().isNotEmpty())
}
assert(e.message == "unknown protocol: unknown")

val errorJvm = "unknown protocol: unknown"
val errorNative = "The URL protocol unknown is not tested"
val message = e.message ?: "_"
assert(message.contains(errorJvm) || message.contains(errorNative))
}

@Test fun `Resource loading using URL returns data`() {
Expand Down
Loading

0 comments on commit 4dc8a3f

Please sign in to comment.