2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.openapi
.project
.impl
;
18 import com
.intellij
.diagnostic
.PluginException
;
19 import com
.intellij
.ide
.plugins
.IdeaPluginDescriptor
;
20 import com
.intellij
.ide
.plugins
.PluginManager
;
21 import com
.intellij
.ide
.startup
.StartupManagerEx
;
22 import com
.intellij
.notification
.*;
23 import com
.intellij
.openapi
.application
.Application
;
24 import com
.intellij
.openapi
.application
.ApplicationManager
;
25 import com
.intellij
.openapi
.application
.ApplicationNamesInfo
;
26 import com
.intellij
.openapi
.application
.PathMacros
;
27 import com
.intellij
.openapi
.application
.ex
.ApplicationEx
;
28 import com
.intellij
.openapi
.application
.ex
.ApplicationManagerEx
;
29 import com
.intellij
.openapi
.components
.ProjectComponent
;
30 import com
.intellij
.openapi
.components
.TrackingPathMacroSubstitutor
;
31 import com
.intellij
.openapi
.components
.impl
.ComponentManagerImpl
;
32 import com
.intellij
.openapi
.components
.impl
.ProjectPathMacroManager
;
33 import com
.intellij
.openapi
.components
.impl
.stores
.IComponentStore
;
34 import com
.intellij
.openapi
.components
.impl
.stores
.IProjectStore
;
35 import com
.intellij
.openapi
.components
.impl
.stores
.UnknownMacroNotification
;
36 import com
.intellij
.openapi
.diagnostic
.Logger
;
37 import com
.intellij
.openapi
.extensions
.ExtensionPointName
;
38 import com
.intellij
.openapi
.extensions
.Extensions
;
39 import com
.intellij
.openapi
.project
.Project
;
40 import com
.intellij
.openapi
.project
.ProjectBundle
;
41 import com
.intellij
.openapi
.project
.ProjectManagerAdapter
;
42 import com
.intellij
.openapi
.project
.ex
.ProjectEx
;
43 import com
.intellij
.openapi
.project
.ex
.ProjectManagerEx
;
44 import com
.intellij
.openapi
.ui
.Messages
;
45 import com
.intellij
.openapi
.ui
.ex
.MessagesEx
;
46 import com
.intellij
.openapi
.util
.Condition
;
47 import com
.intellij
.openapi
.util
.Key
;
48 import com
.intellij
.openapi
.util
.text
.StringUtil
;
49 import com
.intellij
.openapi
.vfs
.VirtualFile
;
50 import org
.jetbrains
.annotations
.NonNls
;
51 import org
.jetbrains
.annotations
.NotNull
;
52 import org
.jetbrains
.annotations
.Nullable
;
53 import org
.picocontainer
.*;
54 import org
.picocontainer
.defaults
.CachingComponentAdapter
;
55 import org
.picocontainer
.defaults
.ConstructorInjectionComponentAdapter
;
57 import java
.io
.IOException
;
58 import java
.text
.MessageFormat
;
60 import java
.util
.concurrent
.atomic
.AtomicBoolean
;
66 public class ProjectImpl
extends ComponentManagerImpl
implements ProjectEx
{
67 private static final Logger LOG
= Logger
.getInstance("#com.intellij.project.impl.ProjectImpl");
68 private static final String PLUGIN_SETTINGS_ERROR
= "Plugin Settings Error";
70 private ProjectManagerImpl myManager
;
72 private MyProjectManagerListener myProjectManagerListener
;
74 private AtomicBoolean mySavingInProgress
= new AtomicBoolean(false);
76 @NonNls private static final String PROJECT_LAYER
= "project-components";
78 public boolean myOptimiseTestLoadSpeed
;
79 @NonNls public static final String TEMPLATE_PROJECT_NAME
= "Default (Template) Project";
80 @NonNls private static final String DEPRECATED_MESSAGE
= "Deprecated method usage: {0}.\n" +
81 "This method will cease to exist in IDEA 7.0 final release.\n" +
82 "Please contact plugin developers for plugin update.";
84 private final Condition myDisposedCondition
= new Condition() {
85 public boolean value(final Object o
) {
89 private final String myName
;
91 public static Key
<Long
> CREATION_TIME
= Key
.create("ProjectImpl.CREATION_TIME");
93 protected ProjectImpl(ProjectManagerImpl manager
, String filePath
, boolean isOptimiseTestLoadSpeed
, String projectName
) {
94 super(ApplicationManager
.getApplication());
95 putUserData(CREATION_TIME
, System
.nanoTime());
97 getPicoContainer().registerComponentInstance(Project
.class, this);
99 getStateStore().setProjectFilePath(filePath
);
101 myOptimiseTestLoadSpeed
= isOptimiseTestLoadSpeed
;
104 myName
= isDefault() ? TEMPLATE_PROJECT_NAME
: projectName
== null ?
getStateStore().getProjectName() : projectName
;
107 protected void boostrapPicoContainer() {
108 Extensions
.instantiateArea(PluginManager
.AREA_IDEA_PROJECT
, this, null);
109 super.boostrapPicoContainer();
110 final MutablePicoContainer picoContainer
= getPicoContainer();
112 final ProjectStoreClassProvider projectStoreClassProvider
= (ProjectStoreClassProvider
)picoContainer
.getComponentInstanceOfType(ProjectStoreClassProvider
.class);
115 picoContainer
.registerComponentImplementation(ProjectPathMacroManager
.class);
116 picoContainer
.registerComponent(new ComponentAdapter() {
117 ComponentAdapter myDelegate
;
120 public ComponentAdapter
getDelegate() {
121 if (myDelegate
== null) {
123 final Class storeClass
= projectStoreClassProvider
.getProjectStoreClass(isDefault());
124 myDelegate
= new CachingComponentAdapter(
125 new ConstructorInjectionComponentAdapter(storeClass
, storeClass
, null, true));
131 public Object
getComponentKey() {
132 return IComponentStore
.class;
135 public Class
getComponentImplementation() {
136 return getDelegate().getComponentImplementation();
139 public Object
getComponentInstance(final PicoContainer container
) throws PicoInitializationException
, PicoIntrospectionException
{
140 return getDelegate().getComponentInstance(container
);
143 public void verify(final PicoContainer container
) throws PicoIntrospectionException
{
144 getDelegate().verify(container
);
147 public void accept(final PicoVisitor visitor
) {
148 visitor
.visitComponentAdapter(this);
149 getDelegate().accept(visitor
);
156 public IProjectStore
getStateStore() {
157 return (IProjectStore
)super.getStateStore();
160 public boolean isOpen() {
161 return ProjectManagerEx
.getInstanceEx().isProjectOpened(this);
164 public Condition
getDisposed() {
165 return myDisposedCondition
;
168 public boolean isInitialized() {
169 return isOpen() && !isDisposed() && StartupManagerEx
.getInstanceEx(this).startupActivityPassed();
172 public void loadProjectComponents() {
173 final Application app
= ApplicationManager
.getApplication();
174 final IdeaPluginDescriptor
[] plugins
= app
.getPlugins();
175 for (IdeaPluginDescriptor plugin
: plugins
) {
176 if (PluginManager
.shouldSkipPlugin(plugin
)) continue;
177 loadComponentsConfiguration(plugin
.getProjectComponents(), plugin
, isDefault());
183 public String
getProjectFilePath() {
185 MessageFormat
.format(DEPRECATED_MESSAGE
, "ProjectImpl.getProjectFilePath()"),
187 return getStateStore().getProjectFilePath();
193 public VirtualFile
getProjectFile() {
195 MessageFormat
.format(DEPRECATED_MESSAGE
, "ProjectImpl.getProjectFile()"),
197 return getStateStore().getProjectFile();
201 public VirtualFile
getBaseDir() {
202 return getStateStore().getProjectBaseDir();
206 public String
getName() {
212 public String
getPresentableUrl() {
213 return getStateStore().getPresentableUrl();
218 public String
getLocationHash() {
219 String str
= getPresentableUrl();
220 if (str
== null) str
= getName();
222 return getName() + Integer
.toHexString(str
.hashCode());
227 public String
getLocation() {
228 return isDisposed() ?
null : getStateStore().getLocation();
233 public VirtualFile
getWorkspaceFile() {
235 MessageFormat
.format(DEPRECATED_MESSAGE
, "ProjectImpl.getWorkspaceFile()"),
237 return getStateStore().getWorkspaceFile();
240 public boolean isOptimiseTestLoadSpeed() {
241 return myOptimiseTestLoadSpeed
;
244 public void setOptimiseTestLoadSpeed(final boolean optimiseTestLoadSpeed
) {
245 myOptimiseTestLoadSpeed
= optimiseTestLoadSpeed
;
251 getMessageBus().syncPublisher(ProjectLifecycleListener
.TOPIC
).projectComponentsInitialized(this);
252 myProjectManagerListener
= new MyProjectManagerListener();
253 myManager
.addProjectManagerListener(this, myProjectManagerListener
);
257 if (ApplicationManagerEx
.getApplicationEx().isDoNotSave()) return; //no need to save
259 if (mySavingInProgress
.compareAndSet(false, true)) {
263 catch (IComponentStore
.SaveCancelledException e
) {
266 catch (PluginException e
) {
267 PluginManager
.disablePlugin(e
.getPluginId().getIdString());
268 Notifications
.Bus
.notify(new Notification(PLUGIN_SETTINGS_ERROR
, "Unable to save plugin settings!",
269 "<p>The plugin <i>" + e
.getPluginId() + "</i> failed to save settings and has been disabled. Please restart" +
270 ApplicationNamesInfo
.getInstance().getFullProductName() + "</p>" +
271 (ApplicationManagerEx
.getApplicationEx().isInternal() ?
"<p>" + StringUtil
.getThrowableText(e
) + "</p>": ""),
272 NotificationType
.ERROR
), NotificationDisplayType
.BALLOON
, this);
273 LOG
.info("Unable to save plugin settings",e
);
275 catch (IOException e
) {
276 MessagesEx
.error(this, ProjectBundle
.message("project.save.error", ApplicationManagerEx
.getApplicationEx().isInternal()
277 ? StringUtil
.getThrowableText(e
)
278 : e
.getMessage())).showLater();
279 LOG
.info("Error saving project", e
);
281 mySavingInProgress
.set(false);
286 public synchronized void dispose() {
287 ApplicationEx application
= ApplicationManagerEx
.getApplicationEx();
288 assert application
.isHeadlessEnvironment() || application
.isUnitTestMode() || application
.isDispatchThread() || application
.isInModalProgressThread();
289 LOG
.assertTrue(!isDisposed());
290 if (myProjectManagerListener
!= null) {
291 myManager
.removeProjectManagerListener(this, myProjectManagerListener
);
295 Extensions
.disposeArea(this);
297 myProjectManagerListener
= null;
301 if (!application
.isDisposed()) {
302 application
.getMessageBus().syncPublisher(ProjectLifecycleListener
.TOPIC
).afterProjectClosed(this);
306 private void projectOpened() {
307 final ProjectComponent
[] components
= getComponents(ProjectComponent
.class);
308 for (ProjectComponent component
: components
) {
310 component
.projectOpened();
312 catch (Throwable e
) {
319 private void projectClosed() {
320 List
<ProjectComponent
> components
= new ArrayList
<ProjectComponent
>(Arrays
.asList(getComponents(ProjectComponent
.class)));
321 Collections
.reverse(components
);
322 for (ProjectComponent component
: components
) {
324 component
.projectClosed();
326 catch (Throwable e
) {
332 public <T
> T
[] getExtensions(final ExtensionPointName
<T
> extensionPointName
) {
333 return Extensions
.getArea(this).getExtensionPoint(extensionPointName
).getExtensions();
336 public String
getDefaultName() {
337 if (isDefault()) return TEMPLATE_PROJECT_NAME
;
339 return getStateStore().getProjectName();
342 private class MyProjectManagerListener
extends ProjectManagerAdapter
{
343 public void projectOpened(Project project
) {
344 LOG
.assertTrue(project
== ProjectImpl
.this);
345 ProjectImpl
.this.projectOpened();
348 public void projectClosed(Project project
) {
349 LOG
.assertTrue(project
== ProjectImpl
.this);
350 ProjectImpl
.this.projectClosed();
354 protected MutablePicoContainer
createPicoContainer() {
355 return Extensions
.getArea(this).getPicoContainer();
358 public boolean isDefault() {
362 public void checkUnknownMacros(final boolean showDialog
) {
363 final IProjectStore stateStore
= getStateStore();
365 final TrackingPathMacroSubstitutor
[] substitutors
= stateStore
.getSubstitutors();
366 final Set
<String
> unknownMacros
= new HashSet
<String
>();
367 for (final TrackingPathMacroSubstitutor substitutor
: substitutors
) {
368 unknownMacros
.addAll(substitutor
.getUnknownMacros(null));
371 if (!unknownMacros
.isEmpty()) {
372 if (!showDialog
|| ProjectMacrosUtil
.checkMacros(this, new HashSet
<String
>(unknownMacros
))) {
373 final PathMacros pathMacros
= PathMacros
.getInstance();
374 final Set
<String
> macros2invalidate
= new HashSet
<String
>(unknownMacros
);
375 for (Iterator it
= macros2invalidate
.iterator(); it
.hasNext();) {
376 final String macro
= (String
)it
.next();
377 final String value
= pathMacros
.getValue(macro
);
378 if (null == value
|| value
.trim().length() == 0) {
383 if (!macros2invalidate
.isEmpty()) {
384 final Set
<String
> components
= new HashSet
<String
>();
385 for (TrackingPathMacroSubstitutor substitutor
: substitutors
) {
386 components
.addAll(substitutor
.getComponents(macros2invalidate
));
389 if (stateStore
.isReloadPossible(components
)) {
390 for (final TrackingPathMacroSubstitutor substitutor
: substitutors
) {
391 substitutor
.invalidateUnknownMacros(macros2invalidate
);
394 final UnknownMacroNotification
[] notifications
=
395 NotificationsManager
.getNotificationsManager().getNotificationsOfType(UnknownMacroNotification
.class, this);
396 for (final UnknownMacroNotification notification
: notifications
) {
397 if (macros2invalidate
.containsAll(notification
.getMacros())) notification
.expire();
400 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
402 stateStore
.reinitComponents(components
, true);
407 if (Messages
.showYesNoDialog(this, "Component could not be reloaded. Reload project?", "Configuration changed",
408 Messages
.getQuestionIcon()) == 0) {
409 ProjectManagerEx
.getInstanceEx().reloadProject(this);
418 public String
toString() {
420 + (isDisposed() ?
"(Disposed) " : "")
421 + (isDefault() ?
"(Default) " : "'" + getLocation()+"'")