|
|
@ -25,9 +25,7 @@ import kotlin.collections.HashMap |
|
|
|
@Keep |
|
|
|
@Keep |
|
|
|
@Suppress("unused", "RegExpRedundantEscape") |
|
|
|
@Suppress("unused", "RegExpRedundantEscape") |
|
|
|
class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
|
|
|
|
var book: BaseBook? = null |
|
|
|
var book = if (ruleData is BaseBook) ruleData else null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var chapter: BookChapter? = null |
|
|
|
var chapter: BookChapter? = null |
|
|
|
var nextChapterUrl: String? = null |
|
|
|
var nextChapterUrl: String? = null |
|
|
|
var content: Any? = null |
|
|
|
var content: Any? = null |
|
|
@ -44,12 +42,18 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
private var objectChangedJS = false |
|
|
|
private var objectChangedJS = false |
|
|
|
private var objectChangedJP = false |
|
|
|
private var objectChangedJP = false |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
init { |
|
|
|
|
|
|
|
if (ruleData is BaseBook) { |
|
|
|
|
|
|
|
book = ruleData |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@JvmOverloads |
|
|
|
@JvmOverloads |
|
|
|
fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule { |
|
|
|
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 |
|
|
|
this.content = content |
|
|
|
isJSON = content.toString().isJson() |
|
|
|
|
|
|
|
setBaseUrl(baseUrl) |
|
|
|
setBaseUrl(baseUrl) |
|
|
|
|
|
|
|
isJSON = content.toString().isJson() |
|
|
|
objectChangedXP = true |
|
|
|
objectChangedXP = true |
|
|
|
objectChangedJS = true |
|
|
|
objectChangedJS = true |
|
|
|
objectChangedJP = true |
|
|
|
objectChangedJP = true |
|
|
@ -59,14 +63,16 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
fun setBaseUrl(baseUrl: String?): AnalyzeRule { |
|
|
|
fun setBaseUrl(baseUrl: String?): AnalyzeRule { |
|
|
|
baseUrl?.let { |
|
|
|
baseUrl?.let { |
|
|
|
this.baseUrl = baseUrl |
|
|
|
this.baseUrl = baseUrl |
|
|
|
|
|
|
|
val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl) |
|
|
|
|
|
|
|
if (urlMatcher.find()) this.baseUrl = baseUrl.substring(0, urlMatcher.start()) |
|
|
|
} |
|
|
|
} |
|
|
|
return this |
|
|
|
return this |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun setRedirectUrl(url: String): URL? { |
|
|
|
fun setRedirectUrl(url: String): URL? { |
|
|
|
kotlin.runCatching { |
|
|
|
kotlin.runCatching { |
|
|
|
val urlMatcher = AnalyzeUrl.paramPattern.matcher(url) |
|
|
|
val urlMatcher = AnalyzeUrl.paramPattern.matcher(baseUrl) |
|
|
|
redirectUrl = URL( if(urlMatcher.find())url.substring(0,urlMatcher.start()) else url) |
|
|
|
redirectUrl = URL(if (urlMatcher.find()) url.substring(0, urlMatcher.start()) else url) |
|
|
|
} |
|
|
|
} |
|
|
|
return redirectUrl |
|
|
|
return redirectUrl |
|
|
|
} |
|
|
|
} |
|
|
@ -122,7 +128,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
@JvmOverloads |
|
|
|
@JvmOverloads |
|
|
|
fun getStringList(rule: String?, isUrl: Boolean = false): List<String>? { |
|
|
|
fun getStringList(rule: String?, isUrl: Boolean = false): List<String>? { |
|
|
|
if (rule.isNullOrEmpty()) return null |
|
|
|
if (rule.isNullOrEmpty()) return null |
|
|
|
val ruleList = splitSourceRule(rule, true) |
|
|
|
val ruleList = splitSourceRule(rule) |
|
|
|
return getStringList(ruleList, isUrl) |
|
|
|
return getStringList(ruleList, isUrl) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -282,7 +288,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
@Suppress("UNCHECKED_CAST") |
|
|
|
@Suppress("UNCHECKED_CAST") |
|
|
|
fun getElements(ruleStr: String): List<Any> { |
|
|
|
fun getElements(ruleStr: String): List<Any> { |
|
|
|
var result: Any? = null |
|
|
|
var result: Any? = null |
|
|
|
val ruleList = splitSourceRule(ruleStr, true) |
|
|
|
val ruleList = splitSourceRule(ruleStr) |
|
|
|
content?.let { o -> |
|
|
|
content?.let { o -> |
|
|
|
if (ruleList.isNotEmpty()) result = o |
|
|
|
if (ruleList.isNotEmpty()) result = o |
|
|
|
for (sourceRule in ruleList) { |
|
|
|
for (sourceRule in ruleList) { |
|
|
@ -357,44 +363,58 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
/** |
|
|
|
/** |
|
|
|
* 分解规则生成规则列表 |
|
|
|
* 分解规则生成规则列表 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
fun splitSourceRule(ruleStr: String?, isList: Boolean = false): List<SourceRule> { |
|
|
|
fun splitSourceRule(ruleStr: String?, mode: Mode = Mode.Default): List<SourceRule> { |
|
|
|
if (ruleStr.isNullOrEmpty()) return ArrayList<SourceRule>() |
|
|
|
var vRuleStr = ruleStr |
|
|
|
val ruleList = ArrayList<SourceRule>() |
|
|
|
val ruleList = ArrayList<SourceRule>() |
|
|
|
var mMode: Mode = Mode.Default |
|
|
|
if (vRuleStr.isNullOrEmpty()) return ruleList |
|
|
|
var start = 0 |
|
|
|
//检测Mode |
|
|
|
//仅首字符为:时为AllInOne,其实:与伪类选择器冲突,建议改成?更合理 |
|
|
|
var mMode: Mode = mode |
|
|
|
if (isList && ruleStr.startsWith(":")) { |
|
|
|
when { |
|
|
|
mMode = Mode.Regex |
|
|
|
vRuleStr.startsWith("@@") -> { |
|
|
|
isRegex = true |
|
|
|
vRuleStr = vRuleStr.substring(2) |
|
|
|
start = 1 |
|
|
|
} |
|
|
|
|
|
|
|
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 start = 0 |
|
|
|
var tmp: String |
|
|
|
var tmp: String |
|
|
|
val jsMatcher = JS_PATTERN.matcher(ruleStr) |
|
|
|
val jsMatcher = JS_PATTERN.matcher(vRuleStr) |
|
|
|
while (jsMatcher.find()) { |
|
|
|
while (jsMatcher.find()) { |
|
|
|
if (jsMatcher.start() > start) { |
|
|
|
if (jsMatcher.start() > start) { |
|
|
|
tmp = ruleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } |
|
|
|
tmp = vRuleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } |
|
|
|
if (tmp.isNotEmpty()) { |
|
|
|
if (!TextUtils.isEmpty(tmp)) { |
|
|
|
ruleList.add(SourceRule(tmp, mMode)) |
|
|
|
ruleList.add(SourceRule(tmp, mMode)) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
ruleList.add(SourceRule(jsMatcher.group(2) ?: jsMatcher.group(1), Mode.Js)) |
|
|
|
ruleList.add(SourceRule(jsMatcher.group(), Mode.Js)) |
|
|
|
start = jsMatcher.end() |
|
|
|
start = jsMatcher.end() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (vRuleStr.length > start) { |
|
|
|
if (ruleStr.length > start) { |
|
|
|
tmp = vRuleStr.substring(start).trim { it <= ' ' } |
|
|
|
tmp = ruleStr.substring(start).trim { it <= ' ' } |
|
|
|
if (!TextUtils.isEmpty(tmp)) { |
|
|
|
if (tmp.isNotEmpty()) { |
|
|
|
|
|
|
|
ruleList.add(SourceRule(tmp, mMode)) |
|
|
|
ruleList.add(SourceRule(tmp, mMode)) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ruleList |
|
|
|
return ruleList |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 规则类 |
|
|
|
* 规则类 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
inner class SourceRule internal constructor(ruleStr: String, mainMode: Mode = Mode.Default) { |
|
|
|
inner class SourceRule internal constructor(ruleStr: String, mainMode: Mode = Mode.Default) { |
|
|
|
internal var mode: Mode |
|
|
|
internal var mode: Mode |
|
|
|
internal var rule: String |
|
|
|
internal var rule: String |
|
|
@ -410,33 +430,39 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
|
|
|
|
|
|
|
|
init { |
|
|
|
init { |
|
|
|
this.mode = mainMode |
|
|
|
this.mode = mainMode |
|
|
|
rule = when { |
|
|
|
if (mode == Mode.Js) { |
|
|
|
ruleStr.startsWith("@CSS:", true) -> { |
|
|
|
rule = if (ruleStr.startsWith("<js>")) { |
|
|
|
mode = Mode.Default |
|
|
|
ruleStr.substring(4, ruleStr.lastIndexOf("<")) |
|
|
|
ruleStr |
|
|
|
} else { |
|
|
|
} |
|
|
|
ruleStr.substring(4) |
|
|
|
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) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ruleStr.startsWith("$.") || isJSON -> { |
|
|
|
|
|
|
|
mode = Mode.Json |
|
|
|
|
|
|
|
ruleStr |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
else -> { |
|
|
|
} else { |
|
|
|
ruleStr |
|
|
|
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 |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
//分离put |
|
|
|
//分离put |
|
|
@ -445,39 +471,35 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
var start = 0 |
|
|
|
var start = 0 |
|
|
|
var tmp: String |
|
|
|
var tmp: String |
|
|
|
val evalMatcher = evalPattern.matcher(rule) |
|
|
|
val evalMatcher = evalPattern.matcher(rule) |
|
|
|
|
|
|
|
while (evalMatcher.find()) { |
|
|
|
if (evalMatcher.find()) { |
|
|
|
if (evalMatcher.start() > start) { |
|
|
|
if (mode != Mode.Js && mode != Mode.Regex |
|
|
|
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 |
|
|
|
&& evalMatcher.start() == 0 |
|
|
|
&& evalMatcher.start() == 0 |
|
|
|
) { |
|
|
|
) { |
|
|
|
mode = Mode.Regex |
|
|
|
mode = Mode.Regex |
|
|
|
} |
|
|
|
} |
|
|
|
do { |
|
|
|
tmp = evalMatcher.group() |
|
|
|
if (evalMatcher.start() > start) { |
|
|
|
when { |
|
|
|
tmp = rule.substring(start, evalMatcher.start()) |
|
|
|
tmp.startsWith("@get:", true) -> { |
|
|
|
if (mode != Mode.Js && mode != Mode.Regex |
|
|
|
ruleType.add(getRuleType) |
|
|
|
&& start == 0 && !tmp.contains("##") |
|
|
|
ruleParam.add(tmp.substring(6, tmp.lastIndex)) |
|
|
|
) { |
|
|
|
|
|
|
|
mode = Mode.Regex |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
splitRegex(tmp) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
tmp = evalMatcher.group() |
|
|
|
tmp.startsWith("{{") -> { |
|
|
|
when { |
|
|
|
ruleType.add(jsRuleType) |
|
|
|
tmp.startsWith("@get:", true) -> { |
|
|
|
ruleParam.add(tmp.substring(2, tmp.length - 2)) |
|
|
|
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() |
|
|
|
else -> { |
|
|
|
} while (evalMatcher.find()) |
|
|
|
splitRegex(tmp) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
start = evalMatcher.end() |
|
|
|
} |
|
|
|
} |
|
|
|
if (rule.length > start) { |
|
|
|
if (rule.length > start) { |
|
|
|
tmp = rule.substring(start) |
|
|
|
tmp = rule.substring(start) |
|
|
@ -493,22 +515,19 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
var tmp: String |
|
|
|
var tmp: String |
|
|
|
val ruleStrArray = ruleStr.split("##") |
|
|
|
val ruleStrArray = ruleStr.split("##") |
|
|
|
val regexMatcher = regexPattern.matcher(ruleStrArray[0]) |
|
|
|
val regexMatcher = regexPattern.matcher(ruleStrArray[0]) |
|
|
|
|
|
|
|
while (regexMatcher.find()) { |
|
|
|
if (regexMatcher.find()) { |
|
|
|
|
|
|
|
if (mode != Mode.Js && mode != Mode.Regex) { |
|
|
|
if (mode != Mode.Js && mode != Mode.Regex) { |
|
|
|
mode = Mode.Regex |
|
|
|
mode = Mode.Regex |
|
|
|
} |
|
|
|
} |
|
|
|
do { |
|
|
|
if (regexMatcher.start() > start) { |
|
|
|
if (regexMatcher.start() > start) { |
|
|
|
tmp = ruleStr.substring(start, regexMatcher.start()) |
|
|
|
tmp = ruleStr.substring(start, regexMatcher.start()) |
|
|
|
ruleType.add(defaultRuleType) |
|
|
|
ruleType.add(defaultRuleType) |
|
|
|
|
|
|
|
ruleParam.add(tmp) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
tmp = regexMatcher.group() |
|
|
|
|
|
|
|
ruleType.add(tmp.substring(1).toInt()) |
|
|
|
|
|
|
|
ruleParam.add(tmp) |
|
|
|
ruleParam.add(tmp) |
|
|
|
start = regexMatcher.end() |
|
|
|
} |
|
|
|
} while (regexMatcher.find()) |
|
|
|
tmp = regexMatcher.group() |
|
|
|
|
|
|
|
ruleType.add(tmp.substring(1).toInt()) |
|
|
|
|
|
|
|
ruleParam.add(tmp) |
|
|
|
|
|
|
|
start = regexMatcher.end() |
|
|
|
} |
|
|
|
} |
|
|
|
if (ruleStr.length > start) { |
|
|
|
if (ruleStr.length > start) { |
|
|
|
tmp = ruleStr.substring(start) |
|
|
|
tmp = ruleStr.substring(start) |
|
|
@ -581,10 +600,15 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private fun isRule(ruleStr: String): Boolean { |
|
|
|
private fun isRule(ruleStr: String): Boolean { |
|
|
|
return ruleStr.startsWith('@') //js首个字符不可能是@,除非是装饰器,所以@开头规定为规则 |
|
|
|
return when { |
|
|
|
|| ruleStr.startsWith("$.") |
|
|
|
ruleStr.startsWith("$.") -> true |
|
|
|
|| ruleStr.startsWith("$[") |
|
|
|
ruleStr.startsWith("@Json:", true) -> true |
|
|
|
|| ruleStr.startsWith("//") |
|
|
|
ruleStr.startsWith("//") -> true |
|
|
|
|
|
|
|
ruleStr.startsWith("@XPath:", true) -> true |
|
|
|
|
|
|
|
ruleStr.startsWith("@CSS:", true) -> true |
|
|
|
|
|
|
|
ruleStr.startsWith("@@") -> true |
|
|
|
|
|
|
|
else -> false |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -662,6 +686,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { |
|
|
|
|
|
|
|
|
|
|
|
companion object { |
|
|
|
companion object { |
|
|
|
private val putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE) |
|
|
|
private val putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE) |
|
|
|
|
|
|
|
private val getPattern = Pattern.compile("@get:\\{([^}]+?)\\}", Pattern.CASE_INSENSITIVE) |
|
|
|
private val evalPattern = |
|
|
|
private val evalPattern = |
|
|
|
Pattern.compile("@get:\\{[^}]+?\\}|\\{\\{[\\w\\W]*?\\}\\}", Pattern.CASE_INSENSITIVE) |
|
|
|
Pattern.compile("@get:\\{[^}]+?\\}|\\{\\{[\\w\\W]*?\\}\\}", Pattern.CASE_INSENSITIVE) |
|
|
|
private val regexPattern = Pattern.compile("\\$\\d{1,2}") |
|
|
|
private val regexPattern = Pattern.compile("\\$\\d{1,2}") |
|
|
|