From 4d407e8a831e29ed0bbd4ec5f32e00e2c8653a55 Mon Sep 17 00:00:00 2001 From: nea Date: Sun, 23 Apr 2023 19:35:20 +0200 Subject: wip --- .../at/hannibal2/skyhanni/config/ConfigManager.kt | 41 ++-- .../skyhanni/config/migration/LoadResult.kt | 38 ++++ .../skyhanni/config/migration/LoadingAdapter.kt | 8 + .../config/migration/MigratingConfigLoader.kt | 231 +++++++++------------ .../skyhanni/config/migration/ResolutionPath.kt | 24 +++ .../skyhanni/events/ConfigMigrationEvent.kt | 26 +-- .../at/hannibal2/skyhanni/utils/ReflectionUtils.kt | 17 ++ 7 files changed, 217 insertions(+), 168 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/config/migration/LoadResult.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/config/migration/LoadingAdapter.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/config/migration/ResolutionPath.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt diff --git a/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt b/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt index 5e0e61b8e..2b1ee74f3 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.config import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.migration.LoadResult import at.hannibal2.skyhanni.config.migration.MigratingConfigLoader import at.hannibal2.skyhanni.events.ConfigLoadEvent import at.hannibal2.skyhanni.features.garden.CropType @@ -11,7 +12,6 @@ import io.github.moulberry.moulconfig.processor.BuiltinMoulConfigGuis import io.github.moulberry.moulconfig.processor.ConfigProcessorDriver import io.github.moulberry.moulconfig.processor.MoulConfigProcessor import java.io.* -import java.lang.RuntimeException import java.nio.charset.StandardCharsets class ConfigManager { @@ -28,16 +28,32 @@ class ConfigManager { private var configFile: File? = null lateinit var processor: MoulConfigProcessor + var lastFailures: List = listOf() + private set + fun loadConfig(file: File): Features { - val x = MigratingConfigLoader.loadConfig( + val migrator = MigratingConfigLoader() + val x = migrator.loadConfig( gson.fromJson(file.readText(), JsonElement::class.java), Features::class.java ) + lastFailures = migrator.allFailures + if (migrator.hasAnyFailure()) { + val backupFile = file.resolveSibling("config-${System.currentTimeMillis()}-backup.json") + logger.error( + "Error while reading $file. Will save backup to $backupFile" + ) + try { + file.copyTo(backupFile) + } catch (e: Exception) { + logger.error("Could not create backup for config file", e) + } + } return when (x) { - MigratingConfigLoader.LoadResult.UseDefault -> Features() - is MigratingConfigLoader.LoadResult.Instance -> x.instance!! - is MigratingConfigLoader.LoadResult.Failure -> throw RuntimeException("Failed to load field ${x.field}", x.exception) - MigratingConfigLoader.LoadResult.Invalid -> error("LoadResult.Invalid returned directly?") + LoadResult.UseDefault -> Features() + is LoadResult.Instance -> x.instance!! + is LoadResult.Failure -> Features() + LoadResult.Invalid -> error("LoadResult.Invalid returned directly?") } } @@ -53,21 +69,8 @@ class ConfigManager { logger.info("Trying to load config from $configFile") if (configFile!!.exists()) { - try { SkyHanniMod.feature = loadConfig(configFile!!) logger.info("Loaded config from file") - } catch (e: Exception) { - val backupFile = configFile!!.resolveSibling("config-${System.currentTimeMillis()}-backup.json") - logger.error( - "Exception while reading $configFile. Will load blank config and save backup to $backupFile", - e - ) - try { - configFile!!.copyTo(backupFile) - } catch (e: Exception) { - logger.error("Could not create backup for config file", e) - } - } } if (SkyHanniMod.feature == null) { diff --git a/src/main/java/at/hannibal2/skyhanni/config/migration/LoadResult.kt b/src/main/java/at/hannibal2/skyhanni/config/migration/LoadResult.kt new file mode 100644 index 000000000..b65a8c9fe --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/migration/LoadResult.kt @@ -0,0 +1,38 @@ +package at.hannibal2.skyhanni.config.migration + +sealed interface LoadResult { + fun map(mapper: (T?) -> V?): LoadResult { + if (this is Instance) { + return Instance(mapper(this.instance)) + } + return this as LoadResult + } + + fun or(other: LoadResult<@UnsafeVariance T>): LoadResult + + data class Instance(val instance: T?) : LoadResult { + override fun or(other: LoadResult): LoadResult { + return this + } + } + + data class Failure(val exception: Throwable, val path: ResolutionPath) : LoadResult { + override fun or(other: LoadResult): LoadResult { + if (other is Invalid || other is Failure) return this + return other + } + } + + object UseDefault : LoadResult { + override fun or(other: LoadResult): LoadResult { + if (other is Instance) return other + return this + } + } + + object Invalid : LoadResult { + override fun or(other: LoadResult): LoadResult { + return other + } + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/config/migration/LoadingAdapter.kt b/src/main/java/at/hannibal2/skyhanni/config/migration/LoadingAdapter.kt new file mode 100644 index 000000000..8aa8f59da --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/migration/LoadingAdapter.kt @@ -0,0 +1,8 @@ +package at.hannibal2.skyhanni.config.migration + +import com.google.gson.JsonElement +import java.lang.reflect.Type + +fun interface LoadingAdapter { + fun adapt(path: ResolutionPath, hierarchy: List, type: Type): LoadResult +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/config/migration/MigratingConfigLoader.kt b/src/main/java/at/hannibal2/skyhanni/config/migration/MigratingConfigLoader.kt index 0ef989d63..ba24eb6fe 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/migration/MigratingConfigLoader.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/migration/MigratingConfigLoader.kt @@ -1,7 +1,9 @@ package at.hannibal2.skyhanni.config.migration -import at.hannibal2.skyhanni.config.migration.MigratingConfigLoader.LoadingAdapter +import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.events.ConfigMigrationEvent +import at.hannibal2.skyhanni.utils.allAccessibleFields +import at.hannibal2.skyhanni.utils.nonGeneric import com.google.gson.JsonElement import com.google.gson.JsonNull import com.google.gson.JsonObject @@ -9,128 +11,70 @@ import com.google.gson.JsonPrimitive import com.google.gson.annotations.Expose import io.github.moulberry.moulconfig.observer.Property import net.minecraftforge.common.MinecraftForge -import java.lang.reflect.* +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type -val Class.allFields: List - get() = this.declaredFields.toList() + (this.superclass?.allFields ?: listOf()) -val Class.allAccessibleFields: List - get() = allFields.also { it.forEach { it.isAccessible = true } } -val Type.nonGeneric: Class<*>? - get() = when (this) { - is ParameterizedType -> this.rawType.nonGeneric - is Class<*> -> this - is WildcardType -> this.upperBounds[0].nonGeneric - is TypeVariable<*> -> this.bounds[0].nonGeneric - else -> null - } - -object MigratingConfigLoader { - - interface ResolutionPath { - val parent: ResolutionPath? - val label: String - fun path(): String = parent?.path()?.let { "$it." } + label - - object Root : ResolutionPath { - override val parent: ResolutionPath? get() = null - override val label: String get() = "Root" - } - - data class FieldChild(val field: Field, override val parent: ResolutionPath) : ResolutionPath { - override val label: String = field.name - } - - data class IndirectChild(override val label: String, override val parent: ResolutionPath) : ResolutionPath - } - - sealed interface LoadResult { - fun map(mapper: (T?) -> V?): LoadResult { - if (this is Instance) { - return Instance(mapper(this.instance)) - } - return this as LoadResult - } - - fun or(other: LoadResult<@UnsafeVariance T>): LoadResult - - data class Instance(val instance: T?) : LoadResult { - override fun or(other: LoadResult): LoadResult { - return this - } - } - - data class Failure(val exception: Throwable, val field: Field?) : LoadResult { - override fun or(other: LoadResult): LoadResult { - if (other is Invalid) return this - return other - } - } - - object UseDefault : LoadResult { - override fun or(other: LoadResult): LoadResult { - if (other is Instance) return other - return this - } - } - - object Invalid : LoadResult { - override fun or(other: LoadResult): LoadResult { - return other - } - } - } - - fun interface LoadingAdapter { - fun adapt(field: Field?, hierarchy: List, type: Type): LoadResult - } +class MigratingConfigLoader { + val logger = SkyHanniMod.getLogger("ConfigMigrator") + val allFailures = mutableListOf() val adapters = listOf( - LoadingAdapter { field, hierarchy, type -> + LoadingAdapter { path, hierarchy, type -> val ng = type.nonGeneric ?: return@LoadingAdapter LoadResult.Invalid if (!ng.isPrimitive) return@LoadingAdapter LoadResult.Invalid + @Suppress("RemoveRedundantQualifierName") loadElement( - field, hierarchy, mapOf( - java.lang.Integer.TYPE to java.lang.Integer::class.java, - java.lang.Boolean.TYPE to java.lang.Boolean::class.java, - java.lang.Short.TYPE to java.lang.Short::class.java, - java.lang.Float.TYPE to java.lang.Float::class.java, - java.lang.Double.TYPE to java.lang.Double::class.java, - java.lang.Long.TYPE to java.lang.Long::class.java, - java.lang.Byte.TYPE to java.lang.Byte::class.java, - java.lang.Character.TYPE to java.lang.Character::class.java, + path, hierarchy, mapOf( + java.lang.Integer.TYPE to Integer::class.java, + java.lang.Boolean.TYPE to Boolean::class.java, + java.lang.Short.TYPE to Short::class.java, + java.lang.Float.TYPE to Float::class.java, + java.lang.Double.TYPE to Double::class.java, + java.lang.Long.TYPE to Long::class.java, + java.lang.Byte.TYPE to Byte::class.java, + java.lang.Character.TYPE to Character::class.java, )[ng]!! ) }, - directLoader { LoadResult.Instance(it.asString) }, - directLoader { LoadResult.Instance(it.asInt) }, - directLoader { LoadResult.Instance(it.asFloat) }, - directLoader { LoadResult.Instance(it.asLong) }, - directLoader { LoadResult.Instance(it.asDouble) }, - directLoader { LoadResult.Instance(it.asNumber) }, - directLoader { LoadResult.Instance(it.asBigDecimal) }, - directLoader { LoadResult.Instance(it.asBigInteger) }, - directLoader { LoadResult.Instance(it.asBoolean) }, - directLoader { LoadResult.Instance(it.asShort) }, - directLoader { LoadResult.Instance(it.asJsonObject) }, - directLoader { LoadResult.Instance(it.asJsonArray) }, - directLoader { LoadResult.Instance(it.asJsonPrimitive) }, - directLoader { LoadResult.Instance(it as JsonNull) }, - directLoader { LoadResult.Instance(it.asByte) }, - directLoader { LoadResult.Instance(it.asCharacter) }, - LoadingAdapter { field, hierarchy, type -> + directLoader { if (!it.asJsonPrimitive.isString) error("String expected, found $it") else LoadResult.Instance(it.asString) }, + directLoader { if (!it.asJsonPrimitive.isNumber) error("Number expected, found $it") else LoadResult.Instance(it.asInt) }, + directLoader { if (!it.asJsonPrimitive.isNumber) error("Number expected, found $it") else LoadResult.Instance(it.asFloat) }, + directLoader { if (!it.asJsonPrimitive.isNumber) error("Number expected, found $it") else LoadResult.Instance(it.asLong) }, + directLoader { if (!it.asJsonPrimitive.isNumber) error("Number expected, found $it") else LoadResult.Instance(it.asDouble) }, + directLoader { if (!it.asJsonPrimitive.isNumber) error("Number expected, found $it") else LoadResult.Instance(it.asNumber) }, + directLoader { if (!it.asJsonPrimitive.isNumber) error("Number expected, found $it") else LoadResult.Instance(it.asBigDecimal) }, + directLoader { if (!it.asJsonPrimitive.isNumber) error("Number expected, found $it") else LoadResult.Instance(it.asBigInteger) }, + directLoader { + if (!it.asJsonPrimitive.isBoolean) error("Boolean expected, found $it") else LoadResult.Instance( + it.asBoolean + ) + }, + directLoader { if (!it.asJsonPrimitive.isNumber) error("Number expected, found $it") else LoadResult.Instance(it.asShort) }, + directLoader { if (!it.isJsonObject) error("JsonObject expected, found $it") else LoadResult.Instance(it.asJsonObject) }, + directLoader { if (!it.isJsonArray) error("JsonArray expected, found $it") else LoadResult.Instance(it.asJsonArray) }, + directLoader { if (!it.isJsonPrimitive) error("JsonPrimitive expected, found $it") else LoadResult.Instance(it.asJsonPrimitive) }, + directLoader { if (!it.isJsonNull) error("Null expected, found $it") else LoadResult.Instance(it as JsonNull) }, + directLoader { if (!it.asJsonPrimitive.isNumber) error("Number expected, found $it") else LoadResult.Instance(it.asByte) }, + directLoader { + if (!it.asJsonPrimitive.isString || it.asString.length > 1) error("1-length String expected, found $it") else LoadResult.Instance( + it.asCharacter + ) + }, + LoadingAdapter { path, hierarchy, type -> val ng = type.nonGeneric ?: return@LoadingAdapter LoadResult.Invalid if (ng != List::class.java) return@LoadingAdapter LoadResult.Invalid type as ParameterizedType + val childPath = ResolutionPath.IndirectChild("element", path) val builder = mutableListOf() for (jsonElement in hierarchy.last()!!.asJsonArray) { - val x = loadElement(null, hierarchy + jsonElement, type.actualTypeArguments[0]) + val x = loadElement(childPath, hierarchy + jsonElement, type.actualTypeArguments[0]) if (x is LoadResult.Instance) { builder.add(x.instance) } else if (x is LoadResult.UseDefault) { return@LoadingAdapter LoadResult.Failure( RuntimeException("Cannot UseDefault for list element"), - field + childPath ) } else { return@LoadingAdapter x @@ -138,28 +82,30 @@ object MigratingConfigLoader { } return@LoadingAdapter LoadResult.Instance(builder) }, - LoadingAdapter { field, hierarchy, type -> + LoadingAdapter { path, hierarchy, type -> val ng = type.nonGeneric ?: return@LoadingAdapter LoadResult.Invalid if (ng != Map::class.java) return@LoadingAdapter LoadResult.Invalid type as ParameterizedType val builder = mutableMapOf() + val keyPath = ResolutionPath.IndirectChild("key", path) + val valuePath = ResolutionPath.IndirectChild("value", path) for ((key, value) in (hierarchy.last()!! as JsonObject).entrySet()) { - val keyEl = loadElement(null, hierarchy + JsonPrimitive(key), type.actualTypeArguments[0]) + val keyEl = loadElement(keyPath, hierarchy + JsonPrimitive(key), type.actualTypeArguments[0]) if (keyEl !is LoadResult.Instance<*>) { if (keyEl is LoadResult.UseDefault) { return@LoadingAdapter LoadResult.Failure( RuntimeException("Cannot UseDefault for map key"), - field + keyPath ) } return@LoadingAdapter keyEl } - val valueEl = loadElement(null, hierarchy + value, type.actualTypeArguments[0]) + val valueEl = loadElement(valuePath, hierarchy + value, type.actualTypeArguments[0]) if (valueEl !is LoadResult.Instance<*>) { if (valueEl is LoadResult.UseDefault) { return@LoadingAdapter LoadResult.Failure( RuntimeException("Cannot UseDefault for map key"), - field + valuePath ) } return@LoadingAdapter keyEl @@ -168,17 +114,17 @@ object MigratingConfigLoader { } return@LoadingAdapter LoadResult.Instance(builder) }, - LoadingAdapter { field, hierarchy, type -> + LoadingAdapter { path, hierarchy, type -> val ng = type.nonGeneric ?: return@LoadingAdapter LoadResult.Invalid if (ng != Property::class.java) return@LoadingAdapter LoadResult.Invalid if (type !is ParameterizedType) return@LoadingAdapter LoadResult.Invalid loadElement( - ng.getDeclaredField("value").also { it.isAccessible = true }, + ResolutionPath.FieldChild(ng.getDeclaredField("value").also { it.isAccessible = true }, path), hierarchy + hierarchy.last(), type.actualTypeArguments[0] ).map { Property.of(it) } }, - LoadingAdapter { field, hierarchy, type -> + LoadingAdapter { path, hierarchy, type -> val ng = type.nonGeneric ?: return@LoadingAdapter LoadResult.Invalid if (!ng.isEnum) return@LoadingAdapter LoadResult.Invalid ng as Class> @@ -190,35 +136,34 @@ object MigratingConfigLoader { fun loadConfig(root: JsonElement, clazz: Class): LoadResult { - return loadElement(null, listOf(root), clazz) - } - - inline fun loader(crossinline block: (field: Field?, hierarchy: List) -> LoadResult): LoadingAdapter { - return LoadingAdapter { field, hierarchy, type -> - if (type.nonGeneric != T::class.java) LoadResult.Invalid - else block(field, hierarchy) - } + val result = loadElement(ResolutionPath.Root, listOf(root), clazz) + logLoadResult(ResolutionPath.Root, result) + return result } inline fun directLoader(crossinline block: (element: JsonElement) -> LoadResult): LoadingAdapter { - return LoadingAdapter { field, hierarchy, type -> + return LoadingAdapter { path, hierarchy, type -> if (type.nonGeneric != T::class.java) LoadResult.Invalid - else block(hierarchy.last()!!) + else try { + block(hierarchy.last()!!) + } catch (e: Throwable) { + LoadResult.Failure(e, path) + } } } - fun loadElement(field: Field?, hierarchy: List, clazz: Class): LoadResult { - return loadElement(field, hierarchy, clazz as Type) as LoadResult + fun loadElement(path: ResolutionPath, hierarchy: List, clazz: Class): LoadResult { + return loadElement(path, hierarchy, clazz as Type) as LoadResult } - fun loadElement(field: Field?, hierarchy: List, type: Type): LoadResult { + fun loadElement(path: ResolutionPath, hierarchy: List, type: Type): LoadResult { var bestResult: LoadResult = LoadResult.Invalid for (adapter in adapters) { val adapt = try { - adapter.adapt(field, hierarchy, type) + adapter.adapt(path, hierarchy, type) } catch (e: Exception) { - LoadResult.Failure(e, field) + LoadResult.Failure(e, path) } if (adapt is LoadResult.Instance<*>) { bestResult = adapt @@ -226,43 +171,63 @@ object MigratingConfigLoader { } bestResult = bestResult.or(adapt) } - val event = ConfigMigrationEvent(field, hierarchy, type, bestResult).also { + val event = ConfigMigrationEvent(this, path, hierarchy, type, bestResult).also { try { MinecraftForge.EVENT_BUS.post(it) } catch (e: Throwable) { - it.value = it.value.or(LoadResult.Failure(e, field)) + it.value = it.value.or(LoadResult.Failure(e, path)) } } if (event.value is LoadResult.Invalid) { return LoadResult.Failure( RuntimeException("Could not resolve a loader for ${type.typeName} (${type.nonGeneric})"), - field + path ) } return event.value } - private fun loadClass(field: Field?, hierarchy: List, type: Type): LoadResult { + private fun loadClass(path: ResolutionPath, hierarchy: List, type: Type): LoadResult { val ng = type.nonGeneric ?: return LoadResult.Invalid if (ng.isAnonymousClass || ng.isEnum || ng.isInterface || ng.isPrimitive) return LoadResult.Invalid val instance = ng.getDeclaredConstructor().also { it.isAccessible = true }.newInstance() require(ng.isInstance(instance)) // this is all we can check at runtime, sadly val toBeFilled = ng.allAccessibleFields.filter { it.isAnnotationPresent(Expose::class.java) } for (childField in toBeFilled) { + val childPath = ResolutionPath.FieldChild(childField, path) when ( val value = loadElement( - childField, + childPath, hierarchy + hierarchy.last()?.asJsonObject?.get(childField.name), childField.genericType ) ) { is LoadResult.Instance -> childField.set(instance, value.instance) LoadResult.UseDefault -> {} - is LoadResult.Failure -> return value - LoadResult.Invalid -> return value + is LoadResult.Failure -> logLoadResult(childPath, value) + LoadResult.Invalid -> logLoadResult(childPath, value) } } return LoadResult.Instance(instance) } + fun logLoadResult(path: ResolutionPath, loadResult: LoadResult<*>) { + when (loadResult) { + is LoadResult.Failure -> { + allFailures.add(loadResult) + logger.error( + "${path}: Encountered failure propagated from ${loadResult.path}", + loadResult.exception + ) + } + + is LoadResult.Instance -> {} + LoadResult.Invalid -> logger.info("${path}: Encountered Invalid load result.") + LoadResult.UseDefault -> logger.info("${path}: Encountered UseDefault load result.") + } + } + + fun hasAnyFailure(): Boolean { + return allFailures.any() + } } \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/config/migration/ResolutionPath.kt b/src/main/java/at/hannibal2/skyhanni/config/migration/ResolutionPath.kt new file mode 100644 index 000000000..1bc915ab0 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/migration/ResolutionPath.kt @@ -0,0 +1,24 @@ +package at.hannibal2.skyhanni.config.migration + +import java.lang.reflect.Field + +sealed class ResolutionPath { + abstract val parent: ResolutionPath? + abstract val label: String + fun path(): String = (parent?.path()?.let { "$it." } ?: "") + label + + object Root : ResolutionPath() { + override val parent: ResolutionPath? get() = null + override val label: String get() = "Root" + } + + class FieldChild(val field: Field, override val parent: ResolutionPath) : ResolutionPath() { + override val label: String = field.name + } + + class IndirectChild(override val label: String, override val parent: ResolutionPath) : ResolutionPath() + + override fun toString(): String { + return path() + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/events/ConfigMigrationEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/ConfigMigrationEvent.kt index 408eacf49..b4a2667f6 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/ConfigMigrationEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/ConfigMigrationEvent.kt @@ -1,41 +1,35 @@ package at.hannibal2.skyhanni.events -import at.hannibal2.skyhanni.config.features.Garden +import at.hannibal2.skyhanni.config.migration.LoadResult import at.hannibal2.skyhanni.config.migration.MigratingConfigLoader +import at.hannibal2.skyhanni.config.migration.ResolutionPath import com.google.gson.JsonElement import com.google.gson.JsonObject -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.lang.reflect.Field import java.lang.reflect.Type import kotlin.reflect.KProperty1 data class ConfigMigrationEvent( - val property: Field?, + val loader: MigratingConfigLoader, + val resolutionPath: ResolutionPath, val objectHierarchy: List, val type: Type, - var value: MigratingConfigLoader.LoadResult<*> + var value: LoadResult<*> ) : LorenzEvent() { - - - @SubscribeEvent - fun migrateSomething(event: ConfigMigrationEvent) { - migrate(Garden::composter) { - parent().parent().child("oldPropertyIdk") - } - } + val property = (resolutionPath as? ResolutionPath.FieldChild)?.field fun use(value: Any?) { - this.value = MigratingConfigLoader.LoadResult.Instance(value) + this.value = LoadResult.Instance(value) } fun isFailing(): Boolean { - return value is MigratingConfigLoader.LoadResult.Failure || value is MigratingConfigLoader.LoadResult.Invalid + return value is LoadResult.Failure || value is LoadResult.Invalid } fun useDefault() { - this.value = MigratingConfigLoader.LoadResult.UseDefault + this.value = LoadResult.UseDefault } data class MigrateContext(val hierarchy: List) { @@ -73,7 +67,7 @@ data class ConfigMigrationEvent( fun migrate(prop: Field, block: MigrateContext.() -> MigrateContext, oldType: Type, mapper: (Any?) -> Any?) { if (prop != property) return this.value = - MigratingConfigLoader.loadElement(null, MigrateContext(objectHierarchy).let(block).hierarchy, oldType) + loader.loadElement(resolutionPath, MigrateContext(objectHierarchy).let(block).hierarchy, oldType) .map(mapper) } } \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt new file mode 100644 index 000000000..898f21cce --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt @@ -0,0 +1,17 @@ +package at.hannibal2.skyhanni.utils + +import java.lang.reflect.* + +val Class.allFields: List + get() = this.declaredFields.toList() + (this.superclass?.allFields ?: listOf()) +val Class.allAccessibleFields: List + get() = allFields.also { it.forEach { it.isAccessible = true } } + +val Type.nonGeneric: Class<*>? + get() = when (this) { + is ParameterizedType -> this.rawType.nonGeneric + is Class<*> -> this + is WildcardType -> this.upperBounds[0].nonGeneric + is TypeVariable<*> -> this.bounds[0].nonGeneric + else -> null + } \ No newline at end of file -- cgit