package org.jetbrains.plugins.groovy.ivy; import com.intellij.codeInsight.AttachSourcesProvider; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.LibraryOrderEntry; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.util.net.HttpConfigurable; import com.intellij.util.net.NetUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.util.Arrays; import java.util.List; /** * @author Sergey Evdokimov */ public abstract class AbstractAttachSourceProvider implements AttachSourcesProvider { private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.ivy.AbstractAttachSourceProvider"); @Nullable protected static VirtualFile getJarByPsiFile(PsiFile psiFile) { VirtualFile virtualFile = psiFile.getVirtualFile(); if (virtualFile == null) return null; VirtualFile jar = JarFileSystem.getInstance().getVirtualFileForJar(psiFile.getVirtualFile()); if (jar == null || !jar.getName().endsWith(".jar")) return null; return jar; } @Nullable protected static Library getLibraryFromOrderEntriesList(List orderEntries) { if (orderEntries.isEmpty()) return null; Library library = orderEntries.get(0).getLibrary(); if (library == null) return null; for (int i = 1; i < orderEntries.size(); i++) { if (!library.equals(orderEntries.get(i).getLibrary())) { return null; } } return library; } protected void addSourceFile(@Nullable VirtualFile jarRoot, @NotNull Library library) { if (jarRoot != null) { if (!Arrays.asList(library.getFiles(OrderRootType.SOURCES)).contains(jarRoot)) { Library.ModifiableModel model = library.getModifiableModel(); model.addRoot(jarRoot, OrderRootType.SOURCES); model.commit(); } } } protected class AttachExistingSourceAction implements AttachSourcesAction { private final String myName; private final VirtualFile mySrcFile; private final Library myLibrary; public AttachExistingSourceAction(VirtualFile srcFile, Library library, String actionName) { mySrcFile = srcFile; myLibrary = library; myName = actionName; } @Override public String getName() { return myName; } @Override public String getBusyText() { return getName(); } @Override public ActionCallback perform(List orderEntriesContainingFile) { ApplicationManager.getApplication().assertIsDispatchThread(); ActionCallback callback = new ActionCallback(); callback.setDone(); if (!mySrcFile.isValid()) return callback; if (myLibrary != getLibraryFromOrderEntriesList(orderEntriesContainingFile)) return callback; AccessToken accessToken = WriteAction.start(); try { addSourceFile(mySrcFile, myLibrary); } finally { accessToken.finish(); } return callback; } } protected abstract class DownloadSourcesAction implements AttachSourcesAction { protected final Project myProject; protected final String myUrl; protected final String myMessageGroupId; public DownloadSourcesAction(Project project, String messageGroupId, String url) { myProject = project; myUrl = url; myMessageGroupId = messageGroupId; } @Override public String getName() { return "Download Sources"; } @Override public String getBusyText() { return "Downloading Sources..."; } protected abstract void storeFile(byte[] content); @Override public ActionCallback perform(List orderEntriesContainingFile) { final ActionCallback callback = new ActionCallback(); Task task = new Task.Backgroundable(myProject, "Downloading sources...", true) { @Override public void run(@NotNull ProgressIndicator indicator) { final ByteArrayOutputStream out; try { LOG.info("Downloading sources jar: " + myUrl); indicator.checkCanceled(); HttpURLConnection urlConnection = HttpConfigurable.getInstance().openHttpConnection(myUrl); int contentLength = urlConnection.getContentLength(); out = new ByteArrayOutputStream(contentLength > 0 ? contentLength : 100 * 1024); InputStream in = urlConnection.getInputStream(); try { NetUtils.copyStreamContent(indicator, in, out, contentLength); } finally { in.close(); } } catch (IOException e) { LOG.warn(e); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { new Notification(myMessageGroupId, "Downloading failed", "Failed to download sources: " + myUrl, NotificationType.ERROR) .notify(getProject()); callback.setDone(); } }); return; } ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { AccessToken accessToken = WriteAction.start(); try { storeFile(out.toByteArray()); } finally { accessToken.finish(); callback.setDone(); } } }); } @Override public void onCancel() { callback.setRejected(); } }; task.queue(); return callback; } } }