From 25d3c646309875e4b3e6af393c623b9532e308a6 Mon Sep 17 00:00:00 2001 From: lisonge Date: Thu, 18 Jan 2024 21:12:12 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../li/songe/gkd/data/RawSubscription.kt | 106 ++++++++++-------- .../main/kotlin/li/songe/gkd/service/AbExt.kt | 30 +++++ .../kotlin/li/songe/gkd/ui/AppItemPage.kt | 8 +- .../kotlin/li/songe/gkd/ui/GlobalRulePage.kt | 8 +- .../li/songe/selector/CommonSelector.kt | 3 +- .../kotlin/li/songe/selector/Selector.kt | 22 ++-- .../songe/selector/data/BinaryExpression.kt | 2 + .../li/songe/selector/data/Expression.kt | 2 + .../songe/selector/data/LogicalExpression.kt | 2 + .../li/songe/selector/data/PropertySegment.kt | 8 +- 10 files changed, 125 insertions(+), 66 deletions(-) diff --git a/app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt b/app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt index 97c98cc..3845982 100644 --- a/app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt +++ b/app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt @@ -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 = 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() 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, ) : 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().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, ) : 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().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 List.findDuplicatedItem(predicate: (T) -> Any?): T? { diff --git a/app/src/main/kotlin/li/songe/gkd/service/AbExt.kt b/app/src/main/kotlin/li/songe/gkd/service/AbExt.kt index b1e8808..b7f527b 100644 --- a/app/src/main/kotlin/li/songe/gkd/service/AbExt.kt +++ b/app/src/main/kotlin/li/songe/gkd/service/AbExt.kt @@ -117,6 +117,36 @@ val getChildren: (AccessibilityNodeInfo) -> Sequence = { } } +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 diff --git a/app/src/main/kotlin/li/songe/gkd/ui/AppItemPage.kt b/app/src/main/kotlin/li/songe/gkd/ui/AppItemPage.kt index 175bffd..fc5140c 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/AppItemPage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/AppItemPage.kt @@ -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 -> diff --git a/app/src/main/kotlin/li/songe/gkd/ui/GlobalRulePage.kt b/app/src/main/kotlin/li/songe/gkd/ui/GlobalRulePage.kt index 6f43266..64fed22 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/GlobalRulePage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/GlobalRulePage.kt @@ -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) diff --git a/selector/src/commonMain/kotlin/li/songe/selector/CommonSelector.kt b/selector/src/commonMain/kotlin/li/songe/selector/CommonSelector.kt index 6a82b0c..c8d407a 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/CommonSelector.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/CommonSelector.kt @@ -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 match(node: T, transform: CommonTransform): 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) } } diff --git a/selector/src/commonMain/kotlin/li/songe/selector/Selector.kt b/selector/src/commonMain/kotlin/li/songe/selector/Selector.kt index 433d150..872cfc2 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/Selector.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/Selector.kt @@ -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() + while (p != null) { + val s = p!!.propertySegment + p = p!!.to?.to + names.addAll(s.propertyNames) + } + names.distinct().toTypedArray() + } + fun match( node: T, transform: Transform, @@ -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 } } } \ No newline at end of file diff --git a/selector/src/commonMain/kotlin/li/songe/selector/data/BinaryExpression.kt b/selector/src/commonMain/kotlin/li/songe/selector/data/BinaryExpression.kt index 8300a62..657f4e0 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/data/BinaryExpression.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/data/BinaryExpression.kt @@ -7,6 +7,8 @@ data class BinaryExpression(val name: String, val operator: CompareOperator, val override fun match(node: T, transform: Transform) = operator.compare(transform.getAttr(node, name), value) + override val propertyNames = listOf(name) + override fun toString() = "${name}${operator}${ if (value is String) { val wrapChar = '"' diff --git a/selector/src/commonMain/kotlin/li/songe/selector/data/Expression.kt b/selector/src/commonMain/kotlin/li/songe/selector/data/Expression.kt index 5971d74..981ee4d 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/data/Expression.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/data/Expression.kt @@ -4,4 +4,6 @@ import li.songe.selector.Transform sealed class Expression { abstract fun match(node: T, transform: Transform): Boolean + + abstract val propertyNames: List } diff --git a/selector/src/commonMain/kotlin/li/songe/selector/data/LogicalExpression.kt b/selector/src/commonMain/kotlin/li/songe/selector/data/LogicalExpression.kt index 6d31910..769c688 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/data/LogicalExpression.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/data/LogicalExpression.kt @@ -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)" diff --git a/selector/src/commonMain/kotlin/li/songe/selector/data/PropertySegment.kt b/selector/src/commonMain/kotlin/li/songe/selector/data/PropertySegment.kt index ecb50d9..36ea52e 100644 --- a/selector/src/commonMain/kotlin/li/songe/selector/data/PropertySegment.kt +++ b/selector/src/commonMain/kotlin/li/songe/selector/data/PropertySegment.kt @@ -12,12 +12,18 @@ data class PropertySegment( val name: String, val expressions: List, ) { + 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 invoke(node: T, transform: Transform) = true }