From c4b71a0aeab67f7cf257f41b783d0810a425eb10 Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Sat, 16 Aug 2025 10:07:04 +0100 Subject: [PATCH 01/13] Move common collectors to new utility class --- .../quartz/utils/CacheCollectors.kt | 359 ++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt new file mode 100644 index 0000000000..ca9377a0df --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt @@ -0,0 +1,359 @@ +/** + * 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 + +object CacheCollectors { + fun interface BiFilter { + fun filter( + k: K, + v: V, + ): Boolean + } + + class BiFilterCollector( + val filter: BiFilter, + ) : BiConsumer { + var results: ArrayList = ArrayList() + + override fun accept( + k: K, + v: V, + ) { + if (filter.filter(k, v)) { + results.add(v) + } + } + } + + class BiFilterUniqueCollector( + val filter: BiFilter, + ) : BiConsumer { + var results: HashSet = HashSet() + + override fun accept( + k: K, + v: V, + ) { + if (filter.filter(k, v)) { + results.add(v) + } + } + } + + fun interface BiMapper { + fun map( + k: K, + v: V, + ): R? + } + + fun interface BiMapperNotNull { + fun map( + k: K, + v: V, + ): R + } + + class BiMapCollector( + val mapper: BiMapper, + ) : BiConsumer { + var results: ArrayList = ArrayList() + + override fun accept( + k: K, + v: V, + ) { + val result = mapper.map(k, v) + if (result != null) { + results.add(result) + } + } + } + + class BiAssociateCollector( + val size: Int, + val mapper: BiMapperNotNull>, + ) : BiConsumer { + var results: LinkedHashMap = LinkedHashMap(size) + + override fun accept( + k: K, + v: V, + ) { + val pair = mapper.map(k, v) + results.put(pair.first, pair.second) + } + } + + class BiAssociateNotNullCollector( + val size: Int, + val mapper: BiMapper?>, + ) : BiConsumer { + var results: LinkedHashMap = 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( + val size: Int, + val mapper: BiMapper, + ) : BiConsumer { + var results: LinkedHashMap = LinkedHashMap(size) + + override fun accept( + k: K, + v: V, + ) { + results.put(k, mapper.map(k, v)) + } + } + + class BiAssociateNotNullWithCollector( + val size: Int, + val mapper: BiMapper, + ) : BiConsumer { + var results: LinkedHashMap = LinkedHashMap(size) + + override fun accept( + k: K, + v: V, + ) { + val newValue = mapper.map(k, v) + if (newValue != null) { + results.put(k, newValue) + } + } + } + + class BiMapUniqueCollector( + val mapper: BiMapper, + ) : BiConsumer { + var results: HashSet = HashSet() + + override fun accept( + k: K, + v: V, + ) { + val result = mapper.map(k, v) + if (result != null) { + results.add(result) + } + } + } + + class BiMapFlattenCollector( + val mapper: BiMapper?>, + ) : BiConsumer { + var results: ArrayList = ArrayList() + + override fun accept( + k: K, + v: V, + ) { + val result = mapper.map(k, v) + if (result != null) { + results.addAll(result) + } + } + } + + class BiMapFlattenUniqueCollector( + val mapper: BiMapper?>, + ) : BiConsumer { + var results: HashSet = HashSet() + + override fun accept( + k: K, + v: V, + ) { + val result = mapper.map(k, v) + if (result != null) { + results.addAll(result) + } + } + } + + fun interface BiNotNullMapper { + fun map( + k: K, + v: V, + ): R + } + + class BiNotNullMapCollector( + val mapper: BiNotNullMapper, + ) : BiConsumer { + var results: ArrayList = ArrayList() + + override fun accept( + k: K, + v: V, + ) { + results.add(mapper.map(k, v)) + } + } + + fun interface BiSumOf { + fun map( + k: K, + v: V, + ): Int + } + + class BiMaxOfCollector( + val filter: BiFilter, + val comparator: Comparator, + ) : BiConsumer { + 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( + val mapper: BiSumOf, + ) : BiConsumer { + var sum = 0 + + override fun accept( + k: K, + v: V, + ) { + sum += mapper.map(k, v) + } + } + + fun interface BiSumOfLong { + fun map( + k: K, + v: V, + ): Long + } + + class BiSumOfLongCollector( + val mapper: BiSumOfLong, + ) : BiConsumer { + var sum = 0L + + override fun accept( + k: K, + v: V, + ) { + sum += mapper.map(k, v) + } + } + + class BiGroupByCollector( + val mapper: BiNotNullMapper, + ) : BiConsumer { + var results = HashMap>() + + override fun accept( + k: K, + v: V, + ) { + val group = mapper.map(k, v) + + val list = results[group] + if (list == null) { + val answer = ArrayList() + answer.add(v) + results[group] = answer + } else { + list.add(v) + } + } + } + + class BiCountByGroupCollector( + val mapper: BiNotNullMapper, + ) : BiConsumer { + var results = HashMap() + + 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( + val mapper: BiNotNullMapper, + val sumOf: BiNotNullMapper, + ) : BiConsumer { + var results = HashMap() + + 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( + val filter: BiFilter, + ) : BiConsumer { + var count = 0 + + override fun accept( + k: K, + v: V, + ) { + if (filter.filter(k, v)) count++ + } + } +} From eab2f96f02ae26a8ede7002327e39bb9b5c3a428 Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Sat, 16 Aug 2025 10:20:52 +0100 Subject: [PATCH 02/13] remove duplicate colletors and Use collectors from new utility class --- .../vitorpamplona/quartz/utils/LargeCache.kt | 400 ++---------------- .../quartz/utils/LargeSoftCache.kt | 400 ++---------------- 2 files changed, 66 insertions(+), 734 deletions(-) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt index bd61c05d19..4254d94bf1 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt @@ -80,116 +80,116 @@ class LargeCache { innerForEach(consumer) } - fun filter(consumer: BiFilter): List { - val runner = BiFilterCollector(consumer) + fun filter(consumer: CacheCollectors.BiFilter): List { + val runner = CacheCollectors.BiFilterCollector(consumer) innerForEach(runner) return runner.results } - fun filterIntoSet(consumer: BiFilter): Set { - val runner = BiFilterUniqueCollector(consumer) + fun filterIntoSet(consumer: CacheCollectors.BiFilter): Set { + val runner = CacheCollectors.BiFilterUniqueCollector(consumer) innerForEach(runner) return runner.results } - fun map(consumer: BiNotNullMapper): List { - val runner = BiNotNullMapCollector(consumer) + fun map(consumer: CacheCollectors.BiNotNullMapper): List { + val runner = CacheCollectors.BiNotNullMapCollector(consumer) innerForEach(runner) return runner.results } - fun mapNotNull(consumer: BiMapper): List { - val runner = BiMapCollector(consumer) + fun mapNotNull(consumer: CacheCollectors.BiMapper): List { + val runner = CacheCollectors.BiMapCollector(consumer) innerForEach(runner) return runner.results } - fun mapNotNullIntoSet(consumer: BiMapper): Set { - val runner = BiMapUniqueCollector(consumer) + fun mapNotNullIntoSet(consumer: CacheCollectors.BiMapper): Set { + val runner = CacheCollectors.BiMapUniqueCollector(consumer) innerForEach(runner) return runner.results } - fun mapFlatten(consumer: BiMapper?>): List { - val runner = BiMapFlattenCollector(consumer) + fun mapFlatten(consumer: CacheCollectors.BiMapper?>): List { + val runner = CacheCollectors.BiMapFlattenCollector(consumer) innerForEach(runner) return runner.results } - fun mapFlattenIntoSet(consumer: BiMapper?>): Set { - val runner = BiMapFlattenUniqueCollector(consumer) + fun mapFlattenIntoSet(consumer: CacheCollectors.BiMapper?>): Set { + val runner = CacheCollectors.BiMapFlattenUniqueCollector(consumer) innerForEach(runner) return runner.results } fun maxOrNullOf( - filter: BiFilter, + filter: CacheCollectors.BiFilter, comparator: Comparator, ): V? { - val runner = BiMaxOfCollector(filter, comparator) + val runner = CacheCollectors.BiMaxOfCollector(filter, comparator) innerForEach(runner) return runner.maxV } - fun sumOf(consumer: BiSumOf): Int { - val runner = BiSumOfCollector(consumer) + fun sumOf(consumer: CacheCollectors.BiSumOf): Int { + val runner = CacheCollectors.BiSumOfCollector(consumer) innerForEach(runner) return runner.sum } - fun sumOfLong(consumer: BiSumOfLong): Long { - val runner = BiSumOfLongCollector(consumer) + fun sumOfLong(consumer: CacheCollectors.BiSumOfLong): Long { + val runner = CacheCollectors.BiSumOfLongCollector(consumer) innerForEach(runner) return runner.sum } - fun groupBy(consumer: BiNotNullMapper): Map> { - val runner = BiGroupByCollector(consumer) + fun groupBy(consumer: CacheCollectors.BiNotNullMapper): Map> { + val runner = CacheCollectors.BiGroupByCollector(consumer) innerForEach(runner) return runner.results } - fun countByGroup(consumer: BiNotNullMapper): Map { - val runner = BiCountByGroupCollector(consumer) + fun countByGroup(consumer: CacheCollectors.BiNotNullMapper): Map { + val runner = CacheCollectors.BiCountByGroupCollector(consumer) innerForEach(runner) return runner.results } fun sumByGroup( - groupMap: BiNotNullMapper, - sumOf: BiNotNullMapper, + groupMap: CacheCollectors.BiNotNullMapper, + sumOf: CacheCollectors.BiNotNullMapper, ): Map { - val runner = BiSumByGroupCollector(groupMap, sumOf) + val runner = CacheCollectors.BiSumByGroupCollector(groupMap, sumOf) innerForEach(runner) return runner.results } - fun count(consumer: BiFilter): Int { - val runner = BiCountIfCollector(consumer) + fun count(consumer: CacheCollectors.BiFilter): Int { + val runner = CacheCollectors.BiCountIfCollector(consumer) innerForEach(runner) return runner.count } fun associate(transform: (K, V) -> Pair): Map { - val runner = BiAssociateCollector(size(), transform) + val runner = CacheCollectors.BiAssociateCollector(size(), transform) innerForEach(runner) return runner.results } fun associateNotNull(transform: (K, V) -> Pair?): Map { - val runner = BiAssociateNotNullCollector(size(), transform) + val runner = CacheCollectors.BiAssociateNotNullCollector(size(), transform) innerForEach(runner) return runner.results } fun associateWith(transform: (K, V) -> U?): Map { - val runner = BiAssociateWithCollector(size(), transform) + val runner = CacheCollectors.BiAssociateWithCollector(size(), transform) innerForEach(runner) return runner.results } fun associateNotNullWith(transform: (K, V) -> U): Map { - val runner = BiAssociateNotNullWithCollector(size(), transform) + val runner = CacheCollectors.BiAssociateNotNullWithCollector(size(), transform) innerForEach(runner) return runner.results } @@ -227,338 +227,4 @@ class LargeCache { buffer.append(postfix) return buffer.toString() } - - fun interface BiFilter { - fun filter( - k: K, - v: V, - ): Boolean - } - - class BiFilterCollector( - val filter: BiFilter, - ) : BiConsumer { - var results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - if (filter.filter(k, v)) { - results.add(v) - } - } - } - - class BiFilterUniqueCollector( - val filter: BiFilter, - ) : BiConsumer { - var results: HashSet = HashSet() - - override fun accept( - k: K, - v: V, - ) { - if (filter.filter(k, v)) { - results.add(v) - } - } - } - - fun interface BiMapper { - fun map( - k: K, - v: V, - ): R? - } - - fun interface BiMapperNotNull { - fun map( - k: K, - v: V, - ): R - } - - class BiMapCollector( - val mapper: BiMapper, - ) : BiConsumer { - var results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.add(result) - } - } - } - - class BiAssociateCollector( - val size: Int, - val mapper: BiMapperNotNull>, - ) : BiConsumer { - var results: LinkedHashMap = LinkedHashMap(size) - - override fun accept( - k: K, - v: V, - ) { - val pair = mapper.map(k, v) - results.put(pair.first, pair.second) - } - } - - class BiAssociateNotNullCollector( - val size: Int, - val mapper: BiMapper?>, - ) : BiConsumer { - var results: LinkedHashMap = 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( - val size: Int, - val mapper: BiMapper, - ) : BiConsumer { - var results: LinkedHashMap = LinkedHashMap(size) - - override fun accept( - k: K, - v: V, - ) { - results.put(k, mapper.map(k, v)) - } - } - - class BiAssociateNotNullWithCollector( - val size: Int, - val mapper: BiMapper, - ) : BiConsumer { - var results: LinkedHashMap = LinkedHashMap(size) - - override fun accept( - k: K, - v: V, - ) { - val newValue = mapper.map(k, v) - if (newValue != null) { - results.put(k, newValue) - } - } - } - - class BiMapUniqueCollector( - val mapper: BiMapper, - ) : BiConsumer { - var results: HashSet = HashSet() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.add(result) - } - } - } - - class BiMapFlattenCollector( - val mapper: BiMapper?>, - ) : BiConsumer { - var results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.addAll(result) - } - } - } - - class BiMapFlattenUniqueCollector( - val mapper: BiMapper?>, - ) : BiConsumer { - var results: HashSet = HashSet() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.addAll(result) - } - } - } - - fun interface BiNotNullMapper { - fun map( - k: K, - v: V, - ): R - } - - class BiNotNullMapCollector( - val mapper: BiNotNullMapper, - ) : BiConsumer { - var results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - results.add(mapper.map(k, v)) - } - } - - fun interface BiSumOf { - fun map( - k: K, - v: V, - ): Int - } - - class BiMaxOfCollector( - val filter: BiFilter, - val comparator: Comparator, - ) : BiConsumer { - 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( - val mapper: BiSumOf, - ) : BiConsumer { - var sum = 0 - - override fun accept( - k: K, - v: V, - ) { - sum += mapper.map(k, v) - } - } - - fun interface BiSumOfLong { - fun map( - k: K, - v: V, - ): Long - } - - class BiSumOfLongCollector( - val mapper: BiSumOfLong, - ) : BiConsumer { - var sum = 0L - - override fun accept( - k: K, - v: V, - ) { - sum += mapper.map(k, v) - } - } - - class BiGroupByCollector( - val mapper: BiNotNullMapper, - ) : BiConsumer { - var results = HashMap>() - - override fun accept( - k: K, - v: V, - ) { - val group = mapper.map(k, v) - - val list = results[group] - if (list == null) { - val answer = ArrayList() - answer.add(v) - results[group] = answer - } else { - list.add(v) - } - } - } - - class BiCountByGroupCollector( - val mapper: BiNotNullMapper, - ) : BiConsumer { - var results = HashMap() - - 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( - val mapper: BiNotNullMapper, - val sumOf: BiNotNullMapper, - ) : BiConsumer { - var results = HashMap() - - 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( - val filter: BiFilter, - ) : BiConsumer { - var count = 0 - - override fun accept( - k: K, - v: V, - ) { - if (filter.filter(k, v)) count++ - } - } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt index 50f539bd69..6f1c90c550 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt @@ -126,116 +126,116 @@ class LargeSoftCache { innerForEach(consumer) } - fun filter(consumer: BiFilter): List { - val runner = BiFilterCollector(consumer) + fun filter(consumer: CacheCollectors.BiFilter): List { + val runner = CacheCollectors.BiFilterCollector(consumer) innerForEach(runner) return runner.results } - fun filterIntoSet(consumer: BiFilter): Set { - val runner = BiFilterUniqueCollector(consumer) + fun filterIntoSet(consumer: CacheCollectors.BiFilter): Set { + val runner = CacheCollectors.BiFilterUniqueCollector(consumer) innerForEach(runner) return runner.results } - fun map(consumer: BiNotNullMapper): List { - val runner = BiNotNullMapCollector(consumer) + fun map(consumer: CacheCollectors.BiNotNullMapper): List { + val runner = CacheCollectors.BiNotNullMapCollector(consumer) innerForEach(runner) return runner.results } - fun mapNotNull(consumer: BiMapper): List { - val runner = BiMapCollector(consumer) + fun mapNotNull(consumer: CacheCollectors.BiMapper): List { + val runner = CacheCollectors.BiMapCollector(consumer) innerForEach(runner) return runner.results } - fun mapNotNullIntoSet(consumer: BiMapper): Set { - val runner = BiMapUniqueCollector(consumer) + fun mapNotNullIntoSet(consumer: CacheCollectors.BiMapper): Set { + val runner = CacheCollectors.BiMapUniqueCollector(consumer) innerForEach(runner) return runner.results } - fun mapFlatten(consumer: BiMapper?>): List { - val runner = BiMapFlattenCollector(consumer) + fun mapFlatten(consumer: CacheCollectors.BiMapper?>): List { + val runner = CacheCollectors.BiMapFlattenCollector(consumer) innerForEach(runner) return runner.results } - fun mapFlattenIntoSet(consumer: BiMapper?>): Set { - val runner = BiMapFlattenUniqueCollector(consumer) + fun mapFlattenIntoSet(consumer: CacheCollectors.BiMapper?>): Set { + val runner = CacheCollectors.BiMapFlattenUniqueCollector(consumer) innerForEach(runner) return runner.results } fun maxOrNullOf( - filter: BiFilter, + filter: CacheCollectors.BiFilter, comparator: Comparator, ): V? { - val runner = BiMaxOfCollector(filter, comparator) + val runner = CacheCollectors.BiMaxOfCollector(filter, comparator) innerForEach(runner) return runner.maxV } - fun sumOf(consumer: BiSumOf): Int { - val runner = BiSumOfCollector(consumer) + fun sumOf(consumer: CacheCollectors.BiSumOf): Int { + val runner = CacheCollectors.BiSumOfCollector(consumer) innerForEach(runner) return runner.sum } - fun sumOfLong(consumer: BiSumOfLong): Long { - val runner = BiSumOfLongCollector(consumer) + fun sumOfLong(consumer: CacheCollectors.BiSumOfLong): Long { + val runner = CacheCollectors.BiSumOfLongCollector(consumer) innerForEach(runner) return runner.sum } - fun groupBy(consumer: BiNotNullMapper): Map> { - val runner = BiGroupByCollector(consumer) + fun groupBy(consumer: CacheCollectors.BiNotNullMapper): Map> { + val runner = CacheCollectors.BiGroupByCollector(consumer) innerForEach(runner) return runner.results } - fun countByGroup(consumer: BiNotNullMapper): Map { - val runner = BiCountByGroupCollector(consumer) + fun countByGroup(consumer: CacheCollectors.BiNotNullMapper): Map { + val runner = CacheCollectors.BiCountByGroupCollector(consumer) innerForEach(runner) return runner.results } fun sumByGroup( - groupMap: BiNotNullMapper, - sumOf: BiNotNullMapper, + groupMap: CacheCollectors.BiNotNullMapper, + sumOf: CacheCollectors.BiNotNullMapper, ): Map { - val runner = BiSumByGroupCollector(groupMap, sumOf) + val runner = CacheCollectors.BiSumByGroupCollector(groupMap, sumOf) innerForEach(runner) return runner.results } - fun count(consumer: BiFilter): Int { - val runner = BiCountIfCollector(consumer) + fun count(consumer: CacheCollectors.BiFilter): Int { + val runner = CacheCollectors.BiCountIfCollector(consumer) innerForEach(runner) return runner.count } fun associate(transform: (K, V) -> Pair): Map { - val runner = BiAssociateCollector(size(), transform) + val runner = CacheCollectors.BiAssociateCollector(size(), transform) innerForEach(runner) return runner.results } fun associateNotNull(transform: (K, V) -> Pair?): Map { - val runner = BiAssociateNotNullCollector(size(), transform) + val runner = CacheCollectors.BiAssociateNotNullCollector(size(), transform) innerForEach(runner) return runner.results } fun associateWith(transform: (K, V) -> U?): Map { - val runner = BiAssociateWithCollector(size(), transform) + val runner = CacheCollectors.BiAssociateWithCollector(size(), transform) innerForEach(runner) return runner.results } fun associateNotNullWith(transform: (K, V) -> U): Map { - val runner = BiAssociateNotNullWithCollector(size(), transform) + val runner = CacheCollectors.BiAssociateNotNullWithCollector(size(), transform) innerForEach(runner) return runner.results } @@ -290,338 +290,4 @@ class LargeSoftCache { } } } - - fun interface BiFilter { - fun filter( - k: K, - v: V, - ): Boolean - } - - class BiFilterCollector( - val filter: BiFilter, - ) : BiConsumer { - var results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - if (filter.filter(k, v)) { - results.add(v) - } - } - } - - class BiFilterUniqueCollector( - val filter: BiFilter, - ) : BiConsumer { - var results: HashSet = HashSet() - - override fun accept( - k: K, - v: V, - ) { - if (filter.filter(k, v)) { - results.add(v) - } - } - } - - fun interface BiMapper { - fun map( - k: K, - v: V, - ): R? - } - - fun interface BiMapperNotNull { - fun map( - k: K, - v: V, - ): R - } - - class BiMapCollector( - val mapper: BiMapper, - ) : BiConsumer { - var results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.add(result) - } - } - } - - class BiAssociateCollector( - val size: Int, - val mapper: BiMapperNotNull>, - ) : BiConsumer { - var results: LinkedHashMap = LinkedHashMap(size) - - override fun accept( - k: K, - v: V, - ) { - val pair = mapper.map(k, v) - results.put(pair.first, pair.second) - } - } - - class BiAssociateNotNullCollector( - val size: Int, - val mapper: BiMapper?>, - ) : BiConsumer { - var results: LinkedHashMap = 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( - val size: Int, - val mapper: BiMapper, - ) : BiConsumer { - var results: LinkedHashMap = LinkedHashMap(size) - - override fun accept( - k: K, - v: V, - ) { - results.put(k, mapper.map(k, v)) - } - } - - class BiAssociateNotNullWithCollector( - val size: Int, - val mapper: BiMapper, - ) : BiConsumer { - var results: LinkedHashMap = LinkedHashMap(size) - - override fun accept( - k: K, - v: V, - ) { - val newValue = mapper.map(k, v) - if (newValue != null) { - results.put(k, newValue) - } - } - } - - class BiMapUniqueCollector( - val mapper: BiMapper, - ) : BiConsumer { - var results: HashSet = HashSet() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.add(result) - } - } - } - - class BiMapFlattenCollector( - val mapper: BiMapper?>, - ) : BiConsumer { - var results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.addAll(result) - } - } - } - - class BiMapFlattenUniqueCollector( - val mapper: BiMapper?>, - ) : BiConsumer { - var results: HashSet = HashSet() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.addAll(result) - } - } - } - - fun interface BiNotNullMapper { - fun map( - k: K, - v: V, - ): R - } - - class BiNotNullMapCollector( - val mapper: BiNotNullMapper, - ) : BiConsumer { - var results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - results.add(mapper.map(k, v)) - } - } - - fun interface BiSumOf { - fun map( - k: K, - v: V, - ): Int - } - - class BiMaxOfCollector( - val filter: BiFilter, - val comparator: Comparator, - ) : BiConsumer { - 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( - val mapper: BiSumOf, - ) : BiConsumer { - var sum = 0 - - override fun accept( - k: K, - v: V, - ) { - sum += mapper.map(k, v) - } - } - - fun interface BiSumOfLong { - fun map( - k: K, - v: V, - ): Long - } - - class BiSumOfLongCollector( - val mapper: BiSumOfLong, - ) : BiConsumer { - var sum = 0L - - override fun accept( - k: K, - v: V, - ) { - sum += mapper.map(k, v) - } - } - - class BiGroupByCollector( - val mapper: BiNotNullMapper, - ) : BiConsumer { - var results = HashMap>() - - override fun accept( - k: K, - v: V, - ) { - val group = mapper.map(k, v) - - val list = results[group] - if (list == null) { - val answer = ArrayList() - answer.add(v) - results[group] = answer - } else { - list.add(v) - } - } - } - - class BiCountByGroupCollector( - val mapper: BiNotNullMapper, - ) : BiConsumer { - var results = HashMap() - - 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( - val mapper: BiNotNullMapper, - val sumOf: BiNotNullMapper, - ) : BiConsumer { - var results = HashMap() - - 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( - val filter: BiFilter, - ) : BiConsumer { - var count = 0 - - override fun accept( - k: K, - v: V, - ) { - if (filter.filter(k, v)) count++ - } - } } From b069f283ac4215efe95cebc0a8c59f07b46e081c Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Sat, 16 Aug 2025 10:36:10 +0100 Subject: [PATCH 03/13] Move common cache operations to new utility class --- .../quartz/utils/CacheOperations.kt | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt new file mode 100644 index 0000000000..b1c9a9cccd --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt @@ -0,0 +1,173 @@ +/** + * 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 { + fun forEach(consumer: BiConsumer) + + fun size(): Int + + fun filter(consumer: CacheCollectors.BiFilter): List { + val runner = CacheCollectors.BiFilterCollector(consumer) + forEach(runner) + return runner.results + } + + fun filterIntoSet(consumer: CacheCollectors.BiFilter): Set { + val runner = CacheCollectors.BiFilterUniqueCollector(consumer) + forEach(runner) + return runner.results + } + + fun map(consumer: CacheCollectors.BiNotNullMapper): List { + val runner = CacheCollectors.BiNotNullMapCollector(consumer) + forEach(runner) + return runner.results + } + + fun mapNotNull(consumer: CacheCollectors.BiMapper): List { + val runner = CacheCollectors.BiMapCollector(consumer) + forEach(runner) + return runner.results + } + + fun mapNotNullIntoSet(consumer: CacheCollectors.BiMapper): Set { + val runner = CacheCollectors.BiMapUniqueCollector(consumer) + forEach(runner) + return runner.results + } + + fun mapFlatten(consumer: CacheCollectors.BiMapper?>): List { + val runner = CacheCollectors.BiMapFlattenCollector(consumer) + forEach(runner) + return runner.results + } + + fun mapFlattenIntoSet(consumer: CacheCollectors.BiMapper?>): Set { + val runner = CacheCollectors.BiMapFlattenUniqueCollector(consumer) + forEach(runner) + return runner.results + } + + fun maxOrNullOf( + filter: CacheCollectors.BiFilter, + comparator: Comparator, + ): V? { + val runner = CacheCollectors.BiMaxOfCollector(filter, comparator) + forEach(runner) + return runner.maxV + } + + fun sumOf(consumer: CacheCollectors.BiSumOf): Int { + val runner = CacheCollectors.BiSumOfCollector(consumer) + forEach(runner) + return runner.sum + } + + fun sumOfLong(consumer: CacheCollectors.BiSumOfLong): Long { + val runner = CacheCollectors.BiSumOfLongCollector(consumer) + forEach(runner) + return runner.sum + } + + fun groupBy(consumer: CacheCollectors.BiNotNullMapper): Map> { + val runner = CacheCollectors.BiGroupByCollector(consumer) + forEach(runner) + return runner.results + } + + fun countByGroup(consumer: CacheCollectors.BiNotNullMapper): Map { + val runner = CacheCollectors.BiCountByGroupCollector(consumer) + forEach(runner) + return runner.results + } + + fun sumByGroup( + groupMap: CacheCollectors.BiNotNullMapper, + sumOf: CacheCollectors.BiNotNullMapper, + ): Map { + val runner = CacheCollectors.BiSumByGroupCollector(groupMap, sumOf) + forEach(runner) + return runner.results + } + + fun count(consumer: CacheCollectors.BiFilter): Int { + val runner = CacheCollectors.BiCountIfCollector(consumer) + forEach(runner) + return runner.count + } + + fun associate(transform: (K, V) -> Pair): Map { + val runner = CacheCollectors.BiAssociateCollector(size(), transform) + forEach(runner) + return runner.results + } + + fun associateNotNull(transform: (K, V) -> Pair?): Map { + val runner = CacheCollectors.BiAssociateNotNullCollector(size(), transform) + forEach(runner) + return runner.results + } + + fun associateWith(transform: (K, V) -> U?): Map { + val runner = CacheCollectors.BiAssociateWithCollector(size(), transform) + forEach(runner) + return runner.results + } + + fun associateNotNullWith(transform: (K, V) -> U): Map { + val runner = CacheCollectors.BiAssociateNotNullWithCollector(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() + } +} From 533d8b18f063b6df71dbd4f3c5217c3ac79b90e6 Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Sat, 16 Aug 2025 10:45:37 +0100 Subject: [PATCH 04/13] Remove duplicate functionality from Cache implementations and use instead common methods in CacheOperations utility class Create override for forEach and size as they are needed internally and do slightly different things --- .../vitorpamplona/quartz/utils/LargeCache.kt | 156 +----------------- .../quartz/utils/LargeSoftCache.kt | 156 +----------------- 2 files changed, 8 insertions(+), 304 deletions(-) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt index 4254d94bf1..962932c9af 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt @@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.utils import java.util.concurrent.ConcurrentSkipListMap import java.util.function.BiConsumer -class LargeCache { +class LargeCache : CacheOperations { private val cache = ConcurrentSkipListMap() fun keys() = cache.keys @@ -34,7 +34,7 @@ class LargeCache { fun remove(key: K) = cache.remove(key) - fun size() = cache.size + override fun size() = cache.size fun isEmpty() = cache.isEmpty() @@ -76,155 +76,7 @@ class LargeCache { } } - fun forEach(consumer: BiConsumer) { - innerForEach(consumer) - } - - fun filter(consumer: CacheCollectors.BiFilter): List { - val runner = CacheCollectors.BiFilterCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun filterIntoSet(consumer: CacheCollectors.BiFilter): Set { - val runner = CacheCollectors.BiFilterUniqueCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun map(consumer: CacheCollectors.BiNotNullMapper): List { - val runner = CacheCollectors.BiNotNullMapCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun mapNotNull(consumer: CacheCollectors.BiMapper): List { - val runner = CacheCollectors.BiMapCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun mapNotNullIntoSet(consumer: CacheCollectors.BiMapper): Set { - val runner = CacheCollectors.BiMapUniqueCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun mapFlatten(consumer: CacheCollectors.BiMapper?>): List { - val runner = CacheCollectors.BiMapFlattenCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun mapFlattenIntoSet(consumer: CacheCollectors.BiMapper?>): Set { - val runner = CacheCollectors.BiMapFlattenUniqueCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun maxOrNullOf( - filter: CacheCollectors.BiFilter, - comparator: Comparator, - ): V? { - val runner = CacheCollectors.BiMaxOfCollector(filter, comparator) - innerForEach(runner) - return runner.maxV - } - - fun sumOf(consumer: CacheCollectors.BiSumOf): Int { - val runner = CacheCollectors.BiSumOfCollector(consumer) - innerForEach(runner) - return runner.sum - } - - fun sumOfLong(consumer: CacheCollectors.BiSumOfLong): Long { - val runner = CacheCollectors.BiSumOfLongCollector(consumer) - innerForEach(runner) - return runner.sum - } - - fun groupBy(consumer: CacheCollectors.BiNotNullMapper): Map> { - val runner = CacheCollectors.BiGroupByCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun countByGroup(consumer: CacheCollectors.BiNotNullMapper): Map { - val runner = CacheCollectors.BiCountByGroupCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun sumByGroup( - groupMap: CacheCollectors.BiNotNullMapper, - sumOf: CacheCollectors.BiNotNullMapper, - ): Map { - val runner = CacheCollectors.BiSumByGroupCollector(groupMap, sumOf) - innerForEach(runner) - return runner.results - } - - fun count(consumer: CacheCollectors.BiFilter): Int { - val runner = CacheCollectors.BiCountIfCollector(consumer) - innerForEach(runner) - return runner.count - } - - fun associate(transform: (K, V) -> Pair): Map { - val runner = CacheCollectors.BiAssociateCollector(size(), transform) - innerForEach(runner) - return runner.results - } - - fun associateNotNull(transform: (K, V) -> Pair?): Map { - val runner = CacheCollectors.BiAssociateNotNullCollector(size(), transform) - innerForEach(runner) - return runner.results - } - - fun associateWith(transform: (K, V) -> U?): Map { - val runner = CacheCollectors.BiAssociateWithCollector(size(), transform) - innerForEach(runner) - return runner.results - } - - fun associateNotNullWith(transform: (K, V) -> U): Map { - val runner = CacheCollectors.BiAssociateNotNullWithCollector(size(), transform) - innerForEach(runner) - return runner.results - } - - private fun innerForEach(runner: BiConsumer) { - 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() + override fun forEach(consumer: BiConsumer) { + cache.forEach(consumer) } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt index 6f1c90c550..17f9ba2656 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt @@ -24,7 +24,7 @@ import java.lang.ref.WeakReference import java.util.concurrent.ConcurrentSkipListMap import java.util.function.BiConsumer -class LargeSoftCache { +class LargeSoftCache : CacheOperations { private val cache = ConcurrentSkipListMap>() fun keys() = cache.keys @@ -43,7 +43,7 @@ class LargeSoftCache { fun remove(key: K) = cache.remove(key) - fun size() = cache.size + override fun size() = cache.size fun isEmpty() = cache.isEmpty() @@ -122,156 +122,8 @@ class LargeSoftCache { println("Cache cleanup completed. Remaining size: ${cache.size}") } - fun forEach(consumer: BiConsumer) { - innerForEach(consumer) - } - - fun filter(consumer: CacheCollectors.BiFilter): List { - val runner = CacheCollectors.BiFilterCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun filterIntoSet(consumer: CacheCollectors.BiFilter): Set { - val runner = CacheCollectors.BiFilterUniqueCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun map(consumer: CacheCollectors.BiNotNullMapper): List { - val runner = CacheCollectors.BiNotNullMapCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun mapNotNull(consumer: CacheCollectors.BiMapper): List { - val runner = CacheCollectors.BiMapCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun mapNotNullIntoSet(consumer: CacheCollectors.BiMapper): Set { - val runner = CacheCollectors.BiMapUniqueCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun mapFlatten(consumer: CacheCollectors.BiMapper?>): List { - val runner = CacheCollectors.BiMapFlattenCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun mapFlattenIntoSet(consumer: CacheCollectors.BiMapper?>): Set { - val runner = CacheCollectors.BiMapFlattenUniqueCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun maxOrNullOf( - filter: CacheCollectors.BiFilter, - comparator: Comparator, - ): V? { - val runner = CacheCollectors.BiMaxOfCollector(filter, comparator) - innerForEach(runner) - return runner.maxV - } - - fun sumOf(consumer: CacheCollectors.BiSumOf): Int { - val runner = CacheCollectors.BiSumOfCollector(consumer) - innerForEach(runner) - return runner.sum - } - - fun sumOfLong(consumer: CacheCollectors.BiSumOfLong): Long { - val runner = CacheCollectors.BiSumOfLongCollector(consumer) - innerForEach(runner) - return runner.sum - } - - fun groupBy(consumer: CacheCollectors.BiNotNullMapper): Map> { - val runner = CacheCollectors.BiGroupByCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun countByGroup(consumer: CacheCollectors.BiNotNullMapper): Map { - val runner = CacheCollectors.BiCountByGroupCollector(consumer) - innerForEach(runner) - return runner.results - } - - fun sumByGroup( - groupMap: CacheCollectors.BiNotNullMapper, - sumOf: CacheCollectors.BiNotNullMapper, - ): Map { - val runner = CacheCollectors.BiSumByGroupCollector(groupMap, sumOf) - innerForEach(runner) - return runner.results - } - - fun count(consumer: CacheCollectors.BiFilter): Int { - val runner = CacheCollectors.BiCountIfCollector(consumer) - innerForEach(runner) - return runner.count - } - - fun associate(transform: (K, V) -> Pair): Map { - val runner = CacheCollectors.BiAssociateCollector(size(), transform) - innerForEach(runner) - return runner.results - } - - fun associateNotNull(transform: (K, V) -> Pair?): Map { - val runner = CacheCollectors.BiAssociateNotNullCollector(size(), transform) - innerForEach(runner) - return runner.results - } - - fun associateWith(transform: (K, V) -> U?): Map { - val runner = CacheCollectors.BiAssociateWithCollector(size(), transform) - innerForEach(runner) - return runner.results - } - - fun associateNotNullWith(transform: (K, V) -> U): Map { - val runner = CacheCollectors.BiAssociateNotNullWithCollector(size(), transform) - innerForEach(runner) - return runner.results - } - - private fun innerForEach(runner: BiConsumer) { - 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) { + cache.forEach(BiConsumerWrapper(this, consumer)) } class BiConsumerWrapper( From f21c7bd258da2cc05315b0140256ef2e1513f4bb Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Sat, 16 Aug 2025 11:20:37 +0100 Subject: [PATCH 05/13] Lint issues: remove unused methods, use indexing operator instead of .get() --- .../quartz/utils/CacheOperations.kt | 12 ------------ .../com/vitorpamplona/quartz/utils/LargeCache.kt | 2 +- .../vitorpamplona/quartz/utils/LargeSoftCache.kt | 16 +--------------- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt index b1c9a9cccd..c44ff5814e 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt @@ -123,24 +123,12 @@ interface CacheOperations { return runner.results } - fun associateNotNull(transform: (K, V) -> Pair?): Map { - val runner = CacheCollectors.BiAssociateNotNullCollector(size(), transform) - forEach(runner) - return runner.results - } - fun associateWith(transform: (K, V) -> U?): Map { val runner = CacheCollectors.BiAssociateWithCollector(size(), transform) forEach(runner) return runner.results } - fun associateNotNullWith(transform: (K, V) -> U): Map { - val runner = CacheCollectors.BiAssociateNotNullWithCollector(size(), transform) - forEach(runner) - return runner.results - } - fun joinToString( separator: CharSequence = ", ", prefix: CharSequence = "", diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt index 962932c9af..fe59fca30a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt @@ -67,7 +67,7 @@ class LargeCache : CacheOperations { key: K, builder: (key: K) -> V, ): Boolean { - val value = cache.get(key) + val value = cache[key] return if (value != null) { false } else { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt index 17f9ba2656..995c0701c8 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeSoftCache.kt @@ -78,7 +78,7 @@ class LargeSoftCache : CacheOperations { 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 : CacheOperations { } } - 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, From a82f4aacbed0f39c28f708e9ff24f20269a45bbe Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Sat, 16 Aug 2025 18:17:27 +0100 Subject: [PATCH 06/13] Use val for references that do not change (eg results) --- .../quartz/utils/CacheCollectors.kt | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt index ca9377a0df..b6203e5c92 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt @@ -33,7 +33,7 @@ object CacheCollectors { class BiFilterCollector( val filter: BiFilter, ) : BiConsumer { - var results: ArrayList = ArrayList() + val results: ArrayList = ArrayList() override fun accept( k: K, @@ -48,7 +48,7 @@ object CacheCollectors { class BiFilterUniqueCollector( val filter: BiFilter, ) : BiConsumer { - var results: HashSet = HashSet() + val results: HashSet = HashSet() override fun accept( k: K, @@ -77,7 +77,7 @@ object CacheCollectors { class BiMapCollector( val mapper: BiMapper, ) : BiConsumer { - var results: ArrayList = ArrayList() + val results: ArrayList = ArrayList() override fun accept( k: K, @@ -94,7 +94,7 @@ object CacheCollectors { val size: Int, val mapper: BiMapperNotNull>, ) : BiConsumer { - var results: LinkedHashMap = LinkedHashMap(size) + val results: LinkedHashMap = LinkedHashMap(size) override fun accept( k: K, @@ -109,7 +109,7 @@ object CacheCollectors { val size: Int, val mapper: BiMapper?>, ) : BiConsumer { - var results: LinkedHashMap = LinkedHashMap(size) + val results: LinkedHashMap = LinkedHashMap(size) override fun accept( k: K, @@ -126,7 +126,7 @@ object CacheCollectors { val size: Int, val mapper: BiMapper, ) : BiConsumer { - var results: LinkedHashMap = LinkedHashMap(size) + val results: LinkedHashMap = LinkedHashMap(size) override fun accept( k: K, @@ -140,7 +140,7 @@ object CacheCollectors { val size: Int, val mapper: BiMapper, ) : BiConsumer { - var results: LinkedHashMap = LinkedHashMap(size) + val results: LinkedHashMap = LinkedHashMap(size) override fun accept( k: K, @@ -156,7 +156,7 @@ object CacheCollectors { class BiMapUniqueCollector( val mapper: BiMapper, ) : BiConsumer { - var results: HashSet = HashSet() + val results: HashSet = HashSet() override fun accept( k: K, @@ -172,7 +172,7 @@ object CacheCollectors { class BiMapFlattenCollector( val mapper: BiMapper?>, ) : BiConsumer { - var results: ArrayList = ArrayList() + val results: ArrayList = ArrayList() override fun accept( k: K, @@ -188,7 +188,7 @@ object CacheCollectors { class BiMapFlattenUniqueCollector( val mapper: BiMapper?>, ) : BiConsumer { - var results: HashSet = HashSet() + val results: HashSet = HashSet() override fun accept( k: K, @@ -211,7 +211,7 @@ object CacheCollectors { class BiNotNullMapCollector( val mapper: BiNotNullMapper, ) : BiConsumer { - var results: ArrayList = ArrayList() + val results: ArrayList = ArrayList() override fun accept( k: K, @@ -284,7 +284,7 @@ object CacheCollectors { class BiGroupByCollector( val mapper: BiNotNullMapper, ) : BiConsumer { - var results = HashMap>() + val results = HashMap>() override fun accept( k: K, @@ -306,7 +306,7 @@ object CacheCollectors { class BiCountByGroupCollector( val mapper: BiNotNullMapper, ) : BiConsumer { - var results = HashMap() + val results = HashMap() override fun accept( k: K, @@ -327,7 +327,7 @@ object CacheCollectors { val mapper: BiNotNullMapper, val sumOf: BiNotNullMapper, ) : BiConsumer { - var results = HashMap() + val results = HashMap() override fun accept( k: K, From fca4aee5f551f967193724135fde054d2bbea45e Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Sat, 16 Aug 2025 13:21:10 -0400 Subject: [PATCH 07/13] Adds a user list to each connected relay to know where this is coming from --- .../relays/common/BasicRelaySetupInfo.kt | 2 + .../common/BasicRelaySetupInfoClickableRow.kt | 67 +++++++++++++++++++ .../common/BasicRelaySetupInfoDialog.kt | 1 + .../relays/common/BasicRelaySetupInfoModel.kt | 32 ++++----- .../connected/ConnectedRelayListViewModel.kt | 24 +++++++ .../vitorpamplona/amethyst/ui/theme/Shape.kt | 1 + 6 files changed, 112 insertions(+), 15 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfo.kt index 3fac4efd4a..1d2655ac16 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfo.kt @@ -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 = emptyList(), ) fun relaySetupInfoBuilder( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoClickableRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoClickableRow.kt index 98da2d9219..ec65c11c5d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoClickableRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoClickableRow.kt @@ -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, + ) + } + } + } + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoDialog.kt index e5b2b7b4aa..6ca31d0b1f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoDialog.kt @@ -44,5 +44,6 @@ fun BasicRelaySetupInfoDialog( onDelete = onDelete, accountViewModel = accountViewModel, onClick = { nav.nav(Route.RelayInfo(item.relay.url)) }, + nav = nav, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt index 9f84c96d5a..89875455a1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt @@ -83,22 +83,24 @@ abstract class BasicRelaySetupInfoModel : ViewModel() { } } - fun clear() { - _relays.update { - val relayList = getRelayList() ?: emptyList() + open fun relayListBuilder(): List { + 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) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/connected/ConnectedRelayListViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/connected/ConnectedRelayListViewModel.kt index 9759533876..a76ebe9ee6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/connected/ConnectedRelayListViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/connected/ConnectedRelayListViewModel.kt @@ -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 { + 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 = account.client .relayStatusFlow() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt index 704938d0da..71568c03c8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt @@ -189,6 +189,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) From a72fff5f8fa611c54d589ff4151db12a5a79850a Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Sat, 16 Aug 2025 18:50:01 +0100 Subject: [PATCH 08/13] Lifted out collectors from CacheCollectors object --- .../quartz/utils/CacheCollectors.kt | 592 +++++++++--------- .../quartz/utils/CacheOperations.kt | 32 +- 2 files changed, 316 insertions(+), 308 deletions(-) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt index b6203e5c92..5b54c8be7b 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheCollectors.kt @@ -20,8 +20,308 @@ */ 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( + private val filter: BiFilter, +) : BiConsumer { + val results: ArrayList = ArrayList() + + override fun accept( + k: K, + v: V, + ) { + if (filter.filter(k, v)) { + results.add(v) + } + } +} + +class BiFilterUniqueCollector( + private val filter: BiFilter, +) : BiConsumer { + val results: HashSet = HashSet() + + override fun accept( + k: K, + v: V, + ) { + if (filter.filter(k, v)) { + results.add(v) + } + } +} + +class BiMapCollector( + private val mapper: BiMapper, +) : BiConsumer { + val results: ArrayList = ArrayList() + + override fun accept( + k: K, + v: V, + ) { + val result = mapper.map(k, v) + if (result != null) { + results.add(result) + } + } +} + +class BiMapUniqueCollector( + private val mapper: BiMapper, +) : BiConsumer { + val results: HashSet = HashSet() + + override fun accept( + k: K, + v: V, + ) { + val result = mapper.map(k, v) + if (result != null) { + results.add(result) + } + } +} + +class BiMapFlattenCollector( + private val mapper: BiMapper?>, +) : BiConsumer { + val results: ArrayList = ArrayList() + + override fun accept( + k: K, + v: V, + ) { + val result = mapper.map(k, v) + if (result != null) { + results.addAll(result) + } + } +} + +class BiMapFlattenUniqueCollector( + private val mapper: BiMapper?>, +) : BiConsumer { + val results: HashSet = HashSet() + + override fun accept( + k: K, + v: V, + ) { + val result = mapper.map(k, v) + if (result != null) { + results.addAll(result) + } + } +} + +class BiNotNullMapCollector( + private val mapper: BiNotNullMapper, +) : BiConsumer { + val results: ArrayList = ArrayList() + + override fun accept( + k: K, + v: V, + ) { + results.add(mapper.map(k, v)) + } +} + +class BiMaxOfCollector( + private val filter: BiFilter, + private val comparator: Comparator, +) : BiConsumer { + 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( + private val mapper: CacheCollectors.BiSumOf, +) : BiConsumer { + private var _sum = 0 + val sum: Int get() = _sum + + override fun accept( + k: K, + v: V, + ) { + _sum += mapper.map(k, v) + } +} + +class BiSumOfLongCollector( + private val mapper: BiSumOfLong, +) : BiConsumer { + private var _sum = 0L + val sum: Long get() = _sum + + override fun accept( + k: K, + v: V, + ) { + _sum += mapper.map(k, v) + } +} + +class BiGroupByCollector( + private val mapper: BiNotNullMapper, +) : BiConsumer { + val results = HashMap>() + + override fun accept( + k: K, + v: V, + ) { + val group = mapper.map(k, v) + val list = results[group] + if (list == null) { + val answer = ArrayList() + answer.add(v) + results[group] = answer + } else { + list.add(v) + } + } +} + +class BiCountByGroupCollector( + private val mapper: BiNotNullMapper, +) : BiConsumer { + val results = HashMap() + + 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( + private val mapper: BiNotNullMapper, + private val sumOf: BiNotNullMapper, +) : BiConsumer { + val results = HashMap() + + 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( + private val filter: BiFilter, +) : BiConsumer { + private var _count = 0 + val count: Int get() = _count + + override fun accept( + k: K, + v: V, + ) { + if (filter.filter(k, v)) _count++ + } +} + +class BiAssociateCollector( + size: Int, + private val mapper: BiMapperNotNull>, +) : BiConsumer { + val results: LinkedHashMap = LinkedHashMap(size) + + override fun accept( + k: K, + v: V, + ) { + val pair = mapper.map(k, v) + results[pair.first] = pair.second + } +} + +class BiAssociateNotNullCollector( + size: Int, + private val mapper: BiMapper?>, +) : BiConsumer { + val results: LinkedHashMap = 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( + size: Int, + private val mapper: BiMapper, +) : BiConsumer { + val results: LinkedHashMap = LinkedHashMap(size) + + override fun accept( + k: K, + v: V, + ) { + results[k] = mapper.map(k, v) + } +} + +class BiAssociateNotNullWithCollector( + size: Int, + private val mapper: BiMapper, +) : BiConsumer { + val results: LinkedHashMap = 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 { fun filter( @@ -30,36 +330,6 @@ object CacheCollectors { ): Boolean } - class BiFilterCollector( - val filter: BiFilter, - ) : BiConsumer { - val results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - if (filter.filter(k, v)) { - results.add(v) - } - } - } - - class BiFilterUniqueCollector( - val filter: BiFilter, - ) : BiConsumer { - val results: HashSet = HashSet() - - override fun accept( - k: K, - v: V, - ) { - if (filter.filter(k, v)) { - results.add(v) - } - } - } - fun interface BiMapper { fun map( k: K, @@ -74,133 +344,6 @@ object CacheCollectors { ): R } - class BiMapCollector( - val mapper: BiMapper, - ) : BiConsumer { - val results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.add(result) - } - } - } - - class BiAssociateCollector( - val size: Int, - val mapper: BiMapperNotNull>, - ) : BiConsumer { - val results: LinkedHashMap = LinkedHashMap(size) - - override fun accept( - k: K, - v: V, - ) { - val pair = mapper.map(k, v) - results.put(pair.first, pair.second) - } - } - - class BiAssociateNotNullCollector( - val size: Int, - val mapper: BiMapper?>, - ) : BiConsumer { - val results: LinkedHashMap = 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( - val size: Int, - val mapper: BiMapper, - ) : BiConsumer { - val results: LinkedHashMap = LinkedHashMap(size) - - override fun accept( - k: K, - v: V, - ) { - results.put(k, mapper.map(k, v)) - } - } - - class BiAssociateNotNullWithCollector( - val size: Int, - val mapper: BiMapper, - ) : BiConsumer { - val results: LinkedHashMap = LinkedHashMap(size) - - override fun accept( - k: K, - v: V, - ) { - val newValue = mapper.map(k, v) - if (newValue != null) { - results.put(k, newValue) - } - } - } - - class BiMapUniqueCollector( - val mapper: BiMapper, - ) : BiConsumer { - val results: HashSet = HashSet() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.add(result) - } - } - } - - class BiMapFlattenCollector( - val mapper: BiMapper?>, - ) : BiConsumer { - val results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.addAll(result) - } - } - } - - class BiMapFlattenUniqueCollector( - val mapper: BiMapper?>, - ) : BiConsumer { - val results: HashSet = HashSet() - - override fun accept( - k: K, - v: V, - ) { - val result = mapper.map(k, v) - if (result != null) { - results.addAll(result) - } - } - } - fun interface BiNotNullMapper { fun map( k: K, @@ -208,19 +351,6 @@ object CacheCollectors { ): R } - class BiNotNullMapCollector( - val mapper: BiNotNullMapper, - ) : BiConsumer { - val results: ArrayList = ArrayList() - - override fun accept( - k: K, - v: V, - ) { - results.add(mapper.map(k, v)) - } - } - fun interface BiSumOf { fun map( k: K, @@ -228,132 +358,10 @@ object CacheCollectors { ): Int } - class BiMaxOfCollector( - val filter: BiFilter, - val comparator: Comparator, - ) : BiConsumer { - 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( - val mapper: BiSumOf, - ) : BiConsumer { - var sum = 0 - - override fun accept( - k: K, - v: V, - ) { - sum += mapper.map(k, v) - } - } - fun interface BiSumOfLong { fun map( k: K, v: V, ): Long } - - class BiSumOfLongCollector( - val mapper: BiSumOfLong, - ) : BiConsumer { - var sum = 0L - - override fun accept( - k: K, - v: V, - ) { - sum += mapper.map(k, v) - } - } - - class BiGroupByCollector( - val mapper: BiNotNullMapper, - ) : BiConsumer { - val results = HashMap>() - - override fun accept( - k: K, - v: V, - ) { - val group = mapper.map(k, v) - - val list = results[group] - if (list == null) { - val answer = ArrayList() - answer.add(v) - results[group] = answer - } else { - list.add(v) - } - } - } - - class BiCountByGroupCollector( - val mapper: BiNotNullMapper, - ) : BiConsumer { - val results = HashMap() - - 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( - val mapper: BiNotNullMapper, - val sumOf: BiNotNullMapper, - ) : BiConsumer { - val results = HashMap() - - 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( - val filter: BiFilter, - ) : BiConsumer { - var count = 0 - - override fun accept( - k: K, - v: V, - ) { - if (filter.filter(k, v)) count++ - } - } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt index c44ff5814e..24e0577079 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/CacheOperations.kt @@ -28,43 +28,43 @@ interface CacheOperations { fun size(): Int fun filter(consumer: CacheCollectors.BiFilter): List { - val runner = CacheCollectors.BiFilterCollector(consumer) + val runner = BiFilterCollector(consumer) forEach(runner) return runner.results } fun filterIntoSet(consumer: CacheCollectors.BiFilter): Set { - val runner = CacheCollectors.BiFilterUniqueCollector(consumer) + val runner = BiFilterUniqueCollector(consumer) forEach(runner) return runner.results } fun map(consumer: CacheCollectors.BiNotNullMapper): List { - val runner = CacheCollectors.BiNotNullMapCollector(consumer) + val runner = BiNotNullMapCollector(consumer) forEach(runner) return runner.results } fun mapNotNull(consumer: CacheCollectors.BiMapper): List { - val runner = CacheCollectors.BiMapCollector(consumer) + val runner = BiMapCollector(consumer) forEach(runner) return runner.results } fun mapNotNullIntoSet(consumer: CacheCollectors.BiMapper): Set { - val runner = CacheCollectors.BiMapUniqueCollector(consumer) + val runner = BiMapUniqueCollector(consumer) forEach(runner) return runner.results } fun mapFlatten(consumer: CacheCollectors.BiMapper?>): List { - val runner = CacheCollectors.BiMapFlattenCollector(consumer) + val runner = BiMapFlattenCollector(consumer) forEach(runner) return runner.results } fun mapFlattenIntoSet(consumer: CacheCollectors.BiMapper?>): Set { - val runner = CacheCollectors.BiMapFlattenUniqueCollector(consumer) + val runner = BiMapFlattenUniqueCollector(consumer) forEach(runner) return runner.results } @@ -73,31 +73,31 @@ interface CacheOperations { filter: CacheCollectors.BiFilter, comparator: Comparator, ): V? { - val runner = CacheCollectors.BiMaxOfCollector(filter, comparator) + val runner = BiMaxOfCollector(filter, comparator) forEach(runner) return runner.maxV } fun sumOf(consumer: CacheCollectors.BiSumOf): Int { - val runner = CacheCollectors.BiSumOfCollector(consumer) + val runner = BiSumOfCollector(consumer) forEach(runner) return runner.sum } fun sumOfLong(consumer: CacheCollectors.BiSumOfLong): Long { - val runner = CacheCollectors.BiSumOfLongCollector(consumer) + val runner = BiSumOfLongCollector(consumer) forEach(runner) return runner.sum } fun groupBy(consumer: CacheCollectors.BiNotNullMapper): Map> { - val runner = CacheCollectors.BiGroupByCollector(consumer) + val runner = BiGroupByCollector(consumer) forEach(runner) return runner.results } fun countByGroup(consumer: CacheCollectors.BiNotNullMapper): Map { - val runner = CacheCollectors.BiCountByGroupCollector(consumer) + val runner = BiCountByGroupCollector(consumer) forEach(runner) return runner.results } @@ -106,25 +106,25 @@ interface CacheOperations { groupMap: CacheCollectors.BiNotNullMapper, sumOf: CacheCollectors.BiNotNullMapper, ): Map { - val runner = CacheCollectors.BiSumByGroupCollector(groupMap, sumOf) + val runner = BiSumByGroupCollector(groupMap, sumOf) forEach(runner) return runner.results } fun count(consumer: CacheCollectors.BiFilter): Int { - val runner = CacheCollectors.BiCountIfCollector(consumer) + val runner = BiCountIfCollector(consumer) forEach(runner) return runner.count } fun associate(transform: (K, V) -> Pair): Map { - val runner = CacheCollectors.BiAssociateCollector(size(), transform) + val runner = BiAssociateCollector(size(), transform) forEach(runner) return runner.results } fun associateWith(transform: (K, V) -> U?): Map { - val runner = CacheCollectors.BiAssociateWithCollector(size(), transform) + val runner = BiAssociateWithCollector(size(), transform) forEach(runner) return runner.results } From c1004dc6433f082f606c5be2805b3b1be1fdbe47 Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Sat, 16 Aug 2025 18:56:48 +0100 Subject: [PATCH 09/13] Use .get( instead of index to keep to code base coding style --- .../src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt index fe59fca30a..962932c9af 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LargeCache.kt @@ -67,7 +67,7 @@ class LargeCache : CacheOperations { key: K, builder: (key: K) -> V, ): Boolean { - val value = cache[key] + val value = cache.get(key) return if (value != null) { false } else { From b38e9247ce6a0e93b11eccf35fbe46ff4a0cd872 Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Sun, 17 Aug 2025 23:02:25 +0100 Subject: [PATCH 10/13] Use pubkeyhex for lazy column key instead of Address --- .../vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt index 16060c726f..70fbd1221d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt @@ -394,7 +394,7 @@ fun EmojiCollectionGallery( LazyColumn( state = listState, ) { - itemsIndexed(emojiCollections, key = { _, item -> item }) { _, item -> + itemsIndexed(emojiCollections, key = { _, item -> item.pubKeyHex }) { _, item -> LoadAddressableNote(item, accountViewModel) { it?.let { WatchAndRenderNote(it, bgColor, accountViewModel, nav, onClick) } } From 41545a156bc071e4f75c719dbb82c6a744f020af Mon Sep 17 00:00:00 2001 From: David Kaspar Date: Mon, 18 Aug 2025 10:44:42 +0100 Subject: [PATCH 11/13] Use explicit annotation target for constructor params See https://youtrack.jetbrains.com/issue/KT-73255 for more details --- .../nip44Encryption/TestPackageClasses.kt | 40 ++++++++-------- .../quartz/experimental/limits/Limits.kt | 48 +++++++------------ .../quartz/nip01Core/signers/EventTemplate.kt | 2 +- .../quartz/nip47WalletConnect/Response.kt | 2 +- .../quartz/nip59Giftwrap/rumors/Rumor.kt | 4 +- .../nip96FileStorage/actions/UploadResult.kt | 6 +-- .../nip96FileStorage/info/ServerInfo.kt | 39 +++++---------- 7 files changed, 54 insertions(+), 87 deletions(-) diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/TestPackageClasses.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/TestPackageClasses.kt index 40ee511f10..4262d36bea 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/TestPackageClasses.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/TestPackageClasses.kt @@ -32,61 +32,59 @@ data class V2( ) data class Valid( - @JsonProperty("get_conversation_key") - val getConversationKey: ArrayList = arrayListOf(), - @JsonProperty("get_message_keys") val getMessageKeys: GetMessageKeys? = GetMessageKeys(), - @JsonProperty("calc_padded_len") val calcPaddedLen: ArrayList> = arrayListOf(), - @JsonProperty("encrypt_decrypt") val encryptDecrypt: ArrayList = arrayListOf(), - @JsonProperty("encrypt_decrypt_long_msg") + @field:JsonProperty("get_conversation_key") val getConversationKey: ArrayList = arrayListOf(), + @field:JsonProperty("get_message_keys") val getMessageKeys: GetMessageKeys? = GetMessageKeys(), + @field:JsonProperty("calc_padded_len") val calcPaddedLen: ArrayList> = arrayListOf(), + @field:JsonProperty("encrypt_decrypt") val encryptDecrypt: ArrayList = arrayListOf(), + @field:JsonProperty("encrypt_decrypt_long_msg") val encryptDecryptLongMsg: ArrayList = arrayListOf(), ) data class Invalid( - @JsonProperty("encrypt_msg_lengths") val encryptMsgLengths: ArrayList = arrayListOf(), - @JsonProperty("get_conversation_key") - val getConversationKey: ArrayList = arrayListOf(), - @JsonProperty("decrypt") val decrypt: ArrayList = arrayListOf(), + @field:JsonProperty("encrypt_msg_lengths") val encryptMsgLengths: ArrayList = arrayListOf(), + @field:JsonProperty("get_conversation_key") val getConversationKey: ArrayList = arrayListOf(), + @field:JsonProperty("decrypt") val decrypt: ArrayList = 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 = 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, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/limits/Limits.kt b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/limits/Limits.kt index 62d0b6da8a..75904c921f 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/experimental/limits/Limits.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/experimental/limits/Limits.kt @@ -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?, - @JsonProperty("blocked_event_kinds") - val blockedEventKinds: Set?, - @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>?, + @field:JsonProperty("can_write") val canWrite: Boolean?, + @field:JsonProperty("can_read") val canRead: Boolean?, + @field:JsonProperty("accepted_event_kinds") val acceptedEventKinds: Set?, + @field:JsonProperty("blocked_event_kinds") val blockedEventKinds: Set?, + @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>?, ) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplate.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplate.kt index 25a2196d67..3d801e9637 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplate.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/EventTemplate.kt @@ -30,7 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.jackson.JsonMapper import com.vitorpamplona.quartz.utils.TimeUtils class EventTemplate( - @JsonProperty("created_at") + @field:JsonProperty("created_at") val createdAt: Long, val kind: Int, val tags: TagArray, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip47WalletConnect/Response.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip47WalletConnect/Response.kt index b895114ed7..4968410be0 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip47WalletConnect/Response.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip47WalletConnect/Response.kt @@ -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 } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip59Giftwrap/rumors/Rumor.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip59Giftwrap/rumors/Rumor.kt index 1a133acb3b..02ad68890e 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip59Giftwrap/rumors/Rumor.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip59Giftwrap/rumors/Rumor.kt @@ -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>?, val content: String?, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip96FileStorage/actions/UploadResult.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip96FileStorage/actions/UploadResult.kt index 554210641a..a4b2d218b7 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip96FileStorage/actions/UploadResult.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip96FileStorage/actions/UploadResult.kt @@ -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 { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip96FileStorage/info/ServerInfo.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip96FileStorage/info/ServerInfo.kt index b1bd838464..7d773bf5c4 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip96FileStorage/info/ServerInfo.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip96FileStorage/info/ServerInfo.kt @@ -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 = arrayListOf(), - @JsonProperty("tos_url") val - tosUrl: String? = null, - @JsonProperty("content_types") val - contentTypes: ArrayList = arrayListOf(), - @JsonProperty("plans") val - plans: Map = 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 = arrayListOf(), + @field:JsonProperty("tos_url") val tosUrl: String? = null, + @field:JsonProperty("content_types") val contentTypes: ArrayList = arrayListOf(), + @field:JsonProperty("plans") val plans: Map = 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 = arrayListOf(), - @JsonProperty("media_transformations") - val mediaTransformations: Map> = 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 = arrayListOf(), + @field:JsonProperty("media_transformations") val mediaTransformations: Map> = emptyMap(), ) From 92e9978859da8d43ce01edea753c5ecdec321846 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Mon, 18 Aug 2025 11:25:57 -0400 Subject: [PATCH 12/13] Moves to parcelable. Fixes: https://github.com/vitorpamplona/amethyst/issues/1413 --- .../amethyst/ui/note/UpdateReactionTypeDialog.kt | 2 +- gradle/libs.versions.toml | 1 + quartz/build.gradle | 1 + .../quartz/nip01Core/tags/addressables/Address.kt | 6 +++++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt index 70fbd1221d..16060c726f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateReactionTypeDialog.kt @@ -394,7 +394,7 @@ fun EmojiCollectionGallery( LazyColumn( state = listState, ) { - itemsIndexed(emojiCollections, key = { _, item -> item.pubKeyHex }) { _, item -> + itemsIndexed(emojiCollections, key = { _, item -> item }) { _, item -> LoadAddressableNote(item, accountViewModel) { it?.let { WatchAndRenderNote(it, bgColor, accountViewModel, nav, onClick) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 95221f9107..65b0d6cb41 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" } \ No newline at end of file diff --git a/quartz/build.gradle b/quartz/build.gradle index 60c5ab93ca..f4e91c4d09 100644 --- a/quartz/build.gradle +++ b/quartz/build.gradle @@ -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 { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/addressables/Address.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/addressables/Address.kt index c2ad939015..8c5edc6d2d 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/addressables/Address.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/tags/addressables/Address.kt @@ -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
{ +) : Comparable
, + Parcelable { fun toValue() = assemble(kind, pubKeyHex, dTag) fun countMemory(): Long = From f5247a54435f07b35acb8d1847c3ddf5ad14dde5 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Mon, 18 Aug 2025 11:37:32 -0400 Subject: [PATCH 13/13] Moves reconnections to 1 minute not 10 seconds --- .../quartz/nip01Core/relay/client/pool/RelayPool.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/relay/client/pool/RelayPool.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/relay/client/pool/RelayPool.kt index 47605b1c62..deb33b5dbe 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/relay/client/pool/RelayPool.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/relay/client/pool/RelayPool.kt @@ -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()) {