mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 16:33:27 +00:00
Merge branch 'vitorpamplona:main' into profiles-list-management
This commit is contained in:
+2
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.stats.RelayStat
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.stats.RelayStats
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
@@ -31,6 +32,7 @@ data class BasicRelaySetupInfo(
|
||||
val relayStat: RelayStat,
|
||||
val paidRelay: Boolean = false,
|
||||
val forcesTor: Boolean = false,
|
||||
val users: List<User> = emptyList(),
|
||||
)
|
||||
|
||||
fun relaySetupInfoBuilder(
|
||||
|
||||
+67
@@ -22,26 +22,37 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.LocalCache.users
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav.nav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.ephemChat.header.loadRelayInfo
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfHorzPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfVertPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.Height25Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.LargeRelayIconModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.ReactionRowHeightChatMaxWidth
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size25dp
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@@ -53,6 +64,7 @@ fun BasicRelaySetupInfoClickableRow(
|
||||
onDelete: ((BasicRelaySetupInfo) -> Unit)?,
|
||||
onClick: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
Column(
|
||||
@@ -90,6 +102,8 @@ fun BasicRelaySetupInfoClickableRow(
|
||||
ReactionRowHeightChatMaxWidth,
|
||||
)
|
||||
|
||||
UsedBy(item, accountViewModel, nav)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = ReactionRowHeightChatMaxWidth,
|
||||
@@ -106,3 +120,56 @@ fun BasicRelaySetupInfoClickableRow(
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun UsedBy(
|
||||
item: BasicRelaySetupInfo,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
if (item.users.isNotEmpty()) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
item.users.getOrNull(0)?.let {
|
||||
UserPicture(
|
||||
user = it,
|
||||
size = Size25dp,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
item.users.getOrNull(1)?.let {
|
||||
UserPicture(
|
||||
user = it,
|
||||
size = Size25dp,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
item.users.getOrNull(2)?.let {
|
||||
UserPicture(
|
||||
user = it,
|
||||
size = Size25dp,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
item.users.getOrNull(3)?.let {
|
||||
UserPicture(
|
||||
user = it,
|
||||
size = Size25dp,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
if (item.users.size > 4) {
|
||||
Box(contentAlignment = Alignment.Center, modifier = Height25Modifier) {
|
||||
Text(
|
||||
text = stringRes(R.string.and_more, item.users.size - 4),
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -44,5 +44,6 @@ fun BasicRelaySetupInfoDialog(
|
||||
onDelete = onDelete,
|
||||
accountViewModel = accountViewModel,
|
||||
onClick = { nav.nav(Route.RelayInfo(item.relay.url)) },
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
+17
-15
@@ -83,22 +83,24 @@ abstract class BasicRelaySetupInfoModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
_relays.update {
|
||||
val relayList = getRelayList() ?: emptyList()
|
||||
open fun relayListBuilder(): List<BasicRelaySetupInfo> {
|
||||
val relayList = getRelayList() ?: emptyList()
|
||||
|
||||
relayList
|
||||
.map {
|
||||
relaySetupInfoBuilder(
|
||||
normalized = it,
|
||||
forcesTor =
|
||||
account.torRelayState.flow.value
|
||||
.useTor(it),
|
||||
)
|
||||
}.distinctBy { it.relay }
|
||||
.sortedBy { it.relayStat.receivedBytes }
|
||||
.reversed()
|
||||
}
|
||||
return relayList
|
||||
.map {
|
||||
relaySetupInfoBuilder(
|
||||
normalized = it,
|
||||
forcesTor =
|
||||
account.torRelayState.flow.value
|
||||
.useTor(it),
|
||||
)
|
||||
}.distinctBy { it.relay }
|
||||
.sortedBy { it.relayStat.receivedBytes }
|
||||
.reversed()
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
_relays.update { relayListBuilder() }
|
||||
}
|
||||
|
||||
fun addRelay(relay: BasicRelaySetupInfo) {
|
||||
|
||||
+24
@@ -20,10 +20,34 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.connected
|
||||
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoModel
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.stats.RelayStats
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
|
||||
class ConnectedRelayListViewModel : BasicRelaySetupInfoModel() {
|
||||
override fun relayListBuilder(): List<BasicRelaySetupInfo> {
|
||||
val relayList = getRelayList()
|
||||
|
||||
return relayList
|
||||
.map {
|
||||
BasicRelaySetupInfo(
|
||||
relay = it,
|
||||
relayStat = RelayStats.get(it),
|
||||
forcesTor =
|
||||
account.torRelayState.flow.value
|
||||
.useTor(it),
|
||||
users =
|
||||
account.followsPerRelay.value[it]?.mapNotNull { hex ->
|
||||
LocalCache.checkGetOrCreateUser(hex)
|
||||
} ?: emptyList(),
|
||||
)
|
||||
}.distinctBy { it.relay }
|
||||
.sortedBy { it.relayStat.receivedBytes }
|
||||
.reversed()
|
||||
}
|
||||
|
||||
override fun getRelayList(): List<NormalizedRelayUrl> =
|
||||
account.client
|
||||
.relayStatusFlow()
|
||||
|
||||
@@ -191,6 +191,7 @@ val UserNameMaxRowHeight = Modifier.fillMaxWidth()
|
||||
|
||||
val Height24dpModifier = Modifier.height(24.dp)
|
||||
val Height4dpModifier = Modifier.height(4.dp)
|
||||
val Height25Modifier = Modifier.height(Size25dp)
|
||||
|
||||
val Height24dpFilledModifier = Modifier.fillMaxWidth().height(24.dp)
|
||||
val Height4dpFilledModifier = Modifier.fillMaxWidth().height(4.dp)
|
||||
|
||||
@@ -146,3 +146,4 @@ jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "k
|
||||
jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
jetbrainsComposeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
serialization = { id = 'org.jetbrains.kotlin.plugin.serialization', version.ref = 'kotlinxSerializationPlugin' }
|
||||
parcelize = { id = "kotlin-parcelize" }
|
||||
@@ -3,6 +3,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
plugins {
|
||||
alias(libs.plugins.androidLibrary)
|
||||
alias(libs.plugins.jetbrainsKotlinAndroid)
|
||||
alias(libs.plugins.parcelize)
|
||||
}
|
||||
|
||||
android {
|
||||
|
||||
+19
-21
@@ -32,61 +32,59 @@ data class V2(
|
||||
)
|
||||
|
||||
data class Valid(
|
||||
@JsonProperty("get_conversation_key")
|
||||
val getConversationKey: ArrayList<GetConversationKey> = arrayListOf(),
|
||||
@JsonProperty("get_message_keys") val getMessageKeys: GetMessageKeys? = GetMessageKeys(),
|
||||
@JsonProperty("calc_padded_len") val calcPaddedLen: ArrayList<ArrayList<Int>> = arrayListOf(),
|
||||
@JsonProperty("encrypt_decrypt") val encryptDecrypt: ArrayList<EncryptDecrypt> = arrayListOf(),
|
||||
@JsonProperty("encrypt_decrypt_long_msg")
|
||||
@field:JsonProperty("get_conversation_key") val getConversationKey: ArrayList<GetConversationKey> = arrayListOf(),
|
||||
@field:JsonProperty("get_message_keys") val getMessageKeys: GetMessageKeys? = GetMessageKeys(),
|
||||
@field:JsonProperty("calc_padded_len") val calcPaddedLen: ArrayList<ArrayList<Int>> = arrayListOf(),
|
||||
@field:JsonProperty("encrypt_decrypt") val encryptDecrypt: ArrayList<EncryptDecrypt> = arrayListOf(),
|
||||
@field:JsonProperty("encrypt_decrypt_long_msg")
|
||||
val encryptDecryptLongMsg: ArrayList<EncryptDecryptLongMsg> = arrayListOf(),
|
||||
)
|
||||
|
||||
data class Invalid(
|
||||
@JsonProperty("encrypt_msg_lengths") val encryptMsgLengths: ArrayList<Int> = arrayListOf(),
|
||||
@JsonProperty("get_conversation_key")
|
||||
val getConversationKey: ArrayList<GetConversationKey> = arrayListOf(),
|
||||
@JsonProperty("decrypt") val decrypt: ArrayList<Decrypt> = arrayListOf(),
|
||||
@field:JsonProperty("encrypt_msg_lengths") val encryptMsgLengths: ArrayList<Int> = arrayListOf(),
|
||||
@field:JsonProperty("get_conversation_key") val getConversationKey: ArrayList<GetConversationKey> = arrayListOf(),
|
||||
@field:JsonProperty("decrypt") val decrypt: ArrayList<Decrypt> = arrayListOf(),
|
||||
)
|
||||
|
||||
data class GetConversationKey(
|
||||
val sec1: String? = null,
|
||||
val pub2: String? = null,
|
||||
val note: String? = null,
|
||||
@JsonProperty("conversation_key") val conversationKey: String? = null,
|
||||
@field:JsonProperty("conversation_key") val conversationKey: String? = null,
|
||||
)
|
||||
|
||||
data class GetMessageKeys(
|
||||
@JsonProperty("conversation_key") val conversationKey: String? = null,
|
||||
@field:JsonProperty("conversation_key") val conversationKey: String? = null,
|
||||
val keys: ArrayList<Keys> = arrayListOf(),
|
||||
)
|
||||
|
||||
data class Keys(
|
||||
@JsonProperty("nonce") val nonce: String? = null,
|
||||
@JsonProperty("chacha_key") val chachaKey: String? = null,
|
||||
@JsonProperty("chacha_nonce") val chachaNonce: String? = null,
|
||||
@JsonProperty("hmac_key") val hmacKey: String? = null,
|
||||
@field:JsonProperty("nonce") val nonce: String? = null,
|
||||
@field:JsonProperty("chacha_key") val chachaKey: String? = null,
|
||||
@field:JsonProperty("chacha_nonce") val chachaNonce: String? = null,
|
||||
@field:JsonProperty("hmac_key") val hmacKey: String? = null,
|
||||
)
|
||||
|
||||
data class EncryptDecrypt(
|
||||
val sec1: String? = null,
|
||||
val sec2: String? = null,
|
||||
@JsonProperty("conversation_key") val conversationKey: String? = null,
|
||||
@field:JsonProperty("conversation_key") val conversationKey: String? = null,
|
||||
val nonce: String? = null,
|
||||
val plaintext: String? = null,
|
||||
val payload: String? = null,
|
||||
)
|
||||
|
||||
data class EncryptDecryptLongMsg(
|
||||
@JsonProperty("conversation_key") val conversationKey: String? = null,
|
||||
@field:JsonProperty("conversation_key") val conversationKey: String? = null,
|
||||
val nonce: String? = null,
|
||||
val pattern: String? = null,
|
||||
val repeat: Int? = null,
|
||||
@JsonProperty("plaintext_sha256") val plaintextSha256: String? = null,
|
||||
@JsonProperty("payload_sha256") val payloadSha256: String? = null,
|
||||
@field:JsonProperty("plaintext_sha256") val plaintextSha256: String? = null,
|
||||
@field:JsonProperty("payload_sha256") val payloadSha256: String? = null,
|
||||
)
|
||||
|
||||
data class Decrypt(
|
||||
@JsonProperty("conversation_key") val conversationKey: String? = null,
|
||||
@field:JsonProperty("conversation_key") val conversationKey: String? = null,
|
||||
val nonce: String? = null,
|
||||
val plaintext: String? = null,
|
||||
val payload: String? = null,
|
||||
|
||||
@@ -23,36 +23,20 @@ package com.vitorpamplona.quartz.experimental.limits
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
|
||||
class Limits(
|
||||
@JsonProperty("can_write")
|
||||
val canWrite: Boolean?,
|
||||
@JsonProperty("can_read")
|
||||
val canRead: Boolean?,
|
||||
@JsonProperty("accepted_event_kinds")
|
||||
val acceptedEventKinds: Set<Int>?,
|
||||
@JsonProperty("blocked_event_kinds")
|
||||
val blockedEventKinds: Set<Int>?,
|
||||
@JsonProperty("min_pow_difficulty")
|
||||
val minPoW: Int?,
|
||||
@JsonProperty("max_message_length")
|
||||
val maxMessageLength: Int?,
|
||||
@JsonProperty("max_subscriptions")
|
||||
val maxSubscriptions: Int?,
|
||||
@JsonProperty("max_filters")
|
||||
val maxFilters: Int?,
|
||||
@JsonProperty("max_limit")
|
||||
val maxLimit: Int?,
|
||||
@JsonProperty("max_event_tags")
|
||||
val maxEventTags: Int?,
|
||||
@JsonProperty("max_content_length")
|
||||
val maxContentLength: Int?,
|
||||
@JsonProperty("created_at_msecs_ago")
|
||||
val createdAtMillisecsAgo: Long?,
|
||||
@JsonProperty("created_at_msecs_ahead")
|
||||
val createdAtMillisecsAhead: Long?,
|
||||
@JsonProperty("filter_rate_limit")
|
||||
val filterRateLimit: Long?,
|
||||
@JsonProperty("publishing_rate_limit")
|
||||
val publishingRateLimit: Long?,
|
||||
@JsonProperty("required_tags")
|
||||
val requiredTags: Array<Array<String>>?,
|
||||
@field:JsonProperty("can_write") val canWrite: Boolean?,
|
||||
@field:JsonProperty("can_read") val canRead: Boolean?,
|
||||
@field:JsonProperty("accepted_event_kinds") val acceptedEventKinds: Set<Int>?,
|
||||
@field:JsonProperty("blocked_event_kinds") val blockedEventKinds: Set<Int>?,
|
||||
@field:JsonProperty("min_pow_difficulty") val minPoW: Int?,
|
||||
@field:JsonProperty("max_message_length") val maxMessageLength: Int?,
|
||||
@field:JsonProperty("max_subscriptions") val maxSubscriptions: Int?,
|
||||
@field:JsonProperty("max_filters") val maxFilters: Int?,
|
||||
@field:JsonProperty("max_limit") val maxLimit: Int?,
|
||||
@field:JsonProperty("max_event_tags") val maxEventTags: Int?,
|
||||
@field:JsonProperty("max_content_length") val maxContentLength: Int?,
|
||||
@field:JsonProperty("created_at_msecs_ago") val createdAtMillisecsAgo: Long?,
|
||||
@field:JsonProperty("created_at_msecs_ahead") val createdAtMillisecsAhead: Long?,
|
||||
@field:JsonProperty("filter_rate_limit") val filterRateLimit: Long?,
|
||||
@field:JsonProperty("publishing_rate_limit") val publishingRateLimit: Long?,
|
||||
@field:JsonProperty("required_tags") val requiredTags: Array<Array<String>>?,
|
||||
)
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ class RelayPool(
|
||||
var lastReconnectCall = TimeUtils.now()
|
||||
|
||||
fun reconnectIfNeedsToORIfItIsTime() {
|
||||
if (lastReconnectCall < TimeUtils.tenSecondsAgo()) {
|
||||
if (lastReconnectCall < TimeUtils.oneMinuteAgo()) {
|
||||
relays.forEach { url, relay ->
|
||||
if (relay.isConnected()) {
|
||||
if (relay.needsToReconnect()) {
|
||||
|
||||
@@ -30,7 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.jackson.JsonMapper
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
class EventTemplate<T : Event>(
|
||||
@JsonProperty("created_at")
|
||||
@field:JsonProperty("created_at")
|
||||
val createdAt: Long,
|
||||
val kind: Int,
|
||||
val tags: TagArray,
|
||||
|
||||
+5
-1
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.tags.addressables
|
||||
|
||||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
@@ -28,12 +29,15 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class Address(
|
||||
val kind: Kind,
|
||||
val pubKeyHex: HexKey,
|
||||
val dTag: String = "",
|
||||
) : Comparable<Address> {
|
||||
) : Comparable<Address>,
|
||||
Parcelable {
|
||||
fun toValue() = assemble(kind, pubKeyHex, dTag)
|
||||
|
||||
fun countMemory(): Long =
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||
|
||||
// RESPONSE OBJECTS
|
||||
abstract class Response(
|
||||
@JsonProperty("result_type") val resultType: String,
|
||||
@field:JsonProperty("result_type") val resultType: String,
|
||||
) {
|
||||
abstract fun countMemory(): Long
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||
|
||||
class Rumor(
|
||||
val id: HexKey?,
|
||||
@JsonProperty("pubkey") val pubKey: HexKey?,
|
||||
@JsonProperty("created_at") val createdAt: Long?,
|
||||
@field:JsonProperty("pubkey") val pubKey: HexKey?,
|
||||
@field:JsonProperty("created_at") val createdAt: Long?,
|
||||
val kind: Int?,
|
||||
val tags: Array<Array<String>>?,
|
||||
val content: String?,
|
||||
|
||||
+2
-4
@@ -27,11 +27,9 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
data class UploadResult(
|
||||
val status: String? = null,
|
||||
val message: String? = null,
|
||||
@JsonProperty("processing_url")
|
||||
val processingUrl: String? = null,
|
||||
@field:JsonProperty("processing_url") val processingUrl: String? = null,
|
||||
val percentage: Int? = null,
|
||||
@JsonProperty("nip94_event")
|
||||
val nip94Event: PartialEvent? = null,
|
||||
@field:JsonProperty("nip94_event") val nip94Event: PartialEvent? = null,
|
||||
) {
|
||||
companion object {
|
||||
fun parse(body: String): UploadResult {
|
||||
|
||||
@@ -27,33 +27,20 @@ typealias PlanName = String
|
||||
typealias MimeType = String
|
||||
|
||||
data class ServerInfo(
|
||||
@JsonProperty("api_url")
|
||||
val apiUrl: String,
|
||||
@JsonProperty("download_url")
|
||||
val downloadUrl: String? = null,
|
||||
@JsonProperty("delegated_to_url")
|
||||
val delegatedToUrl: String? = null,
|
||||
@JsonProperty("supported_nips")
|
||||
val supportedNips: ArrayList<Int> = arrayListOf(),
|
||||
@JsonProperty("tos_url") val
|
||||
tosUrl: String? = null,
|
||||
@JsonProperty("content_types") val
|
||||
contentTypes: ArrayList<MimeType> = arrayListOf(),
|
||||
@JsonProperty("plans") val
|
||||
plans: Map<PlanName, Plan> = mapOf(),
|
||||
@field:JsonProperty("api_url") val apiUrl: String,
|
||||
@field:JsonProperty("download_url") val downloadUrl: String? = null,
|
||||
@field:JsonProperty("delegated_to_url") val delegatedToUrl: String? = null,
|
||||
@field:JsonProperty("supported_nips") val supportedNips: ArrayList<Int> = arrayListOf(),
|
||||
@field:JsonProperty("tos_url") val tosUrl: String? = null,
|
||||
@field:JsonProperty("content_types") val contentTypes: ArrayList<MimeType> = arrayListOf(),
|
||||
@field:JsonProperty("plans") val plans: Map<PlanName, Plan> = mapOf(),
|
||||
)
|
||||
|
||||
data class Plan(
|
||||
@JsonProperty("name") val
|
||||
name: String? = null,
|
||||
@JsonProperty("is_nip98_required") val
|
||||
isNip98Required: Boolean? = null,
|
||||
@JsonProperty("url") val
|
||||
url: String? = null,
|
||||
@JsonProperty("max_byte_size") val
|
||||
maxByteSize: Long? = null,
|
||||
@JsonProperty("file_expiration") val
|
||||
fileExpiration: ArrayList<Int> = arrayListOf(),
|
||||
@JsonProperty("media_transformations")
|
||||
val mediaTransformations: Map<MimeType, Array<String>> = emptyMap(),
|
||||
@field:JsonProperty("name") val name: String? = null,
|
||||
@field:JsonProperty("is_nip98_required") val isNip98Required: Boolean? = null,
|
||||
@field:JsonProperty("url") val url: String? = null,
|
||||
@field:JsonProperty("max_byte_size") val maxByteSize: Long? = null,
|
||||
@field:JsonProperty("file_expiration") val fileExpiration: ArrayList<Int> = arrayListOf(),
|
||||
@field:JsonProperty("media_transformations") val mediaTransformations: Map<MimeType, Array<String>> = emptyMap(),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.utils
|
||||
|
||||
import com.vitorpamplona.quartz.utils.CacheCollectors.BiFilter
|
||||
import com.vitorpamplona.quartz.utils.CacheCollectors.BiMapper
|
||||
import com.vitorpamplona.quartz.utils.CacheCollectors.BiMapperNotNull
|
||||
import com.vitorpamplona.quartz.utils.CacheCollectors.BiNotNullMapper
|
||||
import com.vitorpamplona.quartz.utils.CacheCollectors.BiSumOfLong
|
||||
import java.util.function.BiConsumer
|
||||
|
||||
class BiFilterCollector<K, V>(
|
||||
private val filter: BiFilter<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: ArrayList<V> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) {
|
||||
results.add(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiFilterUniqueCollector<K, V>(
|
||||
private val filter: BiFilter<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: HashSet<V> = HashSet()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) {
|
||||
results.add(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiMapCollector<K, V, R>(
|
||||
private val mapper: BiMapper<K, V, R?>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: ArrayList<R> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.add(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiMapUniqueCollector<K, V, R>(
|
||||
private val mapper: BiMapper<K, V, R?>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: HashSet<R> = HashSet()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.add(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiMapFlattenCollector<K, V, R>(
|
||||
private val mapper: BiMapper<K, V, Collection<R>?>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: ArrayList<R> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.addAll(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiMapFlattenUniqueCollector<K, V, R>(
|
||||
private val mapper: BiMapper<K, V, Collection<R>?>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: HashSet<R> = HashSet()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.addAll(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiNotNullMapCollector<K, V, R>(
|
||||
private val mapper: BiNotNullMapper<K, V, R>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: ArrayList<R> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
results.add(mapper.map(k, v))
|
||||
}
|
||||
}
|
||||
|
||||
class BiMaxOfCollector<K, V>(
|
||||
private val filter: BiFilter<K, V>,
|
||||
private val comparator: Comparator<V>,
|
||||
) : BiConsumer<K, V> {
|
||||
private var _maxK: K? = null
|
||||
private var _maxV: V? = null
|
||||
|
||||
val maxK: K? get() = _maxK
|
||||
val maxV: V? get() = _maxV
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) {
|
||||
if (_maxK == null || comparator.compare(v, _maxV) > 0) {
|
||||
_maxK = k
|
||||
_maxV = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiSumOfCollector<K, V>(
|
||||
private val mapper: CacheCollectors.BiSumOf<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
private var _sum = 0
|
||||
val sum: Int get() = _sum
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
_sum += mapper.map(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
class BiSumOfLongCollector<K, V>(
|
||||
private val mapper: BiSumOfLong<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
private var _sum = 0L
|
||||
val sum: Long get() = _sum
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
_sum += mapper.map(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
class BiGroupByCollector<K, V, R>(
|
||||
private val mapper: BiNotNullMapper<K, V, R>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results = HashMap<R, ArrayList<V>>()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val group = mapper.map(k, v)
|
||||
val list = results[group]
|
||||
if (list == null) {
|
||||
val answer = ArrayList<V>()
|
||||
answer.add(v)
|
||||
results[group] = answer
|
||||
} else {
|
||||
list.add(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiCountByGroupCollector<K, V, R>(
|
||||
private val mapper: BiNotNullMapper<K, V, R>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results = HashMap<R, Int>()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val group = mapper.map(k, v)
|
||||
val count = results[group]
|
||||
if (count == null) {
|
||||
results[group] = 1
|
||||
} else {
|
||||
results[group] = count + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiSumByGroupCollector<K, V, R>(
|
||||
private val mapper: BiNotNullMapper<K, V, R>,
|
||||
private val sumOf: BiNotNullMapper<K, V, Long>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results = HashMap<R, Long>()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val group = mapper.map(k, v)
|
||||
val sum = results[group]
|
||||
if (sum == null) {
|
||||
results[group] = sumOf.map(k, v)
|
||||
} else {
|
||||
results[group] = sum + sumOf.map(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiCountIfCollector<K, V>(
|
||||
private val filter: BiFilter<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
private var _count = 0
|
||||
val count: Int get() = _count
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) _count++
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateCollector<K, V, T, U>(
|
||||
size: Int,
|
||||
private val mapper: BiMapperNotNull<K, V, Pair<T, U>>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: LinkedHashMap<T, U> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val pair = mapper.map(k, v)
|
||||
results[pair.first] = pair.second
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateNotNullCollector<K, V, T, U>(
|
||||
size: Int,
|
||||
private val mapper: BiMapper<K, V, Pair<T, U>?>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: LinkedHashMap<T, U> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val pair = mapper.map(k, v)
|
||||
if (pair != null) {
|
||||
results[pair.first] = pair.second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateWithCollector<K, V, U>(
|
||||
size: Int,
|
||||
private val mapper: BiMapper<K, V, U?>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: LinkedHashMap<K, U?> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
results[k] = mapper.map(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateNotNullWithCollector<K, V, U>(
|
||||
size: Int,
|
||||
private val mapper: BiMapper<K, V, U>,
|
||||
) : BiConsumer<K, V> {
|
||||
val results: LinkedHashMap<K, U> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val newValue = mapper.map(k, v)
|
||||
if (newValue != null) {
|
||||
results[k] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object CacheCollectors {
|
||||
fun interface BiFilter<K, V> {
|
||||
fun filter(
|
||||
k: K,
|
||||
v: V,
|
||||
): Boolean
|
||||
}
|
||||
|
||||
fun interface BiMapper<K, V, R> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): R?
|
||||
}
|
||||
|
||||
fun interface BiMapperNotNull<K, V, R> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): R
|
||||
}
|
||||
|
||||
fun interface BiNotNullMapper<K, V, R> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): R
|
||||
}
|
||||
|
||||
fun interface BiSumOf<K, V> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): Int
|
||||
}
|
||||
|
||||
fun interface BiSumOfLong<K, V> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): Long
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.utils
|
||||
|
||||
import java.util.function.BiConsumer
|
||||
|
||||
interface CacheOperations<K, V> {
|
||||
fun forEach(consumer: BiConsumer<K, V>)
|
||||
|
||||
fun size(): Int
|
||||
|
||||
fun filter(consumer: CacheCollectors.BiFilter<K, V>): List<V> {
|
||||
val runner = BiFilterCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun filterIntoSet(consumer: CacheCollectors.BiFilter<K, V>): Set<V> {
|
||||
val runner = BiFilterUniqueCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> map(consumer: CacheCollectors.BiNotNullMapper<K, V, R>): List<R> {
|
||||
val runner = BiNotNullMapCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapNotNull(consumer: CacheCollectors.BiMapper<K, V, R?>): List<R> {
|
||||
val runner = BiMapCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapNotNullIntoSet(consumer: CacheCollectors.BiMapper<K, V, R?>): Set<R> {
|
||||
val runner = BiMapUniqueCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapFlatten(consumer: CacheCollectors.BiMapper<K, V, Collection<R>?>): List<R> {
|
||||
val runner = BiMapFlattenCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapFlattenIntoSet(consumer: CacheCollectors.BiMapper<K, V, Collection<R>?>): Set<R> {
|
||||
val runner = BiMapFlattenUniqueCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun maxOrNullOf(
|
||||
filter: CacheCollectors.BiFilter<K, V>,
|
||||
comparator: Comparator<V>,
|
||||
): V? {
|
||||
val runner = BiMaxOfCollector(filter, comparator)
|
||||
forEach(runner)
|
||||
return runner.maxV
|
||||
}
|
||||
|
||||
fun sumOf(consumer: CacheCollectors.BiSumOf<K, V>): Int {
|
||||
val runner = BiSumOfCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.sum
|
||||
}
|
||||
|
||||
fun sumOfLong(consumer: CacheCollectors.BiSumOfLong<K, V>): Long {
|
||||
val runner = BiSumOfLongCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.sum
|
||||
}
|
||||
|
||||
fun <R> groupBy(consumer: CacheCollectors.BiNotNullMapper<K, V, R>): Map<R, List<V>> {
|
||||
val runner = BiGroupByCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> countByGroup(consumer: CacheCollectors.BiNotNullMapper<K, V, R>): Map<R, Int> {
|
||||
val runner = BiCountByGroupCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> sumByGroup(
|
||||
groupMap: CacheCollectors.BiNotNullMapper<K, V, R>,
|
||||
sumOf: CacheCollectors.BiNotNullMapper<K, V, Long>,
|
||||
): Map<R, Long> {
|
||||
val runner = BiSumByGroupCollector(groupMap, sumOf)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun count(consumer: CacheCollectors.BiFilter<K, V>): Int {
|
||||
val runner = BiCountIfCollector(consumer)
|
||||
forEach(runner)
|
||||
return runner.count
|
||||
}
|
||||
|
||||
fun <T, U> associate(transform: (K, V) -> Pair<T, U>): Map<T, U> {
|
||||
val runner = BiAssociateCollector(size(), transform)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <U> associateWith(transform: (K, V) -> U?): Map<K, U?> {
|
||||
val runner = BiAssociateWithCollector(size(), transform)
|
||||
forEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun joinToString(
|
||||
separator: CharSequence = ", ",
|
||||
prefix: CharSequence = "",
|
||||
postfix: CharSequence = "",
|
||||
limit: Int = -1,
|
||||
truncated: CharSequence = "...",
|
||||
transform: ((K, V) -> CharSequence)? = null,
|
||||
): String {
|
||||
val buffer = StringBuilder()
|
||||
buffer.append(prefix)
|
||||
var count = 0
|
||||
forEach { key, value ->
|
||||
val str = if (transform != null) transform(key, value) else ""
|
||||
if (str.isNotEmpty()) {
|
||||
if (++count > 1) buffer.append(separator)
|
||||
if (limit < 0 || count <= limit) {
|
||||
when {
|
||||
transform != null -> buffer.append(str)
|
||||
else -> buffer.append("$key $value")
|
||||
}
|
||||
} else {
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
}
|
||||
if (limit >= 0 && count > limit) buffer.append(truncated)
|
||||
buffer.append(postfix)
|
||||
return buffer.toString()
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.utils
|
||||
import java.util.concurrent.ConcurrentSkipListMap
|
||||
import java.util.function.BiConsumer
|
||||
|
||||
class LargeCache<K, V> {
|
||||
class LargeCache<K, V> : CacheOperations<K, V> {
|
||||
private val cache = ConcurrentSkipListMap<K, V>()
|
||||
|
||||
fun keys() = cache.keys
|
||||
@@ -34,7 +34,7 @@ class LargeCache<K, V> {
|
||||
|
||||
fun remove(key: K) = cache.remove(key)
|
||||
|
||||
fun size() = cache.size
|
||||
override fun size() = cache.size
|
||||
|
||||
fun isEmpty() = cache.isEmpty()
|
||||
|
||||
@@ -76,489 +76,7 @@ class LargeCache<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
fun forEach(consumer: BiConsumer<K, V>) {
|
||||
innerForEach(consumer)
|
||||
}
|
||||
|
||||
fun filter(consumer: BiFilter<K, V>): List<V> {
|
||||
val runner = BiFilterCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun filterIntoSet(consumer: BiFilter<K, V>): Set<V> {
|
||||
val runner = BiFilterUniqueCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> map(consumer: BiNotNullMapper<K, V, R>): List<R> {
|
||||
val runner = BiNotNullMapCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapNotNull(consumer: BiMapper<K, V, R?>): List<R> {
|
||||
val runner = BiMapCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapNotNullIntoSet(consumer: BiMapper<K, V, R?>): Set<R> {
|
||||
val runner = BiMapUniqueCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapFlatten(consumer: BiMapper<K, V, Collection<R>?>): List<R> {
|
||||
val runner = BiMapFlattenCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapFlattenIntoSet(consumer: BiMapper<K, V, Collection<R>?>): Set<R> {
|
||||
val runner = BiMapFlattenUniqueCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun maxOrNullOf(
|
||||
filter: BiFilter<K, V>,
|
||||
comparator: Comparator<V>,
|
||||
): V? {
|
||||
val runner = BiMaxOfCollector(filter, comparator)
|
||||
innerForEach(runner)
|
||||
return runner.maxV
|
||||
}
|
||||
|
||||
fun sumOf(consumer: BiSumOf<K, V>): Int {
|
||||
val runner = BiSumOfCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.sum
|
||||
}
|
||||
|
||||
fun sumOfLong(consumer: BiSumOfLong<K, V>): Long {
|
||||
val runner = BiSumOfLongCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.sum
|
||||
}
|
||||
|
||||
fun <R> groupBy(consumer: BiNotNullMapper<K, V, R>): Map<R, List<V>> {
|
||||
val runner = BiGroupByCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> countByGroup(consumer: BiNotNullMapper<K, V, R>): Map<R, Int> {
|
||||
val runner = BiCountByGroupCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> sumByGroup(
|
||||
groupMap: BiNotNullMapper<K, V, R>,
|
||||
sumOf: BiNotNullMapper<K, V, Long>,
|
||||
): Map<R, Long> {
|
||||
val runner = BiSumByGroupCollector(groupMap, sumOf)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun count(consumer: BiFilter<K, V>): Int {
|
||||
val runner = BiCountIfCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.count
|
||||
}
|
||||
|
||||
fun <T, U> associate(transform: (K, V) -> Pair<T, U>): Map<T, U> {
|
||||
val runner = BiAssociateCollector(size(), transform)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <T, U> associateNotNull(transform: (K, V) -> Pair<T, U>?): Map<T, U> {
|
||||
val runner = BiAssociateNotNullCollector(size(), transform)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <U> associateWith(transform: (K, V) -> U?): Map<K, U?> {
|
||||
val runner = BiAssociateWithCollector(size(), transform)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <U> associateNotNullWith(transform: (K, V) -> U): Map<K, U> {
|
||||
val runner = BiAssociateNotNullWithCollector(size(), transform)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
private fun innerForEach(runner: BiConsumer<K, V>) {
|
||||
cache.forEach(runner)
|
||||
}
|
||||
|
||||
fun joinToString(
|
||||
separator: CharSequence = ", ",
|
||||
prefix: CharSequence = "",
|
||||
postfix: CharSequence = "",
|
||||
limit: Int = -1,
|
||||
truncated: CharSequence = "...",
|
||||
transform: ((K, V) -> CharSequence)? = null,
|
||||
): String {
|
||||
val buffer = StringBuilder()
|
||||
buffer.append(prefix)
|
||||
var count = 0
|
||||
forEach { key, value ->
|
||||
val str = if (transform != null) transform(key, value) else ""
|
||||
if (str.isNotEmpty()) {
|
||||
if (++count > 1) buffer.append(separator)
|
||||
if (limit < 0 || count <= limit) {
|
||||
when {
|
||||
transform != null -> buffer.append(str)
|
||||
else -> buffer.append("$key $value")
|
||||
}
|
||||
} else {
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
}
|
||||
if (limit >= 0 && count > limit) buffer.append(truncated)
|
||||
buffer.append(postfix)
|
||||
return buffer.toString()
|
||||
}
|
||||
|
||||
fun interface BiFilter<K, V> {
|
||||
fun filter(
|
||||
k: K,
|
||||
v: V,
|
||||
): Boolean
|
||||
}
|
||||
|
||||
class BiFilterCollector<K, V>(
|
||||
val filter: BiFilter<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: ArrayList<V> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) {
|
||||
results.add(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiFilterUniqueCollector<K, V>(
|
||||
val filter: BiFilter<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: HashSet<V> = HashSet()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) {
|
||||
results.add(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interface BiMapper<K, V, R> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): R?
|
||||
}
|
||||
|
||||
fun interface BiMapperNotNull<K, V, R> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): R
|
||||
}
|
||||
|
||||
class BiMapCollector<K, V, R>(
|
||||
val mapper: BiMapper<K, V, R?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: ArrayList<R> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.add(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateCollector<K, V, T, U>(
|
||||
val size: Int,
|
||||
val mapper: BiMapperNotNull<K, V, Pair<T, U>>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: LinkedHashMap<T, U> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val pair = mapper.map(k, v)
|
||||
results.put(pair.first, pair.second)
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateNotNullCollector<K, V, T, U>(
|
||||
val size: Int,
|
||||
val mapper: BiMapper<K, V, Pair<T, U>?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: LinkedHashMap<T, U> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val pair = mapper.map(k, v)
|
||||
if (pair != null) {
|
||||
results.put(pair.first, pair.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateWithCollector<K, V, U>(
|
||||
val size: Int,
|
||||
val mapper: BiMapper<K, V, U?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: LinkedHashMap<K, U?> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
results.put(k, mapper.map(k, v))
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateNotNullWithCollector<K, V, U>(
|
||||
val size: Int,
|
||||
val mapper: BiMapper<K, V, U>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: LinkedHashMap<K, U> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val newValue = mapper.map(k, v)
|
||||
if (newValue != null) {
|
||||
results.put(k, newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiMapUniqueCollector<K, V, R>(
|
||||
val mapper: BiMapper<K, V, R?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: HashSet<R> = HashSet()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.add(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiMapFlattenCollector<K, V, R>(
|
||||
val mapper: BiMapper<K, V, Collection<R>?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: ArrayList<R> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.addAll(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiMapFlattenUniqueCollector<K, V, R>(
|
||||
val mapper: BiMapper<K, V, Collection<R>?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: HashSet<R> = HashSet()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.addAll(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interface BiNotNullMapper<K, V, R> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): R
|
||||
}
|
||||
|
||||
class BiNotNullMapCollector<K, V, R>(
|
||||
val mapper: BiNotNullMapper<K, V, R>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: ArrayList<R> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
results.add(mapper.map(k, v))
|
||||
}
|
||||
}
|
||||
|
||||
fun interface BiSumOf<K, V> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): Int
|
||||
}
|
||||
|
||||
class BiMaxOfCollector<K, V>(
|
||||
val filter: BiFilter<K, V>,
|
||||
val comparator: Comparator<V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var maxK: K? = null
|
||||
var maxV: V? = null
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) {
|
||||
if (maxK == null || comparator.compare(v, maxV) > 0) {
|
||||
maxK = k
|
||||
maxV = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiSumOfCollector<K, V>(
|
||||
val mapper: BiSumOf<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var sum = 0
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
sum += mapper.map(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
fun interface BiSumOfLong<K, V> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): Long
|
||||
}
|
||||
|
||||
class BiSumOfLongCollector<K, V>(
|
||||
val mapper: BiSumOfLong<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var sum = 0L
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
sum += mapper.map(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
class BiGroupByCollector<K, V, R>(
|
||||
val mapper: BiNotNullMapper<K, V, R>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results = HashMap<R, ArrayList<V>>()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val group = mapper.map(k, v)
|
||||
|
||||
val list = results[group]
|
||||
if (list == null) {
|
||||
val answer = ArrayList<V>()
|
||||
answer.add(v)
|
||||
results[group] = answer
|
||||
} else {
|
||||
list.add(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiCountByGroupCollector<K, V, R>(
|
||||
val mapper: BiNotNullMapper<K, V, R>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results = HashMap<R, Int>()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val group = mapper.map(k, v)
|
||||
|
||||
val count = results[group]
|
||||
if (count == null) {
|
||||
results[group] = 1
|
||||
} else {
|
||||
results[group] = count + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiSumByGroupCollector<K, V, R>(
|
||||
val mapper: BiNotNullMapper<K, V, R>,
|
||||
val sumOf: BiNotNullMapper<K, V, Long>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results = HashMap<R, Long>()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val group = mapper.map(k, v)
|
||||
|
||||
val sum = results[group]
|
||||
if (sum == null) {
|
||||
results[group] = sumOf.map(k, v)
|
||||
} else {
|
||||
results[group] = sum + sumOf.map(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiCountIfCollector<K, V>(
|
||||
val filter: BiFilter<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var count = 0
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) count++
|
||||
}
|
||||
override fun forEach(consumer: BiConsumer<K, V>) {
|
||||
cache.forEach(consumer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.ConcurrentSkipListMap
|
||||
import java.util.function.BiConsumer
|
||||
|
||||
class LargeSoftCache<K, V> {
|
||||
class LargeSoftCache<K, V> : CacheOperations<K, V> {
|
||||
private val cache = ConcurrentSkipListMap<K, WeakReference<V>>()
|
||||
|
||||
fun keys() = cache.keys
|
||||
@@ -43,7 +43,7 @@ class LargeSoftCache<K, V> {
|
||||
|
||||
fun remove(key: K) = cache.remove(key)
|
||||
|
||||
fun size() = cache.size
|
||||
override fun size() = cache.size
|
||||
|
||||
fun isEmpty() = cache.isEmpty()
|
||||
|
||||
@@ -78,7 +78,7 @@ class LargeSoftCache<K, V> {
|
||||
key: K,
|
||||
builder: (key: K) -> V,
|
||||
): V {
|
||||
val softRef = cache.get(key)
|
||||
val softRef = cache[key]
|
||||
val value = softRef?.get()
|
||||
|
||||
return if (value != null) {
|
||||
@@ -89,20 +89,6 @@ class LargeSoftCache<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
fun createIfAbsent(
|
||||
key: K,
|
||||
builder: (key: K) -> V,
|
||||
): Boolean {
|
||||
val softRef = cache.get(key)
|
||||
val value = softRef?.get()
|
||||
return if (value != null) {
|
||||
false
|
||||
} else {
|
||||
val newObject = builder(key)
|
||||
cache.putIfAbsent(key, WeakReference(newObject)) == null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Proactively cleans up the cache by removing entries whose weakly referenced
|
||||
* objects have been garbage collected. While `get` handles cleanup on access,
|
||||
@@ -122,156 +108,8 @@ class LargeSoftCache<K, V> {
|
||||
println("Cache cleanup completed. Remaining size: ${cache.size}")
|
||||
}
|
||||
|
||||
fun forEach(consumer: BiConsumer<K, V>) {
|
||||
innerForEach(consumer)
|
||||
}
|
||||
|
||||
fun filter(consumer: BiFilter<K, V>): List<V> {
|
||||
val runner = BiFilterCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun filterIntoSet(consumer: BiFilter<K, V>): Set<V> {
|
||||
val runner = BiFilterUniqueCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> map(consumer: BiNotNullMapper<K, V, R>): List<R> {
|
||||
val runner = BiNotNullMapCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapNotNull(consumer: BiMapper<K, V, R?>): List<R> {
|
||||
val runner = BiMapCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapNotNullIntoSet(consumer: BiMapper<K, V, R?>): Set<R> {
|
||||
val runner = BiMapUniqueCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapFlatten(consumer: BiMapper<K, V, Collection<R>?>): List<R> {
|
||||
val runner = BiMapFlattenCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> mapFlattenIntoSet(consumer: BiMapper<K, V, Collection<R>?>): Set<R> {
|
||||
val runner = BiMapFlattenUniqueCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun maxOrNullOf(
|
||||
filter: BiFilter<K, V>,
|
||||
comparator: Comparator<V>,
|
||||
): V? {
|
||||
val runner = BiMaxOfCollector(filter, comparator)
|
||||
innerForEach(runner)
|
||||
return runner.maxV
|
||||
}
|
||||
|
||||
fun sumOf(consumer: BiSumOf<K, V>): Int {
|
||||
val runner = BiSumOfCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.sum
|
||||
}
|
||||
|
||||
fun sumOfLong(consumer: BiSumOfLong<K, V>): Long {
|
||||
val runner = BiSumOfLongCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.sum
|
||||
}
|
||||
|
||||
fun <R> groupBy(consumer: BiNotNullMapper<K, V, R>): Map<R, List<V>> {
|
||||
val runner = BiGroupByCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> countByGroup(consumer: BiNotNullMapper<K, V, R>): Map<R, Int> {
|
||||
val runner = BiCountByGroupCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <R> sumByGroup(
|
||||
groupMap: BiNotNullMapper<K, V, R>,
|
||||
sumOf: BiNotNullMapper<K, V, Long>,
|
||||
): Map<R, Long> {
|
||||
val runner = BiSumByGroupCollector(groupMap, sumOf)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun count(consumer: BiFilter<K, V>): Int {
|
||||
val runner = BiCountIfCollector(consumer)
|
||||
innerForEach(runner)
|
||||
return runner.count
|
||||
}
|
||||
|
||||
fun <T, U> associate(transform: (K, V) -> Pair<T, U>): Map<T, U> {
|
||||
val runner = BiAssociateCollector(size(), transform)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <T, U> associateNotNull(transform: (K, V) -> Pair<T, U>?): Map<T, U> {
|
||||
val runner = BiAssociateNotNullCollector(size(), transform)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <U> associateWith(transform: (K, V) -> U?): Map<K, U?> {
|
||||
val runner = BiAssociateWithCollector(size(), transform)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
fun <U> associateNotNullWith(transform: (K, V) -> U): Map<K, U> {
|
||||
val runner = BiAssociateNotNullWithCollector(size(), transform)
|
||||
innerForEach(runner)
|
||||
return runner.results
|
||||
}
|
||||
|
||||
private fun innerForEach(runner: BiConsumer<K, V>) {
|
||||
cache.forEach(BiConsumerWrapper(this, runner))
|
||||
}
|
||||
|
||||
fun joinToString(
|
||||
separator: CharSequence = ", ",
|
||||
prefix: CharSequence = "",
|
||||
postfix: CharSequence = "",
|
||||
limit: Int = -1,
|
||||
truncated: CharSequence = "...",
|
||||
transform: ((K, V) -> CharSequence)? = null,
|
||||
): String {
|
||||
val buffer = StringBuilder()
|
||||
buffer.append(prefix)
|
||||
var count = 0
|
||||
forEach { key, value ->
|
||||
val str = if (transform != null) transform(key, value) else ""
|
||||
if (str.isNotEmpty()) {
|
||||
if (++count > 1) buffer.append(separator)
|
||||
if (limit < 0 || count <= limit) {
|
||||
when {
|
||||
transform != null -> buffer.append(str)
|
||||
else -> buffer.append("$key $value")
|
||||
}
|
||||
} else {
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
}
|
||||
if (limit >= 0 && count > limit) buffer.append(truncated)
|
||||
buffer.append(postfix)
|
||||
return buffer.toString()
|
||||
override fun forEach(consumer: BiConsumer<K, V>) {
|
||||
cache.forEach(BiConsumerWrapper(this, consumer))
|
||||
}
|
||||
|
||||
class BiConsumerWrapper<K, V>(
|
||||
@@ -290,338 +128,4 @@ class LargeSoftCache<K, V> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interface BiFilter<K, V> {
|
||||
fun filter(
|
||||
k: K,
|
||||
v: V,
|
||||
): Boolean
|
||||
}
|
||||
|
||||
class BiFilterCollector<K, V>(
|
||||
val filter: BiFilter<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: ArrayList<V> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) {
|
||||
results.add(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiFilterUniqueCollector<K, V>(
|
||||
val filter: BiFilter<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: HashSet<V> = HashSet()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) {
|
||||
results.add(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interface BiMapper<K, V, R> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): R?
|
||||
}
|
||||
|
||||
fun interface BiMapperNotNull<K, V, R> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): R
|
||||
}
|
||||
|
||||
class BiMapCollector<K, V, R>(
|
||||
val mapper: BiMapper<K, V, R?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: ArrayList<R> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.add(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateCollector<K, V, T, U>(
|
||||
val size: Int,
|
||||
val mapper: BiMapperNotNull<K, V, Pair<T, U>>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: LinkedHashMap<T, U> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val pair = mapper.map(k, v)
|
||||
results.put(pair.first, pair.second)
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateNotNullCollector<K, V, T, U>(
|
||||
val size: Int,
|
||||
val mapper: BiMapper<K, V, Pair<T, U>?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: LinkedHashMap<T, U> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val pair = mapper.map(k, v)
|
||||
if (pair != null) {
|
||||
results.put(pair.first, pair.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateWithCollector<K, V, U>(
|
||||
val size: Int,
|
||||
val mapper: BiMapper<K, V, U?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: LinkedHashMap<K, U?> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
results.put(k, mapper.map(k, v))
|
||||
}
|
||||
}
|
||||
|
||||
class BiAssociateNotNullWithCollector<K, V, U>(
|
||||
val size: Int,
|
||||
val mapper: BiMapper<K, V, U>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: LinkedHashMap<K, U> = LinkedHashMap(size)
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val newValue = mapper.map(k, v)
|
||||
if (newValue != null) {
|
||||
results.put(k, newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiMapUniqueCollector<K, V, R>(
|
||||
val mapper: BiMapper<K, V, R?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: HashSet<R> = HashSet()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.add(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiMapFlattenCollector<K, V, R>(
|
||||
val mapper: BiMapper<K, V, Collection<R>?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: ArrayList<R> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.addAll(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiMapFlattenUniqueCollector<K, V, R>(
|
||||
val mapper: BiMapper<K, V, Collection<R>?>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: HashSet<R> = HashSet()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val result = mapper.map(k, v)
|
||||
if (result != null) {
|
||||
results.addAll(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interface BiNotNullMapper<K, V, R> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): R
|
||||
}
|
||||
|
||||
class BiNotNullMapCollector<K, V, R>(
|
||||
val mapper: BiNotNullMapper<K, V, R>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results: ArrayList<R> = ArrayList()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
results.add(mapper.map(k, v))
|
||||
}
|
||||
}
|
||||
|
||||
fun interface BiSumOf<K, V> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): Int
|
||||
}
|
||||
|
||||
class BiMaxOfCollector<K, V>(
|
||||
val filter: BiFilter<K, V>,
|
||||
val comparator: Comparator<V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var maxK: K? = null
|
||||
var maxV: V? = null
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) {
|
||||
if (maxK == null || comparator.compare(v, maxV) > 0) {
|
||||
maxK = k
|
||||
maxV = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiSumOfCollector<K, V>(
|
||||
val mapper: BiSumOf<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var sum = 0
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
sum += mapper.map(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
fun interface BiSumOfLong<K, V> {
|
||||
fun map(
|
||||
k: K,
|
||||
v: V,
|
||||
): Long
|
||||
}
|
||||
|
||||
class BiSumOfLongCollector<K, V>(
|
||||
val mapper: BiSumOfLong<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var sum = 0L
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
sum += mapper.map(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
class BiGroupByCollector<K, V, R>(
|
||||
val mapper: BiNotNullMapper<K, V, R>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results = HashMap<R, ArrayList<V>>()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val group = mapper.map(k, v)
|
||||
|
||||
val list = results[group]
|
||||
if (list == null) {
|
||||
val answer = ArrayList<V>()
|
||||
answer.add(v)
|
||||
results[group] = answer
|
||||
} else {
|
||||
list.add(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiCountByGroupCollector<K, V, R>(
|
||||
val mapper: BiNotNullMapper<K, V, R>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results = HashMap<R, Int>()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val group = mapper.map(k, v)
|
||||
|
||||
val count = results[group]
|
||||
if (count == null) {
|
||||
results[group] = 1
|
||||
} else {
|
||||
results[group] = count + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiSumByGroupCollector<K, V, R>(
|
||||
val mapper: BiNotNullMapper<K, V, R>,
|
||||
val sumOf: BiNotNullMapper<K, V, Long>,
|
||||
) : BiConsumer<K, V> {
|
||||
var results = HashMap<R, Long>()
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
val group = mapper.map(k, v)
|
||||
|
||||
val sum = results[group]
|
||||
if (sum == null) {
|
||||
results[group] = sumOf.map(k, v)
|
||||
} else {
|
||||
results[group] = sum + sumOf.map(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BiCountIfCollector<K, V>(
|
||||
val filter: BiFilter<K, V>,
|
||||
) : BiConsumer<K, V> {
|
||||
var count = 0
|
||||
|
||||
override fun accept(
|
||||
k: K,
|
||||
v: V,
|
||||
) {
|
||||
if (filter.filter(k, v)) count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user