Commit 1fae390f authored by Him188's avatar Him188

Enhance MessageChainBuilder

parent 604559fb
......@@ -14,6 +14,7 @@
package net.mamoe.mirai.message.data
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
......@@ -53,35 +54,14 @@ class MessageChainBuilder private constructor(
constructor() : this(mutableListOf())
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 {
checkBuilt()
flushCache()
return addAndCheckConstrainSingle(element)
}
fun add(element: Message): Boolean {
checkBuilt()
flushCache()
@Suppress("UNCHECKED_CAST")
return when (element) {
......@@ -93,44 +73,113 @@ class MessageChainBuilder private constructor(
}
override fun addAll(elements: Collection<SingleMessage>): Boolean {
checkBuilt()
flushCache()
return addAll(elements.flatten())
}
@JvmName("addAllFlatten") // erased generic type cause declaration clash
fun addAll(elements: Collection<Message>): Boolean {
checkBuilt()
flushCache()
return addAll(elements.flatten())
}
operator fun Message.unaryPlus() {
checkBuilt()
flushCache()
add(this)
}
operator fun String.unaryPlus() {
checkBuilt()
add(this)
}
operator fun plusAssign(plain: String) {
checkBuilt()
withCache { append(plain) }
}
operator fun plusAssign(message: Message) {
checkBuilt()
flushCache()
this.add(message)
}
operator fun plusAssign(message: SingleMessage) { // avoid resolution ambiguity
checkBuilt()
flushCache()
this.add(message)
}
fun add(plain: String) {
checkBuilt()
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 fun flushCache() {
cache?.let {
......@@ -140,6 +189,7 @@ class MessageChainBuilder private constructor(
}
private inline fun withCache(block: StringBuilder.() -> Unit): MessageChainBuilder {
checkBuilt()
if (cache == null) {
cache = StringBuilder().apply(block)
} else {
......@@ -148,20 +198,29 @@ class MessageChainBuilder private constructor(
return this
}
operator fun plusAssign(charSequence: CharSequence) {
withCache { append(charSequence) }
}
private var built = false
private fun checkBuilt() = check(!built) { "MessageChainBuilder is already built therefore can't modify" }
override fun append(value: Char): Appendable = withCache { append(value) }
override fun append(value: CharSequence?): Appendable = withCache { append(value) }
override fun append(value: CharSequence?, startIndex: Int, endIndex: Int) =
withCache { append(value, startIndex, endIndex) }
private var firstConstrainSingleIndex = -1
fun asMessageChain(): MessageChain {
this.flushCache()
return MessageChainImplByCollection(this) // fast-path, no need to constrain
}
private fun addAndCheckConstrainSingle(element: SingleMessage): Boolean {
if (element is ConstrainSingle<*>) {
if (firstConstrainSingleIndex == -1) {
firstConstrainSingleIndex = container.size
return container.add(element)
}
val key = element.key
/** 同 [asMessageChain] */
fun build(): MessageChain = asMessageChain()
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)
}
}
}
\ No newline at end of file
......@@ -21,11 +21,11 @@ internal class MessageChainBuilderTest {
+PlainText("foo")
+" "
+(PlainText("bar") + " goo ")
+buildMessageChain {
buildMessageChain {
+"1"
+"2"
+"3"
}
}.joinTo(this)
}
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