Last active
September 8, 2019 01:35
-
-
Save killjoy1221/cf728eec565a350846e03aa586697065 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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