[Chat][Feature]Storage Implementation with Tree based data structure (#531)
This commit is contained in:
Родитель
4b219a754f
Коммит
f655e2fe56
|
@ -4,6 +4,10 @@
|
|||
package com.azure.android.communication.ui.chat.repository
|
||||
|
||||
import com.azure.android.communication.ui.chat.models.MessageInfoModel
|
||||
import com.azure.android.communication.ui.chat.repository.storage.MessageRepositoryListReader
|
||||
import com.azure.android.communication.ui.chat.repository.storage.MessageRepositoryListWriter
|
||||
import com.azure.android.communication.ui.chat.repository.storage.MessageRepositoryTreeReader
|
||||
import com.azure.android.communication.ui.chat.repository.storage.MessageRepositoryTreeWriter
|
||||
|
||||
internal class MessageRepository private constructor(
|
||||
val readerDelegate: MessageRepositoryReader,
|
||||
|
@ -31,5 +35,14 @@ internal class MessageRepository private constructor(
|
|||
writerDelegate = writer
|
||||
)
|
||||
}
|
||||
|
||||
fun createTreeBackedRepository(): MessageRepository {
|
||||
val writer = MessageRepositoryTreeWriter()
|
||||
val reader = MessageRepositoryTreeReader(writer)
|
||||
return MessageRepository(
|
||||
readerDelegate = reader,
|
||||
writerDelegate = writer
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package com.azure.android.communication.ui.chat.repository
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat.repository.storage
|
||||
|
||||
import com.azure.android.communication.ui.chat.models.EMPTY_MESSAGE_INFO_MODEL
|
||||
import com.azure.android.communication.ui.chat.models.MessageInfoModel
|
||||
import com.azure.android.communication.ui.chat.repository.MessageRepositoryReader
|
||||
import com.azure.android.communication.ui.chat.repository.MessageRepositoryWriter
|
||||
import java.util.Collections
|
||||
|
||||
internal class MessageRepositoryListWriter : MessageRepositoryWriter {
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat.repository.storage
|
||||
|
||||
import com.azure.android.communication.ui.chat.models.EMPTY_MESSAGE_INFO_MODEL
|
||||
import com.azure.android.communication.ui.chat.models.MessageInfoModel
|
||||
import com.azure.android.communication.ui.chat.repository.MessageRepositoryReader
|
||||
import com.azure.android.communication.ui.chat.repository.MessageRepositoryWriter
|
||||
import java.util.TreeMap
|
||||
|
||||
internal class MessageRepositoryTreeWriter : MessageRepositoryWriter {
|
||||
|
||||
private val treeMapStoragePointer: TreeMap<Long, String> = TreeMap()
|
||||
private val treeMapStorage: TreeMap<String, MessageInfoModel> = TreeMap()
|
||||
|
||||
val size: Int
|
||||
get() = treeMapStorage.size
|
||||
|
||||
override fun addLocalMessage(messageInfoModel: MessageInfoModel) {
|
||||
val orderId: Long = getOrderId(messageInfoModel)
|
||||
|
||||
messageInfoModel.id?.let { treeMapStoragePointer.put(orderId, it) }
|
||||
messageInfoModel.id?.let { treeMapStorage.put(it, messageInfoModel) }
|
||||
}
|
||||
|
||||
override fun addPage(page: List<MessageInfoModel>) {
|
||||
page.forEach { it -> addLocalMessage(it) }
|
||||
}
|
||||
|
||||
override fun addServerMessage(message: MessageInfoModel) {
|
||||
addLocalMessage(message)
|
||||
}
|
||||
|
||||
override fun removeMessage(message: MessageInfoModel) {
|
||||
val orderId = getOrderId(message)
|
||||
|
||||
if (treeMapStoragePointer.contains(orderId)) {
|
||||
treeMapStoragePointer.remove(orderId)
|
||||
treeMapStorage.remove(message.id)
|
||||
}
|
||||
}
|
||||
|
||||
override fun editMessage(message: MessageInfoModel) {
|
||||
val orderId = getOrderId(message)
|
||||
|
||||
if (treeMapStoragePointer.contains(orderId)) {
|
||||
treeMapStorage.get(message.id)?.let { mergeWithPreviousMessage(it, message) }
|
||||
} else {
|
||||
addLocalMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLastMessage(): MessageInfoModel? {
|
||||
val key = treeMapStoragePointer.lastKey()
|
||||
return treeMapStorage.get(treeMapStoragePointer.get(key))!!
|
||||
}
|
||||
|
||||
fun searchItem(kth: Int): MessageInfoModel {
|
||||
|
||||
var highestKey = treeMapStoragePointer.lastKey() + 1
|
||||
var lowestKey = treeMapStoragePointer.firstKey()
|
||||
var elements = 0
|
||||
var midKey: Long = 0
|
||||
while (lowestKey <= highestKey) {
|
||||
midKey = (highestKey + lowestKey).div(2)
|
||||
|
||||
elements = treeMapStoragePointer.headMap(midKey).size
|
||||
|
||||
if (elements < kth) {
|
||||
lowestKey = midKey + 1
|
||||
} else if (elements > kth) {
|
||||
highestKey = midKey - 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
val key = treeMapStoragePointer.headMap(midKey).lastKey()
|
||||
return treeMapStorage.get(treeMapStoragePointer.get(key))!!
|
||||
}
|
||||
|
||||
private fun getOrderId(message: MessageInfoModel): Long {
|
||||
return message.id?.toLong() ?: 0
|
||||
}
|
||||
|
||||
private fun mergeWithPreviousMessage(
|
||||
previousMessage: MessageInfoModel,
|
||||
message: MessageInfoModel
|
||||
): MessageInfoModel {
|
||||
var newMessage = MessageInfoModel(
|
||||
id = previousMessage.id,
|
||||
internalId = previousMessage.internalId,
|
||||
content = message.content,
|
||||
messageType = previousMessage.messageType,
|
||||
version = previousMessage.version,
|
||||
senderDisplayName = previousMessage.senderDisplayName,
|
||||
createdOn = previousMessage.createdOn,
|
||||
editedOn = previousMessage.editedOn,
|
||||
deletedOn = previousMessage.deletedOn,
|
||||
senderCommunicationIdentifier = previousMessage.senderCommunicationIdentifier,
|
||||
isCurrentUser = previousMessage.isCurrentUser,
|
||||
)
|
||||
return newMessage
|
||||
}
|
||||
}
|
||||
|
||||
internal class MessageRepositoryTreeReader(private val writer: MessageRepositoryTreeWriter) : MessageRepositoryReader() {
|
||||
|
||||
override val size: Int
|
||||
get() = writer.size
|
||||
|
||||
override fun get(index: Int): MessageInfoModel = try {
|
||||
writer.searchItem(index + 1)
|
||||
} catch (exception: Exception) {
|
||||
EMPTY_MESSAGE_INFO_MODEL
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.azure.android.communication.ui.chat.repository.storage
|
||||
|
||||
import com.azure.android.communication.ui.chat.models.MessageInfoModel
|
||||
import com.azure.android.communication.ui.chat.repository.MessageRepository
|
||||
import com.azure.android.communication.ui.chat.service.sdk.wrapper.ChatMessageType
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.junit.MockitoJUnitRunner
|
||||
|
||||
@RunWith(MockitoJUnitRunner::class)
|
||||
class MessageRepositoryTreeStorageUnitTest {
|
||||
|
||||
@Test
|
||||
fun messageRepositoryTreeStorage_addPage_test() {
|
||||
|
||||
val storage = MessageRepository.createTreeBackedRepository()
|
||||
val numberOfTestMessages = 170
|
||||
for (i in 1..numberOfTestMessages) {
|
||||
storage.addLocalMessage(
|
||||
MessageInfoModel(
|
||||
id = i.toString(),
|
||||
content = "Message $i",
|
||||
messageType = ChatMessageType.TEXT
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Assert.assertEquals(numberOfTestMessages, storage.size)
|
||||
for (i in 1..numberOfTestMessages) {
|
||||
Assert.assertEquals("Message $i", storage[i - 1].content)
|
||||
}
|
||||
// Assert.assertEquals("Message 16", storage[15].content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun messageRepositoryTreeStorage_removeMessage_test() {
|
||||
|
||||
val storage = MessageRepository.createTreeBackedRepository()
|
||||
val numberOfTestMessages = 17
|
||||
for (i in 1..numberOfTestMessages) {
|
||||
storage.addLocalMessage(
|
||||
MessageInfoModel(
|
||||
id = i.toString(),
|
||||
content = "Message $i",
|
||||
messageType = ChatMessageType.TEXT
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
storage.removeMessage(
|
||||
MessageInfoModel(
|
||||
id = "17",
|
||||
content = "Message 17",
|
||||
messageType = ChatMessageType.TEXT
|
||||
)
|
||||
)
|
||||
|
||||
Assert.assertEquals(numberOfTestMessages - 1, storage.size)
|
||||
Assert.assertEquals(16, storage.getLastMessage()?.id?.toLong() ?: 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun messageRepositoryTreeStorage_getLastMessage_test() {
|
||||
val storage = MessageRepository.createTreeBackedRepository()
|
||||
val numberOfTestMessages = 17
|
||||
for (i in 1..numberOfTestMessages) {
|
||||
storage.addLocalMessage(
|
||||
MessageInfoModel(
|
||||
id = i.toString(),
|
||||
content = "Message $i",
|
||||
messageType = ChatMessageType.TEXT
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
storage.getLastMessage()?.id?.let { Assert.assertEquals(17, it.toLong()) }
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче