diff options
| author | nea <nea@nea.moe> | 2023-04-23 19:35:20 +0200 |
|---|---|---|
| committer | nea <nea@nea.moe> | 2023-04-23 19:35:20 +0200 |
| commit | 4d407e8a831e29ed0bbd4ec5f32e00e2c8653a55 (patch) | |
| tree | 2558c9eded0534e1c2dd5bfff3eef215da5b0d10 | |
| parent | ecdef28c57b6b221a7c41d9fab5c474de2a4d584 (diff) | |
| download | SkyHanni-4d407e8a831e29ed0bbd4ec5f32e00e2c8653a55.tar.gz SkyHanni-4d407e8a831e29ed0bbd4ec5f32e00e2c8653a55.tar.bz2 SkyHanni-4d407e8a831e29ed0bbd4ec5f32e00e2c8653a55.zip | |
wip
7 files changed, 217 insertions, 168 deletions
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<Features> + var lastFailures: List<LoadResult.Failure> = 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<out T> { + fun <V> map(mapper: (T?) -> V?): LoadResult<V> { + if (this is Instance<T>) { + return Instance(mapper(this.instance)) + } + return this as LoadResult<V> + } + + fun or(other: LoadResult<@UnsafeVariance T>): LoadResult<T> + + data class Instance<T>(val instance: T?) : LoadResult<T> { + override fun or(other: LoadResult<T>): LoadResult<T> { + return this + } + } + + data class Failure(val exception: Throwable, val path: ResolutionPath) : LoadResult<Nothing> { + override fun or(other: LoadResult<Nothing>): LoadResult<Nothing> { + if (other is Invalid || other is Failure) return this + return other + } + } + + object UseDefault : LoadResult<Nothing> { + override fun or(other: LoadResult<Nothing>): LoadResult<Nothing> { + if (other is Instance) return other + return this + } + } + + object Invalid : LoadResult<Nothing> { + override fun or(other: LoadResult<Nothing>): LoadResult<Nothing> { + 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<T> { + fun adapt(path: ResolutionPath, hierarchy: List<JsonElement?>, type: Type): LoadResult<T> +}
\ 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 <T> Class<T>.allFields: List<Field> - get() = this.declaredFields.toList() + (this.superclass?.allFields ?: listOf()) -val <T> Class<T>.allAccessibleFields: List<Field> - 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<out T> { - fun <V> map(mapper: (T?) -> V?): LoadResult<V> { - if (this is Instance<T>) { - return Instance(mapper(this.instance)) - } - return this as LoadResult<V> - } - - fun or(other: LoadResult<@UnsafeVariance T>): LoadResult<T> - - data class Instance<T>(val instance: T?) : LoadResult<T> { - override fun or(other: LoadResult<T>): LoadResult<T> { - return this - } - } - - data class Failure(val exception: Throwable, val field: Field?) : LoadResult<Nothing> { - override fun or(other: LoadResult<Nothing>): LoadResult<Nothing> { - if (other is Invalid) return this - return other - } - } - - object UseDefault : LoadResult<Nothing> { - override fun or(other: LoadResult<Nothing>): LoadResult<Nothing> { - if (other is Instance) return other - return this - } - } - - object Invalid : LoadResult<Nothing> { - override fun or(other: LoadResult<Nothing>): LoadResult<Nothing> { - return other - } - } - } - - fun interface LoadingAdapter<T> { - fun adapt(field: Field?, hierarchy: List<JsonElement?>, type: Type): LoadResult<T> - } +class MigratingConfigLoader { + val logger = SkyHanniMod.getLogger("ConfigMigrator") + val allFailures = mutableListOf<LoadResult.Failure>() 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<Any?>() 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<Any?, Any?>() + 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<out Enum<*>> @@ -190,35 +136,34 @@ object MigratingConfigLoader { fun <T : Any> loadConfig(root: JsonElement, clazz: Class<T>): LoadResult<T> { - return loadElement(null, listOf(root), clazz) - } - - inline fun <reified T : Any> loader(crossinline block: (field: Field?, hierarchy: List<JsonElement?>) -> LoadResult<T>): LoadingAdapter<T> { - 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 <reified T : Any> directLoader(crossinline block: (element: JsonElement) -> LoadResult<T>): LoadingAdapter<T> { - 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 <T : Any> loadElement(field: Field?, hierarchy: List<JsonElement?>, clazz: Class<T>): LoadResult<T> { - return loadElement(field, hierarchy, clazz as Type) as LoadResult<T> + fun <T : Any> loadElement(path: ResolutionPath, hierarchy: List<JsonElement?>, clazz: Class<T>): LoadResult<T> { + return loadElement(path, hierarchy, clazz as Type) as LoadResult<T> } - fun loadElement(field: Field?, hierarchy: List<JsonElement?>, type: Type): LoadResult<Any?> { + fun loadElement(path: ResolutionPath, hierarchy: List<JsonElement?>, type: Type): LoadResult<Any?> { var bestResult: LoadResult<Any?> = 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<JsonElement?>, type: Type): LoadResult<Any?> { + private fun loadClass(path: ResolutionPath, hierarchy: List<JsonElement?>, type: Type): LoadResult<Any?> { 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<JsonElement?>, 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<JsonElement?>) { @@ -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 <T> Class<T>.allFields: List<Field> + get() = this.declaredFields.toList() + (this.superclass?.allFields ?: listOf()) +val <T> Class<T>.allAccessibleFields: List<Field> + 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 |
