diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index d0ff5ba2e..e19d90513 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -25,7 +25,9 @@ import kotlin.collections.HashMap @Keep @Suppress("unused", "RegExpRedundantEscape") class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { - var book: BaseBook? = null + + var book = if (ruleData is BaseBook) ruleData else null + var chapter: BookChapter? = null var nextChapterUrl: String? = null var content: Any? = null @@ -42,18 +44,12 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { private var objectChangedJS = false private var objectChangedJP = false - init { - if (ruleData is BaseBook) { - book = ruleData - } - } - @JvmOverloads fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule { - if (content == null) throw AssertionError("Content cannot be null") + if (content == null) throw AssertionError("内容不可空(Content cannot be null)") this.content = content - setBaseUrl(baseUrl) isJSON = content.toString().isJson() + setBaseUrl(baseUrl) objectChangedXP = true objectChangedJS = true objectChangedJP = true @@ -63,8 +59,6 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { fun setBaseUrl(baseUrl: String?): AnalyzeRule { baseUrl?.let { this.baseUrl = baseUrl - val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl) - if (urlMatcher.find()) this.baseUrl = baseUrl.substring(0, urlMatcher.start()) } return this } @@ -128,7 +122,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { @JvmOverloads fun getStringList(rule: String?, isUrl: Boolean = false): List? { if (rule.isNullOrEmpty()) return null - val ruleList = splitSourceRule(rule) + val ruleList = splitSourceRule(rule, true) return getStringList(ruleList, isUrl) } @@ -288,7 +282,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { @Suppress("UNCHECKED_CAST") fun getElements(ruleStr: String): List { var result: Any? = null - val ruleList = splitSourceRule(ruleStr) + val ruleList = splitSourceRule(ruleStr, true) content?.let { o -> if (ruleList.isNotEmpty()) result = o for (sourceRule in ruleList) { @@ -363,60 +357,48 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { /** * 分解规则生成规则列表 */ - fun splitSourceRule(ruleStr: String?, mode: Mode = Mode.Default): List { - var vRuleStr = ruleStr + fun splitSourceRule(ruleStr: String?, isList: Boolean = false): List { + if (ruleStr.isNullOrEmpty()) return ArrayList() val ruleList = ArrayList() - if (vRuleStr.isNullOrEmpty()) return ruleList - //检测Mode - var mMode: Mode = mode - when { - vRuleStr.startsWith("@@") -> { - vRuleStr = vRuleStr.substring(2) - } - vRuleStr.startsWith("@XPath:", true) -> { - mMode = Mode.XPath - vRuleStr = vRuleStr.substring(7) - } - vRuleStr.startsWith("@Json:", true) -> { - mMode = Mode.Json - vRuleStr = vRuleStr.substring(6) - } - vRuleStr.startsWith(":") -> { - mMode = Mode.Regex - isRegex = true - vRuleStr = vRuleStr.substring(1) - } - isRegex -> mMode = Mode.Regex - isJSON -> mMode = Mode.Json - } - //拆分为规则列表 + var mMode: Mode = Mode.Default var start = 0 + //仅首字符为:时为AllInOne,其实:与伪类选择器冲突,建议改成?更合理 + if (isList && ruleStr.startsWith(":")) { + mMode = Mode.Regex + isRegex = true + start = 1 + } var tmp: String - val jsMatcher = JS_PATTERN.matcher(vRuleStr) + val jsMatcher = JS_PATTERN.matcher(ruleStr) while (jsMatcher.find()) { if (jsMatcher.start() > start) { - tmp = vRuleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } - if (!TextUtils.isEmpty(tmp)) { + tmp = ruleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } + if (tmp.isNotEmpty()) { ruleList.add(SourceRule(tmp, mMode)) } } - ruleList.add(SourceRule(jsMatcher.group(), Mode.Js)) + ruleList.add(SourceRule(jsMatcher.group(2) ?: jsMatcher.group(1), Mode.Js)) start = jsMatcher.end() } - if (vRuleStr.length > start) { - tmp = vRuleStr.substring(start).trim { it <= ' ' } - if (!TextUtils.isEmpty(tmp)) { + + if (ruleStr.length > start) { + tmp = ruleStr.substring(start).trim { it <= ' ' } + if (tmp.isNotEmpty()) { ruleList.add(SourceRule(tmp, mMode)) } } + return ruleList } /** * 规则类 */ - inner class SourceRule internal constructor(ruleStr: String, mainMode: Mode = Mode.Default) { - internal var mode: Mode + + inner class SourceRule internal constructor( + ruleStr: String, + internal var mode: Mode = Mode.Default + ) { internal var rule: String internal var replaceRegex = "" internal var replacement = "" @@ -429,41 +411,33 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { private val defaultRuleType = 0 init { - this.mode = mainMode - if (mode == Mode.Js) { - rule = if (ruleStr.startsWith("")) { - ruleStr.substring(4, ruleStr.lastIndexOf("<")) - } else { - ruleStr.substring(4) + rule = when { + mode == Mode.Js -> ruleStr + ruleStr.startsWith("@CSS:", true) -> { + mode = Mode.Default + ruleStr } - } else { - when { - ruleStr.startsWith("@CSS:", true) -> { - mode = Mode.Default - rule = ruleStr - } - ruleStr.startsWith("@@") -> { - mode = Mode.Default - rule = ruleStr.substring(2) - } - ruleStr.startsWith("@XPath:", true) -> { - mode = Mode.XPath - rule = ruleStr.substring(7) - } - ruleStr.startsWith("//") -> {//XPath特征很明显,无需配置单独的识别标头 - mode = Mode.XPath - rule = ruleStr - } - ruleStr.startsWith("@Json:", true) -> { - mode = Mode.Json - rule = ruleStr.substring(6) - } - ruleStr.startsWith("$.") -> { - mode = Mode.Json - rule = ruleStr - } - else -> rule = ruleStr + ruleStr.startsWith("@@") -> { + mode = Mode.Default + ruleStr.substring(2) + } + ruleStr.startsWith("@XPath:", true) -> { + mode = Mode.XPath + ruleStr.substring(7) + } + ruleStr.startsWith("/") -> {//XPath特征很明显,无需配置单独的识别标头 + mode = Mode.XPath + ruleStr + } + ruleStr.startsWith("@Json:", true) -> { + mode = Mode.Json + ruleStr.substring(6) } + isJSON || ruleStr.startsWith("$.") || ruleStr.startsWith("$[") -> { + mode = Mode.Json + ruleStr + } + else -> ruleStr } //分离put rule = splitPutRule(rule, putMap) @@ -471,35 +445,39 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { var start = 0 var tmp: String val evalMatcher = evalPattern.matcher(rule) - while (evalMatcher.find()) { - if (evalMatcher.start() > start) { - tmp = rule.substring(start, evalMatcher.start()) - if (mode != Mode.Js && mode != Mode.Regex - && start == 0 && !tmp.contains("##") - ) { - mode = Mode.Regex - } - splitRegex(tmp) - } else if (mode != Mode.Js && mode != Mode.Regex + + if (evalMatcher.find()) { + if (mode != Mode.Js && mode != Mode.Regex && evalMatcher.start() == 0 ) { mode = Mode.Regex } - tmp = evalMatcher.group() - when { - tmp.startsWith("@get:", true) -> { - ruleType.add(getRuleType) - ruleParam.add(tmp.substring(6, tmp.lastIndex)) - } - tmp.startsWith("{{") -> { - ruleType.add(jsRuleType) - ruleParam.add(tmp.substring(2, tmp.length - 2)) - } - else -> { + do { + if (evalMatcher.start() > start) { + tmp = rule.substring(start, evalMatcher.start()) + if (mode != Mode.Js && mode != Mode.Regex + && start == 0 && !tmp.contains("##") + ) { + mode = Mode.Regex + } splitRegex(tmp) } - } - start = evalMatcher.end() + tmp = evalMatcher.group() + when { + tmp.startsWith("@get:", true) -> { + ruleType.add(getRuleType) + ruleParam.add(tmp.substring(6, tmp.lastIndex)) + } + tmp.startsWith("{{") -> { + ruleType.add(jsRuleType) + ruleParam.add(tmp.substring(2, tmp.length - 2)) + } + else -> { + splitRegex(tmp) + } + } + start = evalMatcher.end() + } while (evalMatcher.find()) } if (rule.length > start) { tmp = rule.substring(start) @@ -515,19 +493,22 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { var tmp: String val ruleStrArray = ruleStr.split("##") val regexMatcher = regexPattern.matcher(ruleStrArray[0]) - while (regexMatcher.find()) { + + if (regexMatcher.find()) { if (mode != Mode.Js && mode != Mode.Regex) { mode = Mode.Regex } - if (regexMatcher.start() > start) { - tmp = ruleStr.substring(start, regexMatcher.start()) - ruleType.add(defaultRuleType) + do { + if (regexMatcher.start() > start) { + tmp = ruleStr.substring(start, regexMatcher.start()) + ruleType.add(defaultRuleType) + ruleParam.add(tmp) + } + tmp = regexMatcher.group() + ruleType.add(tmp.substring(1).toInt()) ruleParam.add(tmp) - } - tmp = regexMatcher.group() - ruleType.add(tmp.substring(1).toInt()) - ruleParam.add(tmp) - start = regexMatcher.end() + start = regexMatcher.end() + } while (regexMatcher.find()) } if (ruleStr.length > start) { tmp = ruleStr.substring(start) @@ -600,15 +581,10 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { } private fun isRule(ruleStr: String): Boolean { - return when { - ruleStr.startsWith("$.") -> true - ruleStr.startsWith("@Json:", true) -> true - ruleStr.startsWith("//") -> true - ruleStr.startsWith("@XPath:", true) -> true - ruleStr.startsWith("@CSS:", true) -> true - ruleStr.startsWith("@@") -> true - else -> false - } + return ruleStr.startsWith('@') //js首个字符不可能是@,除非是装饰器,所以@开头规定为规则 + || ruleStr.startsWith("$.") + || ruleStr.startsWith("$[") + || ruleStr.startsWith("//") } } @@ -686,7 +662,6 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { companion object { private val putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE) - private val getPattern = Pattern.compile("@get:\\{([^}]+?)\\}", Pattern.CASE_INSENSITIVE) private val evalPattern = Pattern.compile("@get:\\{[^}]+?\\}|\\{\\{[\\w\\W]*?\\}\\}", Pattern.CASE_INSENSITIVE) private val regexPattern = Pattern.compile("\\$\\d{1,2}")