1 package com
.intellij
.openapi
.project
.impl
;
3 import com
.intellij
.ide
.AppLifecycleListener
;
4 import com
.intellij
.ide
.highlighter
.WorkspaceFileType
;
5 import com
.intellij
.ide
.impl
.ProjectUtil
;
6 import com
.intellij
.ide
.startup
.impl
.StartupManagerImpl
;
7 import com
.intellij
.openapi
.application
.Application
;
8 import com
.intellij
.openapi
.application
.ApplicationManager
;
9 import com
.intellij
.openapi
.application
.ModalityState
;
10 import com
.intellij
.openapi
.application
.PathManager
;
11 import com
.intellij
.openapi
.application
.ex
.ApplicationEx
;
12 import com
.intellij
.openapi
.application
.ex
.ApplicationManagerEx
;
13 import com
.intellij
.openapi
.application
.impl
.ApplicationImpl
;
14 import com
.intellij
.openapi
.components
.ExportableApplicationComponent
;
15 import com
.intellij
.openapi
.components
.StateStorage
;
16 import com
.intellij
.openapi
.components
.impl
.stores
.IComponentStore
;
17 import com
.intellij
.openapi
.components
.impl
.stores
.IProjectStore
;
18 import com
.intellij
.openapi
.components
.impl
.stores
.XmlElementStorage
;
19 import com
.intellij
.openapi
.diagnostic
.Logger
;
20 import com
.intellij
.openapi
.fileEditor
.FileDocumentManager
;
21 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
22 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
23 import com
.intellij
.openapi
.progress
.ProgressManager
;
24 import com
.intellij
.openapi
.project
.Project
;
25 import com
.intellij
.openapi
.project
.ProjectBundle
;
26 import com
.intellij
.openapi
.project
.ProjectManagerListener
;
27 import com
.intellij
.openapi
.project
.ProjectReloadState
;
28 import com
.intellij
.openapi
.project
.ex
.ProjectEx
;
29 import com
.intellij
.openapi
.project
.ex
.ProjectManagerEx
;
30 import com
.intellij
.openapi
.startup
.StartupManager
;
31 import com
.intellij
.openapi
.ui
.Messages
;
32 import com
.intellij
.openapi
.util
.*;
33 import com
.intellij
.openapi
.util
.io
.FileUtil
;
34 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
35 import com
.intellij
.openapi
.vfs
.VirtualFile
;
36 import com
.intellij
.openapi
.vfs
.VirtualFileEvent
;
37 import com
.intellij
.openapi
.vfs
.VirtualFileManagerListener
;
38 import com
.intellij
.openapi
.vfs
.ex
.VirtualFileManagerEx
;
39 import com
.intellij
.openapi
.vfs
.newvfs
.NewVirtualFile
;
40 import com
.intellij
.util
.Alarm
;
41 import com
.intellij
.util
.ProfilingUtil
;
42 import com
.intellij
.util
.containers
.ContainerUtil
;
43 import com
.intellij
.util
.containers
.HashMap
;
44 import com
.intellij
.util
.io
.fs
.IFile
;
45 import com
.intellij
.util
.messages
.MessageBus
;
46 import com
.intellij
.util
.messages
.MessageBusConnection
;
47 import gnu
.trove
.TObjectLongHashMap
;
48 import org
.jdom
.Element
;
49 import org
.jdom
.JDOMException
;
50 import org
.jetbrains
.annotations
.NonNls
;
51 import org
.jetbrains
.annotations
.NotNull
;
52 import org
.jetbrains
.annotations
.Nullable
;
53 import org
.jetbrains
.annotations
.TestOnly
;
56 import java
.io
.IOException
;
59 public class ProjectManagerImpl
extends ProjectManagerEx
implements NamedJDOMExternalizable
, ExportableApplicationComponent
{
60 private static final boolean LOG_PROJECT_LEAKAGE_IN_TESTS
= false;
61 private static final Logger LOG
= Logger
.getInstance("#com.intellij.project.impl.ProjectManagerImpl");
62 public static final int CURRENT_FORMAT_VERSION
= 4;
64 private static final Key
<ArrayList
<ProjectManagerListener
>> LISTENERS_IN_PROJECT_KEY
= Key
.create("LISTENERS_IN_PROJECT_KEY");
65 @NonNls private static final String ELEMENT_DEFAULT_PROJECT
= "defaultProject";
67 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"})
68 private ProjectImpl myDefaultProject
; // Only used asynchronously in save and dispose, which itself are synchronized.
70 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"})
71 private Element myDefaultProjectRootElement
; // Only used asynchronously in save and dispose, which itself are synchronized.
73 private final ArrayList
<Project
> myOpenProjects
= new ArrayList
<Project
>();
74 private final List
<ProjectManagerListener
> myListeners
= ContainerUtil
.createEmptyCOWList();
76 private Project myCurrentTestProject
= null;
78 private final Map
<VirtualFile
, byte[]> mySavedCopies
= new HashMap
<VirtualFile
, byte[]>();
79 private final TObjectLongHashMap
<VirtualFile
> mySavedTimestamps
= new TObjectLongHashMap
<VirtualFile
>();
80 private final HashMap
<Project
, List
<Pair
<VirtualFile
, StateStorage
>>> myChangedProjectFiles
= new HashMap
<Project
, List
<Pair
<VirtualFile
, StateStorage
>>>();
81 private final Alarm myChangedFilesAlarm
= new Alarm();
82 private final List
<Pair
<VirtualFile
, StateStorage
>> myChangedApplicationFiles
= new ArrayList
<Pair
<VirtualFile
, StateStorage
>>();
83 private volatile int myReloadBlockCount
= 0;
84 private final Map
<Project
, String
> myProjects
= new WeakHashMap
<Project
, String
>();
85 private static final int MAX_LEAKY_PROJECTS
= 42;
87 private static ProjectManagerListener
[] getListeners(Project project
) {
88 ArrayList
<ProjectManagerListener
> array
= project
.getUserData(LISTENERS_IN_PROJECT_KEY
);
89 if (array
== null) return ProjectManagerListener
.EMPTY_ARRAY
;
90 return array
.toArray(new ProjectManagerListener
[array
.size()]);
93 public ProjectManagerImpl(VirtualFileManagerEx virtualFileManagerEx
) {
94 Application app
= ApplicationManager
.getApplication();
95 MessageBus messageBus
= app
.getMessageBus();
96 MessageBusConnection connection
= messageBus
.connect(app
);
97 connection
.subscribe(StateStorage
.STORAGE_TOPIC
, new StateStorage
.Listener() {
98 public void storageFileChanged(final VirtualFileEvent event
, @NotNull final StateStorage storage
) {
99 VirtualFile file
= event
.getFile();
100 if (!file
.isDirectory()) {
101 if (!((ApplicationImpl
)ApplicationManager
.getApplication()).isSaving() && !(event
.getRequestor() instanceof StateStorage
.SaveSession
)) {
102 saveChangedProjectFile(file
, null, storage
);
108 addProjectManagerListener(
109 new ProjectManagerListener() {
111 public void projectOpened(final Project project
) {
112 MessageBus messageBus
= project
.getMessageBus();
113 MessageBusConnection connection
= messageBus
.connect(project
);
114 connection
.subscribe(StateStorage
.STORAGE_TOPIC
, new StateStorage
.Listener() {
115 public void storageFileChanged(final VirtualFileEvent event
, @NotNull final StateStorage storage
) {
116 VirtualFile file
= event
.getFile();
117 if (!file
.isDirectory() && !((ApplicationImpl
)ApplicationManager
.getApplication()).isSaving() && !(event
.getRequestor() instanceof StateStorage
.SaveSession
)) {
118 saveChangedProjectFile(file
, project
, storage
);
124 ProjectManagerListener
[] listeners
= getListeners(project
);
125 for (ProjectManagerListener listener
: listeners
) {
126 listener
.projectOpened(project
);
130 public void projectClosed(Project project
) {
131 ProjectManagerListener
[] listeners
= getListeners(project
);
132 for (ProjectManagerListener listener
: listeners
) {
133 listener
.projectClosed(project
);
137 public boolean canCloseProject(Project project
) {
138 ProjectManagerListener
[] listeners
= getListeners(project
);
139 for (ProjectManagerListener listener
: listeners
) {
140 if (!listener
.canCloseProject(project
)) {
147 public void projectClosing(Project project
) {
148 ProjectManagerListener
[] listeners
= getListeners(project
);
149 for (ProjectManagerListener listener
: listeners
) {
150 listener
.projectClosing(project
);
156 registerExternalProjectFileListener(virtualFileManagerEx
);
159 public void disposeComponent() {
160 Disposer
.dispose(myChangedFilesAlarm
);
161 if (myDefaultProject
!= null) {
162 Disposer
.dispose(myDefaultProject
);
163 myDefaultProject
= null;
167 public void initComponent() {
171 public Project
newProject(final String projectName
, String filePath
, boolean useDefaultProjectSettings
, boolean isDummy
) {
172 filePath
= canonicalize(filePath
);
174 if (LOG_PROJECT_LEAKAGE_IN_TESTS
&& ApplicationManager
.getApplication().isUnitTestMode()) {
175 for (int i
= 0; i
< 42; i
++) {
176 if (myProjects
.size() < MAX_LEAKY_PROJECTS
) break;
181 catch (InterruptedException e
) {
182 throw new RuntimeException(e
);
188 if (myProjects
.size() >= MAX_LEAKY_PROJECTS
) {
189 List
<Project
> copy
= new ArrayList
<Project
>(myProjects
.keySet());
191 throw new TooManyProjectLeakedException(copy
);
196 ProjectImpl project
=
197 createAndInitProject(projectName
, filePath
, false, isDummy
, ApplicationManager
.getApplication().isUnitTestMode(),
198 useDefaultProjectSettings ?
getDefaultProject() : null, null);
199 if (LOG_PROJECT_LEAKAGE_IN_TESTS
) {
200 myProjects
.put(project
, null);
204 catch (final Exception e
) {
206 Messages
.showErrorDialog(message(e
), ProjectBundle
.message("project.load.default.error"));
212 private static String
message(Throwable e
) {
213 String message
= e
.getMessage();
214 if (message
!= null) return message
;
215 message
= e
.getLocalizedMessage();
216 if (message
!= null) return message
;
217 message
= e
.toString();
218 Throwable cause
= e
.getCause();
220 String causeMessage
= message(cause
);
221 return message
+ " (cause: " + causeMessage
+ ")";
227 private ProjectImpl
createAndInitProject(String projectName
, String filePath
, boolean isDefault
, boolean isDummy
, boolean isOptimiseTestLoadSpeed
,
228 @Nullable Project template
, @Nullable Pair
<Class
, Object
> additionalPicoContainerComponents
) throws IOException
{
230 throw new UnsupportedOperationException("Dummy project is deprecated and shall not be used anymore.");
232 final ProjectImpl project
= isDefault ?
new DefaultProject(this, filePath
, isOptimiseTestLoadSpeed
, projectName
) : new ProjectImpl(this, filePath
, false, isOptimiseTestLoadSpeed
, projectName
);
234 if (additionalPicoContainerComponents
!= null) {
235 project
.getPicoContainer().registerComponentInstance(additionalPicoContainerComponents
.first
, additionalPicoContainerComponents
.second
);
238 ApplicationManager
.getApplication().getMessageBus().syncPublisher(ProjectLifecycleListener
.TOPIC
).beforeProjectLoaded(project
);
241 if (template
!= null) {
242 project
.getStateStore().loadProjectFromTemplate((ProjectImpl
)template
);
244 project
.getStateStore().load();
247 catch (IOException e
) {
248 scheduleDispose(project
);
251 catch (final StateStorage
.StateStorageException e
) {
252 scheduleDispose(project
);
256 project
.loadProjectComponents();
258 if (projectName
!= null) {
259 ProjectDetailsComponent
.getInstance(project
).setProjectName(projectName
);
265 private static void scheduleDispose(final ProjectImpl project
) {
266 ApplicationManager
.getApplication().invokeLater(new Runnable() {
268 Disposer
.dispose(project
);
274 public Project
loadProject(String filePath
) throws IOException
, JDOMException
, InvalidDataException
{
276 return loadProject(filePath
, null);
278 catch (StateStorage
.StateStorageException e
) {
279 throw new IOException(e
.getMessage());
284 private Project
loadProject(String filePath
, Pair
<Class
, Object
> additionalPicoContainerComponents
) throws IOException
, StateStorage
.StateStorageException
{
285 filePath
= canonicalize(filePath
);
286 ProjectImpl project
= null;
288 project
= createAndInitProject(null, filePath
, false, false, false, null, additionalPicoContainerComponents
);
290 catch (ProcessCanceledException e
) {
291 if (project
!= null) {
292 scheduleDispose(project
);
301 protected static String
canonicalize(final String filePath
) {
302 if (filePath
== null) return null;
304 return FileUtil
.resolveShortWindowsName(filePath
);
306 catch (IOException e
) {
307 // OK. File does not yet exist so it's canonical path will be equal to its original path.
314 public synchronized boolean isDefaultProjectInitialized() {
315 return myDefaultProject
!= null;
318 public synchronized Project
getDefaultProject() {
319 if (myDefaultProject
== null) {
321 myDefaultProject
= createAndInitProject(null, null, true, false, ApplicationManager
.getApplication().isUnitTestMode(), null, null);
322 myDefaultProjectRootElement
= null;
324 catch (IOException e
) {
327 catch (StateStorage
.StateStorageException e
) {
331 return myDefaultProject
;
335 public Element
getDefaultProjectRootElement() {
336 return myDefaultProjectRootElement
;
340 public Project
[] getOpenProjects() {
341 if (ApplicationManager
.getApplication().isUnitTestMode()) {
342 final Project currentTestProject
= myCurrentTestProject
;
343 if (myOpenProjects
.isEmpty() && currentTestProject
!= null && !currentTestProject
.isDisposed()) {
344 return new Project
[] {currentTestProject
};
347 return myOpenProjects
.toArray(new Project
[myOpenProjects
.size()]);
350 public boolean isProjectOpened(Project project
) {
351 if (ApplicationManager
.getApplication().isUnitTestMode() && myOpenProjects
.isEmpty() && myCurrentTestProject
!= null) {
352 return project
== myCurrentTestProject
;
354 return myOpenProjects
.contains(project
);
357 public boolean openProject(final Project project
) {
358 if (myOpenProjects
.contains(project
)) return false;
359 if (!ApplicationManager
.getApplication().isUnitTestMode() && !((ProjectEx
)project
).getStateStore().checkVersion()) return false;
362 myOpenProjects
.add(project
);
363 fireProjectOpened(project
);
365 final StartupManagerImpl startupManager
= (StartupManagerImpl
)StartupManager
.getInstance(project
);
367 boolean ok
= ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
369 startupManager
.runStartupActivities();
371 }, ProjectBundle
.message("project.load.progress"), true, project
);
374 closeProject(project
, false);
375 notifyProjectOpenFailed();
379 startupManager
.runPostStartupActivities();
384 public Project
loadAndOpenProject(String filePath
) throws IOException
, JDOMException
, InvalidDataException
{
385 return loadAndOpenProject(filePath
, true);
389 public Project
loadAndOpenProject(final String filePath
, final boolean convert
) throws IOException
, JDOMException
, InvalidDataException
{
392 final Pair
<Class
, Object
> convertorComponent
;
395 convertorComponent
= convertProject(filePath
);
397 catch (ProcessCanceledException e
) {
402 convertorComponent
= null;
405 Project project
= loadProjectWithProgress(filePath
, convertorComponent
);
406 if (project
== null) return null;
408 if (!openProject(project
)) {
409 Disposer
.dispose(project
);
415 catch (StateStorage
.StateStorageException e
) {
416 throw new IOException(e
.getMessage());
421 public Project
loadProjectWithProgress(final String filePath
, final Pair
<Class
, Object
> convertorComponent
) throws IOException
{
422 return loadProjectWithProgress(filePath
, convertorComponent
, null);
426 public Project
loadProjectWithProgress(final String filePath
, final Pair
<Class
, Object
> convertorComponent
, Ref
<Boolean
> canceled
) throws IOException
{
427 final IOException
[] io
= {null};
428 final StateStorage
.StateStorageException
[] stateStorage
= {null};
430 if (filePath
!= null) {
431 refreshProjectFiles(filePath
);
434 final Project
[] project
= new Project
[1];
435 boolean ok
= ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
437 final ProgressIndicator indicator
= ProgressManager
.getInstance().getProgressIndicator();
439 if (indicator
!= null) {
440 indicator
.setText(ProjectBundle
.message("loading.components.for", filePath
));
441 indicator
.setIndeterminate(true);
443 project
[0] = loadProject(filePath
, convertorComponent
);
445 catch (IOException e
) {
449 catch (StateStorage
.StateStorageException e
) {
454 if (indicator
!= null) {
455 indicator
.setText(ProjectBundle
.message("initializing.components"));
458 }, ProjectBundle
.message("project.load.progress"), true, null);
461 if (project
[0] != null) {
462 Disposer
.dispose(project
[0]);
465 if (canceled
!= null) {
468 notifyProjectOpenFailed();
471 if (io
[0] != null) throw io
[0];
472 if (stateStorage
[0] != null) throw stateStorage
[0];
474 if (project
[0] == null || !ok
) {
480 private static void refreshProjectFiles(final String filePath
) {
481 if (ApplicationManager
.getApplication().isUnitTestMode() || ApplicationManager
.getApplication().isDispatchThread()) {
482 final File file
= new File(filePath
);
484 VirtualFile projectFile
= LocalFileSystem
.getInstance().refreshAndFindFileByPath(filePath
);
485 if (projectFile
!= null) {
486 projectFile
.refresh(false, false);
489 File iwsFile
= new File(file
.getParentFile(), FileUtil
.getNameWithoutExtension(file
) + WorkspaceFileType
.DOT_DEFAULT_EXTENSION
);
490 VirtualFile wsFile
= LocalFileSystem
.getInstance().refreshAndFindFileByIoFile(iwsFile
);
491 if (wsFile
!= null) {
492 wsFile
.refresh(false, false);
497 VirtualFile projectConfigDir
= LocalFileSystem
.getInstance().refreshAndFindFileByIoFile(new File(filePath
, Project
.DIRECTORY_STORE_FOLDER
));
498 if (projectConfigDir
!= null && projectConfigDir
.isDirectory()) {
499 projectConfigDir
.getChildren();
500 if (projectConfigDir
instanceof NewVirtualFile
) {
501 ((NewVirtualFile
)projectConfigDir
).markDirtyRecursively();
503 projectConfigDir
.refresh(false, true);
511 protected Pair
<Class
, Object
> convertProject(final String filePath
) throws ProcessCanceledException
{
515 private static void notifyProjectOpenFailed() {
516 ApplicationManager
.getApplication().getMessageBus().syncPublisher(AppLifecycleListener
.TOPIC
).projectOpenFailed();
519 private void registerExternalProjectFileListener(VirtualFileManagerEx virtualFileManager
) {
520 virtualFileManager
.addVirtualFileManagerListener(new VirtualFileManagerListener() {
521 public void beforeRefreshStart(boolean asynchonous
) {
524 public void afterRefreshFinish(boolean asynchonous
) {
525 scheduleReloadApplicationAndProject();
530 private void askToReloadProjectIfConfigFilesChangedExternally() {
531 if (!myChangedProjectFiles
.isEmpty() && myReloadBlockCount
== 0) {
532 Set
<Project
> projects
= myChangedProjectFiles
.keySet();
533 List
<Project
> projectsToReload
= new ArrayList
<Project
>();
535 for (Project project
: projects
) {
536 if (shouldReloadProject(project
)) {
537 projectsToReload
.add(project
);
541 for (final Project projectToReload
: projectsToReload
) {
542 reloadProjectImpl(projectToReload
, false, false);
545 myChangedProjectFiles
.clear();
550 private boolean tryToReloadApplication(){
552 final Application app
= ApplicationManager
.getApplication();
554 if (app
.isDisposed()) return false;
555 final HashSet
<Pair
<VirtualFile
, StateStorage
>> causes
= new HashSet
<Pair
<VirtualFile
, StateStorage
>>(myChangedApplicationFiles
);
556 if (causes
.isEmpty()) return true;
558 final boolean[] reloadOk
= {false};
559 final LinkedHashSet
<String
> components
= new LinkedHashSet
<String
>();
561 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
565 reloadOk
[0] = ((ApplicationImpl
)app
).getStateStore().reload(causes
, components
);
567 catch (StateStorage
.StateStorageException e
) {
568 Messages
.showWarningDialog(ProjectBundle
.message("project.reload.failed", e
.getMessage()),
569 ProjectBundle
.message("project.reload.failed.title"));
571 catch (IOException e
) {
572 Messages
.showWarningDialog(ProjectBundle
.message("project.reload.failed", e
.getMessage()),
573 ProjectBundle
.message("project.reload.failed.title"));
578 if (!reloadOk
[0] && !components
.isEmpty()) {
579 String message
= "Application components were changed externally and cannot be reloaded:\n";
580 for (String component
: components
) {
581 message
+= component
+ "\n";
583 message
+= "Shutdown IDEA?";
585 if (Messages
.showYesNoDialog(message
,
586 "Application Configuration Reload", Messages
.getQuestionIcon()) == 0) {
587 for (Pair
<VirtualFile
, StateStorage
> cause
: causes
) {
588 StateStorage stateStorage
= cause
.getSecond();
589 if (stateStorage
instanceof XmlElementStorage
) {
590 ((XmlElementStorage
)stateStorage
).disableSaving();
593 ApplicationManagerEx
.getApplicationEx().exit(true);
601 myChangedApplicationFiles
.clear();
606 private boolean shouldReloadProject(final Project project
) {
607 if (project
.isDisposed()) return false;
608 final HashSet
<Pair
<VirtualFile
, StateStorage
>> causes
= new HashSet
<Pair
<VirtualFile
, StateStorage
>>(myChangedProjectFiles
.get(project
));
610 final boolean[] reloadOk
= {false};
612 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
615 reloadOk
[0] = ((ProjectEx
)project
).getStateStore().reload(causes
);
617 catch (StateStorage
.StateStorageException e
) {
618 Messages
.showWarningDialog(ProjectBundle
.message("project.reload.failed", e
.getMessage()),
619 ProjectBundle
.message("project.reload.failed.title"));
621 catch (IOException e
) {
622 Messages
.showWarningDialog(ProjectBundle
.message("project.reload.failed", e
.getMessage()),
623 ProjectBundle
.message("project.reload.failed.title"));
627 if (reloadOk
[0]) return false;
630 if (causes
.size() == 1) {
631 message
= ProjectBundle
.message("project.reload.external.change.single", causes
.iterator().next().first
.getPresentableUrl());
634 StringBuilder filesBuilder
= new StringBuilder();
635 boolean first
= true;
636 Set
<String
> alreadyShown
= new HashSet
<String
>();
637 for (Pair
<VirtualFile
, StateStorage
> cause
: causes
) {
638 String url
= cause
.first
.getPresentableUrl();
639 if (!alreadyShown
.contains(url
)) {
640 if (!first
) filesBuilder
.append("\n");
642 filesBuilder
.append(url
);
643 alreadyShown
.add(url
);
646 message
= ProjectBundle
.message("project.reload.external.change.multiple", filesBuilder
.toString());
649 return Messages
.showYesNoDialog(project
, message
, ProjectBundle
.message("project.reload.external.change.title"), Messages
.getQuestionIcon()) == 0;
652 public boolean isFileSavedToBeReloaded(VirtualFile candidate
) {
653 return mySavedCopies
.containsKey(candidate
);
656 public void blockReloadingProjectOnExternalChanges() {
657 myReloadBlockCount
++;
660 public void unblockReloadingProjectOnExternalChanges() {
661 myReloadBlockCount
--;
662 scheduleReloadApplicationAndProject();
665 private void scheduleReloadApplicationAndProject() {
666 ApplicationManager
.getApplication().invokeLater(new Runnable() {
668 if (!tryToReloadApplication()) return;
669 askToReloadProjectIfConfigFilesChangedExternally();
672 }, ModalityState
.NON_MODAL
);
676 public void setCurrentTestProject(@Nullable final Project project
) {
677 assert ApplicationManager
.getApplication().isUnitTestMode();
678 myCurrentTestProject
= project
;
682 public Project
getCurrentTestProject() {
683 assert ApplicationManager
.getApplication().isUnitTestMode();
684 return myCurrentTestProject
;
687 public void saveChangedProjectFile(final VirtualFile file
, final Project project
) {
691 registerProjectToReload(project
, file
, null);
694 private void saveChangedProjectFile(final VirtualFile file
, final Project project
, final StateStorage storage
) {
698 registerProjectToReload(project
, file
, storage
);
702 private void registerProjectToReload(final Project project
, final VirtualFile cause
, final StateStorage storage
) {
703 if (project
!= null) {
704 List
<Pair
<VirtualFile
, StateStorage
>> changedProjectFiles
= myChangedProjectFiles
.get(project
);
706 if (changedProjectFiles
== null) {
707 changedProjectFiles
= new ArrayList
<Pair
<VirtualFile
, StateStorage
>>();
708 myChangedProjectFiles
.put(project
, changedProjectFiles
);
711 changedProjectFiles
.add(new Pair
<VirtualFile
, StateStorage
>(cause
, storage
));
714 myChangedApplicationFiles
.add(new Pair
<VirtualFile
, StateStorage
>(cause
, storage
));
717 myChangedFilesAlarm
.cancelAllRequests();
718 myChangedFilesAlarm
.addRequest(new Runnable() {
720 if (myReloadBlockCount
== 0) {
721 scheduleReloadApplicationAndProject();
727 private void copyToTemp(VirtualFile file
) {
729 final byte[] bytes
= file
.contentsToByteArray();
730 mySavedCopies
.put(file
, bytes
);
731 mySavedTimestamps
.put(file
, file
.getTimeStamp());
733 catch (IOException e
) {
738 private void restoreCopy(VirtualFile file
) {
740 if (file
== null) return; // Externally deleted actually.
741 if (!file
.isWritable()) return; // IDEA was unable to save it as well. So no need to restore.
743 final byte[] bytes
= mySavedCopies
.get(file
);
746 file
.setBinaryContent(bytes
, -1, mySavedTimestamps
.get(file
));
748 catch (IOException e
) {
749 Messages
.showWarningDialog(ProjectBundle
.message("project.reload.write.failed", file
.getPresentableUrl()),
750 ProjectBundle
.message("project.reload.write.failed.title"));
755 mySavedCopies
.remove(file
);
756 mySavedTimestamps
.remove(file
);
760 public void reloadProject(final Project p
) {
761 reloadProjectImpl(p
, true, false);
764 public void reloadProjectImpl(final Project p
, final boolean clearCopyToRestore
, boolean takeMemorySnapshot
) {
765 if (clearCopyToRestore
) {
766 mySavedCopies
.clear();
767 mySavedTimestamps
.clear();
769 reloadProject(p
, takeMemorySnapshot
);
772 public void reloadProject(@NotNull Project p
, final boolean takeMemorySnapshot
) {
773 final Project
[] project
= {p
};
775 ProjectReloadState
.getInstance(project
[0]).onBeforeAutomaticProjectReload();
776 final Application application
= ApplicationManager
.getApplication();
778 application
.invokeLater(new Runnable() {
780 LOG
.info("Reloading project.");
781 ProjectImpl projectImpl
= (ProjectImpl
)project
[0];
782 if (projectImpl
.isDisposed()) return;
783 IProjectStore projectStore
= projectImpl
.getStateStore();
784 final String location
= projectImpl
.getLocation();
786 final List
<IFile
> original
;
788 final IComponentStore
.SaveSession saveSession
= projectStore
.startSave();
789 original
= saveSession
.getAllStorageFiles(true);
790 saveSession
.finishSave();
792 catch (IOException e
) {
797 if (project
[0].isDisposed() || ProjectUtil
.closeProject(project
[0])) {
798 application
.runWriteAction(new Runnable() {
800 for (final IFile originalFile
: original
) {
801 restoreCopy(LocalFileSystem
.getInstance().refreshAndFindFileByIoFile(originalFile
));
806 project
[0] = null; // Let it go.
808 if (takeMemorySnapshot
) {
809 ProfilingUtil
.forceCaptureMemorySnapshot();
812 ProjectUtil
.openProject(location
, null, true);
815 }, ModalityState
.NON_MODAL
);
819 public boolean isOpeningProject() {
820 return myCountOfProjectsBeingOpen > 0;
824 public boolean closeProject(final Project project
) {
825 return closeProject(project
, true);
828 private boolean closeProject(final Project project
, final boolean save
) {
829 if (!isProjectOpened(project
)) return true;
830 if (!canClose(project
)) return false;
832 final ShutDownTracker shutDownTracker
= ShutDownTracker
.getInstance();
833 shutDownTracker
.registerStopperThread(Thread
.currentThread());
836 FileDocumentManager
.getInstance().saveAllDocuments();
839 fireProjectClosing(project
);
841 myOpenProjects
.remove(project
);
842 fireProjectClosed(project
);
845 ApplicationEx application
= ApplicationManagerEx
.getApplicationEx();
846 if (!application
.isUnitTestMode()) application
.saveSettings();
850 shutDownTracker
.unregisterStopperThread(Thread
.currentThread());
856 private void fireProjectClosing(Project project
) {
857 if (LOG
.isDebugEnabled()) {
858 LOG
.debug("enter: fireProjectClosing()");
861 for (ProjectManagerListener listener
: myListeners
) {
863 listener
.projectClosing(project
);
865 catch (Exception e
) {
871 public void addProjectManagerListener(ProjectManagerListener listener
) {
872 myListeners
.add(listener
);
875 public void removeProjectManagerListener(ProjectManagerListener listener
) {
876 boolean removed
= myListeners
.remove(listener
);
877 LOG
.assertTrue(removed
);
880 public void addProjectManagerListener(Project project
, ProjectManagerListener listener
) {
881 ArrayList
<ProjectManagerListener
> listeners
= project
.getUserData(LISTENERS_IN_PROJECT_KEY
);
882 if (listeners
== null) {
883 listeners
= new ArrayList
<ProjectManagerListener
>();
884 project
.putUserData(LISTENERS_IN_PROJECT_KEY
, listeners
);
886 listeners
.add(listener
);
889 public void removeProjectManagerListener(Project project
, ProjectManagerListener listener
) {
890 ArrayList
<ProjectManagerListener
> listeners
= project
.getUserData(LISTENERS_IN_PROJECT_KEY
);
891 if (listeners
!= null) {
892 boolean removed
= listeners
.remove(listener
);
893 LOG
.assertTrue(removed
);
896 LOG
.assertTrue(false);
900 private void fireProjectOpened(Project project
) {
901 if (LOG
.isDebugEnabled()) {
902 LOG
.debug("projectOpened");
905 for (ProjectManagerListener listener
: myListeners
) {
907 listener
.projectOpened(project
);
909 catch (Exception e
) {
915 private void fireProjectClosed(Project project
) {
916 if (LOG
.isDebugEnabled()) {
917 LOG
.debug("projectClosed");
920 for (ProjectManagerListener listener
: myListeners
) {
922 listener
.projectClosed(project
);
924 catch (Exception e
) {
930 public boolean canClose(Project project
) {
931 if (LOG
.isDebugEnabled()) {
932 LOG
.debug("enter: canClose()");
935 for (ProjectManagerListener listener
: myListeners
) {
937 if (!listener
.canCloseProject(project
)) return false;
938 } catch (Throwable e
) {
939 LOG
.warn(e
); // DO NOT LET ANY PLUGIN to prevent closing due to exception
946 public void writeExternal(Element parentNode
) throws WriteExternalException
{
947 if (myDefaultProject
!= null) {
948 myDefaultProject
.save();
951 if (myDefaultProjectRootElement
== null) { //read external isn't called if config folder is absent
952 myDefaultProjectRootElement
= new Element(ELEMENT_DEFAULT_PROJECT
);
955 myDefaultProjectRootElement
.detach();
956 parentNode
.addContent(myDefaultProjectRootElement
);
960 public void setDefaultProjectRootElement(final Element defaultProjectRootElement
) {
961 myDefaultProjectRootElement
= defaultProjectRootElement
;
964 public void readExternal(Element parentNode
) throws InvalidDataException
{
965 myDefaultProjectRootElement
= parentNode
.getChild(ELEMENT_DEFAULT_PROJECT
);
967 if (myDefaultProjectRootElement
== null) {
968 myDefaultProjectRootElement
= new Element(ELEMENT_DEFAULT_PROJECT
);
971 myDefaultProjectRootElement
.detach();
974 public String
getExternalFileName() {
975 return "project.default";
979 public String
getComponentName() {
980 return "ProjectManager";
984 public File
[] getExportFiles() {
985 return new File
[]{PathManager
.getOptionsFile(this)};
989 public String
getPresentableName() {
990 return ProjectBundle
.message("project.default.settings");