Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a13d30de authored by Hari's avatar Hari
Browse files

Full rewrite and remove usage of Builder pattern in most cases

parent 524d8bac
Loading
Loading
Loading
Loading
+33 −39
Original line number Diff line number Diff line
@@ -347,7 +347,7 @@ public class ImapFolder extends Folder<ImapMessage> {
        ImapFolder imapFolder = (ImapFolder) folder;
        checkOpen(); //only need READ access

        List<Long> uids = new ArrayList<>(messages.size());
        Set<Long> uids = new HashSet<>(messages.size());
        for (int i = 0, count = messages.size(); i < count; i++) {
            uids.add(Long.parseLong(messages.get(i).getUid()));
        }
@@ -366,16 +366,10 @@ public class ImapFolder extends Folder<ImapMessage> {
            imapFolder.create(FolderType.HOLDS_MESSAGES);
        }

        UidCopyCommand command = new UidCopyCommand.Builder()
                .idSet(uids)
                .destinationFolderName(escapedDestinationFolderName)
                .build();
        UidCopyCommand command = UidCopyCommand.createWithUids(uids, escapedDestinationFolderName);
        UidCopyResponse response = command.execute(connection, this);

        UidCopyResponse copyUidResponse = command.execute(connection, this);
        if (copyUidResponse == null) {
            return null;
        }
        return copyUidResponse.getUidMapping();
        return response == null ? null : response.getUidMapping();
    }

    @Override
@@ -516,10 +510,9 @@ public class ImapFolder extends Folder<ImapMessage> {
        checkOpen();
        UidSearchCommand searchCommand = new UidSearchCommand.Builder()
                .useUids(false)
                .addIdGroup((long) start, (long) end)
                .idGroup((long) start, (long) end)
                .since(earliestDate)
                .forbiddenFlags(includeDeleted ? null : Collections.singleton(Flag.DELETED))
                .listener(listener)
                .build();
        return getMessages(searchCommand.execute(connection, this), listener);
    }
