Commit 1fae390f authored by Him188's avatar Him188

Enhance MessageChainBuilder

parent 604559fb
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
package net.mamoe.mirai.message.data package net.mamoe.mirai.message.data
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai
import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
...@@ -53,35 +54,14 @@ class MessageChainBuilder private constructor( ...@@ -53,35 +54,14 @@ class MessageChainBuilder private constructor(
constructor() : this(mutableListOf()) constructor() : this(mutableListOf())
constructor(initialSize: Int) : this(ArrayList<SingleMessage>(initialSize)) constructor(initialSize: Int) : this(ArrayList<SingleMessage>(initialSize))
private var firstConstrainSingleIndex = -1
private fun addAndCheckConstrainSingle(element: SingleMessage): Boolean {
if (element is ConstrainSingle<*>) {
if (firstConstrainSingleIndex == -1) {
firstConstrainSingleIndex = container.size
return container.add(element)
}
val key = element.key
val index = container.indexOfFirst(firstConstrainSingleIndex) { it is ConstrainSingle<*> && it.key == key }
if (index != -1) {
container[index] = element
} else {
container.add(element)
}
return true
} else {
return container.add(element)
}
}
override fun add(element: SingleMessage): Boolean { override fun add(element: SingleMessage): Boolean {
checkBuilt()
flushCache() flushCache()
return addAndCheckConstrainSingle(element) return addAndCheckConstrainSingle(element)
} }
fun add(element: Message): Boolean { fun add(element: Message): Boolean {
checkBuilt()
flushCache() flushCache()
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return when (element) { return when (element) {
...@@ -93,44 +73,113 @@ class MessageChainBuilder private constructor( ...@@ -93,44 +73,113 @@ class MessageChainBuilder private constructor(
} }
override fun addAll(elements: Collection<SingleMessage>): Boolean { override fun addAll(elements: Collection<SingleMessage>): Boolean {
checkBuilt()
flushCache() flushCache()
return addAll(elements.flatten()) return addAll(elements.flatten())
} }
@JvmName("addAllFlatten") // erased generic type cause declaration clash @JvmName("addAllFlatten") // erased generic type cause declaration clash
fun addAll(elements: Collection<Message>): Boolean { fun addAll(elements: Collection<Message>): Boolean {
checkBuilt()
flushCache() flushCache()
return addAll(elements.flatten()) return addAll(elements.flatten())
} }
operator fun Message.unaryPlus() { operator fun Message.unaryPlus() {
checkBuilt()
flushCache() flushCache()
add(this) add(this)
} }
operator fun String.unaryPlus() { operator fun String.unaryPlus() {
checkBuilt()
add(this) add(this)
} }
operator fun plusAssign(plain: String) { operator fun plusAssign(plain: String) {
checkBuilt()
withCache { append(plain) } withCache { append(plain) }
} }
operator fun plusAssign(message: Message) { operator fun plusAssign(message: Message) {
checkBuilt()
flushCache() flushCache()
this.add(message) this.add(message)
} }
operator fun plusAssign(message: SingleMessage) { // avoid resolution ambiguity operator fun plusAssign(message: SingleMessage) { // avoid resolution ambiguity
checkBuilt()
flushCache() flushCache()
this.add(message) this.add(message)
} }
fun add(plain: String) { fun add(plain: String) {
checkBuilt()
withCache { append(plain) } withCache { append(plain) }
} }
operator fun plusAssign(charSequence: CharSequence) {
checkBuilt()
withCache { append(charSequence) }
}
override fun append(value: Char): MessageChainBuilder = withCache { append(value) }
override fun append(value: CharSequence?): MessageChainBuilder = withCache { append(value) }
override fun append(value: CharSequence?, startIndex: Int, endIndex: Int): MessageChainBuilder =
withCache { append(value, startIndex, endIndex) }
fun append(message: Message): MessageChainBuilder = apply { add(message) }
fun append(message: SingleMessage): MessageChainBuilder = apply { add(message) }
// avoid resolution to extensions
fun asMessageChain(): MessageChain {
built = true
this.flushCache()
return MessageChainImplByCollection(this) // fast-path, no need to constrain
}
/** 同 [asMessageChain] */
fun build(): MessageChain = asMessageChain()
/**
* 将所有已有元素引用复制到一个新的 [MessageChainBuilder]
*/
@SinceMirai("0.38.0")
fun copy(): MessageChainBuilder {
return MessageChainBuilder(container.toMutableList())
}
///////
// FOR IMMUTABLE SAFETY
override fun remove(element: SingleMessage): Boolean {
checkBuilt()
return container.remove(element)
}
override fun removeAll(elements: Collection<SingleMessage>): Boolean {
checkBuilt()
return container.removeAll(elements)
}
override fun removeAt(index: Int): SingleMessage {
checkBuilt()
return container.removeAt(index)
}
override fun clear() {
checkBuilt()
return container.clear()
}
override fun set(index: Int, element: SingleMessage): SingleMessage {
checkBuilt()
return container.set(index, element)
}
///////
// IMPLEMENTATION
private var cache: StringBuilder? = null private var cache: StringBuilder? = null
private fun flushCache() { private fun flushCache() {
cache?.let { cache?.let {
...@@ -140,6 +189,7 @@ class MessageChainBuilder private constructor( ...@@ -140,6 +189,7 @@ class MessageChainBuilder private constructor(
} }
private inline fun withCache(block: StringBuilder.() -> Unit): MessageChainBuilder { private inline fun withCache(block: StringBuilder.() -> Unit): MessageChainBuilder {
checkBuilt()
if (cache == null) { if (cache == null) {
cache = StringBuilder().apply(block) cache = StringBuilder().apply(block)
} else { } else {
...@@ -148,20 +198,29 @@ class MessageChainBuilder private constructor( ...@@ -148,20 +198,29 @@ class MessageChainBuilder private constructor(
return this return this
} }
operator fun plusAssign(charSequence: CharSequence) { private var built = false
withCache { append(charSequence) } private fun checkBuilt() = check(!built) { "MessageChainBuilder is already built therefore can't modify" }
}
override fun append(value: Char): Appendable = withCache { append(value) } private var firstConstrainSingleIndex = -1
override fun append(value: CharSequence?): Appendable = withCache { append(value) }
override fun append(value: CharSequence?, startIndex: Int, endIndex: Int) =
withCache { append(value, startIndex, endIndex) }
fun asMessageChain(): MessageChain { private fun addAndCheckConstrainSingle(element: SingleMessage): Boolean {
this.flushCache() if (element is ConstrainSingle<*>) {
return MessageChainImplByCollection(this) // fast-path, no need to constrain if (firstConstrainSingleIndex == -1) {
firstConstrainSingleIndex = container.size
return container.add(element)
} }
val key = element.key
/** 同 [asMessageChain] */ val index = container.indexOfFirst(firstConstrainSingleIndex) { it is ConstrainSingle<*> && it.key == key }
fun build(): MessageChain = asMessageChain() if (index != -1) {
container[index] = element
} else {
container.add(element)
}
return true
} else {
return container.add(element)
}
}
} }
\ No newline at end of file
...@@ -21,11 +21,11 @@ internal class MessageChainBuilderTest { ...@@ -21,11 +21,11 @@ internal class MessageChainBuilderTest {
+PlainText("foo") +PlainText("foo")
+" " +" "
+(PlainText("bar") + " goo ") +(PlainText("bar") + " goo ")
+buildMessageChain { buildMessageChain {
+"1" +"1"
+"2" +"2"
+"3" +"3"
} }.joinTo(this)
} }
assertEquals("test foo bar goo 123", chain.toString()) assertEquals("test foo bar goo 123", chain.toString())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment