diff --git a/app/src/main/java/io/legado/app/constant/AppPattern.kt b/app/src/main/java/io/legado/app/constant/AppPattern.kt index d4a3e4a19..a94b68d34 100644 --- a/app/src/main/java/io/legado/app/constant/AppPattern.kt +++ b/app/src/main/java/io/legado/app/constant/AppPattern.kt @@ -5,7 +5,9 @@ import java.util.regex.Pattern @Suppress("RegExpRedundantEscape") object AppPattern { val JS_PATTERN: Pattern = - Pattern.compile("([\\w\\W]*?|@js:[\\w\\W]*$)", Pattern.CASE_INSENSITIVE) + Pattern.compile("([\\w\\W]+?)", Pattern.CASE_INSENSITIVE) + val JS_PATTERN_END: Pattern = + Pattern.compile("(@js:[\\w\\W]+)", Pattern.CASE_INSENSITIVE) val EXP_PATTERN: Pattern = Pattern.compile("\\{\\{([\\w\\W]*?)\\}\\}") //匹配格式化后的图片格式 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 b8e503599..e22a90ec6 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 @@ -3,6 +3,7 @@ package io.legado.app.model.analyzeRule import android.text.TextUtils import androidx.annotation.Keep import io.legado.app.constant.AppConst.SCRIPT_ENGINE +import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern.JS_PATTERN import io.legado.app.data.entities.BaseBook import io.legado.app.data.entities.BookChapter @@ -357,58 +358,81 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { /** * 分解规则生成规则列表 */ - fun splitSourceRule(ruleStr: String?, mode: Mode = Mode.Default): List { - var vRuleStr = ruleStr + fun splitSourceRule(ruleStr: String?): 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) + var mMode: Mode = Mode.Default + fun mode(ruleStr0:String)=when { + ruleStr0.startsWith("@@") -> { + mMode = Mode.Default + ruleStr0.substring(2) + } + ruleStr0.startsWith("@XPath:", true) -> { + mMode = Mode.XPath + ruleStr0.substring(7) } - vRuleStr.startsWith("@XPath:", true) -> { + ruleStr0.startsWith("/") -> {//XPath特征很明显,无需配置单独的识别标头 mMode = Mode.XPath - vRuleStr = vRuleStr.substring(7) + ruleStr0 } - vRuleStr.startsWith("@Json:", true) -> { + ruleStr0.startsWith("@Json:", true) -> { mMode = Mode.Json - vRuleStr = vRuleStr.substring(6) + ruleStr0.substring(6) } - vRuleStr.startsWith(":") -> { + ruleStr0.startsWith("$.") || ruleStr0.startsWith("$[") -> { + mMode = Mode.Json + ruleStr0 + } + ruleStr0.startsWith("?") -> { mMode = Mode.Regex isRegex = true - vRuleStr = vRuleStr.substring(1) + ruleStr0.substring(1) + } + else -> { + mMode = Mode.Default + ruleStr0 } - isRegex -> mMode = Mode.Regex - isJSON -> mMode = Mode.Json } //拆分为规则列表 var start = 0 var tmp: String - val jsMatcher = JS_PATTERN.matcher(vRuleStr) - while (jsMatcher.find()) { + 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)) { - ruleList.add(SourceRule(tmp, mMode)) + tmp = ruleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } + if (tmp.isNotEmpty()) { + ruleList.add(SourceRule(mode(tmp), mMode)) } } - ruleList.add(SourceRule(jsMatcher.group(), Mode.Js)) + ruleList.add(SourceRule(jsMatcher.group().run{ substring(4, lastIndexOf("<")) }, Mode.Js)) start = jsMatcher.end() } - if (vRuleStr.length > start) { - tmp = vRuleStr.substring(start).trim { it <= ' ' } - if (!TextUtils.isEmpty(tmp)) { - ruleList.add(SourceRule(tmp, mMode)) + + if (ruleStr.length > start){ + val jsMatcherEnd = AppPattern.JS_PATTERN_END.matcher(ruleStr.substring(start)) + if(jsMatcherEnd.find()){ + tmp = ruleStr.substring(start, jsMatcher.start()).trim { it <= ' ' } + if (tmp.isNotEmpty()) { + ruleList.add(SourceRule(mode(tmp), mMode)) + } } - } + ruleList.add(SourceRule(jsMatcher.group().substring(4), Mode.Js)) + }else{ + tmp = ruleStr.substring(start).trim { it <= ' ' } + if (tmp.isNotEmpty()) { + ruleList.add(SourceRule(mode(tmp), mMode)) + } + } + return ruleList } /** * 规则类 */ + inner class SourceRule internal constructor(ruleStr: String, mainMode: Mode = Mode.Default) { internal var mode: Mode internal var rule: String @@ -424,77 +448,42 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { init { this.mode = mainMode - if (mode == Mode.Js) { - rule = if (ruleStr.startsWith("")) { - ruleStr.substring(4, ruleStr.lastIndexOf("<")) - } else { - ruleStr.substring(4) - } - } 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 - } - } //分离put - rule = splitPutRule(rule, putMap) + rule = splitPutRule(ruleStr, putMap) //@get,{{ }}, 拆分 var start = 0 var tmp: String val evalMatcher = evalPattern.matcher(rule) - while (evalMatcher.find()) { - if (evalMatcher.start() > start) { + + if(evalMatcher.find()){ + + var modeX = mode == Mode.Js || mode == Mode.Regex + if(evalMatcher.start() != 0){ tmp = rule.substring(start, evalMatcher.start()) - if (mode != Mode.Js && mode != Mode.Regex - && start == 0 && !tmp.contains("##") - ) { - mode = Mode.Regex - } + modeX = modeX || tmp.contains("##") splitRegex(tmp) - } else 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 -> { - splitRegex(tmp) + if(!modeX)mode = Mode.Regex + + do{ + 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() + start = evalMatcher.end() + }while (evalMatcher.find()) } + if (rule.length > start) { tmp = rule.substring(start) splitRegex(tmp) @@ -509,19 +498,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) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index 0b44f78fd..862e7759f 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -7,6 +7,7 @@ import com.bumptech.glide.load.model.LazyHeaders import io.legado.app.constant.AppConst.SCRIPT_ENGINE import io.legado.app.constant.AppConst.UA_NAME import io.legado.app.constant.AppPattern.JS_PATTERN +import io.legado.app.constant.AppPattern.JS_PATTERN_END import io.legado.app.data.entities.BaseBook import io.legado.app.data.entities.BookChapter import io.legado.app.help.AppConfig @@ -79,7 +80,7 @@ class AnalyzeUrl( while (jsMatcher.find()) { if (jsMatcher.start() > start) { tmp = - ruleUrl.substring(start, jsMatcher.start()).replace("\n", "").trim { it <= ' ' } + ruleUrl.substring(start, jsMatcher.start()).trim { it <= ' ' } if (tmp.isNotEmpty()) { ruleList.add(tmp) } @@ -88,11 +89,24 @@ class AnalyzeUrl( start = jsMatcher.end() } if (ruleUrl.length > start) { - tmp = ruleUrl.substring(start).replace("\n", "").trim { it <= ' ' } - if (tmp.isNotEmpty()) { - ruleList.add(tmp) + val jsMatcherEnd = JS_PATTERN_END.matcher(ruleUrl.substring(start)) + if(jsMatcherEnd.find()){ + if (jsMatcherEnd.start() > start) { + tmp = + ruleUrl.substring(start, jsMatcherEnd.start()).trim { it <= ' ' } + if (tmp.isNotEmpty()) { + ruleList.add(tmp) + } + } + ruleList.add(jsMatcherEnd.group()) + }else{ + tmp = ruleUrl.substring(start).trim { it <= ' ' } + if (tmp.isNotEmpty()) { + ruleList.add(tmp) + } } } + for (rule in ruleList) { var ruleStr = rule when { @@ -130,7 +144,7 @@ class AnalyzeUrl( bindings["book"] = book //替换所有内嵌{{js}} - val url = analyze.innerJsRule{ + val url = analyze.innerRule("{{","}}"){ when(val jsEval = SCRIPT_ENGINE.eval(it, bindings)){ is String -> jsEval jsEval is Double && jsEval % 1.0 == 0.0 -> String.format("%.0f", jsEval) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/RuleAnalyzer.kt b/app/src/main/java/io/legado/app/model/analyzeRule/RuleAnalyzer.kt index cac6eb760..050625dfc 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/RuleAnalyzer.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/RuleAnalyzer.kt @@ -1,5 +1,9 @@ package io.legado.app.model.analyzeRule +import io.legado.app.utils.isJson +import java.util.ArrayList +import java.util.regex.Pattern + //通用的规则切分处理 class RuleAnalyzer(data: String, code: Boolean = false) { @@ -9,14 +13,11 @@ class RuleAnalyzer(data: String, code: Boolean = false) { private var start = 0 //当前处理字段的开始 private var startX = 0 //当前规则的开始 - private var rule = arrayOf() //分割出的规则列表 + private var rule = ArrayList() //分割出的规则列表 private var step: Int = 0 //分割字符的长度 var elementsType = "" //当前分割字符串 var innerType = true //是否为内嵌{{}} - //设置平衡组函数,json或JavaScript时设置成chompCodeBalanced,否则为chompRuleBalanced - val chompBalanced = if (code) ::chompCodeBalanced else ::chompRuleBalanced - fun trim() { // 修剪当前规则之前的"@"或者空白符 if(queue[pos] == '@' || queue[pos] < '!') { //在while里重复设置start和startX会拖慢执行速度,所以先来个判断是否存在需要修剪的字段,最后再一次性设置start和startX pos++ @@ -67,6 +68,31 @@ class RuleAnalyzer(data: String, code: Boolean = false) { pos++ //逐个试探 } + return false + } + /** + * 从剩余字串中拉出一个字符串,直到但不包括匹配序列(匹配参数列表中一项即为匹配),或剩余字串用完。 + * @param seq 匹配字符串序列 + * @return 成功返回true并设置间隔,失败则直接返回fasle + */ + fun chompToAny(vararg seq: String): Boolean { + var pos = pos //声明新变量记录匹配位置,不更改类本身的位置 + + while (pos != queue.length) { + + for (s in seq) { + if (queue.regionMatches(pos, s, 0, s.length)) { + rule += queue.substring(this.pos, pos) + pos += s.length //跳过分隔符 + ruleTypeList += s //追加类型到列表 + start = this.pos + this.pos = pos //匹配成功, 同步处理位置到类 + return true //匹配就返回 true + } + } + + pos++ //逐个试探 + } return false } @@ -91,85 +117,6 @@ class RuleAnalyzer(data: String, code: Boolean = false) { return -1 } - //其中js只要符合语法,就不用避开任何阅读关键字,自由发挥 - fun chompJsBalanced(innerType:Boolean = true,startPos:Int = pos): Boolean { - - var pos = startPos //声明变量记录临时处理位置 - var depth = 0 //{}嵌套深度 - var depthX = 0 //[]嵌套深度 - var balanced = false //平衡状态 - - var inSingleQuote = false //单引号 - var inDoubleQuote = false //双引号 - var inOtherQuote = false //js原始字串分隔字符 - var inCommit = false //单行注释 - var inCommits = false //多行注释 - var commit= false //是否为注释,不分单行注释还是多行注释 - - val start:String - val end:String - val endChar:Char - - if(innerType){ - start = "{{" - endChar = '}' - end = "}" - }else{ - start = "" - endChar = '<' - end = "/js>" - } - - pos += start.length //跳过起始字符串 - - while(true){ - if (pos == queue.length) break - var c = queue[pos++] - - if(c == ESC) {//跳过转义字符序列 - pos++ - continue - } - - if(commit)when (c) { - '/' -> inCommit = true //匹配单行注释起点 - '*' -> inCommits = true //匹配多行注释起点 - else -> commit = false - } - - if (c == '\'' && !commit && !inDoubleQuote && !inOtherQuote) inSingleQuote = - !inSingleQuote //匹配具有语法功能的单引号 - else if (c == '"' && !commit && !inSingleQuote && !inOtherQuote) inDoubleQuote = - !inDoubleQuote //匹配具有语法功能的双引号 - else if (c == '`' && !commit && !inSingleQuote && !inDoubleQuote) inOtherQuote = - !inOtherQuote //匹配具有语法功能的'`' - else if (c == '/' && !commit && !inSingleQuote && !inDoubleQuote && !inOtherQuote) commit = true //可能是注释 - else if (inCommits) { //匹配多行注释终点 - pos = queue.indexOf("*/", pos) //跳过多行注释 - if(pos == -1)break //没有终点,语法出错,跳出 - continue - - }else if (c == '\n') inCommit = false //单行注释终点 - - if (commit || inSingleQuote || inDoubleQuote || inOtherQuote) continue //语法单元未匹配结束,直接进入下个循环 - - if(c == '[')depthX++ - else if(c == ']')depthX-- - else if(depth == 0){ //正则的[]中的{}不需要平衡,非正则的[]中的{}也无需考虑平衡 - if(c == '{')depth++ - else if(c == '}')depth-- - } - - balanced = depthX <1 && depth<1 - if(!balanced)continue - if(c == endChar && queue.regionMatches(pos, end, 0, end.length)) { - this.pos = pos - return true - } - } - return false - } - /** * 拉出一个非内嵌代码平衡组,存在转义文本 */ @@ -247,7 +194,7 @@ class RuleAnalyzer(data: String, code: Boolean = false) { * 不用正则,不到最后不切片也不用中间变量存储,只在序列中标记当前查找字段的开头结尾,到返回时才切片,高效快速准确切割规则 * 解决jsonPath自带的"&&"和"||"与阅读的规则冲突,以及规则正则或字符串中包含"&&"、"||"、"%%"、"@"导致的冲突 */ - tailrec fun splitRule(vararg split: String): Array { //首段匹配,elementsType为空 + tailrec fun splitRule(vararg split: String): ArrayList { //首段匹配,elementsType为空 if (split.size == 1) { elementsType = split[0] //设置分割字串 @@ -271,7 +218,7 @@ class RuleAnalyzer(data: String, code: Boolean = false) { if (st == -1) { - rule = arrayOf(queue.substring(startX, end)) //压入分隔的首段规则到数组 + rule = arrayListOf(queue.substring(startX, end)) //压入分隔的首段规则到数组 elementsType = queue.substring(end, end + step) //设置组合类型 pos = end + step //跳过分隔符 @@ -288,7 +235,7 @@ class RuleAnalyzer(data: String, code: Boolean = false) { if (st > end) { //先匹配到st1pos,表明分隔字串不在选择器中,将选择器前分隔字串分隔的字段依次压入数组 - rule = arrayOf(queue.substring(startX, end)) //压入分隔的首段规则到数组 + rule = arrayListOf(queue.substring(startX, end)) //压入分隔的首段规则到数组 elementsType = queue.substring(end, end + step) //设置组合类型 pos = end + step //跳过分隔符 @@ -322,7 +269,7 @@ class RuleAnalyzer(data: String, code: Boolean = false) { } @JvmName("splitRuleNext") - private tailrec fun splitRule(): Array { //二段匹配被调用,elementsType非空(已在首段赋值),直接按elementsType查找,比首段采用的方式更快 + private tailrec fun splitRule(): ArrayList { //二段匹配被调用,elementsType非空(已在首段赋值),直接按elementsType查找,比首段采用的方式更快 val end = pos //记录分隔位置 pos = start //重回开始,启动另一种查找 @@ -347,7 +294,7 @@ class RuleAnalyzer(data: String, code: Boolean = false) { if (st > end) { //先匹配到st1pos,表明分隔字串不在选择器中,将选择器前分隔字串分隔的字段依次压入数组 - rule += arrayOf(queue.substring(startX, end)) //压入分隔的首段规则到数组 + rule += arrayListOf(queue.substring(startX, end)) //压入分隔的首段规则到数组 pos = end + step //跳过分隔符 while (consumeTo(elementsType) && pos < st) { //循环切分规则压入数组 @@ -421,92 +368,201 @@ class RuleAnalyzer(data: String, code: Boolean = false) { * @param fr 查找到内嵌规则时,用于解析的函数 * * */ - fun innerJsRule( - fr: (String) -> String? + fun innerRule( + startStr:String, + endStr:String, + fr: (String) -> String? ): String { - val st = StringBuilder() - while (consumeTo("{{")) { //拉取成功返回true,ruleAnalyzes里的字符序列索引变量pos后移相应位置,否则返回false,且isEmpty为true + val st = StringBuilder() + while (consumeTo(startStr)) { //拉取成功返回true,ruleAnalyzes里的字符序列索引变量pos后移相应位置,否则返回false,且isEmpty为true + pos += startStr.length //跳过开始字符串 val posPre = pos //记录consumeTo匹配位置 - if (chompJsBalanced()) { - val frv = fr(queue.substring(posPre + 2, pos - 2)) - if (!frv.isNullOrEmpty()) { - st.append(queue.substring(startX, posPre) + frv) //压入内嵌规则前的内容,及内嵌规则解析得到的字符串 + if (consumeTo(endStr)) { + val frv = fr(queue.substring(posPre, pos)) + st.append(queue.substring(startX, posPre - startStr.length) + frv) //压入内嵌规则前的内容,及内嵌规则解析得到的字符串 + pos += endStr.length //跳过结束字符串 startX = pos //记录下次规则起点 - continue //获取内容成功,继续选择下个内嵌规则 - } } - pos += 2 //拉出字段不平衡,inner只是个普通字串,跳到此inner后继续匹配 } - return if(startX == 0) "" else st.apply { + return if(startX == 0) queue else st.apply { append(queue.substring(startX)) }.toString() } - companion object { - /** - * 转义字符 - */ - private const val ESC = '\\' + val ruleTypeList = ArrayList() + //设置平衡组函数,json或JavaScript时设置成chompCodeBalanced,否则为chompRuleBalanced + val chompBalanced = if (code) ::chompCodeBalanced else ::chompRuleBalanced + enum class Mode { + XPath, Json, Default, Js, Regex + } + /** + * 不用正则,不到最后不切片也不用中间变量存储,只在序列中标记当前查找字段的开头结尾,到返回时才切片,高效快速准确切割规则 + * 解决jsonPath自带的"&&"和"||"与阅读的规则冲突,以及规则正则或字符串中包含"&&"、"||"、"%%"、"@"导致的冲突 + */ + tailrec fun splitAnyRule(): ArrayList { //首段匹配,elementsType为空 - /** - * "" - */ - private const val JSSTART = "" + if (!consumeToAny(* STARTSTR)) { //未找到分隔符 + rule += queue.substring(startX) + return rule + } - /** - * 阅读共有分隔字串起始部分 - * "##","@@","{{","{[","", "@js:" - */ - val splitList2 = arrayOf("##", "@@", "{{", "{[") + val end = pos //记录分隔位置 + pos = start //重回开始,启动另一种查找 - /** - * 发现‘名称-链接’分隔字串 - * "::" - */ - const val splitListFaXian = "::" + do { + val st = findToAny('[', '(') //查找筛选器位置 - /** - * 目录专有起始字符 - * "-" - */ - const val splitListMulu = "-" + if (st == -1) { - /** - * 结果为元素列表的 all in one 模式起始字符 - * "+" - */ - const val splitListTongYi = "+" + rule += arrayOf(queue.substring(startX, end)) //压入分隔的首段规则到数组 - /** - * 结果为元素列表的项的同规则组合结构 - * "||","&&","%%" - */ - val splitListReSplit = arrayOf("||", "&&", "%%") + ruleTypeList += queue.substring(end, end + step) //追加类型到类型列表 + pos = end + step //跳过分隔符 - /** - * js脚本结束字串 - * "" - */ - const val splitListEndJS = "" + while (!chompToAny(elementsType)) { //循环切分规则压入数组 + rule += queue.substring(pos) //将剩余字段压入数组末尾 + return rule + } + } + + if (st > end) { //先匹配到st1pos,表明分隔字串不在选择器中,将选择器前分隔字串分隔的字段依次压入数组 + + rule += arrayOf(queue.substring(startX, end)) //压入分隔的首段规则到数组 + + ruleTypeList += queue.substring(end, end + step) //设置组合类型 + pos = end + step //跳过分隔符 + + while (!chompToAny(elementsType) && pos >= st) { //循环切分规则压入数组 + if (pos > st) { + startX = start + } else { //执行到此,证明后面再无分隔字符 + rule += queue.substring(pos) //将剩余字段压入数组末尾 + return rule + } + } + } + + pos = st //位置回到筛选器处 + val next = if (queue[pos] == '[') ']' else ')' //平衡组末尾字符 + + if (!chompBalanced(queue[pos], next)){ + ruleTypeList.clear() + rule.clear() + consumeToAny("","@js:") + rule += queue.substring(0,pos) + ruleTypeList += queue.substring(pos, pos + 4) //设置组合类型 + } + + } while (end > pos) + + start = pos //设置开始查找筛选器位置的起始位置 + + return splitAnyRule() //递归调用首段匹配 + } + + var isJSON = false + + var isUrl = false + var isUrlList = false + + var isMulu = false + var isreverse = false + var isAllInOne= false + + var isFind = false + private val findName = ArrayList() + + var replaceRegex = "" + var replacement = "" + var replaceFirst = false + val putMap = HashMap() + private val ruleParam = ArrayList() + private val ruleType = ArrayList() + private val getRuleType = -2 + private val jsRuleType = -1 + private val defaultRuleType = 0 + + @JvmOverloads + fun setContent(cont: String,type:String = ""): RuleAnalyzer { + queue = cont + when(type){ + "mulu" -> { + if(queue[0] =='-'){ //目录反转 + isreverse = true + startX++ + pos++ + }else if(queue[0] =='?'){ //AllInOne + isAllInOne = true + startX++ + pos++ + } + isMulu = true + } + "find" -> { + pos = queue.indexOf("::") + findName.add(queue.substring(startX,pos)) + pos+=2 + isFind = true + } + "url" -> { + + isUrl = true + } + "urlList" -> { + + isUrlList = true + } + else -> { + isJSON = queue.toString().isJson() + } + } + + return this + } + + companion object { /** - *内嵌js结束字串 - * "}}" + * 转义字符 */ - const val splitListEndInnerJS = "}}" + private const val ESC = '\\' + + + val validKeys = arrayOf("class", "id", "tag", "text", "children") /** - * 内嵌规则结束字串 - * "]}" + * 参数字符串 */ - const val splitListEndInnerRule = "]}" + private val STARTSTRURL = arrayOf(",{",) + + private val regexPattern = Pattern.compile("\\$\\d{1,2}") + private val putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE) + private val getPattern = Pattern.compile("@get:\\{([^}]+?)\\}", Pattern.CASE_INSENSITIVE) + private val evalPattern = Pattern.compile("\\{\\{[\\w\\W]*?\\}\\}", Pattern.CASE_INSENSITIVE) + + val ENDSTR= mapOf( + "" to "", + "{{" to "}}", + ) /** - * '[', ']', '(', ')','{','}' + * 规则起始字符串 */ - val splitListPublic = charArrayOf('[', ']', '(', ')', '{', '}') + private val STARTSTR = arrayOf("@js:","","","##","@@","@" + ,"{{@", "{{","}}" + ,"}" + , "{@", "{/", "{$", "{?" + , "{class" + , "{id" + , "{tag" + , "{text" + , "{children" + ,"/","$","@xpath:","@json:","@css:" + ,"||", "&&", "%%" + ,"@get:{","@put:{" + ) /** * '*',"/","//",":","::","@","|","@xpath:"