Skip to content

Commit

Permalink
🔨 Refactor the pasteboard reading logic (#385)
Browse files Browse the repository at this point in the history
  • Loading branch information
guiyanakuang authored Feb 25, 2024
1 parent c0904c6 commit 4d0cf03
Show file tree
Hide file tree
Showing 13 changed files with 322 additions and 132 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.clipevery.dao.clip

import com.clipevery.clip.ClipPlugin
import io.realm.kotlin.MutableRealm
import io.realm.kotlin.query.RealmResults
import io.realm.kotlin.types.RealmInstant
import org.mongodb.kbson.ObjectId
Expand All @@ -8,13 +10,17 @@ interface ClipDao {

fun getMaxClipId(): Int

fun createClipData(clipData: ClipData)
fun createClipData(clipData: ClipData): ObjectId

fun deleteClipData(id: ObjectId)

fun getClipData(appInstanceId: String? = null,
limit: Int): RealmResults<ClipData>

fun releaseClipData(id: ObjectId, clipPlugins: List<ClipPlugin>)

fun updateClipItem(update: (MutableRealm) -> Unit)

fun getClipDataGreaterThan(appInstanceId: String? = null,
createTime: RealmInstant): RealmResults<ClipData>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import com.clipevery.dao.clip.ClipAppearItem
import com.clipevery.dao.clip.ClipContent
import com.clipevery.dao.clip.ClipDao
import com.clipevery.dao.clip.ClipData
import com.clipevery.dao.clip.ClipType
import io.github.oshai.kotlinlogging.KotlinLogging
import io.realm.kotlin.MutableRealm
import io.realm.kotlin.ext.toRealmList
import io.realm.kotlin.types.RealmAny
import io.realm.kotlin.types.RealmInstant
import io.realm.kotlin.types.RealmObject
import org.mongodb.kbson.ObjectId
import kotlin.reflect.KClass

class ClipCollector(
Expand All @@ -17,55 +21,74 @@ class ClipCollector(
private val clipDao: ClipDao,
private val clipPlugins: List<ClipPlugin>) {

val logger = KotlinLogging.logger {}
private val logger = KotlinLogging.logger {}

private val collectors: Array<MutableMap<KClass<out ClipItemService>, ClipAppearItem>> = Array(itemCount) { mutableMapOf() }
private val preCollectors: Array<MutableMap<KClass<out ClipItemService>, ClipAppearItem>> = Array(itemCount) { mutableMapOf() }

fun needCollectionItem(itemIndex: Int, kclass: KClass<out ClipItemService>): Boolean {
return !collectors[itemIndex].contains(kclass)
private val updateCollectors: Array<MutableSet<KClass<out ClipItemService>>> = Array(itemCount) { mutableSetOf() }

private var existError = false

fun needPreCollectionItem(itemIndex: Int, kclass: KClass<out ClipItemService>): Boolean {
return !preCollectors[itemIndex].contains(kclass)
}

fun collectItem(itemIndex: Int, kclass: KClass<out ClipItemService>, clipItem: ClipAppearItem) {
collectors[itemIndex][kclass] = clipItem
fun needUpdateCollectItem(itemIndex: Int, kclass: KClass<out ClipItemService>): Boolean {
return !updateCollectors[itemIndex].contains(kclass)
}

fun collectError(clipId: Int, itemIndex: Int, error: Exception) {
logger.error(error) { "Failed to collect item $itemIndex of clip $clipId" }
fun preCollectItem(itemIndex: Int, kclass: KClass<out ClipItemService>, clipItem: ClipAppearItem) {
preCollectors[itemIndex][kclass] = clipItem
}

fun completeCollect(clipId: Int) {
// if there are no collectors, return
if (collectors.isEmpty()) {
return
fun updateCollectItem(itemIndex: Int, kclass: KClass<out ClipItemService>, update: (ClipAppearItem, MutableRealm) -> Unit) {
preCollectors[itemIndex][kclass]?.let{
val updateClipItem: (MutableRealm) -> Unit = { realm ->
update(it, realm)
}
clipDao.updateClipItem(updateClipItem)
}
}

var clipAppearItems: List<ClipAppearItem> = collectors.flatMap { it.values }
fun collectError(clipId: Int, itemIndex: Int, error: Exception) {
logger.error(error) { "Failed to collect item $itemIndex of clip $clipId" }
existError = true
}

for (clipPlugin in clipPlugins) {
clipAppearItems = clipPlugin.pluginProcess(clipAppearItems)
fun createPreClipData(clipId: Int): ObjectId? {
val collector = preCollectors.filter { it.isNotEmpty() }
if (collector.isEmpty()) {
return null
}
val clipAppearItems: List<ClipAppearItem> = preCollectors.flatMap { it.values }

assert(clipAppearItems.isNotEmpty())

val firstItem: ClipAppearItem = clipAppearItems.first()

val remainingItems: List<ClipAppearItem> = clipAppearItems.drop(1)

val clipAppearContent: RealmAny = RealmAny.create(firstItem as RealmObject)

val clipContent = ClipContent(remainingItems.map { RealmAny.create(it as RealmObject) }.toRealmList())
val clipContent = ClipContent(clipAppearItems.map { RealmAny.create(it as RealmObject) }.toRealmList())

val clipData = ClipData().apply {
this.clipId = clipId
this.clipAppearContent = clipAppearContent
this.clipContent = clipContent
this.clipType = firstItem.getClipType()
this.clipSearchContent = firstItem.getSearchContent()
this.md5 = firstItem.md5
this.clipType = ClipType.INVALID
this.md5 = ""
this.appInstanceId = appInfo.appInstanceId
this.preCreate = false
this.createTime = RealmInstant.now()
this.preCreate = true
}
return clipDao.createClipData(clipData)
}

clipDao.createClipData(clipData)
fun completeCollect(id: ObjectId) {
if (existError || preCollectors.isEmpty()) {
try {
clipDao.deleteClipData(id)
} catch (e: Exception) {
logger.error(e) { "Failed to delete clip $id" }
}
} else {
try {
clipDao.releaseClipData(id, clipPlugins)
} catch (e: Exception) {
logger.error(e) { "Failed to release clip $id" }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ interface ClipItemService {

fun getIdentifiers(): List<String>

fun createClipItem(
fun createPreClipItem(
clipId: Int,
itemIndex: Int,
identifier: String,
transferable: Transferable,
clipCollector: ClipCollector
)

fun loadRepresentation(
clipId: Int,
itemIndex: Int,
dataFlavor: DataFlavor,
Expand All @@ -17,19 +25,19 @@ interface ClipItemService {
) {
try {
val transferData = transferable.getTransferData(dataFlavor)
doCreateClipItem(transferData, clipId, itemIndex, dataFlavor, dataFlavorMap, transferable, clipCollector)
doLoadRepresentation(transferData, clipId, itemIndex, dataFlavor, dataFlavorMap, transferable, clipCollector)
} catch (e: Exception) {
collectError(e, clipId, itemIndex, clipCollector)
}
}

fun doCreateClipItem(transferData: Any,
clipId: Int,
itemIndex: Int,
dataFlavor: DataFlavor,
dataFlavorMap: Map<String, List<DataFlavor>>,
transferable: Transferable,
clipCollector: ClipCollector)
fun doLoadRepresentation(transferData: Any,
clipId: Int,
itemIndex: Int,
dataFlavor: DataFlavor,
dataFlavorMap: Map<String, List<DataFlavor>>,
transferable: Transferable,
clipCollector: ClipCollector)

fun collectError(error: Exception,
clipId: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,62 @@ open class DesktopTransferableConsumer(private val appInfo: AppInfo,
val clipCollector = ClipCollector(dataFlavorMap.size, appInfo, clipDao, clipPlugins)

try {
var itemIndex = 0
for (entry in dataFlavorMap) {
val identifier = entry.key
val flavors = entry.value
logger.info { "itemIndex: $itemIndex Transferable flavor: $identifier" }
for (flavor in flavors) {
clipItemServiceMap[identifier]?.let { clipItemService ->
if (clipCollector.needCollectionItem(itemIndex, clipItemService::class)) {
clipItemService.createClipItem(clipId, itemIndex, flavor, dataFlavorMap, transferable, clipCollector)
}
}
}
itemIndex ++
preCollect(clipId, dataFlavorMap, transferable, clipCollector)
clipCollector.createPreClipData(clipId)?.let {
updateClipData(clipId, dataFlavorMap, transferable, clipCollector)
clipCollector.completeCollect(it)
}
clipCollector.completeCollect(clipId)
} catch (e: Exception) {
logger.error(e) { "Failed to consume transferable" }
}
}

private fun preCollect(clipId: Int,
dataFlavorMap: Map<String, List<DataFlavor>>,
transferable: Transferable,
clipCollector: ClipCollector) {
var itemIndex = 0
for (entry in dataFlavorMap) {
val identifier = entry.key
val flavors = entry.value
logger.info { "itemIndex: $itemIndex Transferable flavor: $identifier" }
for (flavor in flavors) {
if (clipItemServiceMap[identifier]?.let { clipItemService ->
if (clipCollector.needPreCollectionItem(itemIndex, clipItemService::class)) {
clipItemService.createPreClipItem(clipId, itemIndex, identifier, transferable, clipCollector)
false
} else {
true
}
} == true) {
break
}
}
itemIndex ++
}
}

private fun updateClipData(clipId: Int,
dataFlavorMap: Map<String, List<DataFlavor>>,
transferable: Transferable,
clipCollector: ClipCollector) {
var itemIndex = 0
for (entry in dataFlavorMap) {
val identifier = entry.key
val flavors = entry.value
for (flavor in flavors) {
if (clipItemServiceMap[identifier]?.let { clipItemService ->
if (clipCollector.needUpdateCollectItem(itemIndex, clipItemService::class)) {
clipItemService.loadRepresentation(clipId, itemIndex, flavor, dataFlavorMap, transferable, clipCollector)
false
} else {
true
}
} == true) {
break
}
}
itemIndex ++
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.clipevery.dao.clip.ClipAppearItem
import com.clipevery.utils.DesktopFileUtils
import com.clipevery.utils.DesktopFileUtils.copyFile
import com.clipevery.utils.DesktopFileUtils.createClipRelativePath
import io.realm.kotlin.MutableRealm
import java.awt.datatransfer.DataFlavor
import java.awt.datatransfer.Transferable
import java.io.File
Expand All @@ -23,7 +24,21 @@ class FileItemService: ClipItemService {
return listOf(FILE_LIST_ID)
}

override fun doCreateClipItem(
override fun createPreClipItem(
clipId: Int,
itemIndex: Int,
identifier: String,
transferable: Transferable,
clipCollector: ClipCollector
) {
FileClipItem().apply {
this.identifier = identifier
}.let {
clipCollector.preCollectItem(itemIndex, this::class, it)
}
}

override fun doLoadRepresentation(
transferData: Any,
clipId: Int,
itemIndex: Int,
Expand All @@ -32,21 +47,25 @@ class FileItemService: ClipItemService {
transferable: Transferable,
clipCollector: ClipCollector
) {


if (transferData is List<*>) {
val files = transferData.filterIsInstance<File>()
if (files.size == 1) {
var clipItem: ClipAppearItem? = null
val fileName = files[0].name
val relativePath = createClipRelativePath(clipId, fileName)
val filePath = DesktopFileUtils.createClipPath(relativePath, isFile = true, AppFileType.FILE)
if (copyFile(files[0].toPath(), filePath)) {
clipItem = FileClipItem().apply {
this.identifier = dataFlavor.humanPresentableName
this.relativePath = relativePath
this.md5 = DesktopFileUtils.getFileMd5(filePath)
val md5 = DesktopFileUtils.getFileMd5(filePath)

val update: (ClipAppearItem, MutableRealm) -> Unit = { clipItem, realm ->
realm.query(FileClipItem::class).query("id == $0", clipItem.id).first().find()?.apply {
this.relativePath = relativePath
this.md5 = md5
}
}
clipCollector.updateCollectItem(itemIndex, this::class, update)
}
clipItem?.let { clipCollector.collectItem(itemIndex, this::class, it) }
} else if (files.size > 1) {
// todo multi files
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.clipevery.clip.ClipItemService
import com.clipevery.clip.item.HtmlClipItem
import com.clipevery.dao.clip.ClipAppearItem
import com.clipevery.utils.md5ByString
import io.realm.kotlin.MutableRealm
import java.awt.datatransfer.DataFlavor
import java.awt.datatransfer.Transferable

Expand All @@ -19,23 +20,36 @@ class HtmlItemService: ClipItemService {
return listOf(HTML_ID)
}

override fun doCreateClipItem(transferData: Any,
clipId: Int,
itemIndex: Int,
dataFlavor: DataFlavor,
dataFlavorMap: Map<String, List<DataFlavor>>,
transferable: Transferable,
clipCollector: ClipCollector) {
var clipItem: ClipAppearItem? = null
override fun createPreClipItem(
clipId: Int,
itemIndex: Int,
identifier: String,
transferable: Transferable,
clipCollector: ClipCollector
) {
HtmlClipItem().apply {
this.identifier = identifier
}.let {
clipCollector.preCollectItem(itemIndex, this::class, it)
}
}

override fun doLoadRepresentation(transferData: Any,
clipId: Int,
itemIndex: Int,
dataFlavor: DataFlavor,
dataFlavorMap: Map<String, List<DataFlavor>>,
transferable: Transferable,
clipCollector: ClipCollector) {
if (transferData is String) {
clipItem = HtmlClipItem().apply {
identifier = dataFlavor.humanPresentableName
html = transferData
md5 = md5ByString(html)
val md5 = md5ByString(transferData)
val update: (ClipAppearItem, MutableRealm) -> Unit = { clipItem, realm ->
realm.query(HtmlClipItem::class).query("id == $0", clipItem.id).first().find()?.apply {
this.html = transferData
this.md5 = md5
}
}
clipCollector.updateCollectItem(itemIndex, this::class, update)
}
clipItem?.let { clipCollector.collectItem(itemIndex, this::class, it) }
}


}
Loading

0 comments on commit 4d0cf03

Please sign in to comment.