@@ -554,7 +547,7 @@ public class ImapFolder extends Folder<ImapMessage> {

        UidSearchCommand searchCommand = new UidSearchCommand.Builder()
                .useUids(false)
                .addIdGroup((long) startIndex, (long) endIndex)
                .idGroup((long) startIndex, (long) endIndex)
                .since(earliestDate)
                .forbiddenFlags(Collections.singleton(Flag.DELETED))
                .build();
@@ -562,7 +555,7 @@ public class ImapFolder extends Folder<ImapMessage> {
        return response.getNumbers().size() > 0;
    }

    protected List<ImapMessage> getMessages(final List<Long> mesgSeqs, final boolean includeDeleted,
    protected List<ImapMessage> getMessages(final Set<Long> mesgSeqs, final boolean includeDeleted,
            final MessageRetrievalListener<ImapMessage> listener) throws MessagingException {

        checkOpen();
@@ -570,7 +563,6 @@ public class ImapFolder extends Folder<ImapMessage> {
                .useUids(false)
                .idSet(mesgSeqs)
                .forbiddenFlags(includeDeleted ? null : Collections.singleton(Flag.DELETED))
                .listener(listener)
                .build();
        return getMessages(searchCommand.execute(connection, this), listener);
    }
@@ -640,18 +632,19 @@ public class ImapFolder extends Folder<ImapMessage> {

        for (int windowStart = 0; windowStart < messages.size(); windowStart += (FETCH_WINDOW_SIZE)) {
            int windowEnd = Math.min(windowStart + FETCH_WINDOW_SIZE, messages.size());
            List<Long> uidWindow = new ArrayList<>(windowEnd - windowStart);
            Set<Long> uidSet = new HashSet<>(windowEnd - windowStart);
            for (String uid : uids.subList(windowStart, windowEnd)) {
                uidWindow.add(Long.parseLong(uid));
                uidSet.add(Long.parseLong(uid));
            }

            int maximumAutoDownloadMessageSize = store.getStoreConfig()
                    .getMaximumAutoDownloadMessageSize();

            try {

                UidFetchCommand command = new UidFetchCommand.Builder()
                        .maximumAutoDownloadMessageSize(store.getStoreConfig().getMaximumAutoDownloadMessageSize())
                        .idSet(uidWindow)
                        .messageParams(fetchProfile, messageMap)
                        .build();
                UidFetchCommand command = UidFetchCommand.createWithMessageParams(uidSet,
                        maximumAutoDownloadMessageSize, messageMap, fetchProfile);

                command.send(connection);

                ImapResponse response;
@@ -725,12 +718,14 @@ public class ImapFolder extends Folder<ImapMessage> {
            BodyFactory bodyFactory) throws MessagingException {
        checkOpen();

        Set<Long> uids = Collections.singleton(Long.parseLong(message.getUid()));
        int maximumAutoDownloadMessageSize = store.getStoreConfig()
                .getMaximumAutoDownloadMessageSize();

        try {
            UidFetchCommand command = new UidFetchCommand.Builder()
                    .maximumAutoDownloadMessageSize(store.getStoreConfig().getMaximumAutoDownloadMessageSize())
                    .idSet(Collections.singleton(Long.parseLong(message.getUid())))
                    .partParams(part, bodyFactory)
                    .build();
            UidFetchCommand command = UidFetchCommand.createWithPartParams(uids,
                    maximumAutoDownloadMessageSize, part, bodyFactory);

            command.send(connection);

            ImapResponse response;
@@ -1207,12 +1202,12 @@ public class ImapFolder extends Folder<ImapMessage> {
        open(OPEN_MODE_RW);
        checkOpen();

        UidStoreCommand command = new UidStoreCommand.Builder()
                .allIds(true)
                .value(value)
                .flagSet(flags)
                .canCreateForwardedFlag(canCreateKeywords || store.getPermanentFlagsIndex().contains(Flag.FORWARDED))
                .build();
        boolean canCreateForwardedFlag = canCreateKeywords ||
                store.getPermanentFlagsIndex().contains(Flag.FORWARDED);


        UidStoreCommand command = UidStoreCommand.createWithAllUids(value, flags,
                canCreateForwardedFlag);
        command.execute(connection, this);
    }

@@ -1249,12 +1244,11 @@ public class ImapFolder extends Folder<ImapMessage> {
            uids.add(Long.parseLong(message.getUid()));
        }

        UidStoreCommand command = new UidStoreCommand.Builder()
                .idSet(uids)
                .value(value)
                .flagSet(flags)
                .canCreateForwardedFlag(canCreateKeywords || store.getPermanentFlagsIndex().contains(Flag.FORWARDED))
                .build();
        boolean canCreateForwardedFlag = canCreateKeywords ||
                store.getPermanentFlagsIndex().contains(Flag.FORWARDED);

        UidStoreCommand command = UidStoreCommand.createWithUids(uids, value, flags,
                canCreateForwardedFlag);
        command.execute(connection, this);
    }

+4 −1
Original line number Diff line number Diff line
@@ -5,9 +5,11 @@ import java.io.IOException;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import android.content.Context;
import android.os.PowerManager;
@@ -596,7 +598,8 @@ class ImapFolderPusher extends ImapFolder {

        private void syncMessages(List<Long> flagSyncMsgSeqs) {
            try {
                List<? extends Message> messageList = getMessages(flagSyncMsgSeqs, true, null);
                Set<Long> messageSeqSet = new HashSet<>(flagSyncMsgSeqs);
                List<? extends Message> messageList = getMessages(messageSeqSet, true, null);

                List<Message> messages = new ArrayList<Message>();
                messages.addAll(messageList);
+54 −91
Original line number Diff line number Diff line
@@ -3,7 +3,6 @@ package com.fsck.k9.mail.store.imap.selectedstate.command;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -19,43 +18,44 @@ import com.fsck.k9.mail.store.imap.selectedstate.response.SelectedStateResponse;

abstract class FolderSelectedStateCommand {

    /* The below limits are 20 octets less than the recommended limits, in order to compensate for the length of the
    command tag, the space after the tag and the CRLF at the end of the command (these are not taken into account when
    calculating the length of the command). For more information, refer to section 4 of RFC 7162.
    /* The below limits are 20 octets less than the recommended limits, in order to compensate for
    the length of the command tag, the space after the tag and the CRLF at the end of the command
    (these are not taken into account when calculating the length of the command). For more
    information, refer to section 4 of RFC 7162.

    The length limit for servers supporting the CONDSTORE extension is large in order to support the QRESYNC parameter
    to the SELECT/EXAMINE commands, which accept a list of known message sequence numbers as well as their corresponding
    UIDs.
    The length limit for servers supporting the CONDSTORE extension is large in order to support
    the QRESYNC parameter to the SELECT/EXAMINE commands, which accept a list of known message
    sequence numbers as well as their corresponding UIDs.
     */
    private static final int LENGTH_LIMIT_WITHOUT_CONDSTORE = 980;
    private static final int LENGTH_LIMIT_WITH_CONDSTORE = 8172;

    Set<Long> idSet;
    List<ContiguousIdGroup> idGroups;
    private Set<Long> idSet;
    private List<ContiguousIdGroup> idGroups;

    abstract String createCommandString();
    FolderSelectedStateCommand(Set<Long> idSet) {
        this.idSet = new TreeSet<>(idSet);
        this.idGroups = new ArrayList<>(0);
    }

    abstract Builder newBuilder();
    abstract String createCommandString();

    String createCombinedIdString() {
        if (idSet != null || idGroups != null) {
        if (idSet.isEmpty() && idGroups.isEmpty()) {
            return "";
        }

        StringBuilder builder = new StringBuilder();

            if (idSet != null) {
        builder.append(ImapUtility.join(",", idSet));
            }
            if (idGroups != null) {
                if (idSet != null) {
        if (!idSet.isEmpty() && !idGroups.isEmpty()) {
            builder.append(",");
        }
        builder.append(ImapUtility.join(",", idGroups));
            }

        builder.append(" ");
        return builder.toString();
    }
        return "";
    }

    public SelectedStateResponse execute(ImapConnection connection, ImapFolder folder) throws MessagingException {
        return null;
@@ -63,17 +63,19 @@ abstract class FolderSelectedStateCommand {

    List<List<ImapResponse>> executeInternal(ImapConnection connection, ImapFolder folder)
            throws IOException, MessagingException {
        List<FolderSelectedStateCommand> commands;
        ImapCommandSplitter.optimizeGroupings(this);
        List<String> commands;
        String commandString = createCommandString();

        if (commandString.length() > getCommandLengthLimit(connection)) {
            commands = ImapCommandSplitter.splitCommand(this, getCommandLengthLimit(connection));
        } else {
            commands = Collections.singletonList(this);
            commands = Collections.singletonList(commandString);
        }

        List<List<ImapResponse>> responses = new ArrayList<>();
        for (FolderSelectedStateCommand command : commands) {
            responses.add(folder.executeSimpleCommand(command.createCommandString()));
        for (String command : commands) {
            responses.add(folder.executeSimpleCommand(command));
        }
        return responses;
    }
@@ -83,17 +85,13 @@ abstract class FolderSelectedStateCommand {
    }

    void setIdSet(Set<Long> idSet) {
        this.idSet = idSet;
        this.idSet = new TreeSet<>(idSet);
    }

    List<ContiguousIdGroup> getIdGroups() {
        return idGroups;
    }

    void setIdGroups(List<ContiguousIdGroup> idGroups) {
        this.idGroups = idGroups;
    }

    private int getCommandLengthLimit(ImapConnection connection) throws IOException, MessagingException  {
        boolean condstoreSupported = connection.isCondstoreCapable();
        if (condstoreSupported) {
@@ -103,70 +101,35 @@ abstract class FolderSelectedStateCommand {
        }
    }

    static abstract class Builder<C extends FolderSelectedStateCommand, B extends Builder<C, B>> {
        C command;
        B builder;

        public Builder() {
            command = createCommand();
            builder = createBuilder();
        }

        abstract C createCommand();

        abstract B createBuilder();

        public B idSet(Collection<Long> idSet) {
            if (idSet != null) {
                command.idSet = new TreeSet<>(idSet);
            } else {
                command.idSet = null;
            }
            return builder;
        }

        B addId(Long id) {
            if (command.idSet == null) {
                command.idSet = new TreeSet<>();
            }
            command.idSet.add(id);
            return builder;
    void useAllIds(boolean useAllIds) {
        if (useAllIds) {
            idSet = Collections.emptySet();
            idGroups = Collections.singletonList(new ContiguousIdGroup(ContiguousIdGroup.FIRST_ID,
                    ContiguousIdGroup.LAST_ID));
        }

        B idRanges(List<ContiguousIdGroup> idGroups) {
            command.idGroups = idGroups;
            return builder;
    }

        public B addIdGroup(Long start, Long end) {
            if (command.idGroups == null) {
                command.idGroups = new ArrayList<>();
    void useOnlyHighestId(boolean useOnlyHighestId) {
        if (useOnlyHighestId) {
            idSet = Collections.emptySet();
            idGroups = Collections.singletonList(new ContiguousIdGroup(ContiguousIdGroup.LAST_ID,
                    ContiguousIdGroup.LAST_ID));
        }
            command.idGroups.add(new ContiguousIdGroup(start, end));
            return builder;
    }

        public B allIds(boolean allIds) {
            if (allIds) {
                command.idSet = null;
                command.idGroups = Collections.singletonList(new ContiguousIdGroup(ContiguousIdGroup.FIRST_ID,
                        ContiguousIdGroup.LAST_ID));
            }
            return builder;
    void addId(Long id) {
        idSet.add(id);
    }

        public B onlyHighestId(boolean onlyHighestId) {
            if (onlyHighestId) {
                command.idSet = null;
                command.idGroups = Collections.singletonList(new ContiguousIdGroup(ContiguousIdGroup.LAST_ID,
                        ContiguousIdGroup.LAST_ID));
    void addIdGroup(Long start, Long end) {
        if (start != null && end != null) {
            idGroups.add(new ContiguousIdGroup(start, end));
        }
            return builder;
    }

        public C build() {
            return command;
        }
    void clearIds() {
        idSet.clear();
        idGroups.clear();
    }

    static class ContiguousIdGroup {
+32 −42
Original line number Diff line number Diff line
@@ -6,44 +6,39 @@ import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import com.fsck.k9.mail.store.imap.selectedstate.command.FolderSelectedStateCommand.Builder;
import com.fsck.k9.mail.store.imap.selectedstate.command.FolderSelectedStateCommand.ContiguousIdGroup;


class ImapCommandSplitter {

    static List<FolderSelectedStateCommand> splitCommand(FolderSelectedStateCommand command, int lengthLimit) {
        if (command.getIdSet() == null && command.getIdGroups() == null) {
    static List<String> splitCommand(FolderSelectedStateCommand command, int lengthLimit) {
        if (command.getIdSet().isEmpty() && command.getIdGroups().isEmpty()) {
            throw new IllegalStateException("The constructed command is too long but does not contain ids");
        }

        List<FolderSelectedStateCommand> commands = new ArrayList<>();
        command = optimizeGroupings(command);
        Set<Long> idSet = command.getIdSet();
        List<ContiguousIdGroup> idGroups = command.getIdGroups();
        List<String> commands = new ArrayList<>();
        Set<Long> idSet = new TreeSet<>(command.getIdSet());
        List<ContiguousIdGroup> idGroups = new ArrayList<>(command.getIdGroups());

        while ((idSet != null && !idSet.isEmpty()) || (idGroups != null && !idGroups.isEmpty())) {
            Builder builder = command.newBuilder()
                    .idSet(null)
                    .idRanges(null);
        while (!idSet.isEmpty() || !idGroups.isEmpty()) {
            command.clearIds();

            int length = builder.build().createCommandString().length();
            int length = command.createCommandString().length();
            while (length < lengthLimit) {
                if (idSet != null && !idSet.isEmpty()) {
                if (!idSet.isEmpty()) {
                    Long first = idSet.iterator().next();
                    length += (String.valueOf(first).length() + 1);
                    if (length < lengthLimit) {
                        builder.addId(first);
                        command.addId(first);
                        idSet.remove(first);
                    } else {
                        break;
                    }

                } else if (idGroups != null && !idGroups.isEmpty()) {
                    ContiguousIdGroup first = command.idGroups.iterator().next();
                } else if (!idGroups.isEmpty()) {
                    ContiguousIdGroup first = idGroups.iterator().next();
                    length += (first.toString().length() + 1);
                    if (length < lengthLimit) {
                        builder.addIdGroup(first.getStart(), first.getEnd());
                        command.addIdGroup(first.getStart(), first.getEnd());
                        idGroups.remove(first);
                    } else {
                        break;
@@ -52,54 +47,49 @@ class ImapCommandSplitter {
                    break;
                }
            }
            commands.add(builder.build());
            commands.add(command.createCommandString());
        }
        return commands;
    }

    static FolderSelectedStateCommand optimizeGroupings(FolderSelectedStateCommand command) {
    static void optimizeGroupings(FolderSelectedStateCommand command) {
        Set<Long> idSet = command.getIdSet();
        List<ContiguousIdGroup> idGroups = command.getIdGroups();
        if (idGroups != null && idGroups.get(0).getEnd() == ContiguousIdGroup.LAST_ID) {
            return command;

        if (idSet.isEmpty() && idGroups.isEmpty()) {
            return;
        }
        if (idGroups.size() == 1 && idGroups.get(0).getEnd() == ContiguousIdGroup.LAST_ID) {
            return;
        }

        TreeSet<Long> fullIdSet = new TreeSet<>();
        if (idSet != null) {
            fullIdSet.addAll(command.idSet);
        }
        if (idGroups != null) {
        fullIdSet.addAll(idSet);
        for (ContiguousIdGroup idGroup : idGroups) {
            for (long i = idGroup.getStart();i <= idGroup.getEnd();i++) {
                fullIdSet.add(i);
            }
        }
        }

        Builder builder = command.newBuilder()
                .idSet(null)
                .idRanges(null);
        command.clearIds();
        List<Long> idList = new ArrayList<>(fullIdSet);
        int start = 0;

        for (int i = 1; i < idList.size();i++) {
            if (idList.get(i - 1) + 1 != idList.get(i)) {
                checkAndAddIds(builder, idList, start, i - 1);
                checkAndAddIds(command, idList, start, i - 1);
                start = i;
            }
        }
        checkAndAddIds(builder, idList, start, idList.size() - 1);
        FolderSelectedStateCommand tempCommand = builder.build();
        command.setIdSet(tempCommand.getIdSet());
        command.setIdGroups(tempCommand.getIdGroups());
        return command;
        checkAndAddIds(command, idList, start, idList.size() - 1);
    }

    private static void checkAndAddIds(Builder builder, List<Long> idList, int start, int end) {
    private static void checkAndAddIds(FolderSelectedStateCommand command, List<Long> idList,
                                       int start, int end) {
        if (start == end) {
            builder.addId(idList.get(start));
            command.addId(idList.get(start));
        } else {
            builder.addIdGroup(idList.get(start), idList.get(end));
            command.addIdGroup(idList.get(start), idList.get(end));
        }
    }
}
+6 −22
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ package com.fsck.k9.mail.store.imap.selectedstate.command;

import java.io.IOException;
import java.util.List;
import java.util.Set;

import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.store.imap.Commands;
@@ -15,7 +16,9 @@ import com.fsck.k9.mail.store.imap.selectedstate.response.UidCopyResponse;
public class UidCopyCommand extends FolderSelectedStateCommand {
    private String destinationFolderName;

    private UidCopyCommand() {
    private UidCopyCommand(Set<Long> uids, String destinationFolderName) {
        super(uids);
        this.destinationFolderName = destinationFolderName;
    }

    @Override
@@ -33,27 +36,8 @@ public class UidCopyCommand extends FolderSelectedStateCommand {
        }
    }

    @Override
    Builder newBuilder() {
        return new Builder().destinationFolderName(destinationFolderName);
    }

    public static class Builder extends FolderSelectedStateCommand.Builder<UidCopyCommand, Builder> {

        public Builder destinationFolderName(String destinationFolderName) {
            command.destinationFolderName = destinationFolderName;
            return builder;
        }

        @Override
        UidCopyCommand createCommand() {
            return new UidCopyCommand();
        }

        @Override
        Builder createBuilder() {
            return this;
        }
    public static UidCopyCommand createWithUids(Set<Long> uids, String destinationFolderName) {
        return new UidCopyCommand(uids, destinationFolderName);
    }
}

Loading