mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-16 03:32:38 +08:00
feat(selector): add methods/props
This commit is contained in:
parent
d68573d728
commit
3dca7789e3
|
@ -275,7 +275,9 @@ fun clearNodeCache(t: Long = System.currentTimeMillis()) {
|
|||
lastCacheTime = t
|
||||
if (BuildConfig.DEBUG) {
|
||||
val sizeList = defaultCacheTransform.cache.sizeList
|
||||
Log.d("cache", "clear cache, sizeList=$sizeList")
|
||||
if (sizeList.any { it > 0 }) {
|
||||
Log.d("cache", "clear cache, sizeList=$sizeList")
|
||||
}
|
||||
}
|
||||
defaultTransform.cache.clear()
|
||||
defaultCacheTransform.cache.clear()
|
||||
|
|
|
@ -147,10 +147,7 @@ val getChildren: (AccessibilityNodeInfo) -> Sequence<AccessibilityNodeInfo> = {
|
|||
}
|
||||
|
||||
private val typeInfo by lazy {
|
||||
initDefaultTypeInfo().apply {
|
||||
nodeType.props = nodeType.props.filter { !it.name.startsWith('_') }.toTypedArray()
|
||||
contextType.props = contextType.props.filter { !it.name.startsWith('_') }.toTypedArray()
|
||||
}.contextType
|
||||
initDefaultTypeInfo().globalType
|
||||
}
|
||||
|
||||
fun Selector.checkSelector(): String? {
|
||||
|
@ -357,6 +354,7 @@ fun createCacheTransform(): CacheTransform {
|
|||
when (target) {
|
||||
is Context<*> -> when (name) {
|
||||
"prev" -> target.prev
|
||||
"current" -> target.current
|
||||
else -> getNodeAttr(target.current as AccessibilityNodeInfo, name)
|
||||
}
|
||||
|
||||
|
@ -369,7 +367,7 @@ fun createCacheTransform(): CacheTransform {
|
|||
when (target) {
|
||||
is AccessibilityNodeInfo -> when (name) {
|
||||
"getChild" -> {
|
||||
args.getIntOrNull()?.let { index ->
|
||||
args.getInt().let { index ->
|
||||
cache.getChild(target, index)
|
||||
}
|
||||
}
|
||||
|
@ -379,11 +377,11 @@ fun createCacheTransform(): CacheTransform {
|
|||
|
||||
is Context<*> -> when (name) {
|
||||
"getPrev" -> {
|
||||
args.getIntOrNull()?.let { target.getPrev(it) }
|
||||
args.getInt().let { target.getPrev(it) }
|
||||
}
|
||||
|
||||
"getChild" -> {
|
||||
args.getIntOrNull()?.let { index ->
|
||||
args.getInt().let { index ->
|
||||
cache.getChild(target.current as AccessibilityNodeInfo, index)
|
||||
}
|
||||
}
|
||||
|
@ -545,9 +543,7 @@ fun createCacheTransform(): CacheTransform {
|
|||
return CacheTransform(transform, cache)
|
||||
}
|
||||
|
||||
private fun List<Any>.getIntOrNull(i: Int = 0): Int? {
|
||||
return getOrNull(i) as? Int ?: return null
|
||||
}
|
||||
private fun List<Any>.getInt(i: Int = 0) = get(i) as Int
|
||||
|
||||
fun createNoCacheTransform(): CacheTransform {
|
||||
val cache = NodeCache()
|
||||
|
@ -569,7 +565,7 @@ fun createNoCacheTransform(): CacheTransform {
|
|||
when (target) {
|
||||
is AccessibilityNodeInfo -> when (name) {
|
||||
"getChild" -> {
|
||||
args.getIntOrNull()?.let { index ->
|
||||
args.getInt().let { index ->
|
||||
cache.getChild(target, index)
|
||||
}
|
||||
}
|
||||
|
@ -579,11 +575,11 @@ fun createNoCacheTransform(): CacheTransform {
|
|||
|
||||
is Context<*> -> when (name) {
|
||||
"getPrev" -> {
|
||||
args.getIntOrNull()?.let { target.getPrev(it) }
|
||||
args.getInt().let { target.getPrev(it) }
|
||||
}
|
||||
|
||||
"getChild" -> {
|
||||
args.getIntOrNull()?.let { index ->
|
||||
args.getInt().let { index ->
|
||||
cache.getChild(target.current as AccessibilityNodeInfo, index)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,14 +58,18 @@ sealed class CompareOperator(val key: String) : Stringify {
|
|||
): Boolean {
|
||||
val left = leftExp.getAttr(context, transform)
|
||||
val right = rightExp.getAttr(context, transform)
|
||||
return compare(left, right)
|
||||
}
|
||||
|
||||
override fun allowType(left: ValueExpression, right: ValueExpression) = true
|
||||
|
||||
fun compare(left: Any?, right: Any?): Boolean {
|
||||
return if (left is CharSequence && right is CharSequence) {
|
||||
left.contentReversedEquals(right)
|
||||
} else {
|
||||
left == right
|
||||
}
|
||||
}
|
||||
|
||||
override fun allowType(left: ValueExpression, right: ValueExpression) = true
|
||||
}
|
||||
|
||||
data object NotEqual : CompareOperator("!=") {
|
||||
|
|
|
@ -28,7 +28,7 @@ data class UnknownMemberMethodException(
|
|||
@JsExport
|
||||
data class MismatchParamTypeException(
|
||||
val call: ValueExpression.CallExpression,
|
||||
val argument: ValueExpression.LiteralExpression,
|
||||
val argument: ValueExpression,
|
||||
val type: PrimitiveType
|
||||
) : SelectorCheckException("Mismatch Param Type: ${argument.value} should be ${type.key}")
|
||||
|
||||
|
|
|
@ -138,76 +138,129 @@ private fun getExpType(exp: ValueExpression, typeInfo: TypeInfo): PrimitiveType?
|
|||
is ValueExpression.BooleanLiteral -> PrimitiveType.BooleanType
|
||||
is ValueExpression.IntLiteral -> PrimitiveType.IntType
|
||||
is ValueExpression.StringLiteral -> PrimitiveType.StringType
|
||||
is ValueExpression.Variable -> checkVariable(exp, typeInfo).type
|
||||
is ValueExpression.Variable -> checkVariable(exp, typeInfo, typeInfo).type
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkVariable(value: ValueExpression.Variable, typeInfo: TypeInfo): TypeInfo {
|
||||
private fun checkMethod(
|
||||
method: MethodInfo,
|
||||
value: ValueExpression.CallExpression,
|
||||
globalTypeInfo: TypeInfo
|
||||
): TypeInfo {
|
||||
method.params.forEachIndexed { index, argTypeInfo ->
|
||||
when (val argExp = value.arguments[index]) {
|
||||
is ValueExpression.NullLiteral -> {}
|
||||
is ValueExpression.BooleanLiteral -> {
|
||||
if (argTypeInfo.type != PrimitiveType.BooleanType) {
|
||||
throw MismatchParamTypeException(
|
||||
value,
|
||||
argExp,
|
||||
PrimitiveType.BooleanType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is ValueExpression.IntLiteral -> {
|
||||
if (argTypeInfo.type != PrimitiveType.IntType) {
|
||||
throw MismatchParamTypeException(value, argExp, PrimitiveType.IntType)
|
||||
}
|
||||
}
|
||||
|
||||
is ValueExpression.StringLiteral -> {
|
||||
if (argTypeInfo.type != PrimitiveType.StringType) {
|
||||
throw MismatchParamTypeException(
|
||||
value,
|
||||
argExp,
|
||||
PrimitiveType.StringType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is ValueExpression.Variable -> {
|
||||
val type = checkVariable(argExp, argTypeInfo, globalTypeInfo)
|
||||
if (type.type != argTypeInfo.type) {
|
||||
throw MismatchParamTypeException(
|
||||
value,
|
||||
argExp,
|
||||
type.type
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return method.returnType
|
||||
}
|
||||
|
||||
private fun checkVariable(
|
||||
value: ValueExpression.Variable,
|
||||
currentTypeInfo: TypeInfo,
|
||||
globalTypeInfo: TypeInfo,
|
||||
): TypeInfo {
|
||||
return when (value) {
|
||||
is ValueExpression.CallExpression -> {
|
||||
val method = when (value.callee) {
|
||||
val methods = when (value.callee) {
|
||||
is ValueExpression.CallExpression -> {
|
||||
throw IllegalArgumentException("Unsupported nested call")
|
||||
}
|
||||
|
||||
is ValueExpression.Identifier -> {
|
||||
// getChild(0)
|
||||
typeInfo.methods.find { it.name == value.callee.value && it.params.size == value.arguments.size }
|
||||
?: throw UnknownIdentifierMethodException(value.callee)
|
||||
globalTypeInfo.methods.filter { it.name == value.callee.value && it.params.size == value.arguments.size }
|
||||
.apply {
|
||||
if (isEmpty()) {
|
||||
throw UnknownIdentifierMethodException(value.callee)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is ValueExpression.MemberExpression -> {
|
||||
// parent.getChild(0)
|
||||
checkVariable(
|
||||
value.callee.object0, typeInfo
|
||||
).methods.find { it.name == value.callee.property && it.params.size == value.arguments.size }
|
||||
?: throw UnknownMemberMethodException(value.callee)
|
||||
value.callee.object0,
|
||||
currentTypeInfo,
|
||||
globalTypeInfo
|
||||
).methods.filter { it.name == value.callee.property && it.params.size == value.arguments.size }
|
||||
.apply {
|
||||
if (isEmpty()) {
|
||||
throw UnknownMemberMethodException(value.callee)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
method.params.forEachIndexed { index, argTypeInfo ->
|
||||
when (val argExp = value.arguments[index]) {
|
||||
is ValueExpression.NullLiteral -> {}
|
||||
is ValueExpression.BooleanLiteral -> {
|
||||
if (argTypeInfo.type != PrimitiveType.BooleanType) {
|
||||
throw MismatchParamTypeException(
|
||||
value,
|
||||
argExp,
|
||||
PrimitiveType.BooleanType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is ValueExpression.IntLiteral -> {
|
||||
if (argTypeInfo.type != PrimitiveType.IntType) {
|
||||
throw MismatchParamTypeException(value, argExp, PrimitiveType.IntType)
|
||||
}
|
||||
}
|
||||
|
||||
is ValueExpression.StringLiteral -> {
|
||||
if (argTypeInfo.type != PrimitiveType.StringType) {
|
||||
throw MismatchParamTypeException(
|
||||
value,
|
||||
argExp,
|
||||
PrimitiveType.StringType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is ValueExpression.Variable -> {
|
||||
checkVariable(argExp, argTypeInfo)
|
||||
if (methods.size == 1) {
|
||||
checkMethod(methods[0], value, globalTypeInfo)
|
||||
return methods[0].returnType
|
||||
}
|
||||
methods.forEachIndexed { i, method ->
|
||||
try {
|
||||
checkMethod(method, value, globalTypeInfo)
|
||||
return method.returnType
|
||||
} catch (e: SelectorCheckException) {
|
||||
if (i == methods.size - 1) {
|
||||
throw e
|
||||
}
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return method.returnType
|
||||
if (value.callee is ValueExpression.Identifier) {
|
||||
throw UnknownIdentifierMethodException(value.callee)
|
||||
} else if (value.callee is ValueExpression.MemberExpression) {
|
||||
throw UnknownMemberMethodException(value.callee)
|
||||
}
|
||||
throw IllegalArgumentException("Unsupported nested call")
|
||||
}
|
||||
|
||||
is ValueExpression.Identifier -> {
|
||||
typeInfo.props.find { it.name == value.value }?.type
|
||||
globalTypeInfo.props.find { it.name == value.value }?.type
|
||||
?: throw UnknownIdentifierException(value)
|
||||
}
|
||||
|
||||
is ValueExpression.MemberExpression -> {
|
||||
checkVariable(value.object0, typeInfo).props.find { it.name == value.property }?.type
|
||||
checkVariable(
|
||||
value.object0,
|
||||
currentTypeInfo,
|
||||
globalTypeInfo
|
||||
).props.find { it.name == value.property }?.type
|
||||
?: throw UnknownMemberException(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,11 @@ data class MethodInfo(
|
|||
val name: String,
|
||||
val returnType: TypeInfo,
|
||||
val params: Array<TypeInfo> = emptyArray(),
|
||||
) {
|
||||
) : Stringify {
|
||||
override fun stringify(): String {
|
||||
return "$name(${params.joinToString(", ") { it.stringify() }}): ${returnType.stringify()}"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
||||
|
@ -48,7 +52,15 @@ data class TypeInfo(
|
|||
val type: PrimitiveType,
|
||||
var props: Array<PropInfo> = arrayOf(),
|
||||
var methods: Array<MethodInfo> = arrayOf(),
|
||||
) {
|
||||
) : Stringify {
|
||||
override fun stringify(): String {
|
||||
return if (type is PrimitiveType.ObjectType) {
|
||||
type.name
|
||||
} else {
|
||||
type.key
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
||||
|
|
|
@ -30,6 +30,9 @@ sealed class ValueExpression(open val value: Any?, open val type: String) : Posi
|
|||
get() = arrayOf(value)
|
||||
override val methods: Array<String>
|
||||
get() = emptyArray()
|
||||
|
||||
val isEqual = name == "equal"
|
||||
val isNotEqual = name == "notEqual"
|
||||
}
|
||||
|
||||
data class MemberExpression internal constructor(
|
||||
|
@ -52,6 +55,10 @@ sealed class ValueExpression(open val value: Any?, open val type: String) : Posi
|
|||
get() = arrayOf(*object0.properties, property)
|
||||
override val methods: Array<String>
|
||||
get() = object0.methods
|
||||
|
||||
val isPropertyOr = property == "or"
|
||||
val isPropertyAnd = property == "and"
|
||||
val isPropertyIfElse = property == "ifElse"
|
||||
}
|
||||
|
||||
data class CallExpression internal constructor(
|
||||
|
@ -74,23 +81,66 @@ sealed class ValueExpression(open val value: Any?, open val type: String) : Posi
|
|||
}
|
||||
|
||||
is Identifier -> {
|
||||
transform.getInvoke(
|
||||
context,
|
||||
callee.name,
|
||||
arguments.map {
|
||||
it.getAttr(context, transform).whenNull { return null }
|
||||
when {
|
||||
callee.isEqual -> {
|
||||
CompareOperator.Equal.compare(
|
||||
arguments[0].getAttr(context, transform),
|
||||
arguments[1].getAttr(context, transform)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
callee.isNotEqual -> {
|
||||
!CompareOperator.Equal.compare(
|
||||
arguments[0].getAttr(context, transform),
|
||||
arguments[1].getAttr(context, transform)
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
transform.getInvoke(
|
||||
context,
|
||||
callee.name,
|
||||
arguments.map {
|
||||
it.getAttr(context, transform).whenNull { return null }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is MemberExpression -> {
|
||||
transform.getInvoke(
|
||||
callee.object0.getAttr(context, transform).whenNull { return null },
|
||||
callee.property,
|
||||
arguments.map {
|
||||
it.getAttr(context, transform).whenNull { return null }
|
||||
val objectValue =
|
||||
callee.object0.getAttr(context, transform).whenNull { return null }
|
||||
when {
|
||||
callee.isPropertyOr -> {
|
||||
(objectValue as Boolean) ||
|
||||
(arguments[0].getAttr(context, transform)
|
||||
.whenNull { return null } as Boolean)
|
||||
}
|
||||
)
|
||||
|
||||
callee.isPropertyAnd -> {
|
||||
(objectValue as Boolean) &&
|
||||
(arguments[0].getAttr(context, transform)
|
||||
.whenNull { return null } as Boolean)
|
||||
}
|
||||
|
||||
callee.isPropertyIfElse -> {
|
||||
if (objectValue as Boolean) {
|
||||
arguments[0].getAttr(context, transform)
|
||||
} else {
|
||||
arguments[1].getAttr(context, transform)
|
||||
}
|
||||
}
|
||||
|
||||
else -> transform.getInvoke(
|
||||
objectValue,
|
||||
callee.property,
|
||||
arguments.map {
|
||||
it.getAttr(context, transform).whenNull { return null }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,17 +77,36 @@ class DefaultTypeInfo(
|
|||
val stringType: TypeInfo,
|
||||
val contextType: TypeInfo,
|
||||
val nodeType: TypeInfo,
|
||||
val globalType: TypeInfo
|
||||
)
|
||||
|
||||
@JsExport
|
||||
fun initDefaultTypeInfo(): DefaultTypeInfo {
|
||||
fun initDefaultTypeInfo(webField: Boolean = false): DefaultTypeInfo {
|
||||
val booleanType = TypeInfo(PrimitiveType.BooleanType)
|
||||
val intType = TypeInfo(PrimitiveType.IntType)
|
||||
val stringType = TypeInfo(PrimitiveType.StringType)
|
||||
val nodeType = TypeInfo(PrimitiveType.ObjectType("node"))
|
||||
val contextType = TypeInfo(PrimitiveType.ObjectType("context"))
|
||||
val globalType = TypeInfo(PrimitiveType.ObjectType("global"))
|
||||
|
||||
fun buildMethods(name: String, returnType: TypeInfo, paramsSize: Int): Array<MethodInfo> {
|
||||
return arrayOf(
|
||||
MethodInfo(name, returnType, Array(paramsSize) { booleanType }),
|
||||
MethodInfo(name, returnType, Array(paramsSize) { intType }),
|
||||
MethodInfo(name, returnType, Array(paramsSize) { stringType }),
|
||||
MethodInfo(name, returnType, Array(paramsSize) { nodeType }),
|
||||
MethodInfo(name, returnType, Array(paramsSize) { contextType }),
|
||||
)
|
||||
}
|
||||
|
||||
booleanType.methods = arrayOf(
|
||||
MethodInfo("toInt", intType),
|
||||
MethodInfo("or", booleanType, arrayOf(booleanType)),
|
||||
MethodInfo("and", booleanType, arrayOf(booleanType)),
|
||||
MethodInfo("not", booleanType),
|
||||
*buildMethods("ifElse", booleanType, 2),
|
||||
)
|
||||
|
||||
intType.methods = arrayOf(
|
||||
MethodInfo("toString", stringType),
|
||||
MethodInfo("toString", stringType, arrayOf(intType)),
|
||||
|
@ -96,6 +115,10 @@ fun initDefaultTypeInfo(): DefaultTypeInfo {
|
|||
MethodInfo("times", intType, arrayOf(intType)),
|
||||
MethodInfo("div", intType, arrayOf(intType)),
|
||||
MethodInfo("rem", intType, arrayOf(intType)),
|
||||
MethodInfo("more", booleanType, arrayOf(intType)),
|
||||
MethodInfo("moreEqual", booleanType, arrayOf(intType)),
|
||||
MethodInfo("less", booleanType, arrayOf(intType)),
|
||||
MethodInfo("lessEqual", booleanType, arrayOf(intType)),
|
||||
)
|
||||
stringType.props = arrayOf(
|
||||
PropInfo("length", intType),
|
||||
|
@ -110,13 +133,15 @@ fun initDefaultTypeInfo(): DefaultTypeInfo {
|
|||
MethodInfo("indexOf", intType, arrayOf(stringType)),
|
||||
MethodInfo("indexOf", intType, arrayOf(stringType, intType)),
|
||||
)
|
||||
|
||||
val contextType = TypeInfo(PrimitiveType.ObjectType("context"))
|
||||
val nodeType = TypeInfo(PrimitiveType.ObjectType("node"))
|
||||
|
||||
nodeType.props = arrayOf(
|
||||
PropInfo("_id", intType),
|
||||
PropInfo("_pid", intType),
|
||||
* (if (webField) {
|
||||
arrayOf(
|
||||
PropInfo("_id", intType),
|
||||
PropInfo("_pid", intType),
|
||||
)
|
||||
} else {
|
||||
emptyArray()
|
||||
}),
|
||||
|
||||
PropInfo("id", stringType),
|
||||
PropInfo("vid", stringType),
|
||||
|
@ -155,13 +180,21 @@ fun initDefaultTypeInfo(): DefaultTypeInfo {
|
|||
contextType.props = arrayOf(
|
||||
*nodeType.props,
|
||||
PropInfo("prev", contextType),
|
||||
PropInfo("current", contextType),
|
||||
)
|
||||
globalType.methods = arrayOf(
|
||||
*contextType.methods,
|
||||
*buildMethods("equal", booleanType, 2),
|
||||
*buildMethods("notEqual", booleanType, 2),
|
||||
)
|
||||
globalType.props = arrayOf(*contextType.props)
|
||||
return DefaultTypeInfo(
|
||||
booleanType = booleanType,
|
||||
intType = intType,
|
||||
stringType = stringType,
|
||||
contextType = contextType,
|
||||
nodeType = nodeType
|
||||
nodeType = nodeType,
|
||||
globalType = globalType
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -169,23 +202,39 @@ fun initDefaultTypeInfo(): DefaultTypeInfo {
|
|||
fun getIntInvoke(target: Int, name: String, args: List<Any>): Any? {
|
||||
return when (name) {
|
||||
"plus" -> {
|
||||
target + (args.getIntOrNull() ?: return null)
|
||||
target + args.getInt()
|
||||
}
|
||||
|
||||
"minus" -> {
|
||||
target - (args.getIntOrNull() ?: return null)
|
||||
target - args.getInt()
|
||||
}
|
||||
|
||||
"times" -> {
|
||||
target * (args.getIntOrNull() ?: return null)
|
||||
target * args.getInt()
|
||||
}
|
||||
|
||||
"div" -> {
|
||||
target / (args.getIntOrNull()?.also { if (it == 0) return null } ?: return null)
|
||||
target / args.getInt().also { if (it == 0) return null }
|
||||
}
|
||||
|
||||
"rem" -> {
|
||||
target % (args.getIntOrNull()?.also { if (it == 0) return null } ?: return null)
|
||||
target % args.getInt().also { if (it == 0) return null }
|
||||
}
|
||||
|
||||
"more" -> {
|
||||
target > args.getInt()
|
||||
}
|
||||
|
||||
"moreEqual" -> {
|
||||
target >= args.getInt()
|
||||
}
|
||||
|
||||
"less" -> {
|
||||
target < args.getInt()
|
||||
}
|
||||
|
||||
"lessEqual" -> {
|
||||
target <= args.getInt()
|
||||
}
|
||||
|
||||
else -> null
|
||||
|
@ -193,11 +242,7 @@ fun getIntInvoke(target: Int, name: String, args: List<Any>): Any? {
|
|||
}
|
||||
|
||||
|
||||
internal fun List<Any>.getIntOrNull(i: Int = 0): Int? {
|
||||
val v = getOrNull(i)
|
||||
if (v is Int) return v
|
||||
return null
|
||||
}
|
||||
internal fun List<Any>.getInt(i: Int = 0) = get(i) as Int
|
||||
|
||||
@JsExport
|
||||
fun getStringInvoke(target: String, name: String, args: List<Any>): Any? {
|
||||
|
@ -208,6 +253,7 @@ fun getStringInvoke(target: String, name: String, args: List<Any>): Any? {
|
|||
fun getBooleanInvoke(target: Boolean, name: String, args: List<Any>): Any? {
|
||||
return when (name) {
|
||||
"toInt" -> if (target) 1 else 0
|
||||
"not" -> !target
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
@ -215,11 +261,11 @@ fun getBooleanInvoke(target: Boolean, name: String, args: List<Any>): Any? {
|
|||
fun getCharSequenceInvoke(target: CharSequence, name: String, args: List<Any>): Any? {
|
||||
return when (name) {
|
||||
"get" -> {
|
||||
target.getOrNull(args.getIntOrNull() ?: return null).toString()
|
||||
target.getOrNull(args.getInt()).toString()
|
||||
}
|
||||
|
||||
"at" -> {
|
||||
val i = args.getIntOrNull() ?: return null
|
||||
val i = args.getInt()
|
||||
if (i < 0) {
|
||||
target.getOrNull(target.length + i).toString()
|
||||
} else {
|
||||
|
@ -230,7 +276,7 @@ fun getCharSequenceInvoke(target: CharSequence, name: String, args: List<Any>):
|
|||
"substring" -> {
|
||||
when (args.size) {
|
||||
1 -> {
|
||||
val start = args.getIntOrNull() ?: return null
|
||||
val start = args.getInt()
|
||||
if (start < 0) return null
|
||||
if (start >= target.length) return ""
|
||||
target.substring(
|
||||
|
@ -240,10 +286,10 @@ fun getCharSequenceInvoke(target: CharSequence, name: String, args: List<Any>):
|
|||
}
|
||||
|
||||
2 -> {
|
||||
val start = args.getIntOrNull() ?: return null
|
||||
val start = args.getInt()
|
||||
if (start < 0) return null
|
||||
if (start >= target.length) return ""
|
||||
val end = args.getIntOrNull(1) ?: return null
|
||||
val end = args.getInt(1)
|
||||
if (end < start) return null
|
||||
target.substring(
|
||||
start,
|
||||
|
@ -260,7 +306,7 @@ fun getCharSequenceInvoke(target: CharSequence, name: String, args: List<Any>):
|
|||
"toInt" -> when (args.size) {
|
||||
0 -> target.toString().toIntOrNull()
|
||||
1 -> {
|
||||
val radix = args.getIntOrNull() ?: return null
|
||||
val radix = args.getInt()
|
||||
if (radix !in 2..36) {
|
||||
return null
|
||||
}
|
||||
|
@ -279,7 +325,7 @@ fun getCharSequenceInvoke(target: CharSequence, name: String, args: List<Any>):
|
|||
|
||||
2 -> {
|
||||
val str = args[0] as? CharSequence ?: return null
|
||||
val startIndex = args.getIntOrNull(1) ?: return null
|
||||
val startIndex = args.getInt(1)
|
||||
target.indexOf(str.toString(), startIndex)
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class ParserTest {
|
|||
private fun getNodeInvoke(target: TestNode, name: String, args: List<Any>): Any? {
|
||||
when (name) {
|
||||
"getChild" -> {
|
||||
val arg = (args.getIntOrNull() ?: return null)
|
||||
val arg = args.getInt()
|
||||
return target.children.getOrNull(arg)
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ class ParserTest {
|
|||
when (target) {
|
||||
is Context<*> -> when (name) {
|
||||
"prev" -> target.prev
|
||||
"current" -> target.current
|
||||
else -> getNodeAttr(target.current as TestNode, name)
|
||||
}
|
||||
|
||||
|
@ -66,7 +67,7 @@ class ParserTest {
|
|||
is TestNode -> getNodeInvoke(target, name, args)
|
||||
is Context<*> -> when (name) {
|
||||
"getPrev" -> {
|
||||
args.getIntOrNull()?.let { target.getPrev(it) }
|
||||
args.getInt().let { target.getPrev(it) }
|
||||
}
|
||||
|
||||
else -> getNodeInvoke(target.current as TestNode, name, args)
|
||||
|
@ -168,7 +169,8 @@ class ParserTest {
|
|||
|
||||
@Test
|
||||
fun check_query() {
|
||||
val text = "@TextView[getPrev(0).text=`签到提醒`] - [text=`签到提醒`] <<n [vid=`webViewContainer`]"
|
||||
val text =
|
||||
"@TextView[getPrev(0).text=`签到提醒`] - [text=`签到提醒`] <<n [vid=`webViewContainer`]"
|
||||
val selector = Selector.parse(text)
|
||||
println("selector: $selector")
|
||||
println(selector.targetIndex)
|
||||
|
@ -231,11 +233,16 @@ class ParserTest {
|
|||
|
||||
@Test
|
||||
fun check_regex() {
|
||||
val source = "[1<parent.getChild][vid=`im_cover`]"
|
||||
val source = "[vid=`im_cover`][top.more(319).not()=true]"
|
||||
println("source:$source")
|
||||
val selector = Selector.parse(source)
|
||||
val snapshotNode = getOrDownloadNode("https://i.gkd.li/i/14445410")
|
||||
println("selector:$selector")
|
||||
val error = selector.checkType(typeInfo)
|
||||
if (error != null) {
|
||||
println("error:$error")
|
||||
return
|
||||
}
|
||||
println("selector:${selector.stringify()}")
|
||||
println("result:" + transform.querySelectorAll(snapshotNode, selector).map { n -> n.id }
|
||||
.toList())
|
||||
}
|
||||
|
@ -256,20 +263,21 @@ class ParserTest {
|
|||
)
|
||||
}
|
||||
|
||||
private val typeInfo by lazy { initDefaultTypeInfo(webField = true).globalType }
|
||||
|
||||
@Test
|
||||
fun check_type() {
|
||||
val source =
|
||||
"[prev!=null&&visibleToUser=true][((parent.getChild(0,).getChild( (0), )=null) && (((2 >= 1)))) || (name=null && desc=null)]"
|
||||
"[prev!=null&&visibleToUser=true&&equal(index, depth)=true][((parent.getChild(0,).getChild( (0), )=null) && (((2 >= 1)))) || (name=null && desc=null)]"
|
||||
val selector = Selector.parse(source)
|
||||
val typeInfo = initDefaultTypeInfo().contextType
|
||||
val error = selector.checkType(typeInfo)
|
||||
println("useCache: ${selector.useCache}")
|
||||
println("error: $error")
|
||||
println("check_type: $selector")
|
||||
println("check_type: ${selector.stringify()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun check_qf(){
|
||||
fun check_qf() {
|
||||
val source = "@UIView[clickable=true] -3 FlattenUIText[text=`a`||text=`b`||vid=`233`]"
|
||||
val selector = Selector.parse(source)
|
||||
println("fastQuery: ${selector.fastQueryList}")
|
||||
|
|
Loading…
Reference in New Issue
Block a user