Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔨 Refactor the pasteboard reading logic #385

Merged
merged 2 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading