UnknownSdkTracker - include missing SDKs that we cannot fix, add tests

GitOrigin-RevId: 0737b750ccdb1ab1e48b565d079b794763cffdbd
This commit is contained in:
Eugene Petrenko
2019-12-18 10:32:28 +00:00
committed by intellij-monorepo-bot
parent 1e7b6a860b
commit fcbf5a8013
4 changed files with 307 additions and 197 deletions
@@ -1,28 +1,21 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInsight.daemon.impl;
import com.intellij.codeInsight.intention.IntentionActionWithOptions;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.UnknownSdkEditorNotification;
import com.intellij.openapi.projectRoots.impl.UnknownSdkEditorNotification.SdkFixInfo;
import com.intellij.openapi.projectRoots.impl.UnknownSdkTracker;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
import com.intellij.ui.EditorNotificationPanel;
import com.intellij.ui.EditorNotificationsImpl;
import org.assertj.core.api.AssertionsForClassTypes;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Assert;
import java.util.List;
@@ -32,24 +25,92 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Pavel.Dolgov
*/
public class SdkSetupNotificationTest extends JavaCodeInsightFixtureTestCase {
public void testProjectSdk() {
final EditorNotificationPanel panel = configureBySdkAndText(IdeaTestUtil.getMockJdk18(), false, "Sample.java", "class Sample {}");
assertThat(panel).isNull();
Sdk sdk = IdeaTestUtil.getMockJdk18();
setProjectSdk(sdk);
ModuleRootModificationUtil.setSdkInherited(getModule());
final List<SdkFixInfo> fixes = detectMissingSdks();
assertThat(fixes)
.withFailMessage(String.valueOf(fixes))
.isEmpty();
}
public void testMissingProjectJdk() {
WriteAction.run(() -> {
ProjectRootManager.getInstance(getProject()).setProjectSdkName("missingSDK", JavaSdk.getInstance().getName());
});
final List<SdkFixInfo> fixes = detectMissingSdks();
assertThat(fixes)
.withFailMessage(String.valueOf(fixes))
.hasSize(1)
.first()
.returns("missingSDK", SdkFixInfo::getSdkName);
}
public void testMissingModuleJdk() {
WriteAction.run(() -> {
ModifiableRootModel model = ModuleRootManager.getInstance(getModule()).getModifiableModel();
model.setInvalidSdk("missingSDK", JavaSdk.getInstance().getName());
model.commit();
});
final List<SdkFixInfo> fixes = detectMissingSdks();
assertThat(fixes)
.withFailMessage(String.valueOf(fixes))
.hasSize(1)
.first()
.returns("missingSDK", SdkFixInfo::getSdkName);
}
public void testNoProjectSdk() {
final EditorNotificationPanel panel = configureBySdkAndText(null, false, "Sample.java", "class Sample {}");
assertSdkSetupPanelShown(panel, "Setup SDK");
setProjectSdk(null);
ModuleRootModificationUtil.setSdkInherited(getModule());
final List<SdkFixInfo> fixes = detectMissingSdks();
assertThat(fixes)
.withFailMessage(String.valueOf(fixes))
.isNotEmpty();
/*
final IntentionActionWithOptions action = fixes.getIntentionAction();
assertThat(action).isNotNull();
final String text = action.getText();
assertThat(text).isNotNull();
if (!text.startsWith("Setup SDK")) {
final int length = Math.min(text.length(), "Setup SDK".length());
assertThat(text.substring(0, length)).isEqualTo("Setup SDK");
}*/
}
public void testModuleSdk() {
final EditorNotificationPanel panel = configureBySdkAndText(IdeaTestUtil.getMockJdk18(), true, "Sample.java", "class Sample {}");
assertThat(panel).isNull();
Sdk sdk = IdeaTestUtil.getMockJdk18();
ModuleRootModificationUtil.setModuleSdk(getModule(), sdk);
final List<SdkFixInfo> fixes = detectMissingSdks();
assertThat(fixes)
.withFailMessage(String.valueOf(fixes))
.isEmpty();
}
public void testNoModuleSdk() {
final EditorNotificationPanel panel = configureBySdkAndText(null, true, "Sample.java", "class Sample {}");
assertSdkSetupPanelShown(panel, "Setup SDK");
ModuleRootModificationUtil.setModuleSdk(getModule(), null);
final List<SdkFixInfo> fixes = detectMissingSdks();
assertThat(fixes)
.withFailMessage(String.valueOf(fixes))
.isNotEmpty();
/*final IntentionActionWithOptions action = panel.getIntentionAction();
assertThat(action).isNotNull();
final String text = action.getText();
assertThat(text).isNotNull();
if (!text.startsWith("Setup SDK")) {
final int length = Math.min(text.length(), "Setup SDK".length());
assertThat(text.substring(0, length)).isEqualTo("Setup SDK");
}*/
}
@Override
@@ -59,61 +120,25 @@ public class SdkSetupNotificationTest extends JavaCodeInsightFixtureTestCase {
setProjectSdk(IdeaTestUtil.getMockJdk17());
}
@NotNull
private List<SdkFixInfo> detectMissingSdks() {
UnknownSdkTracker.getInstance(getProject()).updateUnknownSdks();
@Nullable
@SuppressWarnings("SameParameterValue")
protected EditorNotificationPanel configureBySdkAndText(@Nullable Sdk sdk,
boolean isModuleSdk,
@NotNull String name,
@NotNull String text) {
if (isModuleSdk) {
ModuleRootModificationUtil.setModuleSdk(getModule(), sdk);
}
else {
setProjectSdk(sdk);
ModuleRootModificationUtil.setSdkInherited(getModule());
}
final PsiFile psiFile = myFixture.configureByText(name, text);
FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(getProject());
VirtualFile virtualFile = psiFile.getVirtualFile();
final FileEditor[] editors = fileEditorManager.openFile(virtualFile, true);
Disposer.register(myFixture.getTestRootDisposable(), new Disposable() {
@Override
public void dispose() {
fileEditorManager.closeFile(virtualFile);
}
});
AssertionsForClassTypes.assertThat(editors).hasSize(1);
EditorNotificationsImpl.completeAsyncTasks();
List<? extends EditorNotificationPanel> data = editors[0].getUserData(UnknownSdkEditorNotification.NOTIFICATIONS);
if (data == null) return null;
Assert.assertEquals("Only one notification was expected, but were " + data, 1, data.size());
return data.iterator().next();
return UnknownSdkEditorNotification.getInstance(getProject()).getNotifications();
}
private void setProjectSdk(@Nullable Sdk sdk) {
if (sdk != null) {
final Sdk foundJdk = ReadAction.compute(() -> ProjectJdkTable.getInstance().findJdk(sdk.getName()));
if (foundJdk == null) {
WriteAction.run(() -> ProjectJdkTable.getInstance().addJdk(sdk, myFixture.getProjectDisposable()));
}
}
WriteAction.run(() -> ProjectRootManager.getInstance(getProject()).setProjectSdk(sdk));
}
WriteAction.run(() -> {
ProjectJdkTable jdkTable = ProjectJdkTable.getInstance();
@SuppressWarnings("SameParameterValue")
private static void assertSdkSetupPanelShown(@Nullable EditorNotificationPanel panel,
@NotNull String expectedMessagePrefix) {
AssertionsForClassTypes.assertThat(panel).isNotNull();
final IntentionActionWithOptions action = panel.getIntentionAction();
AssertionsForClassTypes.assertThat(action).isNotNull();
final String text = action.getText();
AssertionsForClassTypes.assertThat(text).isNotNull();
if (!text.startsWith(expectedMessagePrefix)) {
final int length = Math.min(text.length(), expectedMessagePrefix.length());
AssertionsForClassTypes.assertThat(text.substring(0, length)).isEqualTo(expectedMessagePrefix);
}
if (sdk != null) {
final Sdk foundJdk = jdkTable.findJdk(sdk.getName());
if (foundJdk == null) {
jdkTable.addJdk(sdk, myFixture.getProjectDisposable());
}
}
ProjectRootManager.getInstance(getProject()).setProjectSdk(sdk);
});
}
}
@@ -33,10 +33,11 @@ public class UnknownSdkBalloonNotification {
}
public void notifyFixedSdks(@NotNull Map<? extends UnknownSdk, LocalSdkFix> localFixes) {
if (localFixes.isEmpty()) return;
final String title;
final String change;
final StringBuilder message = new StringBuilder();
if (localFixes.isEmpty()) return;
Set<String> usages = new TreeSet<>();
for (Map.Entry<? extends UnknownSdk, LocalSdkFix> entry : localFixes.entrySet()) {
@@ -1,24 +1,27 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.projectRoots.impl;
import com.google.common.collect.ImmutableList;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.SdkType;
import com.intellij.openapi.projectRoots.impl.UnknownSdkResolver.DownloadSdkFix;
import com.intellij.openapi.projectRoots.impl.UnknownSdkResolver.UnknownSdk;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.EditorNotificationPanel;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.Map;
public class UnknownSdkEditorNotification implements Disposable {
public static final Key<List<MissingSdkNotificationPanel>> NOTIFICATIONS = Key.create("notifications added to the editor");
@@ -31,7 +34,7 @@ public class UnknownSdkEditorNotification implements Disposable {
private final Project myProject;
private final FileEditorManager myFileEditorManager;
private Map<UnknownSdk, DownloadSdkFix> myNotifications = new HashMap<>();
private final Map<String, SdkFixInfo> myNotifications = new TreeMap<>();
UnknownSdkEditorNotification(@NotNull Project project) {
myProject = project;
@@ -48,36 +51,40 @@ public class UnknownSdkEditorNotification implements Disposable {
});
}
private static class MissingSdkNotificationPanel extends EditorNotificationPanel {
private final UnknownSdk myInfo;
private MissingSdkNotificationPanel(@NotNull final UnknownSdk info) {
myInfo = info;
}
public boolean isSameProblemAs(@NotNull MissingSdkNotificationPanel panel) {
return this.myInfo.equals(panel.myInfo);
}
}
@NotNull
private MissingSdkNotificationPanel createPanelFor(@NotNull UnknownSdk info,
@NotNull DownloadSdkFix fix) {
String sdkName = info.getSdkType().getPresentableName();
private MissingSdkNotificationPanel createPanelFor(@NotNull SdkFixInfo info) {
Pair<UnknownSdk, DownloadSdkFix> fixInfo = info.getFixInfo();
String sdkName = info.getSdkName();
SdkType sdkType = fixInfo != null ? fixInfo.first.getSdkType() : null;
String sdkTypeName = sdkType != null ? sdkType.getPresentableName() : "SDK";
MissingSdkNotificationPanel panel = new MissingSdkNotificationPanel(info);
panel.setProject(myProject);
panel.setProviderKey(EDITOR_NOTIFICATIONS_KEY);
panel.setText(sdkName + " \"" + info.getSdkName() + "\" is missing");
panel.setText(sdkTypeName + " \"" + sdkName + "\" is missing");
panel.createActionLabel("Download " + sdkName + " (" + fix.getDownloadDescription() + ")", () -> {
removeNotification(panel);
UnknownSdkTracker.getInstance(myProject).applyDownloadableFix(info, fix);
});
if (fixInfo != null) {
UnknownSdk unknownSdk = fixInfo.first;
DownloadSdkFix fix = fixInfo.second;
panel.createActionLabel("Configure...", () -> {
UnknownSdkTracker.getInstance(myProject).showSdkSelectionPopup(info, panel, () -> removeNotification(panel));
});
panel.createActionLabel("Download " + sdkTypeName + " (" + fix.getDownloadDescription() + ")", () -> {
removeNotification(panel);
UnknownSdkTracker.getInstance(myProject).applyDownloadableFix(unknownSdk, fix);
});
}
panel.createActionLabel("Configure...",
() -> {
//FileEditorManager#addTopComponent wraps the panel to implement borders, unwrapping
Container container = panel.getParent();
if (container == null) container = panel;
UnknownSdkTracker
.getInstance(myProject)
.showSdkSelectionPopup(sdkName, sdkType, container, () -> removeNotification(panel));
}
);
return panel;
}
@@ -85,8 +92,22 @@ public class UnknownSdkEditorNotification implements Disposable {
@Override
public void dispose() { }
public void showNotifications(@NotNull final Map<? extends UnknownSdk, DownloadSdkFix> files) {
myNotifications = new HashMap<>(files);
@NotNull
public List<SdkFixInfo> getNotifications() {
return ImmutableList.copyOf(myNotifications.values());
}
public void showNotifications(@NotNull List<String> unifiableSdkNames,
@NotNull Map<UnknownSdk, DownloadSdkFix> files) {
myNotifications.clear();
for (String name : unifiableSdkNames) {
myNotifications.put(name, SdkFixInfo.forBroken(name));
}
for (Map.Entry<UnknownSdk, DownloadSdkFix> e : files.entrySet()) {
myNotifications.put(e.getKey().getSdkName(), SdkFixInfo.forFix(e));
}
for (FileEditor editor : myFileEditorManager.getAllEditors()) {
updateEditorNotifications(editor);
@@ -94,12 +115,14 @@ public class UnknownSdkEditorNotification implements Disposable {
}
private void removeNotification(@NotNull MissingSdkNotificationPanel expiredPanel) {
myNotifications.remove(expiredPanel.myInfo);
myNotifications.remove(expiredPanel.myInfo.getSdkName());
for (FileEditor editor : myFileEditorManager.getAllEditors()) {
List<MissingSdkNotificationPanel> notifications = editor.getUserData(NOTIFICATIONS);
if (notifications == null) continue;
for (MissingSdkNotificationPanel panel : new ArrayList<>(notifications)) {
if (panel.isSameProblemAs(expiredPanel)) {
if (panel.myInfo.equals(expiredPanel.myInfo)) {
myFileEditorManager.removeTopComponent(editor, panel);
notifications.remove(panel);
}
@@ -116,16 +139,83 @@ public class UnknownSdkEditorNotification implements Disposable {
myFileEditorManager.removeTopComponent(editor, component);
}
notifications.clear();
} else {
}
else {
notifications = new SmartList<>();
editor.putUserData(NOTIFICATIONS, notifications);
}
for (Map.Entry<UnknownSdk, DownloadSdkFix> e : myNotifications.entrySet()) {
MissingSdkNotificationPanel notification = createPanelFor(e.getKey(), e.getValue());
for (SdkFixInfo info : myNotifications.values()) {
MissingSdkNotificationPanel notification = createPanelFor(info);
notifications.add(notification);
myFileEditorManager.addTopComponent(editor, notification);
}
}
public static class MissingSdkNotificationPanel extends EditorNotificationPanel {
private final SdkFixInfo myInfo;
private MissingSdkNotificationPanel(@NotNull final SdkFixInfo info) {
myInfo = info;
}
@NotNull
public SdkFixInfo getInfo() {
return myInfo;
}
}
public static class SdkFixInfo {
@NotNull
static SdkFixInfo forBroken(@NotNull String sdkName) {
return new SdkFixInfo(sdkName);
}
@NotNull
static SdkFixInfo forFix(@NotNull Map.Entry<? extends UnknownSdk, DownloadSdkFix> e) {
UnknownSdk sdk = e.getKey();
DownloadSdkFix fix = e.getValue();
return new SdkFixInfo(sdk.getSdkName()) {
@NotNull
@Override
public Pair<UnknownSdk, DownloadSdkFix> getFixInfo() {
return Pair.create(sdk, fix);
}
};
}
private final String mySdkName;
private SdkFixInfo(@NotNull String sdkName) {
mySdkName = sdkName;
}
@NotNull
public String getSdkName() {
return mySdkName;
}
@Nullable
public Pair<UnknownSdk, DownloadSdkFix> getFixInfo() {
return null;
}
@Override
public final int hashCode() {
return Objects.hashCode(mySdkName);
}
@Override
public final boolean equals(Object obj) {
return obj instanceof SdkFixInfo && Objects.equals(((SdkFixInfo)obj).mySdkName, mySdkName);
}
@Override
public String toString() {
return "SdkFixInfo{" +
"mySdkName='" + mySdkName + '\'' +
'}';
}
}
}
@@ -1,7 +1,9 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.projectRoots.impl;
import com.google.common.collect.*;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.WriteAction;
@@ -25,7 +27,9 @@ import com.intellij.openapi.roots.ui.configuration.projectRoot.SdkDownloadTracke
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.TripleFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -59,61 +63,77 @@ public class UnknownSdkTracker {
.run(new Task.Backgroundable(myProject, "Resolving SDKs", false, ALWAYS_BACKGROUND) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
List<UnknownSdkLookup> lookups = new ArrayList<>();
UnknownSdkResolver.EP_NAME.forEachExtensionSafe(ext -> {
UnknownSdkLookup resolver = ext.createResolver(UnknownSdkTracker.this.myProject, indicator);
if (resolver != null) {
lookups.add(resolver);
}
});
if (lookups.isEmpty()) return;
updateUnknownSdksWithProgress(indicator, lookups);
updateUnknownSdksWithProgress(indicator);
}
});
}
private void updateUnknownSdksWithProgress(@NotNull ProgressIndicator indicator,
@NotNull List<UnknownSdkLookup> lookups) {
List<MissingSdkInfo> fixable = ReadAction.compute(() -> collectUnknownSdks());
if (fixable.isEmpty()) return;
private void updateUnknownSdksWithProgress(@NotNull ProgressIndicator indicator) {
List<String> totallyUnknownSdks = new ArrayList<>();
List<UnknownSdk> fixable = new ArrayList<>();
ReadAction.run(() -> collectAndGroupSdkUsages(totallyUnknownSdks, fixable));
Map<MissingSdkInfo, LocalSdkFix> localFixes = findLocalFixes(indicator, fixable, lookups);
Map<MissingSdkInfo, DownloadSdkFix> downloadFixes = findDownloadFixes(indicator,
ContainerUtil.filter(fixable, info -> !localFixes.containsKey(info)),
lookups);
if (localFixes.isEmpty() && downloadFixes.isEmpty()) return;
List<UnknownSdkLookup> lookups = collectSdkLookups(indicator);
Map<UnknownSdk, LocalSdkFix> localFixes = findFixesAndRemoveFixable(indicator, fixable, lookups, UnknownSdkLookup::proposeLocalFix);
Map<UnknownSdk, DownloadSdkFix> downloadFixes = findFixesAndRemoveFixable(indicator, fixable, lookups, UnknownSdkLookup::proposeDownload);
fixable.forEach(it -> totallyUnknownSdks.add(it.getSdkName()));
ApplicationManager.getApplication().invokeLater(() -> {
indicator.setText("Configuring SDKs...");
configureLocalSdks(localFixes);
UnknownSdkBalloonNotification.getInstance(myProject).notifyFixedSdks(localFixes);
UnknownSdkEditorNotification.getInstance(myProject).showNotifications(downloadFixes);
UnknownSdkEditorNotification.getInstance(myProject).showNotifications(totallyUnknownSdks, downloadFixes);
});
}
@NotNull
private List<MissingSdkInfo> collectUnknownSdks() {
Map<String, MissingSdkInfo> myInfos = new HashMap<>();
List<SdkUsage> usages = SdkUsagesCollector.getInstance(myProject).collectSdkUsages();
private List<UnknownSdkLookup> collectSdkLookups(@NotNull ProgressIndicator indicator) {
List<UnknownSdkLookup> lookups = new ArrayList<>();
UnknownSdkResolver.EP_NAME.forEachExtensionSafe(ext -> {
UnknownSdkLookup resolver = ext.createResolver(myProject, indicator);
if (resolver != null) {
lookups.add(resolver);
}
});
return lookups;
}
private void collectAndGroupSdkUsages(@NotNull List<String> totallyUnknownSdks,
@NotNull List<UnknownSdk> resolvableSdks) {
SetMultimap<String, String> sdkToTypes = MultimapBuilder
.treeKeys(String.CASE_INSENSITIVE_ORDER)
.hashSetValues()
.build();
ProjectJdkTable jdkTable = ProjectJdkTable.getInstance();
for (SdkUsage usage : usages) {
for (SdkUsage usage : SdkUsagesCollector.getInstance(myProject).collectSdkUsages()) {
String sdkName = usage.getSdkName();
//we do not track existing SDKs
if (jdkTable.findJdk(sdkName) != null) continue;
MissingSdkInfo info = myInfos.get(sdkName);
if (info == null) {
info = new MissingSdkInfo(sdkName);
myInfos.put(sdkName, info);
}
info.attachSdkType(usage.getSdkTypeName());
String typeName = usage.getSdkTypeName();
sdkToTypes.put(sdkName, typeName);
}
return ContainerUtil.filter(myInfos.values(), sdk -> sdk.mySdkType != null);
for (Map.Entry<String, Collection<String>> entry : sdkToTypes.asMap().entrySet()) {
Collection<String> sdkTypes = entry.getValue();
SdkType sdkType = null;
if (sdkTypes.size() == 1) {
sdkType = SdkType.findByName(ContainerUtil.getFirstItem(sdkTypes));
}
String sdkName = entry.getKey();
if (sdkType == null) {
totallyUnknownSdks.add(sdkName);
}
else {
resolvableSdks.add(new MissingSdkInfo(sdkName, sdkType));
}
}
}
public void applyDownloadableFix(@NotNull UnknownSdk info, @NotNull DownloadSdkFix fix) {
@@ -153,14 +173,15 @@ public class UnknownSdkTracker {
});
}
public void showSdkSelectionPopup(@NotNull UnknownSdk info,
@NotNull JComponent panel,
public void showSdkSelectionPopup(@NotNull String sdkName,
@Nullable SdkType sdkType,
@NotNull Container underneathRightOfComponent,
@NotNull Runnable onSelectionMade) {
ProjectSdksModel model = new ProjectSdksModel();
SdkListModelBuilder modelBuilder = new SdkListModelBuilder(
myProject,
model,
sdkType -> Objects.equals(sdkType, info.getSdkType()),
sdkType != null ? type -> Objects.equals(type, sdkType) : null,
null,
null);
@@ -175,16 +196,13 @@ public class UnknownSdkTracker {
@Override
public void sdkAdded(@NotNull Sdk sdk) {
//it is easier and safer than committing the ProjectSdksModel instance
registerNewSdkInJdkTable(info, sdk);
registerNewSdkInJdkTable(sdkName, sdk);
wasSdkCreated.set(true);
}
});
//FileEditorManager#addTopComponent wraps the panel to implement borders, unwrapping
Container container = panel.getParent();
if (container == null) container = panel;
popup.showUnderneathToTheRightOf(
container,
underneathRightOfComponent,
() -> {
if (wasSdkCreated.get()) {
onSelectionMade.run();
@@ -193,9 +211,9 @@ public class UnknownSdkTracker {
);
}
private static void configureLocalSdks(@NotNull Map<MissingSdkInfo, LocalSdkFix> localFixes) {
for (Map.Entry<MissingSdkInfo, LocalSdkFix> e : localFixes.entrySet()) {
MissingSdkInfo info = e.getKey();
private static void configureLocalSdks(@NotNull Map<UnknownSdk, LocalSdkFix> localFixes) {
for (Map.Entry<UnknownSdk, LocalSdkFix> e : localFixes.entrySet()) {
UnknownSdk info = e.getKey();
LocalSdkFix fix = e.getValue();
Sdk sdk = createSdkPrototype(info);
@@ -216,32 +234,20 @@ public class UnknownSdkTracker {
}
@NotNull
private static Map<MissingSdkInfo, LocalSdkFix> findLocalFixes(@NotNull ProgressIndicator indicator,
@NotNull List<MissingSdkInfo> infos,
@NotNull List<UnknownSdkLookup> lookups) {
Map<MissingSdkInfo, LocalSdkFix> result = new LinkedHashMap<>();
for (MissingSdkInfo info : infos) {
private static <R> Map<UnknownSdk, R> findFixesAndRemoveFixable(@NotNull ProgressIndicator indicator,
@NotNull List<UnknownSdk> infos,
@NotNull List<UnknownSdkLookup> lookups,
@NotNull TripleFunction<UnknownSdkLookup, UnknownSdk, ProgressIndicator, R> fun) {
Map<UnknownSdk, R> result = new LinkedHashMap<>();
for (Iterator<UnknownSdk> iterator = infos.iterator(); iterator.hasNext(); ) {
UnknownSdk info = iterator.next();
for (UnknownSdkLookup lookup : lookups) {
LocalSdkFix fix = lookup.proposeLocalFix(info, indicator);
if (fix == null) continue;
result.put(info, fix);
}
}
return result;
}
@NotNull
private static Map<MissingSdkInfo, DownloadSdkFix> findDownloadFixes(@NotNull ProgressIndicator indicator,
@NotNull List<MissingSdkInfo> infos,
@NotNull List<UnknownSdkLookup> lookups) {
Map<MissingSdkInfo, DownloadSdkFix> result = new LinkedHashMap<>();
for (MissingSdkInfo info : infos) {
for (UnknownSdkLookup lookup : lookups) {
DownloadSdkFix fix = lookup.proposeDownload(info, indicator);
if (fix == null) continue;
result.put(info, fix);
R fix = fun.fun(lookup, info, indicator);
if (fix != null) {
result.put(info, fix);
iterator.remove();
break;
}
}
}
return result;
@@ -253,16 +259,20 @@ public class UnknownSdkTracker {
}
private static void registerNewSdkInJdkTable(@NotNull UnknownSdk info, @NotNull Sdk sdk) {
registerNewSdkInJdkTable(info.getSdkName(), sdk);
}
private static void registerNewSdkInJdkTable(@NotNull String sdkName, @NotNull Sdk sdk) {
WriteAction.run(() -> {
ProjectJdkTable table = ProjectJdkTable.getInstance();
Sdk clash = table.findJdk(info.getSdkName());
Sdk clash = table.findJdk(sdkName);
if (clash != null) {
LOG.warn("SDK with name " + info.getSdkName() + " already exists: clash=" + clash + ", new=" + sdk);
LOG.warn("SDK with name " + sdkName + " already exists: clash=" + clash + ", new=" + sdk);
return;
}
SdkModificator mod = sdk.getSdkModificator();
mod.setName(info.getSdkName());
mod.setName(sdkName);
mod.commitChanges();
table.addJdk(sdk);
@@ -271,26 +281,11 @@ public class UnknownSdkTracker {
private static class MissingSdkInfo implements UnknownSdk {
@NotNull private final String mySdkName;
@NotNull private final Set<String> mySdkTypeNames = new HashSet<>(); // nullable keys are ok
@Nullable private SdkType mySdkType;
@NotNull private final SdkType mySdkType;
MissingSdkInfo(@NotNull String sdkName) {
private MissingSdkInfo(@NotNull String sdkName, @NotNull SdkType sdkType) {
mySdkName = sdkName;
}
void attachSdkType(@Nullable String name) {
if (!mySdkTypeNames.add(name)) return; //null is ok here
if (name == null || mySdkTypeNames.size() != 1) {
mySdkType = null;
}
else {
for (SdkType type : SdkType.getAllTypes()) {
if (type.getName().equals(name) && type.allowCreationByUser()) {
mySdkType = type;
}
}
}
mySdkType = sdkType;
}
@NotNull
@@ -302,7 +297,6 @@ public class UnknownSdkTracker {
@NotNull
@Override
public SdkType getSdkType() {
assert mySdkType != null;
return mySdkType;
}