[ex] 17284 AE: ComponentManagerImpl.getStateStore + Notification Model synchronizatio...
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / project / impl / ProjectImpl.java
blob3a9b498ea3b6a23aafcb23b029487a211ff302f7
1 /*
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;
59 import java.util.*;
60 import java.util.concurrent.atomic.AtomicBoolean;
63 /**
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) {
86 return isDisposed();
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;
103 myManager = manager;
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));
128 return myDelegate;
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);
155 @NotNull
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());
181 @Deprecated
182 @NotNull
183 public String getProjectFilePath() {
184 LOG.warn(
185 MessageFormat.format(DEPRECATED_MESSAGE, "ProjectImpl.getProjectFilePath()"),
186 new Throwable());
187 return getStateStore().getProjectFilePath();
191 @Deprecated
192 @Nullable
193 public VirtualFile getProjectFile() {
194 LOG.warn(
195 MessageFormat.format(DEPRECATED_MESSAGE, "ProjectImpl.getProjectFile()"),
196 new Throwable());
197 return getStateStore().getProjectFile();
200 @Nullable
201 public VirtualFile getBaseDir() {
202 return getStateStore().getProjectBaseDir();
205 @NotNull
206 public String getName() {
207 return myName;
210 @Nullable
211 @NonNls
212 public String getPresentableUrl() {
213 return getStateStore().getPresentableUrl();
216 @NotNull
217 @NonNls
218 public String getLocationHash() {
219 String str = getPresentableUrl();
220 if (str == null) str = getName();
222 return getName() + Integer.toHexString(str.hashCode());
225 @Nullable
226 @NonNls
227 public String getLocation() {
228 return isDisposed() ? null : getStateStore().getLocation();
231 @Deprecated
232 @Nullable
233 public VirtualFile getWorkspaceFile() {
234 LOG.warn(
235 MessageFormat.format(DEPRECATED_MESSAGE, "ProjectImpl.getWorkspaceFile()"),
236 new Throwable());
237 return getStateStore().getWorkspaceFile();
240 public boolean isOptimiseTestLoadSpeed() {
241 return myOptimiseTestLoadSpeed;
244 public void setOptimiseTestLoadSpeed(final boolean optimiseTestLoadSpeed) {
245 myOptimiseTestLoadSpeed = optimiseTestLoadSpeed;
249 public void init() {
250 super.init();
251 getMessageBus().syncPublisher(ProjectLifecycleListener.TOPIC).projectComponentsInitialized(this);
252 myProjectManagerListener = new MyProjectManagerListener();
253 myManager.addProjectManagerListener(this, myProjectManagerListener);
256 public void save() {
257 if (ApplicationManagerEx.getApplicationEx().isDoNotSave()) return; //no need to save
259 if (mySavingInProgress.compareAndSet(false, true)) {
260 try {
261 doSave();
263 catch (IComponentStore.SaveCancelledException e) {
264 LOG.info(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);
280 } finally {
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);
294 disposeComponents();
295 Extensions.disposeArea(this);
296 myManager = null;
297 myProjectManagerListener = null;
299 super.dispose();
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) {
309 try {
310 component.projectOpened();
312 catch (Throwable e) {
313 LOG.error(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) {
323 try {
324 component.projectClosed();
326 catch (Throwable e) {
327 LOG.error(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() {
359 return false;
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) {
379 it.remove();
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() {
401 public void run() {
402 stateStore.reinitComponents(components, true);
406 else {
407 if (Messages.showYesNoDialog(this, "Component could not be reloaded. Reload project?", "Configuration changed",
408 Messages.getQuestionIcon()) == 0) {
409 ProjectManagerEx.getInstanceEx().reloadProject(this);
417 @Override
418 public String toString() {
419 return "Project "
420 + (isDisposed() ? "(Disposed) " : "")
421 + (isDefault() ? "(Default) " : "'" + getLocation()+"'")