aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornea <nea@nea.moe>2023-04-23 19:35:20 +0200
committernea <nea@nea.moe>2023-04-23 19:35:20 +0200
commit4d407e8a831e29ed0bbd4ec5f32e00e2c8653a55 (patch)
tree2558c9eded0534e1c2dd5bfff3eef215da5b0d10
parentecdef28c57b6b221a7c41d9fab5c474de2a4d584 (diff)
downloadSkyHanni-4d407e8a831e29ed0bbd4ec5f32e00e2c8653a55.tar.gz
SkyHanni-4d407e8a831e29ed0bbd4ec5f32e00e2c8653a55.tar.bz2
SkyHanni-4d407e8a831e29ed0bbd4ec5f32e00e2c8653a55.zip
wip
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt41
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/migration/LoadResult.kt38
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/migration/LoadingAdapter.kt8
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/migration/MigratingConfigLoader.kt231
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/migration/ResolutionPath.kt24
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/ConfigMigrationEvent.kt26
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/ReflectionUtils.kt17
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