Make CombinedMessage internal

This commit is contained in:
Him188 2020-04-06 19:13:11 +08:00
parent b8b749bf65
commit 4184b5f7d9
3 changed files with 126 additions and 148 deletions

View File

@ -14,7 +14,6 @@ package net.mamoe.mirai.message.data
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.PlannedRemoval
import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
@ -29,8 +28,7 @@ import kotlin.jvm.JvmSynthetic
* *
* Left-biased list * Left-biased list
*/ */
@MiraiInternalAPI("this API is going to be internal") internal class CombinedMessage
class CombinedMessage
internal constructor( internal constructor(
internal val left: Message, // 必须已经完成 constrain single internal val left: Message, // 必须已经完成 constrain single
internal val tail: Message internal val tail: Message
@ -44,22 +42,11 @@ internal constructor(
return asSequence().iterator() return asSequence().iterator()
} }
@PlannedRemoval("1.0.0") override val size: Int by lazy {
@Deprecated( var size = 0
"有歧义, 自行使用 contentToString() 比较", size += if (left is MessageChain) left.size else 1
ReplaceWith("this.contentToString() == other"), size += if (tail is MessageChain) tail.size else 1
DeprecationLevel.HIDDEN size
)
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")
} }
@OptIn(MiraiExperimentalAPI::class) @OptIn(MiraiExperimentalAPI::class)

View File

