From 4184b5f7d9a553549b04f80a1e5679846f7f0c05 Mon Sep 17 00:00:00 2001 From: Him188 Date: Mon, 6 Apr 2020 19:13:11 +0800 Subject: [PATCH] Make CombinedMessage internal --- .../message/data/CombinedMessage.kt | 25 +- .../net.mamoe.mirai/message/data/Message.kt | 214 ++++++++++-------- .../message/data/MessageChain.kt | 35 +-- 3 files changed, 126 insertions(+), 148 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt index a2eb493b1..2b1a8e3d2 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt @@ -14,7 +14,6 @@ package net.mamoe.mirai.message.data import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiInternalAPI -import net.mamoe.mirai.utils.PlannedRemoval import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName import kotlin.jvm.JvmSynthetic @@ -29,8 +28,7 @@ import kotlin.jvm.JvmSynthetic * * Left-biased list */ -@MiraiInternalAPI("this API is going to be internal") -class CombinedMessage +internal class CombinedMessage internal constructor( internal val left: Message, // 必须已经完成 constrain single internal val tail: Message @@ -44,22 +42,11 @@ internal constructor( return asSequence().iterator() } - @PlannedRemoval("1.0.0") - @Deprecated( - "有歧义, 自行使用 contentToString() 比较", - ReplaceWith("this.contentToString() == other"), - DeprecationLevel.HIDDEN - ) - override fun contains(sub: String): Boolean { - return contentToString().contains(sub) - } - - override val size: Int = when { - left === EmptyMessageChain && tail !== EmptyMessageChain -> 1 - left === EmptyMessageChain && tail === EmptyMessageChain -> 0 - left !== EmptyMessageChain && tail === EmptyMessageChain -> 1 - left !== EmptyMessageChain && tail !== EmptyMessageChain -> 2 - else -> error("stub") + override val size: Int by lazy { + var size = 0 + size += if (left is MessageChain) left.size else 1 + size += if (tail is MessageChain) tail.size else 1 + size } @OptIn(MiraiExperimentalAPI::class) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt index f0f33704d..e76a0b03d 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt @@ -61,7 +61,6 @@ import kotlin.jvm.JvmSynthetic * @see HummerMessage 一些特殊的消息, 如 [闪照][FlashImage], [戳一戳][PokeMessage] * * @see MessageChain 消息链(即 `List`) - * @see CombinedMessage 链接的两个消息 * @see buildMessageChain 构造一个 [MessageChain] * * @see Contact.sendMessage 发送消息 @@ -104,79 +103,19 @@ interface Message { @Suppress("DEPRECATION_ERROR") @OptIn(MiraiInternalAPI::class) @JvmSynthetic // in java they should use `plus` instead - fun followedBy(tail: Message): MessageChain { - when { - this is SingleMessage && tail is SingleMessage -> { - if (this is ConstrainSingle<*> && tail is ConstrainSingle<*>) { - if (this.key == tail.key) - return SingleMessageChainImpl(tail) - } - return CombinedMessage(this, tail) - } - - this is SingleMessage -> { // tail is not - tail as MessageChain - - if (this is ConstrainSingle<*>) { - val key = this.key - if (tail.any { (it as? ConstrainSingle<*>)?.key == key }) { - return tail - } - } - return CombinedMessage(this, tail) - } - - tail is SingleMessage -> { - this as MessageChain - - if (tail is ConstrainSingle<*> && this.hasDuplicationOfConstrain(tail.key)) { - val iterator = this.iterator() - var tailUsed = false - return MessageChainImplByCollection( - constrainSingleMessagesImpl { - if (iterator.hasNext()) { - iterator.next() - } else if (!tailUsed) { - tailUsed = true - tail - } else null - } - ) - } - - return CombinedMessage(this, tail) - } - - else -> { // both chain - this as MessageChain - tail as MessageChain - - var iterator = this.iterator() - var tailUsed = false - return MessageChainImplByCollection( - constrainSingleMessagesImpl { - if (iterator.hasNext()) { - iterator.next() - } else if (!tailUsed) { - tailUsed = true - iterator = tail.iterator() - iterator.next() - } else null - } - ) - } - } - } + fun followedBy(tail: Message): MessageChain = followedByImpl(tail) /** * 得到包含 mirai 消息元素代码的, 易读的字符串. 如 `At(member) + "test"` 将转为 `"[mirai:at:qqId]test"` * + * 在使用消息相关 DSL 和扩展时, 一些内容比较的实现均使用的是 [contentToString] 而不是 [toString] + * * 各个 [SingleMessage] 的转换示例: * [PlainText]: "Hello" * [GroupImage]: "[mirai:image:{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png]" * [FriendImage]: "[mirai:image:/f8f1ab55-bf8e-4236-b55e-955848d7069f]" * [PokeMessage]: "[mirai:poke:1,-1]" - * [MessageChain]: 直接无间隔地连接所有元素. + * [MessageChain]: 直接无间隔地连接所有元素 (`joinToString("")`) * * @see contentToString */ @@ -184,8 +123,11 @@ interface Message { /** * 转为最接近官方格式的字符串. 如 `At(member) + "test"` 将转为 `"@群名片 test"`. - * 对于 [NullMessageChain], 这个函数返回空字符串 "" - * 对于其他 [MessageChain], 这个函数返回值同 [toString] + * + * 对于 [NullMessageChain], 这个函数返回空字符串 ""; + * 对于其他 [MessageChain], 这个函数返回值同 [toString]. + * + * 在使用消息相关 DSL 和扩展时, 一些内容比较的实现均使用的是 [contentToString] 而不是 [toString] */ @SinceMirai("0.34.0") fun contentToString(): String @@ -209,9 +151,9 @@ interface Message { @Deprecated( "有歧义, 自行使用 contentToString() 比较", ReplaceWith("this.contentToString() == other"), - DeprecationLevel.HIDDEN + DeprecationLevel.ERROR ) - infix fun eq(other: Message): Boolean = this.toString() == other.toString() + infix fun eq(other: Message): Boolean = this.contentToString() == other.contentToString() /** * 将 [contentToString] 与 [other] 比较 @@ -221,7 +163,7 @@ interface Message { @Deprecated( "有歧义, 自行使用 contentToString() 比较", ReplaceWith("this.contentToString() == other"), - DeprecationLevel.HIDDEN + DeprecationLevel.ERROR ) infix fun eq(other: String): Boolean = this.contentToString() == other @@ -230,40 +172,40 @@ interface Message { @Deprecated( "有歧义, 自行使用 contentToString() 比较", ReplaceWith("this.contentToString() == other"), - DeprecationLevel.HIDDEN + DeprecationLevel.ERROR ) operator fun contains(sub: String): Boolean = false @PlannedRemoval("1.0.0") - @Suppress("INAPPLICABLE_JVM_NAME") + @Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE") @JvmName("followedBy") @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @JvmSynthetic fun followedBy1(tail: Message): CombinedMessage = this.followedByInternalForBinaryCompatibility(tail) @PlannedRemoval("1.0.0") - @Suppress("INAPPLICABLE_JVM_NAME") + @Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE") @JvmName("plus") @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @JvmSynthetic fun plus1(another: Message): CombinedMessage = this.followedByInternalForBinaryCompatibility(another) @PlannedRemoval("1.0.0") - @Suppress("INAPPLICABLE_JVM_NAME") + @Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE") @JvmName("plus") @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @JvmSynthetic fun plus1(another: SingleMessage): CombinedMessage = this.followedByInternalForBinaryCompatibility(another) @PlannedRemoval("1.0.0") - @Suppress("INAPPLICABLE_JVM_NAME") + @Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE") @JvmName("plus") @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @JvmSynthetic fun plus1(another: String): CombinedMessage = this.followedByInternalForBinaryCompatibility(another.toMessage()) @PlannedRemoval("1.0.0") - @Suppress("INAPPLICABLE_JVM_NAME") + @Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE") @JvmName("plus") @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @JvmSynthetic @@ -272,25 +214,6 @@ interface Message { } -@OptIn(MiraiInternalAPI::class) -private fun Message.hasDuplicationOfConstrain(key: Message.Key<*>): Boolean { - return when (this) { - is SingleMessage -> (this as? ConstrainSingle<*>)?.key == key - is CombinedMessage -> return this.left.hasDuplicationOfConstrain(key) || this.tail.hasDuplicationOfConstrain(key) - is SingleMessageChainImpl -> (this.delegate as? ConstrainSingle<*>)?.key == key - is MessageChainImplByCollection -> this.delegate.any { (it as? ConstrainSingle<*>)?.key == key } - is MessageChainImplBySequence -> this.any { (it as? ConstrainSingle<*>)?.key == key } - else -> error("stub") - } -} - -@OptIn(MiraiInternalAPI::class) -@JvmSynthetic -@Suppress("DEPRECATION_ERROR") -internal fun Message.followedByInternalForBinaryCompatibility(tail: Message): CombinedMessage { - return CombinedMessage(EmptyMessageChain, this.followedBy(tail)) -} - @JvmSynthetic @Suppress("UNCHECKED_CAST") suspend inline fun Message.sendTo(contact: C): MessageReceipt { @@ -313,18 +236,20 @@ inline operator fun Message.times(count: Int): MessageChain = this.repeat(count) @Suppress("OverridingDeprecatedMember") interface SingleMessage : Message, CharSequence, Comparable { - override operator fun contains(sub: String): Boolean = sub in this.contentToString() + /** + * 即 `sub in this.contentToString()` + */ + /* final */ override operator fun contains(sub: String): Boolean = sub in this.contentToString() /** * 比较两个消息的 [contentToString] */ - override infix fun eq(other: Message): Boolean = this.contentToString() == other.contentToString() + /* final */ override infix fun eq(other: Message): Boolean = this.contentToString() == other.contentToString() /** * 将 [contentToString] 与 [other] 比较 */ - override infix fun eq(other: String): Boolean = this.contentToString() == other - + /* final */ override infix fun eq(other: String): Boolean = this.contentToString() == other } /** @@ -360,4 +285,93 @@ interface MessageContent : SingleMessage @JvmSynthetic @Suppress("UNCHECKED_CAST") suspend inline fun MessageChain.sendTo(contact: C): MessageReceipt = - contact.sendMessage(this) as MessageReceipt \ No newline at end of file + contact.sendMessage(this) as MessageReceipt + + +///////////////////// +/// IMPLEMENTATIONS +////////////////////// + + +@OptIn(MiraiInternalAPI::class) +private fun Message.hasDuplicationOfConstrain(key: Message.Key<*>): Boolean { + return when (this) { + is SingleMessage -> (this as? ConstrainSingle<*>)?.key == key + is CombinedMessage -> return this.left.hasDuplicationOfConstrain(key) || this.tail.hasDuplicationOfConstrain(key) + is SingleMessageChainImpl -> (this.delegate as? ConstrainSingle<*>)?.key == key + is MessageChainImplByCollection -> this.delegate.any { (it as? ConstrainSingle<*>)?.key == key } + is MessageChainImplBySequence -> this.any { (it as? ConstrainSingle<*>)?.key == key } + else -> error("stub") + } +} + +@OptIn(MiraiInternalAPI::class) +@JvmSynthetic +@Suppress("DEPRECATION_ERROR") +internal fun Message.followedByInternalForBinaryCompatibility(tail: Message): CombinedMessage { + return CombinedMessage(EmptyMessageChain, this.followedBy(tail)) +} + +private fun Message.followedByImpl(tail: Message): MessageChain { + when { + this is SingleMessage && tail is SingleMessage -> { + if (this is ConstrainSingle<*> && tail is ConstrainSingle<*>) { + if (this.key == tail.key) + return SingleMessageChainImpl(tail) + } + return CombinedMessage(this, tail) + } + + this is SingleMessage -> { // tail is not + tail as MessageChain + + if (this is ConstrainSingle<*>) { + val key = this.key + if (tail.any { (it as? ConstrainSingle<*>)?.key == key }) { + return tail + } + } + return CombinedMessage(this, tail) + } + + tail is SingleMessage -> { + this as MessageChain + + if (tail is ConstrainSingle<*> && this.hasDuplicationOfConstrain(tail.key)) { + val iterator = this.iterator() + var tailUsed = false + return MessageChainImplByCollection( + constrainSingleMessagesImpl { + if (iterator.hasNext()) { + iterator.next() + } else if (!tailUsed) { + tailUsed = true + tail + } else null + } + ) + } + + return CombinedMessage(this, tail) + } + + else -> { // both chain + this as MessageChain + tail as MessageChain + + var iterator = this.iterator() + var tailUsed = false + return MessageChainImplByCollection( + constrainSingleMessagesImpl { + if (iterator.hasNext()) { + iterator.next() + } else if (!tailUsed) { + tailUsed = true + iterator = tail.iterator() + iterator.next() + } else null + } + ) + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt index 12d44e021..7af7b2c84 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt @@ -43,10 +43,10 @@ interface MessageChain : Message, Iterable { @PlannedRemoval("1.0.0") @Deprecated( "有歧义, 自行使用 contentToString() 比较", - ReplaceWith("this.contentToString() == other"), - DeprecationLevel.HIDDEN + level = DeprecationLevel.ERROR, + replaceWith = ReplaceWith("this.contentToString().contains(sub)") ) - override operator fun contains(sub: String): Boolean + /* final */ override operator fun contains(sub: String): Boolean = this.contentToString().contains(sub) /** * 元素数量. [EmptyMessageChain] 不参加计数. @@ -289,17 +289,6 @@ fun Iterable.asMessageChain(): MessageChain = @JvmSynthetic inline fun MessageChain.asMessageChain(): MessageChain = this // 避免套娃 -@JvmSynthetic -@OptIn(MiraiInternalAPI::class) -fun CombinedMessage.asMessageChain(): MessageChain { - @OptIn(MiraiExperimentalAPI::class) - if (left is SingleMessage && this.tail is SingleMessage) { - @Suppress("UNCHECKED_CAST") - return (this as Iterable).asMessageChain() - } - return (this as Iterable).asMessageChain() -} // 避免套娃 - /** * 将 [this] [扁平化后][flatten] 委托为一个 [MessageChain] */ @@ -390,19 +379,11 @@ fun Message.flatten(): Sequence { @OptIn(MiraiInternalAPI::class) return when (this) { is MessageChain -> this.asSequence() - is CombinedMessage -> this.flatten() // already constrained single. + is CombinedMessage -> this.asSequence() // already constrained single. else -> sequenceOf(this as SingleMessage) } } -@JvmSynthetic // make Java user happier with less methods -@OptIn(MiraiInternalAPI::class) -fun CombinedMessage.flatten(): Sequence { - // already constrained single. - @Suppress("UNCHECKED_CAST") - return (this as Iterable).asSequence() -} - @JvmSynthetic // make Java user happier with less methods inline fun MessageChain.flatten(): Sequence = this.asSequence() // fast path @@ -410,10 +391,10 @@ inline fun MessageChain.flatten(): Sequence = this.asSequence() / /** - * 不含任何元素的 [MessageChain] + * 不含任何元素的 [MessageChain]. */ object EmptyMessageChain : MessageChain, Iterator { - override fun contains(sub: String): Boolean = sub.isEmpty() + override val size: Int get() = 0 override fun toString(): String = "" override fun contentToString(): String = "" @@ -434,7 +415,6 @@ object NullMessageChain : MessageChain { override fun contentToString(): String = "" override val size: Int get() = 0 override fun equals(other: Any?): Boolean = other === this - override fun contains(sub: String): Boolean = error("accessing NullMessageChain") override fun iterator(): MutableIterator = error("accessing NullMessageChain") } @@ -516,7 +496,6 @@ internal class MessageChainImplByCollection constructor( get() = field ?: this.delegate.joinToString("") { it.contentToString() }.also { field = it } override fun contentToString(): String = contentToStringTemp!! - override operator fun contains(sub: String): Boolean = sub in contentToStringTemp!! } /** @@ -543,7 +522,6 @@ internal class MessageChainImplBySequence constructor( get() = field ?: this.joinToString("") { it.contentToString() }.also { field = it } override fun contentToString(): String = contentToStringTemp!! - override operator fun contains(sub: String): Boolean = sub in contentToStringTemp!! } /** @@ -557,7 +535,6 @@ internal class SingleMessageChainImpl constructor( override fun toString(): String = this.delegate.toString() override fun contentToString(): String = this.delegate.contentToString() override fun iterator(): Iterator = iterator { yield(delegate) } - override operator fun contains(sub: String): Boolean = sub in delegate } // endregion \ No newline at end of file