Store downloads differently.
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 18 Apr 2013 04:13:30 +0000 (06:13 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 18 Apr 2013 04:13:30 +0000 (06:13 +0200)
src/main/java/net/pterodactylus/xdcc/core/Core.java

index 33a22f8..607246c 100644 (file)
@@ -17,6 +17,8 @@
 
 package net.pterodactylus.xdcc.core;
 
+import static net.pterodactylus.xdcc.data.Download.FILTER_RUNNING;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -66,11 +68,15 @@ import net.pterodactylus.xdcc.data.Pack;
 import net.pterodactylus.xdcc.data.Server;
 
 import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
 import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Table;
 import com.google.common.eventbus.EventBus;
@@ -114,7 +120,7 @@ public class Core extends AbstractExecutionThreadService {
        private final Table<Network, String, Bot> networkBots = HashBasedTable.create();
 
        /** The current downloads. */
-       private final Map<String, Download> downloads = Maps.newHashMap();
+       private final Multimap<String, Download> downloads = HashMultimap.create();
 
        /** The current DCC receivers. */
        private final Collection<DccReceiver> dccReceivers = Lists.newArrayList();
@@ -556,18 +562,41 @@ public class Core extends AbstractExecutionThreadService {
         *              The DCC SEND event
         */
        @Subscribe
-       public void dccSendReceived(DccSendReceived dccSendReceived) {
-               Optional<Network> network = getNetwork(dccSendReceived.connection());
+       public void dccSendReceived(final DccSendReceived dccSendReceived) {
+               final Optional<Network> network = getNetwork(dccSendReceived.connection());
                if (!network.isPresent()) {
                        return;
                }
 
-               Download download = downloads.get(dccSendReceived.filename());
-               if (download == null) {
+               Collection<Download> packDownloads = downloads.get(dccSendReceived.filename());
+               if (packDownloads.isEmpty()) {
                        /* unknown download, ignore. */
                        return;
                }
 
+               /* check if it’s already downloading. */
+               Collection<Download> runningDownloads = FluentIterable.from(packDownloads).filter(FILTER_RUNNING).toSet();
+               if (!runningDownloads.isEmpty()) {
+                       eventBus.post(new GenericMessage(String.format("Ignoring offer for %s, it’s already being downloaded.", dccSendReceived.filename())));
+                       return;
+               }
+
+               /* locate the correct download. */
+               Collection<Download> requestedDownload = FluentIterable.from(packDownloads).filter(new Predicate<Download>() {
+
+                       @Override
+                       public boolean apply(Download download) {
+                               return download.bot().network().equals(network.get()) && download.bot().name().equalsIgnoreCase(dccSendReceived.source().nick().get());
+                       }
+               }).toSet();
+
+               /* we did not request this download. */
+               if (requestedDownload.isEmpty()) {
+                       return;
+               }
+
+               Download download = requestedDownload.iterator().next();
+
                /* check if the file already exists. */
                File outputFile = new File(temporaryDirectory, dccSendReceived.filename());
                if (outputFile.exists()) {
@@ -583,7 +612,7 @@ public class Core extends AbstractExecutionThreadService {
                                }
 
                                /* remove download. */
-                               downloads.remove(download.pack().name());
+                               downloads.removeAll(download.pack().name());
                                return;
                        }
 
@@ -612,25 +641,48 @@ public class Core extends AbstractExecutionThreadService {
        }
 
        @Subscribe
-       public void dccAcceptReceived(DccAcceptReceived dccAcceptReceived) {
-               Optional<Network> network = getNetwork(dccAcceptReceived.connection());
+       public void dccAcceptReceived(final DccAcceptReceived dccAcceptReceived) {
+               final Optional<Network> network = getNetwork(dccAcceptReceived.connection());
                if (!network.isPresent()) {
                        return;
                }
 
-               Download download = downloads.get(dccAcceptReceived.filename());
-               if (download == null) {
+               Collection<Download> packDownloads = downloads.get(dccAcceptReceived.filename());
+               if (packDownloads.isEmpty()) {
                        /* unknown download, ignore. */
                        return;
                }
 
+               /* check if it’s already downloading. */
+               Collection<Download> runningDownloads = FluentIterable.from(packDownloads).filter(FILTER_RUNNING).toSet();
+               if (!runningDownloads.isEmpty()) {
+                       eventBus.post(new GenericMessage(String.format("Ignoring offer for %s, it’s already being downloaded.", dccAcceptReceived.filename())));
+                       return;
+               }
+
+               /* locate the correct download. */
+               Collection<Download> requestedDownload = FluentIterable.from(packDownloads).filter(new Predicate<Download>() {
+
+                       @Override
+                       public boolean apply(Download download) {
+                               return download.bot().network().equals(network.get()) && download.bot().name().equalsIgnoreCase(dccAcceptReceived.source().nick().get());
+                       }
+               }).toSet();
+
+               /* we did not request this download. */
+               if (requestedDownload.isEmpty()) {
+                       return;
+               }
+
+               Download download = requestedDownload.iterator().next();
+
                try {
                        File outputFile = new File(temporaryDirectory, dccAcceptReceived.filename());
                        if (outputFile.length() != dccAcceptReceived.position()) {
                                eventBus.post(new GenericError(String.format("Download %s from %s does not start at the right position!")));
                                logger.log(Level.WARNING, String.format("Download %s from %s: have %d bytes but wants to resume from %d!", dccAcceptReceived.filename(), dccAcceptReceived.source(), outputFile.length(), dccAcceptReceived.position()));
 
-                               downloads.remove(download.pack().name());
+                               downloads.removeAll(download.pack().name());
                                return;
                        }
                        OutputStream outputStream = new FileOutputStream(outputFile, true);
@@ -652,11 +704,15 @@ public class Core extends AbstractExecutionThreadService {
         */
        @Subscribe
        public void dccDownloadFinished(DccDownloadFinished dccDownloadFinished) {
-               Download download = downloads.get(dccDownloadFinished.dccReceiver().filename());
-               if (download == null) {
-                       /* probably shouldn’t happen. */
+
+               /* locate the correct download. */
+               Collection<Download> requestedDownload = FluentIterable.from(downloads.get(dccDownloadFinished.dccReceiver().filename())).filter(FILTER_RUNNING).toSet();
+               if (requestedDownload.isEmpty()) {
+                       /* this seems wrong. */
+                       logger.warning("Download finished but could not be located.");
                        return;
                }
+               Download download = requestedDownload.iterator().next();
 
                try {
                        download.outputStream().close();
@@ -664,7 +720,7 @@ public class Core extends AbstractExecutionThreadService {
                        file.renameTo(new File(finalDirectory, download.pack().name()));
                        eventBus.post(new DownloadFinished(download));
                        dccReceivers.remove(dccDownloadFinished.dccReceiver());
-                       downloads.remove(download.pack().name());
+                       downloads.removeAll(download.pack().name());
                } catch (IOException ioe1) {
                        /* TODO - handle all the errors. */
                        logger.log(Level.WARNING, String.format("Could not move file %s to directory %s.", download.filename(), finalDirectory), ioe1);
@@ -679,17 +735,21 @@ public class Core extends AbstractExecutionThreadService {
         */
        @Subscribe
        public void dccDownloadFailed(DccDownloadFailed dccDownloadFailed) {
-               Download download = downloads.get(dccDownloadFailed.dccReceiver().filename());
-               if (download == null) {
-                       /* probably shouldn’t happen. */
+
+               /* locate the correct download. */
+               Collection<Download> requestedDownload = FluentIterable.from(downloads.get(dccDownloadFailed.dccReceiver().filename())).filter(FILTER_RUNNING).toSet();
+               if (requestedDownload.isEmpty()) {
+                       /* this seems wrong. */
+                       logger.warning("Download finished but could not be located.");
                        return;
                }
+               Download download = requestedDownload.iterator().next();
 
                try {
                        Closeables.close(download.outputStream(), true);
                        eventBus.post(new DownloadFailed(download));
                        dccReceivers.remove(dccDownloadFailed.dccReceiver());
-                       downloads.remove(download.pack().name());
+                       downloads.removeAll(download.pack().name());
                } catch (IOException ioe1) {
                        /* swallow silently. */
                }