package org.limewire.core.impl; import java.io.File; import java.util.List; import java.util.Set; import java.util.SortedMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.CopyOnWriteArrayList; import org.limewire.bittorrent.Torrent; import org.limewire.core.api.callback.GuiCallback; import org.limewire.core.api.callback.GuiCallbackService; import org.limewire.core.api.download.DownloadAction; import org.limewire.core.api.download.DownloadException; import org.limewire.core.impl.download.DownloadListener; import org.limewire.core.impl.download.DownloadListenerList; import org.limewire.core.impl.magnet.MagnetLinkImpl; import org.limewire.core.impl.monitor.IncomingSearchListener; import org.limewire.core.impl.monitor.IncomingSearchListenerList; import org.limewire.core.impl.search.QueryReplyListener; import org.limewire.core.impl.search.QueryReplyListenerList; import org.limewire.core.impl.upload.UploadListener; import org.limewire.core.impl.upload.UploadListenerList; import org.limewire.i18n.I18nMarker; import org.limewire.io.GUID; import org.limewire.io.IpPort; import org.limewire.service.ErrorService; import org.limewire.service.MessageService; import com.google.inject.Inject; import com.google.inject.Singleton; import com.limegroup.gnutella.ActivityCallback; import com.limegroup.gnutella.DownloadManager; import com.limegroup.gnutella.Downloader; import com.limegroup.gnutella.RemoteFileDesc; import com.limegroup.gnutella.Uploader; import com.limegroup.gnutella.browser.MagnetOptions; import com.limegroup.gnutella.messages.QueryReply; import com.limegroup.gnutella.messages.QueryRequest; /** * An implementation of the UI callback to handle notifications about * asynchronous backend events. */ @Singleton class GlueActivityCallback implements ActivityCallback, QueryReplyListenerList, DownloadListenerList, UploadListenerList, IncomingSearchListenerList, GuiCallbackService { private final SortedMap<byte[], List<QueryReplyListener>> queryReplyListeners; private final List<DownloadListener> downloadListeners = new CopyOnWriteArrayList<DownloadListener>(); private final List<UploadListener> uploadListeners = new CopyOnWriteArrayList<UploadListener>(); private final List<IncomingSearchListener> monitorListeners = new CopyOnWriteArrayList<IncomingSearchListener>(); private final DownloadManager downloadManager; private GuiCallback guiCallback = null; @Inject public GlueActivityCallback(DownloadManager downloadManager) { this.downloadManager = downloadManager; queryReplyListeners = new ConcurrentSkipListMap<byte[], List<QueryReplyListener>>( GUID.GUID_BYTE_COMPARATOR); } @Override public void addQueryReplyListener(byte[] guid, QueryReplyListener listener) { synchronized (queryReplyListeners) { List<QueryReplyListener> listeners = queryReplyListeners.get(guid); if (listeners == null) { listeners = new CopyOnWriteArrayList<QueryReplyListener>(); queryReplyListeners.put(guid, listeners); } listeners.add(listener); } } @Override public void removeQueryReplyListener(byte[] guid, QueryReplyListener listener) { synchronized (queryReplyListeners) { List<QueryReplyListener> listeners = queryReplyListeners.get(guid); if (listeners != null) { listeners.remove(listener); if (listeners.isEmpty()) { queryReplyListeners.remove(guid); } } } } @Override public void addDownloadListener(DownloadListener listener) { downloadListeners.add(listener); } @Override public void removeDownloadListener(DownloadListener listener) { downloadListeners.remove(listener); } @Override public void handleMagnets(MagnetOptions[] magnets) { if(guiCallback != null) { for(MagnetOptions magnetOption : magnets) { guiCallback.handleMagnet(new MagnetLinkImpl(magnetOption)); } } } @Override public void handleQueryResult(RemoteFileDesc rfd, QueryReply queryReply, Set<? extends IpPort> locs) { List<QueryReplyListener> listeners = queryReplyListeners.get(queryReply.getGUID()); if (listeners != null) { for (QueryReplyListener listener : listeners) { listener.handleQueryReply(rfd, queryReply, locs); } } } // TODO: address and port - are ignored @Override public void handleQuery(QueryRequest query, String address, int port) { for (IncomingSearchListener listener : monitorListeners) { listener.handleQueryString(query.getQuery()); } } public void handleSharedFileUpdate(File file) { //TODO PJV this looks like it is supposed to update the gui with the meta data changes about how many hits this file received in searches and how many times it has been uploaded. //Can we get rid of this now and use a property listener when we need this data again? could fireProperty events when incrementHitcount etc. are called } @Override public void handleTorrent(final File torrentFile) { if(torrentFile != null && torrentFile.exists() && torrentFile.length() > 0) { try { downloadManager.downloadTorrent(torrentFile, null, false); } catch (DownloadException e) { handleDownloadException(new DownloadAction() { @Override public void download(File saveDirectory, boolean overwrite) throws DownloadException { downloadManager.downloadTorrent(torrentFile, saveDirectory, overwrite); } @Override public void downloadCanceled(DownloadException ignored) { //nothing to do } },e,false); } } } @Override public void installationCorrupted() { MessageService.showError(I18nMarker.marktr("<html><b>Your LimeWire may have been corrupted by a virus or trojan!</b><br><br>Please visit <a href=\"http://www.limewire.com/corrupted\">www.limewire.com</a> and download the newest official version of LimeWire.</html>")); } @Override public boolean isQueryAlive(GUID guid) { return queryReplyListeners.containsKey(guid.bytes()); } @Override public void addUpload(Uploader u) { for (UploadListener listener : uploadListeners) { listener.uploadAdded(u); } } @Override public void removeUpload(Uploader u) { for (UploadListener listener : uploadListeners) { listener.uploadRemoved(u); } } @Override public void restoreApplication() { if(guiCallback != null) { guiCallback.restoreApplication(); } } @Override public String translate(String s) { if(guiCallback != null) { return guiCallback.translate(s); } return s; } @Override public void uploadsComplete() { for (UploadListener listener : uploadListeners) { listener.uploadsCompleted(); } } @Override public void addDownload(Downloader d) { for (DownloadListener listener : downloadListeners) { listener.downloadAdded(d); } } @Override public void downloadsComplete() { for (DownloadListener listener : downloadListeners) { listener.downloadsCompleted(); } } // TODO: if no prompt is ever intended then this should be renamed @Override public void promptAboutCorruptDownload(Downloader dloader) { //just kill the download if it is corrupt dloader.discardCorruptDownload(true); } @Override public void warnUser(String filename, String warning) { if(guiCallback != null) guiCallback.warnUser(filename, warning); } @Override public void removeDownload(Downloader d) { for (DownloadListener listener : downloadListeners) { listener.downloadRemoved(d); } } public void setGuiCallback(GuiCallback guiCallback) { this.guiCallback = guiCallback; } @Override public void handleDownloadException(DownloadAction downLoadAction, DownloadException e, boolean supportsNewSaveDir) { if(guiCallback != null) { guiCallback.handleDownloadException(downLoadAction, e, supportsNewSaveDir); } else { ErrorService.error(e, "Error handling DownloadException. GuiCallBack not yet initialized."); } } @Override public void addUploadListener(UploadListener listener) { uploadListeners.add(listener); } @Override public void removeUploadListener(UploadListener listener) { uploadListeners.remove(listener); } @Override public void addIncomingSearchListener(IncomingSearchListener listener) { monitorListeners.add(listener); } @Override public void removeIncomingSearchListener(IncomingSearchListener listener) { monitorListeners.remove(listener); } @Override public boolean promptTorrentUploadCancel(Torrent torrent) { boolean approve = true;//default to true if(guiCallback != null) { if (!torrent.isStarted()) { return false; } if(!torrent.isFinished()) { approve = guiCallback.promptUserQuestion(I18nMarker.marktr("If you stop this upload, the torrent download will stop. Are you sure you want to do this?")); } else if (torrent.getSeedRatio() < 1.0f) { approve = guiCallback.promptUserQuestion(I18nMarker.marktr("This upload is a torrent and it hasn\'t seeded enough. You should let it upload some more. Are you sure you want to stop it?")); } } return approve; } @Override public boolean promptTorrentFilePriorities(Torrent torrent) { if(guiCallback != null) { return guiCallback.promptTorrentFilePriorities(torrent); } return true; } }