mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-16 03:32:38 +08:00
perf: 优化校验错误提示
This commit is contained in:
parent
01545a77db
commit
25d3c64630
|
@ -1,6 +1,5 @@
|
|||
package li.songe.gkd.data
|
||||
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
|
@ -12,6 +11,7 @@ import kotlinx.serialization.json.int
|
|||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.long
|
||||
import li.songe.gkd.service.allowPropertyNames
|
||||
import li.songe.gkd.util.json
|
||||
import li.songe.gkd.util.json5ToJson
|
||||
import li.songe.selector.Selector
|
||||
|
@ -30,7 +30,6 @@ data class RawSubscription(
|
|||
val apps: List<RawApp> = emptyList(),
|
||||
) {
|
||||
|
||||
@IgnoredOnParcel
|
||||
val categoryToGroupsMap by lazy {
|
||||
val allAppGroups = apps.flatMap { a -> a.groups.map { g -> g to a } }
|
||||
allAppGroups.groupBy { g ->
|
||||
|
@ -38,7 +37,6 @@ data class RawSubscription(
|
|||
}
|
||||
}
|
||||
|
||||
@IgnoredOnParcel
|
||||
val groupToCategoryMap by lazy {
|
||||
val map = mutableMapOf<RawAppGroup, RawCategory>()
|
||||
categoryToGroupsMap.forEach { (key, value) ->
|
||||
|
@ -51,12 +49,10 @@ data class RawSubscription(
|
|||
map
|
||||
}
|
||||
|
||||
@IgnoredOnParcel
|
||||
val appGroups by lazy {
|
||||
apps.flatMap { a -> a.groups }
|
||||
}
|
||||
|
||||
@IgnoredOnParcel
|
||||
val numText by lazy {
|
||||
val appsSize = apps.size
|
||||
val appGroupsSize = appGroups.size
|
||||
|
@ -166,30 +162,37 @@ data class RawSubscription(
|
|||
override val rules: List<RawGlobalRule>,
|
||||
) : RawGroupProps, RawGlobalRuleProps {
|
||||
|
||||
@IgnoredOnParcel
|
||||
val valid by lazy {
|
||||
rules.all { r ->
|
||||
r.matches.all { s -> Selector.check(s) } && (r.excludeMatches
|
||||
?: emptyList()).all { s ->
|
||||
Selector.check(
|
||||
s
|
||||
)
|
||||
}
|
||||
}
|
||||
val allSelectorStrings by lazy {
|
||||
rules.map { r -> r.matches + (r.excludeMatches ?: emptyList()) }.flatten().distinct()
|
||||
}
|
||||
|
||||
@IgnoredOnParcel
|
||||
val allExampleUrls by lazy {
|
||||
mutableListOf<String>().apply {
|
||||
if (exampleUrls != null) {
|
||||
addAll(exampleUrls)
|
||||
}
|
||||
rules.forEach { r ->
|
||||
if (r.exampleUrls != null) {
|
||||
addAll(r.exampleUrls)
|
||||
}
|
||||
val allSelector by lazy {
|
||||
allSelectorStrings.map { s -> Selector.parseOrNull(s) }
|
||||
}
|
||||
|
||||
val unknownPropertyNames by lazy {
|
||||
allSelector.filterNotNull().flatMap { r -> r.propertyNames.toList() }.distinct()
|
||||
.filterNot { n -> allowPropertyNames.contains(n) }
|
||||
}
|
||||
|
||||
val errorDesc by lazy {
|
||||
allSelector.forEachIndexed { i, s ->
|
||||
if (s == null) {
|
||||
return@lazy "非法选择器:${allSelectorStrings[i]}"
|
||||
}
|
||||
}
|
||||
unknownPropertyNames.forEach { n ->
|
||||
return@lazy "非法属性名:${n}"
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
val valid by lazy { errorDesc == null }
|
||||
|
||||
val allExampleUrls by lazy {
|
||||
((exampleUrls ?: emptyList()) + rules.flatMap { r ->
|
||||
r.exampleUrls ?: emptyList()
|
||||
}).distinct()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,30 +242,37 @@ data class RawSubscription(
|
|||
override val rules: List<RawAppRule>,
|
||||
) : RawGroupProps, RawAppRuleProps {
|
||||
|
||||
@IgnoredOnParcel
|
||||
val valid by lazy {
|
||||
rules.all { r ->
|
||||
r.matches.all { s -> Selector.check(s) } && (r.excludeMatches
|
||||
?: emptyList()).all { s ->
|
||||
Selector.check(
|
||||
s
|
||||
)
|
||||
}
|
||||
}
|
||||
val allSelectorStrings by lazy {
|
||||
rules.map { r -> r.matches + (r.excludeMatches ?: emptyList()) }.flatten().distinct()
|
||||
}
|
||||
|
||||
@IgnoredOnParcel
|
||||
val allExampleUrls by lazy {
|
||||
mutableListOf<String>().apply {
|
||||
if (exampleUrls != null) {
|
||||
addAll(exampleUrls)
|
||||
}
|
||||
rules.forEach { r ->
|
||||
if (r.exampleUrls != null) {
|
||||
addAll(r.exampleUrls)
|
||||
}
|
||||
val allSelector by lazy {
|
||||
allSelectorStrings.map { s -> Selector.parseOrNull(s) }
|
||||
}
|
||||
|
||||
val unknownPropertyNames by lazy {
|
||||
allSelector.filterNotNull().flatMap { r -> r.propertyNames.toList() }.distinct()
|
||||
.filterNot { n -> allowPropertyNames.contains(n) }
|
||||
}
|
||||
|
||||
val errorDesc by lazy {
|
||||
allSelector.forEachIndexed { i, s ->
|
||||
if (s == null) {
|
||||
return@lazy "非法选择器:${allSelectorStrings[i]}"
|
||||
}
|
||||
}
|
||||
unknownPropertyNames.forEach { n ->
|
||||
return@lazy "非法属性名:${n}"
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
val valid by lazy { errorDesc == null }
|
||||
|
||||
val allExampleUrls by lazy {
|
||||
((exampleUrls ?: emptyList()) + rules.flatMap { r ->
|
||||
r.exampleUrls ?: emptyList()
|
||||
}).distinct()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,8 +524,7 @@ data class RawSubscription(
|
|||
},
|
||||
rules = jsonObject["rules"]?.jsonArray?.map { jsonElement ->
|
||||
jsonToGlobalRule(jsonElement.jsonObject)
|
||||
} ?: emptyList()
|
||||
)
|
||||
} ?: emptyList())
|
||||
}
|
||||
|
||||
private fun jsonToSubscriptionRaw(rootJson: JsonObject): RawSubscription {
|
||||
|
@ -542,8 +551,7 @@ data class RawSubscription(
|
|||
} ?: emptyList(),
|
||||
globalGroups = rootJson["globalGroups"]?.jsonArray?.mapIndexed { index, jsonElement ->
|
||||
jsonToGlobalGroups(jsonElement.jsonObject, index)
|
||||
} ?: emptyList()
|
||||
)
|
||||
} ?: emptyList())
|
||||
}
|
||||
|
||||
private fun <T> List<T>.findDuplicatedItem(predicate: (T) -> Any?): T? {
|
||||
|
|
|
@ -117,6 +117,36 @@ val getChildren: (AccessibilityNodeInfo) -> Sequence<AccessibilityNodeInfo> = {
|
|||
}
|
||||
}
|
||||
|
||||
val allowPropertyNames = setOf(
|
||||
"id",
|
||||
"vid",
|
||||
|
||||
"name",
|
||||
"text",
|
||||
"text.length",
|
||||
"desc",
|
||||
"desc.length",
|
||||
|
||||
"clickable",
|
||||
"focusable",
|
||||
"checkable",
|
||||
"checked",
|
||||
"editable",
|
||||
"longClickable",
|
||||
"visibleToUser",
|
||||
|
||||
"left",
|
||||
"top",
|
||||
"right",
|
||||
"bottom",
|
||||
"width",
|
||||
"height",
|
||||
|
||||
"index",
|
||||
"depth",
|
||||
"childCount"
|
||||
)
|
||||
|
||||
private val getAttr: (AccessibilityNodeInfo, String) -> Any? = { node, name ->
|
||||
when (name) {
|
||||
"id" -> node.viewIdResourceName
|
||||
|
|
|
@ -377,8 +377,8 @@ fun AppItemPage(
|
|||
toast("不能更改规则组的key")
|
||||
return@TextButton
|
||||
}
|
||||
if (!newGroupRaw.valid) {
|
||||
toast("非法规则:存在非法选择器")
|
||||
if (newGroupRaw.errorDesc != null) {
|
||||
toast(newGroupRaw.errorDesc!!)
|
||||
return@TextButton
|
||||
}
|
||||
setEditGroupRaw(null)
|
||||
|
@ -499,8 +499,8 @@ fun AppItemPage(
|
|||
}
|
||||
newAppRaw.groups
|
||||
}
|
||||
if (!tempGroups.all { g -> g.valid }) {
|
||||
toast("非法规则:存在非法选择器")
|
||||
tempGroups.find { g -> g.errorDesc != null }?.errorDesc?.let { errorDesc ->
|
||||
toast(errorDesc)
|
||||
return@TextButton
|
||||
}
|
||||
tempGroups.forEach { g ->
|
||||
|
|
|
@ -231,8 +231,8 @@ fun GlobalRulePage(subsItemId: Long, focusGroupKey: Int? = null) {
|
|||
toast("非法规则\n${e.message ?: e}")
|
||||
return@TextButton
|
||||
}
|
||||
if (!newGroup.valid) {
|
||||
toast("非法规则:存在非法选择器")
|
||||
if (newGroup.errorDesc != null) {
|
||||
toast(newGroup.errorDesc!!)
|
||||
return@TextButton
|
||||
}
|
||||
if (rawSubs.globalGroups.any { g -> g.name == newGroup.name }) {
|
||||
|
@ -349,8 +349,8 @@ fun GlobalRulePage(subsItemId: Long, focusGroupKey: Int? = null) {
|
|||
toast("不能更改规则组的key")
|
||||
return@TextButton
|
||||
}
|
||||
if (!newGroupRaw.valid) {
|
||||
toast("非法规则:存在非法选择器")
|
||||
if (newGroupRaw.errorDesc != null) {
|
||||
toast(newGroupRaw.errorDesc!!)
|
||||
return@TextButton
|
||||
}
|
||||
setEditGroupRaw(null)
|
||||
|
|
|
@ -12,6 +12,7 @@ class CommonSelector private constructor(
|
|||
val tracks = selector.tracks
|
||||
val trackIndex = selector.trackIndex
|
||||
val connectKeys = selector.connectKeys
|
||||
val propertyNames = selector.propertyNames
|
||||
|
||||
fun <T : Any> match(node: T, transform: CommonTransform<T>): T? {
|
||||
return selector.match(node, transform.transform)
|
||||
|
@ -26,7 +27,7 @@ class CommonSelector private constructor(
|
|||
|
||||
companion object {
|
||||
fun parse(source: String) = CommonSelector(Selector.parse(source))
|
||||
fun check(source: String) = Selector.check(source)
|
||||
fun parseOrNull(source: String) = Selector.parseOrNull(source)?.let(::CommonSelector)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,17 @@ class Selector internal constructor(private val propertyWrapper: PropertyWrapper
|
|||
keys.toTypedArray()
|
||||
}
|
||||
|
||||
val propertyNames by lazy {
|
||||
var p: PropertyWrapper? = propertyWrapper
|
||||
val names = mutableSetOf<String>()
|
||||
while (p != null) {
|
||||
val s = p!!.propertySegment
|
||||
p = p!!.to?.to
|
||||
names.addAll(s.propertyNames)
|
||||
}
|
||||
names.distinct().toTypedArray()
|
||||
}
|
||||
|
||||
fun <T> match(
|
||||
node: T,
|
||||
transform: Transform<T>,
|
||||
|
@ -84,13 +95,10 @@ class Selector internal constructor(private val propertyWrapper: PropertyWrapper
|
|||
|
||||
companion object {
|
||||
fun parse(source: String) = ParserSet.selectorParser(source)
|
||||
fun check(source: String): Boolean {
|
||||
return try {
|
||||
ParserSet.selectorParser(source)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
fun parseOrNull(source: String) = try {
|
||||
ParserSet.selectorParser(source)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ data class BinaryExpression(val name: String, val operator: CompareOperator, val
|
|||
override fun <T> match(node: T, transform: Transform<T>) =
|
||||
operator.compare(transform.getAttr(node, name), value)
|
||||
|
||||
override val propertyNames = listOf(name)
|
||||
|
||||
override fun toString() = "${name}${operator}${
|
||||
if (value is String) {
|
||||
val wrapChar = '"'
|
||||
|
|
|
@ -4,4 +4,6 @@ import li.songe.selector.Transform
|
|||
|
||||
sealed class Expression {
|
||||
abstract fun <T> match(node: T, transform: Transform<T>): Boolean
|
||||
|
||||
abstract val propertyNames: List<String>
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ data class LogicalExpression(
|
|||
return operator.compare(node, transform, left, right)
|
||||
}
|
||||
|
||||
override val propertyNames = left.propertyNames + right.propertyNames
|
||||
|
||||
override fun toString(): String {
|
||||
val leftStr = if (left is LogicalExpression && left.operator != operator) {
|
||||
"($left)"
|
||||
|
|
|
@ -12,12 +12,18 @@ data class PropertySegment(
|
|||
val name: String,
|
||||
val expressions: List<Expression>,
|
||||
) {
|
||||
private val matchAnyName = name.isBlank() || name == "*"
|
||||
|
||||
val propertyNames =
|
||||
(if (matchAnyName) listOf("name") else emptyList()) + expressions.map { e -> e.propertyNames }
|
||||
.flatten()
|
||||
|
||||
override fun toString(): String {
|
||||
val matchTag = if (tracked) "@" else ""
|
||||
return matchTag + name + expressions.joinToString("") { "[$it]" }
|
||||
}
|
||||
|
||||
private val matchName = if (name.isBlank() || name == "*") {
|
||||
private val matchName = if (matchAnyName) {
|
||||
object : NodeMatchFc {
|
||||
override fun <T> invoke(node: T, transform: Transform<T>) = true
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user