9c403d9145fb8ed8d993a1302abdde7b74676234
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / project / impl / ProjectImpl.java
1 // 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.
2 package com.intellij.openapi.project.impl;
3
4 import com.intellij.configurationStore.StoreUtil;
5 import com.intellij.diagnostic.Activity;
6 import com.intellij.diagnostic.StartUpMeasurer;
7 import com.intellij.diagnostic.StartUpMeasurer.Phases;
8 import com.intellij.ide.plugins.ContainerDescriptor;
9 import com.intellij.ide.plugins.IdeaPluginDescriptorImpl;
10 import com.intellij.ide.plugins.PluginManagerCore;
11 import com.intellij.ide.startup.StartupManagerEx;
12 import com.intellij.openapi.application.Application;
13 import com.intellij.openapi.application.ApplicationManager;
14 import com.intellij.openapi.application.ex.ApplicationManagerEx;
15 import com.intellij.openapi.application.impl.LaterInvocator;
16 import com.intellij.openapi.components.ServiceManager;
17 import com.intellij.openapi.components.StorageScheme;
18 import com.intellij.openapi.components.impl.stores.IComponentStore;
19 import com.intellij.openapi.components.impl.stores.IProjectStore;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.fileEditor.FileEditorManager;
22 import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
23 import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl;
24 import com.intellij.openapi.module.ModuleManager;
25 import com.intellij.openapi.module.impl.ModuleManagerImpl;
26 import com.intellij.openapi.progress.ProgressIndicator;
27 import com.intellij.openapi.project.DumbAwareRunnable;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.project.ProjectManager;
30 import com.intellij.openapi.project.ex.ProjectEx;
31 import com.intellij.openapi.project.ex.ProjectManagerEx;
32 import com.intellij.openapi.startup.StartupManager;
33 import com.intellij.openapi.util.AtomicNotNullLazyValue;
34 import com.intellij.openapi.util.Key;
35 import com.intellij.openapi.vfs.LocalFileSystem;
36 import com.intellij.openapi.vfs.VirtualFile;
37 import com.intellij.openapi.wm.WindowManager;
38 import com.intellij.openapi.wm.impl.FrameTitleBuilder;
39 import com.intellij.project.ProjectStoreOwner;
40 import com.intellij.psi.impl.DebugUtil;
41 import com.intellij.serviceContainer.PlatformComponentManagerImpl;
42 import com.intellij.util.PathUtil;
43 import com.intellij.util.TimedReference;
44 import org.jetbrains.annotations.*;
45
46 import javax.swing.*;
47 import java.nio.file.Path;
48
49 public class ProjectImpl extends PlatformComponentManagerImpl implements ProjectEx, ProjectStoreOwner {
50   private static final Logger LOG = Logger.getInstance("#com.intellij.project.impl.ProjectImpl");
51
52   public static final Key<Long> CREATION_TIME = Key.create("ProjectImpl.CREATION_TIME");
53
54   /**
55    * @deprecated use {@link #getCreationTrace()}
56    */
57   @Deprecated
58   public static final Key<String> CREATION_TRACE = Key.create("ProjectImpl.CREATION_TRACE");
59   @TestOnly
60   public static final String LIGHT_PROJECT_NAME = "light_temp";
61
62   private String myName;
63   private final boolean myLight;
64   static boolean ourClassesAreLoaded;
65   private final String creationTrace;
66
67   private final AtomicNotNullLazyValue<IComponentStore> myComponentStore = AtomicNotNullLazyValue.createValue(() -> {
68     //noinspection CodeBlock2Expr
69     return ServiceManager.getService(ProjectStoreFactory.class).createStore(this);
70   });
71
72   protected ProjectImpl(@NotNull Path filePath, @Nullable String projectName) {
73     super(ApplicationManager.getApplication());
74
75     putUserData(CREATION_TIME, System.nanoTime());
76     creationTrace = ApplicationManager.getApplication().isUnitTestMode() ? DebugUtil.currentStackTrace() : null;
77
78     getPicoContainer().registerComponentInstance(Project.class, this);
79
80     myName = projectName;
81     // light project may be changed later during test, so we need to remember its initial state
82     //noinspection TestOnlyProblems
83     myLight = ApplicationManager.getApplication().isUnitTestMode() && filePath.toString().contains(LIGHT_PROJECT_NAME);
84   }
85
86   static final String TEMPLATE_PROJECT_NAME = "Default (Template) Project";
87   // default project constructor
88   ProjectImpl() {
89     super(ApplicationManager.getApplication());
90
91     putUserData(CREATION_TIME, System.nanoTime());
92     if (ApplicationManager.getApplication().isUnitTestMode()) {
93       putUserData(CREATION_TRACE, DebugUtil.currentStackTrace());
94     }
95
96     creationTrace = ApplicationManager.getApplication().isUnitTestMode() ? DebugUtil.currentStackTrace() : null;
97
98     myName = TEMPLATE_PROJECT_NAME;
99     myLight = false;
100   }
101
102
103
104   @Override
105   public boolean isDisposed() {
106     return super.isDisposed() || temporarilyDisposed;
107   }
108
109   @Override
110   @TestOnly
111   public boolean isLight() {
112     return myLight;
113   }
114
115   private volatile boolean temporarilyDisposed;
116   @TestOnly
117   void setTemporarilyDisposed(boolean disposed) {
118     temporarilyDisposed = disposed;
119   }
120
121   @TestOnly
122   boolean isTemporarilyDisposed() {
123     return temporarilyDisposed;
124   }
125
126   @Override
127   public void setProjectName(@NotNull String projectName) {
128     if (!projectName.equals(myName)) {
129       myName = projectName;
130
131       StartupManager.getInstance(this).runWhenProjectIsInitialized((DumbAwareRunnable)() -> {
132         if (isDisposed()) return;
133
134         JFrame frame = WindowManager.getInstance().getFrame(this);
135         String title = FrameTitleBuilder.getInstance().getProjectTitle(this);
136         if (frame != null && title != null) {
137           frame.setTitle(title);
138         }
139       });
140     }
141   }
142
143   // do not call for default project
144   @NotNull
145   public final IProjectStore getStateStore() {
146     return (IProjectStore)getComponentStore();
147   }
148
149   @Override
150   @NotNull
151   public IComponentStore getComponentStore() {
152     return myComponentStore.getValue();
153   }
154
155   @Override
156   public boolean isOpen() {
157     return ProjectManagerEx.getInstanceEx().isProjectOpened(this);
158   }
159
160   @Override
161   public boolean isInitialized() {
162     return !isDisposed() && isOpen() && StartupManagerEx.getInstanceEx(this).startupActivityPassed();
163   }
164
165   @NotNull
166   @Override
167   protected ContainerDescriptor getContainerDescriptor(@NotNull IdeaPluginDescriptorImpl pluginDescriptor) {
168     return pluginDescriptor.getProject();
169   }
170
171   @Nullable
172   @Override
173   public @SystemIndependent String getProjectFilePath() {
174     return getStateStore().getProjectFilePath();
175   }
176
177   @Override
178   public VirtualFile getProjectFile() {
179     return LocalFileSystem.getInstance().findFileByPath(getStateStore().getProjectFilePath());
180   }
181
182   @Override
183   public VirtualFile getBaseDir() {
184     return LocalFileSystem.getInstance().findFileByPath(getStateStore().getProjectBasePath());
185   }
186
187   @Override
188   @Nullable
189   public @SystemIndependent String getBasePath() {
190     return getStateStore().getProjectBasePath();
191   }
192
193   @NotNull
194   @Override
195   public String getName() {
196     if (myName == null) {
197       return getStateStore().getProjectName();
198     }
199     return myName;
200   }
201
202   @Override
203   public @SystemDependent String getPresentableUrl() {
204     IProjectStore store = getStateStore();
205     return PathUtil.toSystemDependentName(store.getStorageScheme() == StorageScheme.DIRECTORY_BASED ? store.getProjectBasePath() : store.getProjectFilePath());
206   }
207
208   @NotNull
209   @NonNls
210   @Override
211   public String getLocationHash() {
212     String str = getPresentableUrl();
213     if (str == null) {
214       str = getName();
215     }
216
217     final String prefix = getStateStore().getStorageScheme() == StorageScheme.DIRECTORY_BASED ? "" : getName();
218     return prefix + Integer.toHexString(str.hashCode());
219   }
220
221   @Override
222   @Nullable
223   public VirtualFile getWorkspaceFile() {
224     String workspaceFilePath = getStateStore().getWorkspaceFilePath();
225     return workspaceFilePath == null ? null : LocalFileSystem.getInstance().findFileByPath(workspaceFilePath);
226   }
227
228   public void registerComponents() {
229     String activityNamePrefix = activityNamePrefix();
230     Activity activity = activityNamePrefix == null ? null : StartUpMeasurer.start(activityNamePrefix + Phases.REGISTER_COMPONENTS_SUFFIX);
231     //  at this point of time plugins are already loaded by application - no need to pass indicator to getLoadedPlugins call
232     registerComponents(PluginManagerCore.getLoadedPlugins());
233     if (activity != null) {
234       activity.end();
235     }
236
237     Application app = ApplicationManager.getApplication();
238     app.getMessageBus().syncPublisher(ProjectLifecycleListener.TOPIC).projectComponentsRegistered(this);
239   }
240
241   public void init(@Nullable ProgressIndicator indicator) {
242     Application application = ApplicationManager.getApplication();
243     createComponents(indicator);
244     if (indicator != null && !application.isHeadlessEnvironment()) {
245       distributeProgress(indicator);
246     }
247
248     if (myName == null) {
249       myName = getStateStore().getProjectName();
250     }
251     application.getMessageBus().syncPublisher(ProjectLifecycleListener.TOPIC).projectComponentsInitialized(this);
252   }
253
254   @Override
255   protected void setProgressDuringInit(@NotNull ProgressIndicator indicator) {
256     indicator.setFraction(getPercentageOfComponentsLoaded() / (ourClassesAreLoaded ? 10 : 2));
257   }
258
259   private void distributeProgress(@NotNull ProgressIndicator indicator) {
260     ModuleManager moduleManager = ModuleManager.getInstance(this);
261     if (!(moduleManager instanceof ModuleManagerImpl)) {
262       return;
263     }
264
265     double toDistribute = 1 - indicator.getFraction();
266     int modulesCount = ((ModuleManagerImpl)moduleManager).getModulePathsCount();
267     EditorsSplitters splitters = ((FileEditorManagerImpl)FileEditorManager.getInstance(this)).getMainSplitters();
268     int editors = splitters.getEditorsCount();
269
270     double modulesPart = ourClassesAreLoaded || editors == 0 ? toDistribute : toDistribute * 0.5;
271     if (modulesCount != 0) {
272
273       double step = modulesPart / modulesCount;
274       ((ModuleManagerImpl)moduleManager).setProgressStep(step);
275     }
276
277     if (editors != 0) {
278       splitters.setProgressStep(toDistribute - modulesPart / editors);
279     }
280   }
281
282   @Override
283   public void save() {
284     if (!ApplicationManagerEx.getApplicationEx().isSaveAllowed()) {
285       // no need to save
286       return;
287     }
288
289     // ensure that expensive save operation is not performed before startupActivityPassed
290     // first save may be quite cost operation, because cache is not warmed up yet
291     if (!isInitialized()) {
292       LOG.debug("Skip save for " + getName() + ": not initialized");
293       return;
294     }
295
296     StoreUtil.saveSettings(this, false);
297   }
298
299   @Override
300   public synchronized void dispose() {
301     Application application = ApplicationManager.getApplication();
302     application.assertWriteAccessAllowed();  // dispose must be under write action
303
304     // can call dispose only via com.intellij.ide.impl.ProjectUtil.closeAndDispose()
305     if (ProjectManagerEx.getInstanceEx().isProjectOpened(this)) {
306       throw new IllegalStateException("Must call .dispose() for a closed project only. See ProjectManager.closeProject() or ProjectUtil.closeAndDispose().");
307     }
308
309     // we use super here, because temporarilyDisposed will be true if project closed
310     LOG.assertTrue(!super.isDisposed(), this + " is disposed already");
311     disposeComponents();
312
313     super.dispose();
314
315     if (!application.isDisposed()) {
316       application.getMessageBus().syncPublisher(ProjectLifecycleListener.TOPIC).afterProjectClosed(this);
317     }
318     ((ProjectManagerImpl)ProjectManager.getInstance()).updateTheOnlyProjectField();
319
320     TimedReference.disposeTimed();
321     LaterInvocator.purgeExpiredItems();
322   }
323
324   @Override
325   public boolean isDefault() {
326     return false;
327   }
328
329   @NonNls
330   @Override
331   public String toString() {
332     return "Project" +
333            (isDisposed() ? " (Disposed" + (temporarilyDisposed ? " temporarily" : "") + ")"
334                          : " '" + (myComponentStore.isComputed() ? getPresentableUrl() : "<no component store>") + "'") +
335            " " + myName;
336   }
337
338   @TestOnly
339   public String getCreationTrace() {
340     return creationTrace;
341   }
342
343   @Nullable
344   @ApiStatus.Internal
345   @Override
346   public String activityNamePrefix() {
347     return "project ";
348   }
349 }