@ -61,7 +61,6 @@ import kotlin.jvm.JvmSynthetic
* @see HummerMessage 一些特殊的消息, [闪照][FlashImage], [戳一戳][PokeMessage] * @see HummerMessage 一些特殊的消息, [闪照][FlashImage], [戳一戳][PokeMessage]
* *
* @see MessageChain 消息链( `List<Message>`) * @see MessageChain 消息链( `List<Message>`)
* @see CombinedMessage 链接的两个消息
* @see buildMessageChain 构造一个 [MessageChain] * @see buildMessageChain 构造一个 [MessageChain]
* *
* @see Contact.sendMessage 发送消息 * @see Contact.sendMessage 发送消息
@ -104,79 +103,19 @@ interface Message {
@Suppress("DEPRECATION_ERROR") @Suppress("DEPRECATION_ERROR")
@OptIn(MiraiInternalAPI::class) @OptIn(MiraiInternalAPI::class)
@JvmSynthetic // in java they should use `plus` instead @JvmSynthetic // in java they should use `plus` instead
fun followedBy(tail: Message): MessageChain { fun followedBy(tail: Message): MessageChain = followedByImpl(tail)
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
}
)
}
}
}
/** /**
* 得到包含 mirai 消息元素代码的, 易读的字符串. `At(member) + "test"` 将转为 `"[mirai:at:qqId]test"` * 得到包含 mirai 消息元素代码的, 易读的字符串. `At(member) + "test"` 将转为 `"[mirai:at:qqId]test"`
* *
* 在使用消息相关 DSL 和扩展时, 一些内容比较的实现均使用的是 [contentToString] 而不是 [toString]
*
* 各个 [SingleMessage] 的转换示例: * 各个 [SingleMessage] 的转换示例:
* [PlainText]: "Hello" * [PlainText]: "Hello"
* [GroupImage]: "[mirai:image:{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png]" * [GroupImage]: "[mirai:image:{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png]"
* [FriendImage]: "[mirai:image:/f8f1ab55-bf8e-4236-b55e-955848d7069f]" * [FriendImage]: "[mirai:image:/f8f1ab55-bf8e-4236-b55e-955848d7069f]"
* [PokeMessage]: "[mirai:poke:1,-1]" * [PokeMessage]: "[mirai:poke:1,-1]"
* [MessageChain]: 直接无间隔地连接所有元素. * [MessageChain]: 直接无间隔地连接所有元素 (`joinToString("")`)
* *
* @see contentToString * @see contentToString
*/ */
@ -184,8 +123,11 @@ interface Message {
/** /**
* 转为最接近官方格式的字符串. `At(member) + "test"` 将转为 `"@群名片 test"`. * 转为最接近官方格式的字符串. `At(member) + "test"` 将转为 `"@群名片 test"`.
* 对于 [NullMessageChain], 这个函数返回空字符串 "" *
* 对于其他 [MessageChain], 这个函数返回值同 [toString] * 对于 [NullMessageChain], 这个函数返回空字符串 "";
* 对于其他 [MessageChain], 这个函数返回值同 [toString].
*
* 在使用消息相关 DSL 和扩展时, 一些内容比较的实现均使用的是 [contentToString] 而不是 [toString]
*/ */
@SinceMirai("0.34.0") @SinceMirai("0.34.0")
fun contentToString(): String fun contentToString(): String
@ -209,9 +151,9 @@ interface Message {
@Deprecated( @Deprecated(
"有歧义, 自行使用 contentToString() 比较", "有歧义, 自行使用 contentToString() 比较",
ReplaceWith("this.contentToString() == other"), 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] 比较 * [contentToString] [other] 比较
@ -221,7 +163,7 @@ interface Message {
@Deprecated( @Deprecated(
"有歧义, 自行使用 contentToString() 比较", "有歧义, 自行使用 contentToString() 比较",
ReplaceWith("this.contentToString() == other"), ReplaceWith("this.contentToString() == other"),
DeprecationLevel.HIDDEN DeprecationLevel.ERROR
) )
infix fun eq(other: String): Boolean = this.contentToString() == other infix fun eq(other: String): Boolean = this.contentToString() == other
@ -230,40 +172,40 @@ interface Message {
@Deprecated( @Deprecated(
"有歧义, 自行使用 contentToString() 比较", "有歧义, 自行使用 contentToString() 比较",
ReplaceWith("this.contentToString() == other"), ReplaceWith("this.contentToString() == other"),
DeprecationLevel.HIDDEN DeprecationLevel.ERROR
) )
operator fun contains(sub: String): Boolean = false operator fun contains(sub: String): Boolean = false
@PlannedRemoval("1.0.0") @PlannedRemoval("1.0.0")
@Suppress("INAPPLICABLE_JVM_NAME") @Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE")
@JvmName("followedBy") @JvmName("followedBy")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@JvmSynthetic @JvmSynthetic
fun followedBy1(tail: Message): CombinedMessage = this.followedByInternalForBinaryCompatibility(tail) fun followedBy1(tail: Message): CombinedMessage = this.followedByInternalForBinaryCompatibility(tail)
@PlannedRemoval("1.0.0") @PlannedRemoval("1.0.0")
@Suppress("INAPPLICABLE_JVM_NAME") @Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE")
@JvmName("plus") @JvmName("plus")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@JvmSynthetic @JvmSynthetic
fun plus1(another: Message): CombinedMessage = this.followedByInternalForBinaryCompatibility(another) fun plus1(another: Message): CombinedMessage = this.followedByInternalForBinaryCompatibility(another)
@PlannedRemoval("1.0.0") @PlannedRemoval("1.0.0")
@Suppress("INAPPLICABLE_JVM_NAME") @Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE")
@JvmName("plus") @JvmName("plus")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@JvmSynthetic @JvmSynthetic
fun plus1(another: SingleMessage): CombinedMessage = this.followedByInternalForBinaryCompatibility(another) fun plus1(another: SingleMessage): CombinedMessage = this.followedByInternalForBinaryCompatibility(another)
@PlannedRemoval("1.0.0") @PlannedRemoval("1.0.0")
@Suppress("INAPPLICABLE_JVM_NAME") @Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE")
@JvmName("plus") @JvmName("plus")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@JvmSynthetic @JvmSynthetic
fun plus1(another: String): CombinedMessage = this.followedByInternalForBinaryCompatibility(another.toMessage()) fun plus1(another: String): CombinedMessage = this.followedByInternalForBinaryCompatibility(another.toMessage())
@PlannedRemoval("1.0.0") @PlannedRemoval("1.0.0")
@Suppress("INAPPLICABLE_JVM_NAME") @Suppress("INAPPLICABLE_JVM_NAME", "EXPOSED_FUNCTION_RETURN_TYPE")
@JvmName("plus") @JvmName("plus")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@JvmSynthetic @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 @JvmSynthetic
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
suspend inline fun <C : Contact> Message.sendTo(contact: C): MessageReceipt<C> { suspend inline fun <C : Contact> Message.sendTo(contact: C): MessageReceipt<C> {
@ -313,18 +236,20 @@ inline operator fun Message.times(count: Int): MessageChain = this.repeat(count)
@Suppress("OverridingDeprecatedMember") @Suppress("OverridingDeprecatedMember")
interface SingleMessage : Message, CharSequence, Comparable<String> { interface SingleMessage : Message, CharSequence, Comparable<String> {
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] * 比较两个消息的 [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] 比较 * [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 @JvmSynthetic
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
suspend inline fun <C : Contact> MessageChain.sendTo(contact: C): MessageReceipt<C> = suspend inline fun <C : Contact> MessageChain.sendTo(contact: C): MessageReceipt<C> =
contact.sendMessage(this) as MessageReceipt<C> contact.sendMessage(this) as MessageReceipt<C>
/////////////////////
/// 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
}
)
}
}
}

View File

@ -43,10 +43,10 @@ interface MessageChain : Message, Iterable<SingleMessage> {
@PlannedRemoval("1.0.0") @PlannedRemoval("1.0.0")
@Deprecated( @Deprecated(
"有歧义, 自行使用 contentToString() 比较", "有歧义, 自行使用 contentToString() 比较",
ReplaceWith("this.contentToString() == other"), level = DeprecationLevel.ERROR,
DeprecationLevel.HIDDEN 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] 不参加计数. * 元素数量. [EmptyMessageChain] 不参加计数.
@ -289,17 +289,6 @@ fun Iterable<SingleMessage>.asMessageChain(): MessageChain =
@JvmSynthetic @JvmSynthetic
inline fun MessageChain.asMessageChain(): MessageChain = this // 避免套娃 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<SingleMessage>).asMessageChain()
}
return (this as Iterable<Message>).asMessageChain()
} // 避免套娃
/** /**
* [this] [扁平化后][flatten] 委托为一个 [MessageChain] * [this] [扁平化后][flatten] 委托为一个 [MessageChain]
*/ */
@ -390,19 +379,11 @@ fun Message.flatten(): Sequence<SingleMessage> {
@OptIn(MiraiInternalAPI::class) @OptIn(MiraiInternalAPI::class)
return when (this) { return when (this) {
is MessageChain -> this.asSequence() is MessageChain -> this.asSequence()
is CombinedMessage -> this.flatten() // already constrained single. is CombinedMessage -> this.asSequence() // already constrained single.
else -> sequenceOf(this as SingleMessage) else -> sequenceOf(this as SingleMessage)
} }
} }
@JvmSynthetic // make Java user happier with less methods
@OptIn(MiraiInternalAPI::class)
fun CombinedMessage.flatten(): Sequence<SingleMessage> {
// already constrained single.
@Suppress("UNCHECKED_CAST")
return (this as Iterable<SingleMessage>).asSequence()
}
@JvmSynthetic // make Java user happier with less methods @JvmSynthetic // make Java user happier with less methods
inline fun MessageChain.flatten(): Sequence<SingleMessage> = this.asSequence() // fast path inline fun MessageChain.flatten(): Sequence<SingleMessage> = this.asSequence() // fast path
@ -410,10 +391,10 @@ inline fun MessageChain.flatten(): Sequence<SingleMessage> = this.asSequence() /
/** /**
* 不含任何元素的 [MessageChain] * 不含任何元素的 [MessageChain].
*/ */
object EmptyMessageChain : MessageChain, Iterator<SingleMessage> { object EmptyMessageChain : MessageChain, Iterator<SingleMessage> {
override fun contains(sub: String): Boolean = sub.isEmpty()
override val size: Int get() = 0 override val size: Int get() = 0
override fun toString(): String = "" override fun toString(): String = ""
override fun contentToString(): String = "" override fun contentToString(): String = ""
@ -434,7 +415,6 @@ object NullMessageChain : MessageChain {
override fun contentToString(): String = "" override fun contentToString(): String = ""
override val size: Int get() = 0 override val size: Int get() = 0
override fun equals(other: Any?): Boolean = other === this override fun equals(other: Any?): Boolean = other === this
override fun contains(sub: String): Boolean = error("accessing NullMessageChain")
override fun iterator(): MutableIterator<SingleMessage> = error("accessing NullMessageChain") override fun iterator(): MutableIterator<SingleMessage> = error("accessing NullMessageChain")
} }
@ -516,7 +496,6 @@ internal class MessageChainImplByCollection constructor(
get() = field ?: this.delegate.joinToString("") { it.contentToString() }.also { field = it } get() = field ?: this.delegate.joinToString("") { it.contentToString() }.also { field = it }
override fun contentToString(): String = contentToStringTemp!! 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 } get() = field ?: this.joinToString("") { it.contentToString() }.also { field = it }
override fun contentToString(): String = contentToStringTemp!! 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 toString(): String = this.delegate.toString()
override fun contentToString(): String = this.delegate.contentToString() override fun contentToString(): String = this.delegate.contentToString()
override fun iterator(): Iterator<SingleMessage> = iterator { yield(delegate) } override fun iterator(): Iterator<SingleMessage> = iterator { yield(delegate) }
override operator fun contains(sub: String): Boolean = sub in delegate
} }
// endregion // endregion