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