Merge pull request #6266 from thundernest/convert_to_kotlin3
Convert `IdGrouper` to Kotlin
This commit is contained in:
commit
d6ba51b899
6 changed files with 118 additions and 174 deletions
|
@ -1,94 +0,0 @@
|
|||
package com.fsck.k9.mail.store.imap;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
|
||||
class IdGrouper {
|
||||
static GroupedIds groupIds(Set<Long> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
throw new IllegalArgumentException("groupId() must be called with non-empty set of ids");
|
||||
}
|
||||
|
||||
if (ids.size() < 2) {
|
||||
return new GroupedIds(ids, Collections.<ContiguousIdGroup>emptyList());
|
||||
}
|
||||
|
||||
TreeSet<Long> orderedIds = new TreeSet<>(ids);
|
||||
Iterator<Long> orderedIdIterator = orderedIds.iterator();
|
||||
Long previousId = orderedIdIterator.next();
|
||||
|
||||
TreeSet<Long> remainingIds = new TreeSet<>();
|
||||
remainingIds.add(previousId);
|
||||
List<ContiguousIdGroup> idGroups = new ArrayList<>();
|
||||
long currentIdGroupStart = -1L;
|
||||
long currentIdGroupEnd = -1L;
|
||||
while (orderedIdIterator.hasNext()) {
|
||||
Long currentId = orderedIdIterator.next();
|
||||
if (previousId + 1L == currentId) {
|
||||
if (currentIdGroupStart == -1L) {
|
||||
remainingIds.remove(previousId);
|
||||
currentIdGroupStart = previousId;
|
||||
currentIdGroupEnd = currentId;
|
||||
} else {
|
||||
currentIdGroupEnd = currentId;
|
||||
}
|
||||
} else {
|
||||
if (currentIdGroupStart != -1L) {
|
||||
idGroups.add(new ContiguousIdGroup(currentIdGroupStart, currentIdGroupEnd));
|
||||
currentIdGroupStart = -1L;
|
||||
}
|
||||
remainingIds.add(currentId);
|
||||
}
|
||||
|
||||
previousId = currentId;
|
||||
}
|
||||
|
||||
if (currentIdGroupStart != -1L) {
|
||||
idGroups.add(new ContiguousIdGroup(currentIdGroupStart, currentIdGroupEnd));
|
||||
}
|
||||
|
||||
return new GroupedIds(remainingIds, idGroups);
|
||||
}
|
||||
|
||||
|
||||
static class GroupedIds {
|
||||
public final Set<Long> ids;
|
||||
public final List<ContiguousIdGroup> idGroups;
|
||||
|
||||
|
||||
GroupedIds(Set<Long> ids, List<ContiguousIdGroup> idGroups) {
|
||||
if (ids.isEmpty() && idGroups.isEmpty()) {
|
||||
throw new IllegalArgumentException("Must have at least one id");
|
||||
}
|
||||
|
||||
this.ids = ids;
|
||||
this.idGroups = idGroups;
|
||||
}
|
||||
}
|
||||
|
||||
static class ContiguousIdGroup {
|
||||
public final long start;
|
||||
public final long end;
|
||||
|
||||
|
||||
ContiguousIdGroup(long start, long end) {
|
||||
if (start >= end) {
|
||||
throw new IllegalArgumentException("start >= end");
|
||||
}
|
||||
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return start + ":" + end;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.fsck.k9.mail.store.imap
|
||||
|
||||
private const val NO_VALID_ID = -1L
|
||||
|
||||
internal object IdGrouper {
|
||||
fun groupIds(ids: Set<Long>): GroupedIds {
|
||||
require(ids.isNotEmpty()) { "groupIds() must be called with non-empty set of IDs" }
|
||||
|
||||
if (ids.size < 2) return GroupedIds(ids, emptyList())
|
||||
|
||||
val orderedIds = ids.toSortedSet()
|
||||
val firstId = orderedIds.first()
|
||||
|
||||
val remainingIds = mutableSetOf(firstId)
|
||||
val idGroups = mutableListOf<ContiguousIdGroup>()
|
||||
|
||||
var previousId = firstId
|
||||
var currentIdGroupStart = NO_VALID_ID
|
||||
var currentIdGroupEnd = NO_VALID_ID
|
||||
for (currentId in orderedIds.asSequence().drop(1)) {
|
||||
if (previousId + 1L == currentId) {
|
||||
if (currentIdGroupStart == NO_VALID_ID) {
|
||||
remainingIds.remove(previousId)
|
||||
currentIdGroupStart = previousId
|
||||
currentIdGroupEnd = currentId
|
||||
} else {
|
||||
currentIdGroupEnd = currentId
|
||||
}
|
||||
} else {
|
||||
if (currentIdGroupStart != NO_VALID_ID) {
|
||||
idGroups.add(ContiguousIdGroup(currentIdGroupStart, currentIdGroupEnd))
|
||||
currentIdGroupStart = NO_VALID_ID
|
||||
}
|
||||
remainingIds.add(currentId)
|
||||
}
|
||||
|
||||
previousId = currentId
|
||||
}
|
||||
|
||||
if (currentIdGroupStart != NO_VALID_ID) {
|
||||
idGroups.add(ContiguousIdGroup(currentIdGroupStart, currentIdGroupEnd))
|
||||
}
|
||||
|
||||
return GroupedIds(remainingIds, idGroups)
|
||||
}
|
||||
}
|
||||
|
||||
internal class GroupedIds(@JvmField val ids: Set<Long>, @JvmField val idGroups: List<ContiguousIdGroup>) {
|
||||
init {
|
||||
require(ids.isNotEmpty() || idGroups.isNotEmpty()) { "Must have at least one ID" }
|
||||
}
|
||||
}
|
||||
|
||||
internal class ContiguousIdGroup(val start: Long, val end: Long) {
|
||||
init {
|
||||
require(start < end) { "start >= end" }
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "$start:$end"
|
||||
}
|
||||
}
|
|
@ -6,9 +6,6 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.fsck.k9.mail.store.imap.IdGrouper.ContiguousIdGroup;
|
||||
import com.fsck.k9.mail.store.imap.IdGrouper.GroupedIds;
|
||||
|
||||
|
||||
class ImapCommandSplitter {
|
||||
static List<String> splitCommand(String prefix, String suffix, GroupedIds groupedIds, int lengthLimit) {
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
package com.fsck.k9.mail.store.imap;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
public class IdGrouperTest {
|
||||
@Test
|
||||
public void groupIds_withSingleContiguousGroup() throws Exception {
|
||||
Set<Long> ids = newSet(1L, 2L, 3L);
|
||||
|
||||
IdGrouper.GroupedIds groupedIds = IdGrouper.groupIds(ids);
|
||||
|
||||
assertEquals(0, groupedIds.ids.size());
|
||||
assertEquals(1, groupedIds.idGroups.size());
|
||||
assertEquals("1:3", groupedIds.idGroups.get(0).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupIds_withoutContiguousGroup() throws Exception {
|
||||
Set<Long> ids = newSet(23L, 42L, 2L, 5L);
|
||||
|
||||
IdGrouper.GroupedIds groupedIds = IdGrouper.groupIds(ids);
|
||||
|
||||
assertEquals(ids, groupedIds.ids);
|
||||
assertEquals(0, groupedIds.idGroups.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupIds_withMultipleContiguousGroups() throws Exception {
|
||||
Set<Long> ids = newSet(1L, 3L, 4L, 5L, 6L, 10L, 12L, 13L, 14L, 23L);
|
||||
|
||||
IdGrouper.GroupedIds groupedIds = IdGrouper.groupIds(ids);
|
||||
|
||||
assertEquals(newSet(1L, 10L, 23L), groupedIds.ids);
|
||||
assertEquals(2, groupedIds.idGroups.size());
|
||||
assertEquals("3:6", groupedIds.idGroups.get(0).toString());
|
||||
assertEquals("12:14", groupedIds.idGroups.get(1).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupIds_withSingleId() throws Exception {
|
||||
Set<Long> ids = newSet(23L);
|
||||
|
||||
IdGrouper.GroupedIds groupedIds = IdGrouper.groupIds(ids);
|
||||
|
||||
assertEquals(newSet(23L), groupedIds.ids);
|
||||
assertEquals(0, groupedIds.idGroups.size());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void groupIds_withEmptySet_shouldThrow() throws Exception {
|
||||
IdGrouper.groupIds(newSet());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void groupIds_withNullArgument_shouldThrow() throws Exception {
|
||||
IdGrouper.groupIds(null);
|
||||
}
|
||||
|
||||
|
||||
private static Set<Long> newSet(Long... values) {
|
||||
HashSet<Long> set = new HashSet<>(values.length);
|
||||
set.addAll(Arrays.asList(values));
|
||||
return set;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package com.fsck.k9.mail.store.imap
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
|
||||
class IdGrouperTest {
|
||||
@Test
|
||||
fun `groupIds() with single contiguous group`() {
|
||||
val ids = setOf(1L, 2L, 3L)
|
||||
|
||||
val groupedIds = IdGrouper.groupIds(ids)
|
||||
|
||||
assertThat(groupedIds.ids).isEmpty()
|
||||
assertThat(groupedIds.idGroups.mapToString()).containsExactly("1:3")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `groupIds() without contiguous group`() {
|
||||
val ids = setOf(23L, 42L, 2L, 5L)
|
||||
|
||||
val groupedIds = IdGrouper.groupIds(ids)
|
||||
|
||||
assertThat(groupedIds.ids).isEqualTo(ids)
|
||||
assertThat(groupedIds.idGroups).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `groupIds() with multiple contiguous groups`() {
|
||||
val ids = setOf(1L, 3L, 4L, 5L, 6L, 10L, 12L, 13L, 14L, 23L)
|
||||
|
||||
val groupedIds = IdGrouper.groupIds(ids)
|
||||
|
||||
assertThat(groupedIds.ids).containsExactly(1L, 10L, 23L)
|
||||
assertThat(groupedIds.idGroups.mapToString()).containsExactly("3:6", "12:14")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `groupIds() with single ID`() {
|
||||
val ids = setOf(23L)
|
||||
|
||||
val groupedIds = IdGrouper.groupIds(ids)
|
||||
|
||||
assertThat(groupedIds.ids).containsExactly(23L)
|
||||
assertThat(groupedIds.idGroups).isEmpty()
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun `groupIds() with empty set should throw`() {
|
||||
IdGrouper.groupIds(emptySet())
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> List<T>.mapToString() = map { it.toString() }
|
|
@ -6,7 +6,6 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.fsck.k9.mail.store.imap.IdGrouper.GroupedIds;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -24,7 +23,7 @@ public class ImapCommandSplitterTest {
|
|||
@Test
|
||||
public void splitCommand_withManyNonContiguousIds_shouldSplitCommand() throws Exception {
|
||||
Set<Long> ids = createNonContiguousIdSet(10000, 10500, 2);
|
||||
GroupedIds groupedIds = new GroupedIds(ids, Collections.<IdGrouper.ContiguousIdGroup>emptyList());
|
||||
GroupedIds groupedIds = new GroupedIds(ids, Collections.emptyList());
|
||||
|
||||
List<String> commands = ImapCommandSplitter.splitCommand(COMMAND_PREFIX, COMMAND_SUFFIX, groupedIds, 980);
|
||||
|
||||
|
@ -39,7 +38,7 @@ public class ImapCommandSplitterTest {
|
|||
Set<Long> idSet = Sets.union(
|
||||
createNonContiguousIdSet(10000, 10298, 2),
|
||||
createNonContiguousIdSet(10402, 10500, 2));
|
||||
List<IdGrouper.ContiguousIdGroup> idGroups = singletonList(new IdGrouper.ContiguousIdGroup(10300L, 10400L));
|
||||
List<ContiguousIdGroup> idGroups = singletonList(new ContiguousIdGroup(10300L, 10400L));
|
||||
GroupedIds groupedIds = new GroupedIds(idSet, idGroups);
|
||||
|
||||
List<String> commands = ImapCommandSplitter.splitCommand(COMMAND_PREFIX, COMMAND_SUFFIX, groupedIds, 980);
|
||||
|
@ -55,7 +54,7 @@ public class ImapCommandSplitterTest {
|
|||
@Test
|
||||
public void splitCommand_withEmptySuffix_shouldCreateCommandWithoutTrailingSpace() throws Exception {
|
||||
Set<Long> ids = createNonContiguousIdSet(1, 2, 1);
|
||||
GroupedIds groupedIds = new GroupedIds(ids, Collections.<IdGrouper.ContiguousIdGroup>emptyList());
|
||||
GroupedIds groupedIds = new GroupedIds(ids, Collections.<ContiguousIdGroup>emptyList());
|
||||
|
||||
List<String> commands = ImapCommandSplitter.splitCommand("UID SEARCH UID", "", groupedIds, 980);
|
||||
|
||||
|
|
Loading…
Reference in a new issue