IDEA-248142 JavaEE: do not capture LibraryDependentToolWindow extensions in NBRA...
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / LibraryDependentToolWindowManager.java
1 // Copyright 2000-2020 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.
2 package com.intellij.openapi.wm.impl;
3
4 import com.intellij.ProjectTopics;
5 import com.intellij.openapi.application.Application;
6 import com.intellij.openapi.application.ApplicationManager;
7 import com.intellij.openapi.application.ModalityState;
8 import com.intellij.openapi.application.ReadAction;
9 import com.intellij.openapi.extensions.ExtensionNotApplicableException;
10 import com.intellij.openapi.extensions.ExtensionPointListener;
11 import com.intellij.openapi.extensions.PluginDescriptor;
12 import com.intellij.openapi.project.Project;
13 import com.intellij.openapi.roots.ModuleRootEvent;
14 import com.intellij.openapi.roots.ModuleRootListener;
15 import com.intellij.openapi.startup.StartupActivity;
16 import com.intellij.openapi.wm.ToolWindow;
17 import com.intellij.openapi.wm.ToolWindowManager;
18 import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
19 import com.intellij.openapi.wm.ext.LibraryDependentToolWindow;
20 import com.intellij.openapi.wm.ext.LibrarySearchHelper;
21 import com.intellij.util.concurrency.SequentialTaskExecutor;
22 import com.intellij.util.containers.ContainerUtil;
23 import com.intellij.util.messages.SimpleMessageBusConnection;
24 import org.jetbrains.annotations.NotNull;
25 import org.jetbrains.annotations.Nullable;
26
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Objects;
30 import java.util.Set;
31 import java.util.concurrent.Executor;
32
33 final class LibraryDependentToolWindowManager implements StartupActivity {
34   private static final Executor ourExecutor = SequentialTaskExecutor.createSequentialApplicationPoolExecutor("LibraryDependentToolWindowManager");
35
36   LibraryDependentToolWindowManager() {
37     Application app = ApplicationManager.getApplication();
38     if (app.isUnitTestMode() || app.isHeadlessEnvironment()) {
39       throw ExtensionNotApplicableException.INSTANCE;
40     }
41   }
42
43   @Override
44   public void runActivity(@NotNull Project project) {
45     ModuleRootListener rootListener = new ModuleRootListener() {
46       @Override
47       public void rootsChanged(@NotNull ModuleRootEvent event) {
48         checkToolWindowStatuses(project);
49       }
50     };
51
52     checkToolWindowStatuses(project);
53
54     SimpleMessageBusConnection connection = project.getMessageBus().simpleConnect();
55     connection.subscribe(ProjectTopics.PROJECT_ROOTS, rootListener);
56     LibraryDependentToolWindow.EXTENSION_POINT_NAME.addExtensionPointListener(new ExtensionPointListener<>() {
57       @Override
58       public void extensionAdded(@NotNull LibraryDependentToolWindow extension, @NotNull PluginDescriptor pluginDescriptor) {
59         checkToolWindowStatuses(project, extension.id);
60       }
61
62       @Override
63       public void extensionRemoved(@NotNull LibraryDependentToolWindow extension, @NotNull PluginDescriptor pluginDescriptor) {
64         ToolWindow window = ToolWindowManager.getInstance(project).getToolWindow(extension.id);
65         if (window != null) {
66           window.remove();
67         }
68       }
69     }, project);
70   }
71
72   private void checkToolWindowStatuses(@NotNull Project project) {
73     checkToolWindowStatuses(project, null);
74   }
75
76   private void checkToolWindowStatuses(@NotNull Project project, @Nullable String extensionId) {
77     ModalityState currentModalityState = ModalityState.current();
78     ReadAction
79       .nonBlocking(() -> {
80         List<LibraryDependentToolWindow> extensions = LibraryDependentToolWindow.EXTENSION_POINT_NAME.getExtensionList();
81         if (extensionId != null) {
82           extensions = ContainerUtil.filter(extensions, ltw -> Objects.equals(ltw.id, extensionId));
83         }
84
85         Set<LibraryDependentToolWindow> existing = new HashSet<>(ContainerUtil.findAll(extensions, ltw -> {
86           LibrarySearchHelper helper = ltw.getLibrarySearchHelper();
87           return helper != null && helper.isLibraryExists(project);
88         }));
89
90         return new LibraryWindowsState(project, extensions, existing);
91       })
92       .inSmartMode(project)
93       .coalesceBy(this, project)
94       .finishOnUiThread(currentModalityState, LibraryDependentToolWindowManager::applyWindowsState)
95       .submit(ourExecutor);
96   }
97
98   private static void applyWindowsState(LibraryWindowsState state) {
99     ToolWindowManagerEx toolWindowManagerEx = ToolWindowManagerEx.getInstanceEx(state.project);
100     for (LibraryDependentToolWindow libraryToolWindow : state.extensions) {
101       ToolWindow toolWindow = toolWindowManagerEx.getToolWindow(libraryToolWindow.id);
102       if (state.existing.contains(libraryToolWindow)) {
103         if (toolWindow == null) {
104           toolWindowManagerEx.initToolWindow(libraryToolWindow);
105         }
106       }
107       else {
108         if (toolWindow != null) {
109           toolWindow.remove();
110         }
111       }
112     }
113   }
114
115   private static class LibraryWindowsState {
116     final @NotNull Project project;
117     final List<LibraryDependentToolWindow> extensions;
118     final Set<LibraryDependentToolWindow> existing;
119
120     private LibraryWindowsState(@NotNull Project project,
121                                 List<LibraryDependentToolWindow> extensions,
122                                 Set<LibraryDependentToolWindow> existing) {
123       this.project = project;
124       this.extensions = extensions;
125       this.existing = existing;
126     }
127   }
128 }