Skip to content

Commit

Permalink
Add ManagedConfig
Browse files Browse the repository at this point in the history
  • Loading branch information
nea89o committed Jan 11, 2024
1 parent 2ffdfdc commit cd76281
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.github.moulberry.moulconfig.managed

import com.google.gson.GsonBuilder
import io.github.moulberry.moulconfig.observer.PropertyTypeAdapterFactory

class GsonMapper<T>(val clazz: Class<T>) : DataMapper<T> {
val gsonBuilder = GsonBuilder()
.registerTypeAdapterFactory(PropertyTypeAdapterFactory())
private val gson by lazy { gsonBuilder.create() }
override fun serialize(value: T): String {
return gson.toJson(value)
}

override fun createDefault(): T {
return clazz.newInstance()
}

override fun deserialize(string: String): T {
return gson.fromJson(string, clazz)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,39 @@ import java.io.File
import java.lang.management.ManagementFactory
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.util.function.BiConsumer
import java.util.function.Consumer

class ManagedDataFile<T> internal constructor(
open class ManagedDataFile<T> internal constructor(
val file: File,
val mapper: DataMapper<T>,
private val loadFailed: Consumer<Exception>,
private val saveFailed: Consumer<Exception>,
private val beforeLoad: Runnable,
private val afterLoad: Runnable,
private val beforeSave: Runnable,
private val afterSave: Runnable,
private val loadFailed: BiConsumer<ManagedDataFile<T>, Exception>,
private val saveFailed: BiConsumer<ManagedDataFile<T>, Exception>,
private val beforeLoad: Consumer<ManagedDataFile<T>>,
private val afterLoad: Consumer<ManagedDataFile<T>>,
private val beforeSave: Consumer<ManagedDataFile<T>>,
private val afterSave: Consumer<ManagedDataFile<T>>,
) {
constructor(builder: ManagedDataFileBuilder<T>) : this(
builder.file,
builder.mapper,
builder.loadFailed,
builder.saveFailed,
builder.beforeLoad,
builder.afterLoad,
builder.beforeSave,
builder.afterSave
)

companion object {
@JvmStatic
@JvmOverloads
fun <T> create(
file: File,
mapper: DataMapper<T>,
clazz: Class<T>,
consumer: (ManagedDataFileBuilder<T>.() -> Unit) = {}
): ManagedDataFile<T> {
return ManagedDataFileBuilder(file, mapper).apply(consumer).build()
return ManagedDataFile(ManagedDataFileBuilder(file, clazz).apply(consumer))
}
}

Expand All @@ -36,13 +48,17 @@ class ManagedDataFile<T> internal constructor(
}

fun reloadFromFile() {
beforeLoad.run()
beforeLoad.accept(this)
try {
instance = mapper.deserialize(file.readText())
if (file.exists()) {
instance = mapper.deserialize(file.readText())
} else {
instance = mapper.createDefault()
}
} catch (ex: Exception) {
loadFailed.accept(ex)
loadFailed.accept(this, ex)
}
afterLoad.run()
afterLoad.accept(this)
}

private fun createUniqueExtraFile(identifier: String, directory: File = file.parentFile): File {
Expand All @@ -53,7 +69,7 @@ class ManagedDataFile<T> internal constructor(
}

fun saveToFile() {
beforeSave.run()
beforeSave.accept(this)
val toSave = mapper.serialize(instance)
val temporarySaveFile = createUniqueExtraFile("save")
try {
Expand All @@ -66,8 +82,8 @@ class ManagedDataFile<T> internal constructor(
StandardCopyOption.REPLACE_EXISTING
)
} catch (ex: Exception) {
saveFailed.accept(ex)
saveFailed.accept(this, ex)
}
afterSave.run()
afterSave.accept(this)
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
package io.github.moulberry.moulconfig.managed

import java.io.File
import java.util.function.BiConsumer
import java.util.function.Consumer

class ManagedDataFileBuilder<T> internal constructor(
open class ManagedDataFileBuilder<T>(
var file: File,
var mapper: DataMapper<T>,
val clazz: Class<T>
) {

fun throwOnFailure() {
loadFailed = Consumer { throw it }
saveFailed = Consumer { throw it }
loadFailed = BiConsumer { _, ex -> throw ex }
saveFailed = BiConsumer { _, ex -> throw ex }
}

var loadFailed: Consumer<Exception> = Consumer {}
var saveFailed: Consumer<Exception> = Consumer {}
var beforeLoad: Runnable = Runnable {}
var afterLoad: Runnable = Runnable {}
var beforeSave: Runnable = Runnable {}
var afterSave: Runnable = Runnable {}
var mapper: DataMapper<T> = GsonMapper(clazz)

fun build(): ManagedDataFile<T> {
return ManagedDataFile(
file,
mapper,
loadFailed, saveFailed, beforeLoad, afterLoad, beforeSave, afterSave
)
@JvmOverloads
fun jsonMapper(function: GsonMapper<T>.() -> Unit = {}) {
mapper = GsonMapper(clazz).also(function)
}

open var loadFailed: BiConsumer<ManagedDataFile<T>, Exception> = BiConsumer { _, _ -> }
open var saveFailed: BiConsumer<ManagedDataFile<T>, Exception> = BiConsumer { _, _ -> }
open var beforeLoad: Consumer<ManagedDataFile<T>> = Consumer {}
open var afterLoad: Consumer<ManagedDataFile<T>> = Consumer {}
open var beforeSave: Consumer<ManagedDataFile<T>> = Consumer {}
open var afterSave: Consumer<ManagedDataFile<T>> = Consumer {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.github.moulberry.moulconfig.managed

import io.github.moulberry.moulconfig.Config
import io.github.moulberry.moulconfig.gui.GuiOptionEditor
import io.github.moulberry.moulconfig.gui.GuiScreenElementWrapper
import io.github.moulberry.moulconfig.gui.MoulConfigEditor
import io.github.moulberry.moulconfig.processor.BuiltinMoulConfigGuis
import io.github.moulberry.moulconfig.processor.ConfigProcessorDriver
import io.github.moulberry.moulconfig.processor.MoulConfigProcessor
import io.github.moulberry.moulconfig.processor.ProcessedOption
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiScreen
import java.io.File
import java.util.function.BiFunction
import java.util.function.Consumer

class ManagedConfig<T : Config>(private val builder: ManagedConfigBuilder<T>) : ManagedDataFile<T>(builder.apply {
afterLoad = Consumer<ManagedDataFile<T>> { (it as ManagedConfig<T>).rebuildConfigProcessor(builder) }
.andThen(afterLoad)
}) {

companion object {
@JvmStatic
@JvmOverloads
fun <T : Config> create(
file: File,
clazz: Class<T>,
consumer: (ManagedConfigBuilder<T>.() -> Unit) = {}
): ManagedConfig<T> {
return ManagedConfig(ManagedConfigBuilder(file, clazz).apply(consumer))
}
}

lateinit var processor: MoulConfigProcessor<T>
private set


fun rebuildConfigProcessor() {
rebuildConfigProcessor(builder)
}
private fun rebuildConfigProcessor(builder: ManagedConfigBuilder<T>) {
processor = buildProcessor(builder)
}

/**
* Helper function to introduce the A type parameter so that two objects can be cast to be that same A variable.
*/
@Suppress("NOTHING_TO_INLINE")
private inline fun <A : Annotation> cast(processor: MoulConfigProcessor<T>, annotation: Class<A>, method: Any) {
@Suppress("UNCHECKED_CAST")
processor.registerConfigEditor(annotation, method as BiFunction<ProcessedOption, A, GuiOptionEditor>)
}

private fun buildProcessor(builder: ManagedConfigBuilder<T>): MoulConfigProcessor<T> {
val processor = MoulConfigProcessor(this.instance)
if (builder.useDefaultProcessors) {
BuiltinMoulConfigGuis.addProcessors(processor)
}
builder.customProcessors.forEach { (annotation, method) ->
cast(processor, annotation, method)
}
ConfigProcessorDriver.processConfig(this.instance.javaClass, this.instance, processor)
return processor
}

fun getEditor(): MoulConfigEditor<T> {
return MoulConfigEditor(processor)
}

fun getGui(): GuiScreen {
return GuiScreenElementWrapper(getEditor())
}

fun openConfigGui() {
Minecraft.getMinecraft().displayGuiScreen(getGui())
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.github.moulberry.moulconfig.managed

import io.github.moulberry.moulconfig.gui.GuiOptionEditor
import io.github.moulberry.moulconfig.processor.ProcessedOption
import java.io.File
import java.util.function.BiFunction

class ManagedConfigBuilder<T>(file: File, clazz: Class<T>) : ManagedDataFileBuilder<T>(file, clazz) {
var useDefaultProcessors = true
internal val customProcessors =
mutableListOf<Pair<Class<out Annotation>, BiFunction<ProcessedOption, Annotation, GuiOptionEditor>>>()

fun clearCustomProcessors() {
customProcessors.clear()
}

inline fun <reified A : Annotation> customProcessor(noinline editorGenerator: (ProcessedOption, A) -> GuiOptionEditor) {
customProcessor(A::class.java, editorGenerator)
}

fun <A : Annotation> customProcessor(
annotation: Class<A>,
editorGenerator: BiFunction<ProcessedOption, in A, GuiOptionEditor>
) {
@Suppress("UNCHECKED_CAST")
customProcessors.add(
Pair(
annotation,
editorGenerator as BiFunction<ProcessedOption, Annotation, GuiOptionEditor>
)
)
}

}

0 comments on commit cd76281

Please sign in to comment.