diff --git a/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java b/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java index 0ed979cc3019..3ad8e96e11b1 100644 --- a/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java +++ b/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java @@ -16,6 +16,8 @@ package com.intellij.util.download.impl; +import com.google.common.util.concurrent.AtomicDouble; +import com.intellij.concurrency.JobLauncher; import com.intellij.ide.IdeBundle; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.Result; @@ -27,6 +29,7 @@ import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.util.AbstractProgressIndicatorBase; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; @@ -34,6 +37,8 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; +import com.intellij.util.Processor; +import com.intellij.util.containers.hash.LinkedHashMap; import com.intellij.util.download.DownloadableFileDescription; import com.intellij.util.download.FileDownloader; import com.intellij.util.io.UrlConnectionUtil; @@ -49,6 +54,7 @@ import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; /** * @author nik @@ -147,49 +153,60 @@ public class FileDownloaderImpl implements FileDownloader { @NotNull @Override - public List> download(@NotNull File targetDir) throws IOException { + public List> download(@NotNull final File targetDir) throws IOException { final List> downloadedFiles = new ArrayList>(); final List> existingFiles = new ArrayList>(); - ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - if (indicator == null) { - indicator = new EmptyProgressIndicator(); - } + final ProgressIndicator parentIndicator = getProgressIndicator(); try { - for (int i = 0; i < myFileDescriptions.size(); i++) { - DownloadableFileDescription description = myFileDescriptions.get(i); - indicator.checkCanceled(); - indicator.setText(IdeBundle.message("progress.downloading.0.of.1.file.text", i + 1, myFileDescriptions.size())); + final AtomicReference ioException = new AtomicReference(); + final ConcurrentTasksProgressManager progressManager = new ConcurrentTasksProgressManager(parentIndicator, myFileDescriptions.size()); + parentIndicator.setText(IdeBundle.message("progress.downloading.0.files.text", myFileDescriptions.size())); + boolean finished = JobLauncher.getInstance().invokeConcurrentlyUnderProgress(myFileDescriptions, parentIndicator, false, false, new Processor() { + @Override + public boolean process(DownloadableFileDescription description) { + SubTaskProgressIndicator indicator = progressManager.createSubTaskIndicator(); + indicator.checkCanceled(); - final File existing = new File(targetDir, description.getDefaultFileName()); - final String url = description.getDownloadUrl(); - if (url.startsWith(LIB_SCHEMA)) { - indicator.setText2(IdeBundle.message("progress.locate.file.text", description.getPresentableFileName())); - final String path = FileUtil.toSystemDependentName(StringUtil.trimStart(url, LIB_SCHEMA)); - final File file = PathManager.findFileInLibDirectory(path); - existingFiles.add(Pair.create(file, description)); - } - else if (url.startsWith(LocalFileSystem.PROTOCOL_PREFIX)) { - String path = FileUtil.toSystemDependentName(StringUtil.trimStart(url, LocalFileSystem.PROTOCOL_PREFIX)); - File file = new File(path); - if (file.exists()) { + final File existing = new File(targetDir, description.getDefaultFileName()); + final String url = description.getDownloadUrl(); + if (url.startsWith(LIB_SCHEMA)) { + final String path = FileUtil.toSystemDependentName(StringUtil.trimStart(url, LIB_SCHEMA)); + final File file = PathManager.findFileInLibDirectory(path); existingFiles.add(Pair.create(file, description)); } - } - else { - File downloaded; - try { - downloaded = downloadFile(description, existing, indicator); - } - catch (IOException e) { - throw new IOException(IdeBundle.message("error.file.download.failed", description.getDownloadUrl(), e.getMessage()), e); - } - if (FileUtil.filesEqual(downloaded, existing)) { - existingFiles.add(Pair.create(existing, description)); + else if (url.startsWith(LocalFileSystem.PROTOCOL_PREFIX)) { + String path = FileUtil.toSystemDependentName(StringUtil.trimStart(url, LocalFileSystem.PROTOCOL_PREFIX)); + File file = new File(path); + if (file.exists()) { + existingFiles.add(Pair.create(file, description)); + } } else { - downloadedFiles.add(Pair.create(downloaded, description)); + File downloaded; + try { + downloaded = downloadFile(description, existing, indicator); + } + catch (IOException e) { + ioException.compareAndSet(null, new IOException(IdeBundle.message("error.file.download.failed", description.getDownloadUrl(), + e.getMessage()), e)); + return false; + } + if (FileUtil.filesEqual(downloaded, existing)) { + existingFiles.add(Pair.create(existing, description)); + } + else { + downloadedFiles.add(Pair.create(downloaded, description)); + } } + indicator.finished(); + return true; } + }); + if (!finished) { + if (ioException.get() != null) { + throw ioException.get(); + } + throw new ProcessCanceledException(); } List> localFiles = new ArrayList>(); localFiles.addAll(moveToDir(downloadedFiles, targetDir)); @@ -206,6 +223,12 @@ public class FileDownloaderImpl implements FileDownloader { } } + @NotNull + private static ProgressIndicator getProgressIndicator() { + ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + return indicator == null ? new EmptyProgressIndicator() : indicator; + } + @Nullable private static VirtualFile chooseDirectoryForFiles(Project project, JComponent parentComponent) { final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); @@ -330,4 +353,82 @@ public class FileDownloaderImpl implements FileDownloader { public List> downloadAndReturnWithDescriptions() { return downloadWithProgress(myDirectoryForDownloadedFilesPath, myProject, myParentComponent); } + + private static class ConcurrentTasksProgressManager { + private final ProgressIndicator myParent; + private final int myTasksCount; + private final AtomicDouble myTotalFraction; + private LinkedHashMap myText2Stack = new LinkedHashMap(); + + private ConcurrentTasksProgressManager(ProgressIndicator parent, int tasksCount) { + myParent = parent; + myTasksCount = tasksCount; + myTotalFraction = new AtomicDouble(); + } + + public void updateFraction(double delta) { + myTotalFraction.addAndGet(delta / myTasksCount); + myParent.setFraction(myTotalFraction.get()); + } + + public SubTaskProgressIndicator createSubTaskIndicator() { + return new SubTaskProgressIndicator(this); + } + + public void setText2(@NotNull SubTaskProgressIndicator subTask, @Nullable String text) { + if (text != null) { + myText2Stack.put(subTask, text); + myParent.setText2(text); + } + else { + myText2Stack.remove(subTask); + String prev = myText2Stack.getLastAddedValue(); + if (prev != null) { + myParent.setText2(prev); + } + } + } + } + + private static class SubTaskProgressIndicator extends AbstractProgressIndicatorBase { + private final AtomicDouble myFraction; + private final ConcurrentTasksProgressManager myProgressManager; + + private SubTaskProgressIndicator(ConcurrentTasksProgressManager progressManager) { + myProgressManager = progressManager; + myFraction = new AtomicDouble(); + } + + @Override + public void setFraction(double newValue) { + double oldValue = myFraction.getAndSet(newValue); + myProgressManager.updateFraction(newValue - oldValue); + } + + @Override + public void setIndeterminate(boolean indeterminate) { + if (myProgressManager.myTasksCount > 1) return; + super.setIndeterminate(indeterminate); + } + + @Override + public void setText2(String text) { + myProgressManager.setText2(this, text); + } + + @Override + public double getFraction() { + return myFraction.get(); + } + + public void finished() { + setFraction(1); + myProgressManager.setText2(this, null); + } + + @Override + public boolean isCanceled() { + return super.isCanceled() || myProgressManager.myParent.isCanceled(); + } + } } diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties index 2f0f461797f9..e5852f28a28c 100644 --- a/platform/platform-resources-en/src/messages/IdeBundle.properties +++ b/platform/platform-resources-en/src/messages/IdeBundle.properties @@ -987,7 +987,7 @@ progress.download.0.title=Downloading {0} progress.download.file.text=Downloading ''{0}'' from ''{1}''... progress.connecting.to.download.file.text=Connecting to ''{0}''... progress.locate.file.text=Locating ''{0}''... -progress.downloading.0.of.1.file.text=Downloading {0} of {1} {1, choice, 1#file|2#files}... +progress.downloading.0.files.text=Downloading {0} {0, choice, 1#file|2#files}... dialog.directory.for.downloaded.files.title=Downloaded files will be copied to selected directory error.file.download.failed=Failed to download ''{0}'': {1}