Skip to content

Instantly share code, notes, and snippets.

@killjoy1221
Last active September 8, 2019 01:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save killjoy1221/cf728eec565a350846e03aa586697065 to your computer and use it in GitHub Desktop.
Save killjoy1221/cf728eec565a350846e03aa586697065 to your computer and use it in GitHub Desktop.
package mnm.mods.tabbychat.util.config
import com.google.common.reflect.TypeToken
import kotlin.reflect.KProperty
sealed class AbstractValue<T> {
abstract val type: TypeToken<T>?
abstract val value: T
override fun toString(): String {
return value.toString()
}
}
class Value<T>(
override val type: TypeToken<T>,
val default: () -> T) : AbstractValue<T>() {
override var value = default()
fun reset() {
value = default()
}
}
class ValueList<T>(
override val type: TypeToken<MutableList<T>>,
override val value: MutableList<T> = mutableListOf()
) : AbstractValue<MutableList<T>>(), MutableList<T> by value
class ValueMap<T>(
override val type: TypeToken<MutableMap<String, T>>,
override val value: MutableMap<String, T> = mutableMapOf()
) : AbstractValue<MutableMap<String, T>>(), MutableMap<String, T> by value
abstract class ValueObject<T : ValueObject<T>> : AbstractValue<T>() {
override val type: Nothing? = null
@Suppress("UNCHECKED_CAST")
override val value: T
get() = this as T
internal val properties: MutableMap<String, AbstractValue<out Any>> = mutableMapOf()
protected inline fun <reified T : Any> value(noinline default: () -> T) = ValueDelegate {
Value(typeToken(), default)
}
protected inline fun <reified T : Any> list() = ValueDelegate {
ValueList<T>(typeToken())
}
protected inline fun <reified T> map() = ValueDelegate {
ValueMap<T>(typeToken())
}
protected fun <T : ValueObject<T>> obj(default: () -> T) = ValueDelegate(default)
protected inline fun <reified T> typeToken() = object : TypeToken<T>() {}
}
class ValueDelegate<V : AbstractValue<T>, T : Any>(val factory: () -> V) {
@Suppress("UNCHECKED_CAST")
operator fun getValue(thisRef: ValueObject<*>, property: KProperty<*>): V {
return thisRef.properties.getOrPut(property.name) {
factory()
} as V
}
}
package mnm.mods.tabbychat.util.config
import com.google.gson.GsonBuilder
import com.google.gson.JsonParseException
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import mnm.mods.tabbychat.CONFIG
import mnm.mods.tabbychat.TabbyChat
import net.minecraft.util.EnumTypeAdapterFactory
import java.io.IOException
import java.nio.file.Files
import java.nio.file.NoSuchFileException
import java.nio.file.Path
/**
* Used for creating settings and saving/loading them in the JSON format. Start
* by creating delegate properties.
*
* @author Matthew Messinger
*/
abstract class SettingsFile<T : SettingsFile<T>>(@field:Transient val path: Path) : ValueObject<SettingsFile<T>>() {
private val gson = GsonBuilder()
.registerTypeAdapter(this::class.java, Serializer())
.setPrettyPrinting()
.registerTypeAdapterFactory(EnumTypeAdapterFactory())
.create()
fun save() {
try {
Files.createDirectories(path.parent)
Files.newBufferedWriter(path).use {
gson.toJson(this, it)
}
} catch (e: IOException) {
TabbyChat.logger.error(CONFIG, "Failed to save config to {}", path, e)
}
}
fun load() {
try {
Files.newBufferedReader(path).use {
gson.fromJson(it, javaClass)
}
} catch (e: NoSuchFileException) {
TabbyChat.logger.info(CONFIG, "Saving default config to {}", path)
save()
} catch (e: IOException) {
TabbyChat.logger.error(CONFIG, "Failed to load config from {}", path, e)
} catch (e: JsonParseException) {
TabbyChat.logger.error(CONFIG, "Syntax or schema error in {}", path, e)
}
}
private inner class Serializer : TypeAdapter<SettingsFile<T>>() {
override fun read(reader: JsonReader): SettingsFile<T> {
readObject(reader, properties)
return this@SettingsFile
}
fun readObject(reader: JsonReader, root: MutableMap<String, AbstractValue<out Any>>) {
while (reader.hasNext()) {
when (val value = root[reader.nextName()]) {
is Value -> value.value = gson.fromJson(reader, value.type.type)
is ValueList<*> -> {
value.clear()
value.addAll(gson.fromJson(reader, value.type.type))
}
is ValueMap<*> -> {
value.clear()
value.putAll(gson.fromJson(reader, value.type.type))
}
is ValueObject -> readObject(reader, value.properties)
}
}
}
override fun write(writer: JsonWriter, obj: SettingsFile<T>) {
writeObject(writer, obj.properties)
}
fun writeObject(writer: JsonWriter, root: MutableMap<String, AbstractValue<out Any>>) {
for ((key, value: AbstractValue<out Any>) in root) {
writer.name(key)
when (value) {
is Value<*> -> gson.toJson(value.value, value.type.type, writer)
is ValueList<*> -> gson.toJson(value.value, value.type.type, writer)
is ValueMap<*> -> gson.toJson(value.value, value.type.type, writer)
is ValueObject<*> -> writeObject(writer, value.value.properties)
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment