pull/1155/head
gedoor 3 years ago
parent 6cdb889c6e
commit 538d40822e
  1. 137
      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<String>? {
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<Any> {
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<SourceRule> {
var vRuleStr = ruleStr
fun splitSourceRule(ruleStr: String?, isList: Boolean = false): List<SourceRule> {
if (ruleStr.isNullOrEmpty()) return ArrayList<SourceRule>()
val ruleList = ArrayList<SourceRule>()
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(":") -> {
var mMode: Mode = Mode.Default
var start = 0
//仅首字符为:时为AllInOne,其实:与伪类选择器冲突,建议改成?更合理
if (isList && ruleStr.startsWith(":")) {
mMode = Mode.Regex
isRegex = true
vRuleStr = vRuleStr.substring(1)
start = 1
}
isRegex -> mMode = Mode.Regex
isJSON -> mMode = Mode.Json
}
//拆分为规则列表
var start = 0
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("<js>")) {
ruleStr.substring(4, ruleStr.lastIndexOf("<"))
} else {
ruleStr.substring(4)
}
} else {
when {
rule = when {
mode == Mode.Js -> ruleStr
ruleStr.startsWith("@CSS:", true) -> {
mode = Mode.Default
rule = ruleStr
ruleStr
}
ruleStr.startsWith("@@") -> {
mode = Mode.Default
rule = ruleStr.substring(2)
ruleStr.substring(2)
}
ruleStr.startsWith("@XPath:", true) -> {
mode = Mode.XPath
rule = ruleStr.substring(7)
ruleStr.substring(7)
}
ruleStr.startsWith("//") -> {//XPath特征很明显,无需配置单独的识别标头
ruleStr.startsWith("/") -> {//XPath特征很明显,无需配置单独的识别标头
mode = Mode.XPath
rule = ruleStr
ruleStr
}
ruleStr.startsWith("@Json:", true) -> {
mode = Mode.Json
rule = ruleStr.substring(6)
ruleStr.substring(6)
}
ruleStr.startsWith("$.") -> {
isJSON || ruleStr.startsWith("$.") || ruleStr.startsWith("$[") -> {
mode = Mode.Json
rule = ruleStr
}
else -> rule = ruleStr
ruleStr
}
else -> ruleStr
}
//分离put
rule = splitPutRule(rule, putMap)
@ -471,7 +445,14 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
var start = 0
var tmp: String
val evalMatcher = evalPattern.matcher(rule)
while (evalMatcher.find()) {
if (evalMatcher.find()) {
if (mode != Mode.Js && mode != Mode.Regex
&& evalMatcher.start() == 0
) {
mode = Mode.Regex
}
do {
if (evalMatcher.start() > start) {
tmp = rule.substring(start, evalMatcher.start())
if (mode != Mode.Js && mode != Mode.Regex
@ -480,10 +461,6 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
mode = Mode.Regex
}
splitRegex(tmp)
} else if (mode != Mode.Js && mode != Mode.Regex
&& evalMatcher.start() == 0
) {
mode = Mode.Regex
}
tmp = evalMatcher.group()
when {
@ -500,6 +477,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
}
}
start = evalMatcher.end()
} while (evalMatcher.find())
}
if (rule.length > start) {
tmp = rule.substring(start)
@ -515,10 +493,12 @@ 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
}
do {
if (regexMatcher.start() > start) {
tmp = ruleStr.substring(start, regexMatcher.start())
ruleType.add(defaultRuleType)
@ -528,6 +508,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions {
ruleType.add(tmp.substring(1).toInt())
ruleParam.add(tmp)
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}")

Loading…
Cancel
Save