Horis 3 سال پیش
والد
کامیت
fe0f3c5106

+ 3 - 1
app/src/main/java/io/legado/app/help/AppWebDav.kt

@@ -93,7 +93,9 @@ object AppWebDav {
         val names = arrayListOf<String>()
         authorization?.let {
             var files = WebDav(rootWebDavUrl, it).listFiles()
-            files = files.reversed()
+            files = files.sortedWith { o1, o2 ->
+                AlphanumComparator.compare(o1.displayName, o2.displayName)
+            }.reversed()
             files.forEach { webDav ->
                 val name = webDav.displayName
                 if (name.startsWith("backup")) {

+ 1 - 1
app/src/main/java/io/legado/app/help/book/ContentProcessor.kt

@@ -68,7 +68,7 @@ class ContentProcessor private constructor(
         }
     }
 
-    fun upRemoveSameTitle() {
+    private fun upRemoveSameTitle() {
         val book = appDb.bookDao.getBookByOrigin(bookName, bookOrigin) ?: return
         removeSameTitleCache.clear()
         val files = BookHelp.getChapterFiles(book).filter {

+ 22 - 11
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt

@@ -13,7 +13,7 @@ import io.legado.app.model.webBook.WebBook
 import io.legado.app.utils.*
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withTimeout
-import org.jsoup.nodes.Entities
+import org.apache.commons.text.StringEscapeUtils
 import org.mozilla.javascript.NativeObject
 import java.net.URL
 import java.util.regex.Pattern
@@ -49,6 +49,8 @@ class AnalyzeRule(
     private var objectChangedJS = false
     private var objectChangedJP = false
 
+    private val stringRuleCache = hashMapOf<String, List<SourceRule>>()
+
     @JvmOverloads
     fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule {
         if (content == null) throw AssertionError("内容不可空(Content cannot be null)")
@@ -128,7 +130,7 @@ class AnalyzeRule(
     @JvmOverloads
     fun getStringList(rule: String?, mContent: Any? = null, isUrl: Boolean = false): List<String>? {
         if (rule.isNullOrEmpty()) return null
-        val ruleList = splitSourceRule(rule, false)
+        val ruleList = splitSourceRuleCacheString(rule)
         return getStringList(ruleList, mContent, isUrl)
     }
 
@@ -208,13 +210,13 @@ class AnalyzeRule(
     @JvmOverloads
     fun getString(ruleStr: String?, mContent: Any? = null, isUrl: Boolean = false): String {
         if (TextUtils.isEmpty(ruleStr)) return ""
-        val ruleList = splitSourceRule(ruleStr)
+        val ruleList = splitSourceRuleCacheString(ruleStr)
         return getString(ruleList, mContent, isUrl)
     }
 
     fun getString(ruleStr: String?, unescape: Boolean): String {
         if (TextUtils.isEmpty(ruleStr)) return ""
-        val ruleList = splitSourceRule(ruleStr)
+        val ruleList = splitSourceRuleCacheString(ruleStr)
         return getString(ruleList, unescape = unescape)
     }
 
@@ -270,13 +272,7 @@ class AnalyzeRule(
         }
         if (result == null) result = ""
         val str = if (unescape) {
-            kotlin.runCatching {
-                Entities.unescape(result.toString())
-            }.onFailure {
-                log("Entities.unescape() error\n${it.localizedMessage}")
-            }.getOrElse {
-                result.toString()
-            }
+            StringEscapeUtils.unescapeHtml3(result.toString())
         } else result.toString()
         if (isUrl) {
             return if (str.isBlank()) {
@@ -414,6 +410,21 @@ class AnalyzeRule(
         return vResult
     }
 
+    /**
+     * getString 类规则缓存
+     */
+    fun splitSourceRuleCacheString(ruleStr: String?) : List<SourceRule> {
+        if (ruleStr.isNullOrEmpty()) return emptyList()
+        val cacheRule = stringRuleCache[ruleStr]
+        return if (cacheRule != null) {
+            cacheRule
+        } else {
+            val rules = splitSourceRule(ruleStr)
+            stringRuleCache[ruleStr] = rules
+            rules
+        }
+    }
+
     /**
      * 分解规则生成规则列表
      */

+ 2 - 5
app/src/main/java/io/legado/app/model/localBook/LocalBook.kt

@@ -23,6 +23,7 @@ import io.legado.app.lib.webdav.WebDavException
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.utils.*
 import kotlinx.coroutines.runBlocking
+import org.apache.commons.text.StringEscapeUtils
 import org.jsoup.nodes.Entities
 import splitties.init.appCtx
 import java.io.*
@@ -133,11 +134,7 @@ object LocalBook {
         }
         if (book.isEpub) {
             content = content?.replace("&lt;img", "&lt; img", true) ?: return null
-            return kotlin.runCatching {
-                Entities.unescape(content)
-            }.onFailure {
-                AppLog.put("HTML实体解码失败\n${it.localizedMessage}", it)
-            }.getOrDefault(content)
+            return StringEscapeUtils.unescapeHtml3(content)
         }
         return content
     }

+ 76 - 0
app/src/main/java/io/legado/app/utils/AlphanumComparator.kt

@@ -0,0 +1,76 @@
+package io.legado.app.utils
+
+object AlphanumComparator : Comparator<String> {
+
+    override fun compare(s1: String, s2: String): Int {
+        var thisMarker = 0
+        var thatMarker = 0
+        val s1Length = s1.length
+        val s2Length = s2.length
+
+        while (thisMarker < s1Length && thatMarker < s2Length) {
+            val thisChunk = getChunk(s1, s1Length, thisMarker)
+            thisMarker += thisChunk.length
+
+            val thatChunk = getChunk(s2, s2Length, thatMarker)
+            thatMarker += thatChunk.length
+
+            // If both chunks contain numeric characters, sort them numerically.
+            var result: Int
+            if (isDigit(thisChunk[0]) && isDigit(thatChunk[0])) {
+                // Simple chunk comparison by length.
+                val thisChunkLength = thisChunk.length
+                result = thisChunkLength - thatChunk.length
+                // If equal, the first different number counts.
+                if (result == 0) {
+                    for (i in 0 until thisChunkLength) {
+                        result = thisChunk[i] - thatChunk[i]
+                        if (result != 0) {
+                            return result
+                        }
+                    }
+                }
+            } else {
+                result = thisChunk.compareTo(thatChunk)
+            }
+
+            if (result != 0) {
+                return result
+            }
+        }
+
+        return s1Length - s2Length
+    }
+
+    private fun getChunk(string: String, length: Int, marker: Int): String {
+        var current = marker
+        val chunk = StringBuilder()
+        var c = string[current]
+        chunk.append(c)
+        current++
+        if (isDigit(c)) {
+            while (current < length) {
+                c = string[current]
+                if (!isDigit(c)) {
+                    break
+                }
+                chunk.append(c)
+                current++
+            }
+        } else {
+            while (current < length) {
+                c = string[current]
+                if (isDigit(c)) {
+                    break
+                }
+                chunk.append(c)
+                current++
+            }
+        }
+        return chunk.toString()
+    }
+
+    private fun isDigit(ch: Char): Boolean {
+        return ch in '0'..'9'
+    }
+}

+ 3 - 6
app/src/main/java/io/legado/app/utils/HtmlFormatter.kt

@@ -2,6 +2,7 @@ package io.legado.app.utils
 
 import io.legado.app.constant.AppLog
 import io.legado.app.model.analyzeRule.AnalyzeUrl
+import org.apache.commons.text.StringEscapeUtils
 import org.jsoup.nodes.Entities
 import java.net.URL
 import java.util.regex.Pattern
@@ -31,12 +32,8 @@ object HtmlFormatter {
             .replace("\\s*\\n+\\s*".toRegex(), "\n  ")
             .replace("^[\\n\\s]+".toRegex(), "  ")
             .replace("[\\n\\s]+$".toRegex(), "")
-            .let { s ->
-                kotlin.runCatching {
-                    Entities.unescape(s)
-                }.onFailure {
-                    AppLog.put("Entities.unescape() error\n${it.localizedMessage}", it)
-                }.getOrDefault(s)
+            .let {
+                StringEscapeUtils.unescapeHtml3(it)
             }
     }
 

+ 6 - 2
app/src/main/java/io/legado/app/utils/MD5Utils.kt

@@ -9,12 +9,16 @@ import java.io.InputStream
 @Suppress("unused")
 object MD5Utils {
 
+    private val MD5Digester by lazy {
+        DigestUtil.digester("MD5")
+    }
+
     fun md5Encode(str: String?): String {
-        return DigestUtil.digester("MD5").digestHex(str)
+        return MD5Digester.digestHex(str)
     }
 
     fun md5Encode(inputStream: InputStream): String {
-        return DigestUtil.digester("MD5").digestHex(inputStream)
+        return MD5Digester.digestHex(inputStream)
     }
 
     fun md5Encode16(str: String): String {

+ 4 - 6
modules/rhino1.7.3/src/main/java/com/script/AbstractScriptEngine.kt

@@ -32,9 +32,11 @@ abstract class AbstractScriptEngine(val bindings: Bindings? = null) : ScriptEngi
             200 -> {
                 context.setBindings(bindings, 200)
             }
+
             100 -> {
                 context.setBindings(bindings, 100)
             }
+
             else -> {
                 throw IllegalArgumentException("Invalid scope value.")
             }
@@ -84,15 +86,11 @@ abstract class AbstractScriptEngine(val bindings: Bindings? = null) : ScriptEngi
     }
 
     override fun getScriptContext(bindings: Bindings): ScriptContext {
-        val ctx = SimpleScriptContext()
+        val ctx = SimpleScriptContext(bindings, context.errorWriter, context.reader, context.writer)
         val gs = getBindings(200)
         if (gs != null) {
             ctx.setBindings(gs, 200)
         }
-        ctx.setBindings(bindings, 100)
-        ctx.reader = context.reader
-        ctx.writer = context.writer
-        ctx.errorWriter = context.errorWriter
         return ctx
     }
-}
+}

+ 11 - 5
modules/rhino1.7.3/src/main/java/com/script/SimpleScriptContext.kt

@@ -9,12 +9,14 @@ import java.io.Reader
 import java.io.Writer
 import java.util.*
 
-open class SimpleScriptContext : ScriptContext {
-    private var engineScope: Bindings = SimpleBindings()
-    override var errorWriter: Writer = PrintWriter(System.err, true)
-    private var globalScope: Bindings? = null
-    override var reader: Reader = InputStreamReader(System.`in`)
+open class SimpleScriptContext(
+    private var engineScope: Bindings = SimpleBindings(),
+    override var errorWriter: Writer = PrintWriter(System.err, true),
+    override var reader: Reader = InputStreamReader(System.`in`),
     override var writer: Writer = PrintWriter(System.out, true)
+) : ScriptContext {
+    private var globalScope: Bindings? = null
+
     override fun setBindings(bindings: Bindings?, scope: Int) {
         when (scope) {
             100 -> {
@@ -24,6 +26,7 @@ open class SimpleScriptContext : ScriptContext {
                 engineScope = bindings
                 return
             }
+
             200 -> {
                 globalScope = bindings
                 return
@@ -46,6 +49,7 @@ open class SimpleScriptContext : ScriptContext {
             100 -> {
                 return engineScope[name]
             }
+
             200 -> {
                 return globalScope?.get(name)
             }
@@ -58,6 +62,7 @@ open class SimpleScriptContext : ScriptContext {
             100 -> {
                 return getBindings(100)?.remove(name)
             }
+
             200 -> {
                 return getBindings(200)?.remove(name)
             }
@@ -71,6 +76,7 @@ open class SimpleScriptContext : ScriptContext {
                 engineScope[name] = value
                 return
             }
+
             200 -> {
                 globalScope?.put(name, value)
                 return