From 2eb138646d7d5cc7723fa51bd93f1e5c3c01d391 Mon Sep 17 00:00:00 2001 From: kunfei Date: Mon, 17 Oct 2022 20:17:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 3 - .../legado/app/data/entities/BookChapter.kt | 6 +- .../legado/app/help/book/ContentProcessor.kt | 7 +- .../io/legado/app/utils/RegexExtensions.kt | 74 +++++++++++++------ 4 files changed, 58 insertions(+), 32 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 293991271..465f241f8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -212,9 +212,6 @@ dependencies { //implementation('com.github.gedoor:rhino-android:1.8') implementation(fileTree(dir: 'lib', include: ['rhino-*.jar'])) - //正则匹配,android自带的正则匹配出现超时问题无法处理 - implementation('com.google.re2j:re2j:1.7') - //网络 implementation('com.squareup.okhttp3:okhttp:4.10.0') implementation(fileTree(dir: 'cronetlib', include: ['*.jar', '*.aar'])) diff --git a/app/src/main/java/io/legado/app/data/entities/BookChapter.kt b/app/src/main/java/io/legado/app/data/entities/BookChapter.kt index 8e3f9d745..47406882d 100644 --- a/app/src/main/java/io/legado/app/data/entities/BookChapter.kt +++ b/app/src/main/java/io/legado/app/data/entities/BookChapter.kt @@ -100,10 +100,10 @@ data class BookChapter( if (item.pattern.isNotEmpty()) { try { val mDisplayTitle = if (item.isRegex) { - displayTitle.replaceRegex( - item.pattern, + displayTitle.replace( + item.pattern.toRegex(), item.replacement - ).getOrThrow() + ) } else { displayTitle.replace(item.pattern, item.replacement) } diff --git a/app/src/main/java/io/legado/app/help/book/ContentProcessor.kt b/app/src/main/java/io/legado/app/help/book/ContentProcessor.kt index a6acfeeb4..098684a1f 100644 --- a/app/src/main/java/io/legado/app/help/book/ContentProcessor.kt +++ b/app/src/main/java/io/legado/app/help/book/ContentProcessor.kt @@ -9,7 +9,6 @@ import io.legado.app.data.entities.ReplaceRule import io.legado.app.exception.RegexTimeoutException import io.legado.app.help.config.AppConfig import io.legado.app.help.config.ReadBookConfig -import io.legado.app.utils.replaceRegex import io.legado.app.utils.stackTraceStr import io.legado.app.utils.toastOnUi import kotlinx.coroutines.CancellationException @@ -141,10 +140,10 @@ class ContentProcessor private constructor( if (item.pattern.isNotEmpty()) { try { mContent = if (item.isRegex) { - mContent.replaceRegex( - item.pattern, + mContent.replace( + item.pattern.toRegex(), item.replacement - ).getOrThrow() + ) } else { mContent.replace(item.pattern, item.replacement) } diff --git a/app/src/main/java/io/legado/app/utils/RegexExtensions.kt b/app/src/main/java/io/legado/app/utils/RegexExtensions.kt index aa1d5bd41..f1a4a300f 100644 --- a/app/src/main/java/io/legado/app/utils/RegexExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/RegexExtensions.kt @@ -1,34 +1,64 @@ package io.legado.app.utils -import com.google.re2j.Pattern +import android.util.Log +import androidx.core.os.postDelayed import com.script.SimpleBindings import io.legado.app.constant.AppConst +import io.legado.app.exception.RegexTimeoutException +import io.legado.app.help.CrashHandler +import io.legado.app.help.coroutine.Coroutine +import kotlinx.coroutines.suspendCancellableCoroutine +import splitties.init.appCtx +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException + +private val handler by lazy { buildMainHandler() } /** - * 带有js功能的正则替换 - * 采用com.google.re2j.Pattern,不会导致无限执行 + * 带有超时检测的正则替换 */ -fun CharSequence.replaceRegex(regex: String, replacement: String): Result { - return kotlin.runCatching { - val charSequence = this - val isJs = replacement.startsWith("@js:") - val replacement1 = if (isJs) replacement.substring(4) else replacement - val pattern = Pattern.compile(regex) - val matcher = pattern.matcher(charSequence) - val stringBuffer = StringBuffer() - while (matcher.find()) { - if (isJs) { - val bindings = SimpleBindings() - bindings["result"] = matcher.group() - val jsResult = - AppConst.SCRIPT_ENGINE.eval(replacement1, bindings).toString() - matcher.appendReplacement(stringBuffer, jsResult) - } else { - matcher.appendReplacement(stringBuffer, replacement1) +suspend fun CharSequence.replace(regex: Regex, replacement: String, timeout: Long): String { + val charSequence = this@replace + val isJs = replacement.startsWith("@js:") + val replacement1 = if (isJs) replacement.substring(4) else replacement + return suspendCancellableCoroutine { block -> + val coroutine = Coroutine.async { + try { + val pattern = regex.toPattern() + val matcher = pattern.matcher(charSequence) + val stringBuffer = StringBuffer() + while (matcher.find()) { + if (isJs) { + val bindings = SimpleBindings() + bindings["result"] = matcher.group() + val jsResult = + AppConst.SCRIPT_ENGINE.eval(replacement1, bindings).toString() + matcher.appendReplacement(stringBuffer, jsResult) + } else { + matcher.appendReplacement(stringBuffer, replacement1) + } + } + matcher.appendTail(stringBuffer) + Log.e("regex", "end") + block.resume(stringBuffer.toString()) + } catch (e: Exception) { + block.resumeWithException(e) + } + } + handler.postDelayed(timeout) { + if (coroutine.isActive) { + val timeoutMsg = "替换超时,3秒后还未结束将重启应用\n替换规则$regex\n替换内容:${this}" + val exception = RegexTimeoutException(timeoutMsg) + block.cancel(exception) + appCtx.longToastOnUi(timeoutMsg) + CrashHandler.saveCrashInfo2File(exception) + handler.postDelayed(3000) { + if (coroutine.isActive) { + appCtx.restart() + } + } } } - matcher.appendTail(stringBuffer) - stringBuffer.toString() } }