Skip to content

Commit 2fe81f2

Browse files
committed
Changes to support comment locale
1 parent d6baea0 commit 2fe81f2

File tree

3 files changed

+109
-15
lines changed

3 files changed

+109
-15
lines changed

core/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ dependencies {
4141

4242
compileOnly("org.ow2.asm:asm:9.8")
4343
compileOnly("org.ow2.asm:asm-commons:9.8")
44+
45+
compileOnly("com.google.guava:guava:33.3.1-jre")
4446
}
4547

4648
tasks.test {

core/src/main/kotlin/io/github/rothes/esu/core/config/EsuConfig.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ object EsuConfig {
2020
URLConnection.setDefaultUseCaches("jar", false)
2121
}
2222

23+
internal var initialized = false
2324
private lateinit var data: ConfigData
2425

2526
init {
@@ -30,6 +31,7 @@ object EsuConfig {
3031

3132
fun reloadConfig() {
3233
data = load()
34+
initialized = true
3335

3436
URLConnection.setDefaultUseCaches("jar", !data.disableJarFileCache)
3537
}

core/src/main/kotlin/io/github/rothes/esu/core/configuration/ConfigLoader.kt

Lines changed: 105 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package io.github.rothes.esu.core.configuration
22

3+
import com.google.common.cache.CacheBuilder
34
import io.github.rothes.esu.core.EsuCore
45
import io.github.rothes.esu.core.config.EsuConfig
56
import io.github.rothes.esu.core.configuration.meta.*
67
import io.github.rothes.esu.core.configuration.serializer.*
78
import io.github.rothes.esu.core.module.configuration.EmptyConfiguration
9+
import io.github.rothes.esu.core.util.tree.TreeNode
810
import io.github.rothes.esu.lib.org.spongepowered.configurate.BasicConfigurationNode
911
import io.github.rothes.esu.lib.org.spongepowered.configurate.CommentedConfigurationNode
1012
import io.github.rothes.esu.lib.org.spongepowered.configurate.CommentedConfigurationNodeIntermediary
@@ -21,16 +23,23 @@ import io.leangen.geantyref.GenericTypeReflector
2123
import io.leangen.geantyref.TypeToken
2224
import net.kyori.adventure.text.Component
2325
import java.lang.reflect.Type
26+
import java.net.JarURLConnection
27+
import java.net.URLDecoder
2428
import java.nio.file.Files
2529
import java.nio.file.Path
2630
import java.util.*
31+
import java.util.concurrent.TimeUnit
32+
import java.util.jar.JarFile
2733
import java.util.zip.ZipException
2834
import kotlin.io.path.*
29-
import kotlin.jvm.java
3035
import kotlin.jvm.optionals.getOrNull
3136

3237
object ConfigLoader {
3338

39+
private val langCache = CacheBuilder.newBuilder()
40+
.expireAfterAccess(8, TimeUnit.HOURS)
41+
.build<ClassLoader, TreeNode<List<String>>>()
42+
3443
private val serverAdventure = try {
3544
Component.text()
3645
true
@@ -144,7 +153,22 @@ object ConfigLoader {
144153
if (path.isDirectory()) {
145154
throw IllegalArgumentException("Path '$path' is a directory")
146155
}
147-
val loader = createBuilder().path(path).let(settings.yamlLoader).build()
156+
val resourceNode =
157+
if (settings.findResource) {
158+
val p = settings.basePath.relativize(path)
159+
val lang = getLangCache(clazz.classLoader, p.pathString)
160+
val locale = if (EsuConfig.initialized) EsuConfig.get().locale else Locale.getDefault().language + '_' + Locale.getDefault().country.lowercase()
161+
val resource = lang.find { it.nameWithoutExtension == locale }
162+
?: lang.firstOrNull { it.nameWithoutExtension.substringBefore('_') == locale.substringBefore('_') }
163+
resource?.let {
164+
val conn = clazz.classLoader.getResource(it.resourcePath)!!.openConnection() as JarURLConnection
165+
conn.useCaches = false
166+
conn.connect()
167+
val reader = conn.getInputStream().bufferedReader()
168+
createBuilder(null).source { reader }.let(settings.yamlLoader).build().load()
169+
}
170+
} else null
171+
val loader = createBuilder(resourceNode).path(path).let(settings.yamlLoader).build()
148172
val node = settings.nodeMapper(loader.load())
149173
val t = settings.modifier.invoke(node.require(clazz), path)
150174
node.set(clazz, t)
@@ -162,7 +186,7 @@ object ConfigLoader {
162186
println((serial as ObjectMapper.Factory).get(clazz).load(BasicConfigurationNode.root(node.options().shouldCopyDefaults(false))))
163187
}
164188

165-
fun save(path: Path, obj: Any, yamlLoader: YamlConfigurationLoader = createBuilder().path(path).build()) {
189+
fun save(path: Path, obj: Any, yamlLoader: YamlConfigurationLoader = createBuilder(null).path(path).build()) {
166190
if (path.isDirectory()) {
167191
throw IllegalArgumentException("Path '$path' is a directory")
168192
}
@@ -171,7 +195,7 @@ object ConfigLoader {
171195
yamlLoader.save(node)
172196
}
173197

174-
fun createBuilder(): YamlConfigurationLoader.Builder {
198+
fun createBuilder(resourceNode: ConfigurationNode?): YamlConfigurationLoader.Builder {
175199
return YamlConfigurationLoader.builder()
176200
.indent(2)
177201
.nodeStyle(NodeStyle.BLOCK)
@@ -183,12 +207,15 @@ object ConfigLoader {
183207
.addProcessor(Comment::class.java) { data, _ ->
184208
Processor { _, destination ->
185209
if (destination is CommentedConfigurationNodeIntermediary<*>) {
186-
if (data.overrideOld.isEmpty() || destination.comment() == null) {
187-
destination.commentIfAbsent(data.value.trimIndent())
210+
val resourceComment = (resourceNode?.node(destination.path()) as? CommentedConfigurationNodeIntermediary<*>)?.comment()
211+
if (resourceComment != null && destination.comment() == data.value.trimIndent()) {
212+
destination.comment(resourceComment)
213+
} else if (data.overrideOld.isEmpty() || destination.comment() == null) {
214+
destination.commentIfAbsent(resourceComment ?: data.value.trimIndent())
188215
} else if (data.overrideOld.first() == OVERRIDE_ALWAYS) {
189-
destination.comment(data.value.trimIndent())
216+
destination.comment(resourceComment ?: data.value.trimIndent())
190217
} else if (data.overrideOld.map { it.trimIndent() }.contains(destination.comment())) {
191-
destination.comment(data.value.trimIndent())
218+
destination.comment(resourceComment ?: data.value.trimIndent())
192219
}
193220
}
194221
}
@@ -318,17 +345,53 @@ object ConfigLoader {
318345
}
319346
}
320347

348+
private fun getLangCache(classLoader: ClassLoader, path: String): List<LangResource> {
349+
val tree = langCache.getIfPresent(classLoader) ?: let {
350+
val root = TreeNode<List<String>>()
351+
JarFile(URLDecoder.decode(javaClass.protectionDomain.codeSource.location.path, Charsets.UTF_8)).use { jarFile ->
352+
val entries = jarFile.entries()
353+
while (entries.hasMoreElements()) {
354+
val entry = entries.nextElement()
355+
val fullName = entry.name
356+
if (fullName.startsWith("lang/") && fullName.last() != '/') {
357+
val split = fullName.substringAfter('/').split('/')
358+
val path = split.dropLast(1)
359+
val node = root.getOrCreateNode(path)
360+
val list = node.value as? MutableList<String> ?: mutableListOf<String>().also { node.value = it }
361+
list.add(split.last())
362+
}
363+
}
364+
}
365+
langCache.put(classLoader, root)
366+
root
367+
}
368+
val node = tree.getNode(path.split('/'))
369+
return node?.value?.map { LangResource(it, node.path) } ?: listOf()
370+
}
371+
372+
private class LangResource(
373+
val name: String,
374+
path: String,
375+
) {
376+
val nameWithoutExtension = name.substringBeforeLast('.')
377+
val resourcePath = "lang/$path$name"
378+
}
379+
321380
open class LoaderSettings<T>(
322381
val yamlLoader: (YamlConfigurationLoader.Builder) -> YamlConfigurationLoader.Builder = { it },
323382
val nodeMapper: (ConfigurationNode) -> ConfigurationNode = { it },
324-
val modifier: (T, Path) -> T = { it, _ -> it }
383+
val modifier: (T, Path) -> T = { it, _ -> it },
384+
val findResource: Boolean = true,
385+
val basePath: Path = EsuCore.instance.baseConfigPath(),
325386
) {
326387

327388
class Builder<T> {
328389

329390
var yamlLoader: (YamlConfigurationLoader.Builder) -> YamlConfigurationLoader.Builder = { it }
330391
var nodeMapper: (ConfigurationNode) -> ConfigurationNode = { it }
331392
var modifier: (T, Path) -> T = { it, _ -> it }
393+
var findResource: Boolean = true
394+
var basePath: Path = EsuCore.instance.baseConfigPath()
332395

333396
fun yamlLoader(yamlLoader: (YamlConfigurationLoader.Builder) -> YamlConfigurationLoader.Builder): Builder<T> {
334397
this.yamlLoader = yamlLoader
@@ -345,7 +408,17 @@ object ConfigLoader {
345408
return this
346409
}
347410

348-
fun build() = LoaderSettings(yamlLoader, nodeMapper, modifier)
411+
fun findResource(findResource: Boolean): Builder<T> {
412+
this.findResource = findResource
413+
return this
414+
}
415+
416+
fun basePath(basePath: Path): Builder<T> {
417+
this.basePath = basePath
418+
return this
419+
}
420+
421+
fun build() = LoaderSettings(yamlLoader, nodeMapper, modifier, findResource, basePath)
349422

350423
}
351424
}
@@ -358,8 +431,10 @@ object ConfigLoader {
358431
val keyMapper: (Path) -> String = { it.nameWithoutExtension },
359432
yamlLoader: (YamlConfigurationLoader.Builder) -> YamlConfigurationLoader.Builder = { it },
360433
nodeMapper: (ConfigurationNode) -> ConfigurationNode = { it },
361-
modifier: (T, Path) -> T = { it, _ -> it }
362-
): LoaderSettings<T>(yamlLoader, nodeMapper, modifier) {
434+
modifier: (T, Path) -> T = { it, _ -> it },
435+
findResource: Boolean = true,
436+
basePath: Path = EsuCore.instance.baseConfigPath(),
437+
): LoaderSettings<T>(yamlLoader, nodeMapper, modifier, findResource, basePath) {
363438

364439
constructor(
365440
vararg forceLoad: String,
@@ -368,8 +443,10 @@ object ConfigLoader {
368443
yamlLoader: (YamlConfigurationLoader.Builder) -> YamlConfigurationLoader.Builder = { it },
369444
keyMapper: (Path) -> String = { it.nameWithoutExtension },
370445
nodeMapper: (ConfigurationNode) -> ConfigurationNode = { it },
371-
modifier: (T, Path) -> T = { it, _ -> it }
372-
): this(forceLoad.toList(), create, loadSubDirectories, keyMapper, yamlLoader, nodeMapper, modifier)
446+
modifier: (T, Path) -> T = { it, _ -> it },
447+
findResource: Boolean = true,
448+
basePath: Path = EsuCore.instance.baseConfigPath(),
449+
): this(forceLoad.toList(), create, loadSubDirectories, keyMapper, yamlLoader, nodeMapper, modifier, findResource, basePath)
373450

374451

375452
class Builder<T> {
@@ -381,6 +458,8 @@ object ConfigLoader {
381458
var yamlLoader: (YamlConfigurationLoader.Builder) -> YamlConfigurationLoader.Builder = { it }
382459
var nodeMapper: (ConfigurationNode) -> ConfigurationNode = { it }
383460
var modifier: (T, Path) -> T = { it, _ -> it }
461+
var findResource: Boolean = true
462+
var basePath: Path = EsuCore.instance.baseConfigPath()
384463

385464
fun forceLoad(vararg forceLoad: String): Builder<T> {
386465
this.forceLoad = forceLoad.toList()
@@ -422,7 +501,18 @@ object ConfigLoader {
422501
return this
423502
}
424503

425-
fun build() = LoaderSettingsMulti(forceLoad, createKeys, loadSubDirectories, keyMapper, yamlLoader, nodeMapper, modifier)
504+
fun findResource(findResource: Boolean): Builder<T> {
505+
this.findResource = findResource
506+
return this
507+
}
508+
509+
fun basePath(basePath: Path): Builder<T> {
510+
this.basePath = basePath
511+
return this
512+
}
513+
514+
515+
fun build() = LoaderSettingsMulti(forceLoad, createKeys, loadSubDirectories, keyMapper, yamlLoader, nodeMapper, modifier, findResource, basePath)
426516

427517
}
428518
}

0 commit comments

Comments
 (0)