merge editing unknown macros for modules & project + expire notifications
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / project / impl / ProjectImpl.java
blobac88a85697c411a6285f66ca4ac4c3517741782b
1 package com.intellij.openapi.project.impl;
3 import com.intellij.diagnostic.PluginException;
4 import com.intellij.ide.plugins.IdeaPluginDescriptor;
5 import com.intellij.ide.plugins.PluginManager;
6 import com.intellij.ide.startup.StartupManagerEx;
7 import com.intellij.notification.NotificationsManager;
8 import com.intellij.openapi.application.Application;
9 import com.intellij.openapi.application.ApplicationManager;
10 import com.intellij.openapi.application.ApplicationNamesInfo;
11 import com.intellij.openapi.application.PathMacros;
12 import com.intellij.openapi.application.ex.ApplicationEx;
13 import com.intellij.openapi.application.ex.ApplicationManagerEx;
14 import com.intellij.openapi.components.ProjectComponent;
15 import com.intellij.openapi.components.TrackingPathMacroSubstitutor;
16 import com.intellij.openapi.components.impl.ComponentManagerImpl;
17 import com.intellij.openapi.components.impl.ProjectPathMacroManager;
18 import com.intellij.openapi.components.impl.stores.IComponentStore;
19 import com.intellij.openapi.components.impl.stores.IProjectStore;
20 import com.intellij.openapi.components.impl.stores.UnknownMacroNotification;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.extensions.ExtensionPointName;
23 import com.intellij.openapi.extensions.Extensions;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.project.ProjectBundle;
26 import com.intellij.openapi.project.ProjectManagerAdapter;
27 import com.intellij.openapi.project.ex.ProjectEx;
28 import com.intellij.openapi.project.ex.ProjectManagerEx;
29 import com.intellij.openapi.ui.Messages;
30 import com.intellij.openapi.ui.ex.MessagesEx;
31 import com.intellij.openapi.util.Condition;
32 import com.intellij.openapi.util.Key;
33 import com.intellij.openapi.util.text.StringUtil;
34 import com.intellij.openapi.vfs.VirtualFile;
35 import org.jetbrains.annotations.NonNls;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
38 import org.picocontainer.*;
39 import org.picocontainer.defaults.CachingComponentAdapter;
40 import org.picocontainer.defaults.ConstructorInjectionComponentAdapter;
42 import java.io.IOException;
43 import java.text.MessageFormat;
44 import java.util.*;
45 import java.util.concurrent.atomic.AtomicBoolean;
48 /**
51 public class ProjectImpl extends ComponentManagerImpl implements ProjectEx {
52 private static final Logger LOG = Logger.getInstance("#com.intellij.project.impl.ProjectImpl");
54 private ProjectManagerImpl myManager;
56 private MyProjectManagerListener myProjectManagerListener;
58 private AtomicBoolean mySavingInProgress = new AtomicBoolean(false);
60 @NonNls private static final String PROJECT_LAYER = "project-components";
62 public boolean myOptimiseTestLoadSpeed;
63 @NonNls public static final String TEMPLATE_PROJECT_NAME = "Default (Template) Project";
64 @NonNls private static final String DEPRECATED_MESSAGE = "Deprecated method usage: {0}.\n" +
65 "This method will cease to exist in IDEA 7.0 final release.\n" +
66 "Please contact plugin developers for plugin update.";
68 private final Condition myDisposedCondition = new Condition() {
69 public boolean value(final Object o) {
70 return isDisposed();
73 private final String myName;
75 public static Key<Long> CREATION_TIME = Key.create("ProjectImpl.CREATION_TIME");
77 protected ProjectImpl(ProjectManagerImpl manager, String filePath, boolean isOptimiseTestLoadSpeed, String projectName) {
78 super(ApplicationManager.getApplication());
79 putUserData(CREATION_TIME, System.nanoTime());
81 getPicoContainer().registerComponentInstance(Project.class, this);
83 getStateStore().setProjectFilePath(filePath);
85 myOptimiseTestLoadSpeed = isOptimiseTestLoadSpeed;
87 myManager = manager;
88 myName = isDefault() ? TEMPLATE_PROJECT_NAME : projectName == null ? getStateStore().getProjectName() : projectName;
91 protected void boostrapPicoContainer() {
92 Extensions.instantiateArea(PluginManager.AREA_IDEA_PROJECT, this, null);
93 super.boostrapPicoContainer();
94 final MutablePicoContainer picoContainer = getPicoContainer();
96 final ProjectStoreClassProvider projectStoreClassProvider = (ProjectStoreClassProvider)picoContainer.getComponentInstanceOfType(ProjectStoreClassProvider.class);
99 picoContainer.registerComponentImplementation(ProjectPathMacroManager.class);
100 picoContainer.registerComponent(new ComponentAdapter() {
101 ComponentAdapter myDelegate;
104 public ComponentAdapter getDelegate() {
105 if (myDelegate == null) {
107 final Class storeClass = projectStoreClassProvider.getProjectStoreClass(isDefault());
108 myDelegate = new CachingComponentAdapter(
109 new ConstructorInjectionComponentAdapter(storeClass, storeClass, null, true));
112 return myDelegate;
115 public Object getComponentKey() {
116 return IComponentStore.class;
119 public Class getComponentImplementation() {
120 return getDelegate().getComponentImplementation();
123 public Object getComponentInstance(final PicoContainer container) throws PicoInitializationException, PicoIntrospectionException {
124 return getDelegate().getComponentInstance(container);
127 public void verify(final PicoContainer container) throws PicoIntrospectionException {
128 getDelegate().verify(container);
131 public void accept(final PicoVisitor visitor) {
132 visitor.visitComponentAdapter(this);
133 getDelegate().accept(visitor);
139 @NotNull
140 public IProjectStore getStateStore() {
141 return (IProjectStore)super.getStateStore();
144 public boolean isOpen() {
145 return ProjectManagerEx.getInstanceEx().isProjectOpened(this);
148 public Condition getDisposed() {
149 return myDisposedCondition;
152 public boolean isInitialized() {
153 return isOpen() && !isDisposed() && StartupManagerEx.getInstanceEx(this).startupActivityPassed();
156 public void loadProjectComponents() {
157 final Application app = ApplicationManager.getApplication();
158 final IdeaPluginDescriptor[] plugins = app.getPlugins();
159 for (IdeaPluginDescriptor plugin : plugins) {
160 if (PluginManager.shouldSkipPlugin(plugin)) continue;
161 loadComponentsConfiguration(plugin.getProjectComponents(), plugin, isDefault());
165 @Deprecated
166 @NotNull
167 public String getProjectFilePath() {
168 LOG.warn(
169 MessageFormat.format(DEPRECATED_MESSAGE, "ProjectImpl.getProjectFilePath()"),
170 new Throwable());
171 return getStateStore().getProjectFilePath();
175 @Deprecated
176 @Nullable
177 public VirtualFile getProjectFile() {
178 LOG.warn(
179 MessageFormat.format(DEPRECATED_MESSAGE, "ProjectImpl.getProjectFile()"),
180 new Throwable());
181 return getStateStore().getProjectFile();
184 @Nullable
185 public VirtualFile getBaseDir() {
186 return getStateStore().getProjectBaseDir();
189 @NotNull
190 public String getName() {
191 return myName;
194 @Nullable
195 @NonNls
196 public String getPresentableUrl() {
197 return getStateStore().getPresentableUrl();
200 @NotNull
201 @NonNls
202 public String getLocationHash() {
203 String str = getPresentableUrl();
204 if (str == null) str = getName();
206 return getName() + Integer.toHexString(str.hashCode());
209 @NotNull
210 @NonNls
211 public String getLocation() {
212 return getStateStore().getLocation();
215 @Deprecated
216 @Nullable
217 public VirtualFile getWorkspaceFile() {
218 LOG.warn(
219 MessageFormat.format(DEPRECATED_MESSAGE, "ProjectImpl.getWorkspaceFile()"),
220 new Throwable());
221 return getStateStore().getWorkspaceFile();
224 public boolean isOptimiseTestLoadSpeed() {
225 return myOptimiseTestLoadSpeed;
228 public void setOptimiseTestLoadSpeed(final boolean optimiseTestLoadSpeed) {
229 myOptimiseTestLoadSpeed = optimiseTestLoadSpeed;
233 public void init() {
234 super.init();
235 getMessageBus().syncPublisher(ProjectLifecycleListener.TOPIC).projectComponentsInitialized(this);
236 myProjectManagerListener = new MyProjectManagerListener();
237 myManager.addProjectManagerListener(this, myProjectManagerListener);
240 public void save() {
241 if (ApplicationManagerEx.getApplicationEx().isDoNotSave()) return; //no need to save
243 if (mySavingInProgress.compareAndSet(false, true)) {
244 try {
245 doSave();
247 catch (IComponentStore.SaveCancelledException e) {
248 LOG.info(e);
250 catch (PluginException e) {
251 PluginManager.disablePlugin(e.getPluginId().getIdString());
252 MessagesEx.error(this, "The plugin " +
253 e.getPluginId() +
254 " failed to save settings and has been disabled. Please restart " +
255 ApplicationNamesInfo.getInstance().getFullProductName() +
256 (ApplicationManagerEx.getApplicationEx().isInternal() ? "\n" + StringUtil.getThrowableText(e) : ""));
258 catch (IOException e) {
259 MessagesEx.error(this, ProjectBundle.message("project.save.error", ApplicationManagerEx.getApplicationEx().isInternal()
260 ? StringUtil.getThrowableText(e)
261 : e.getMessage())).showLater();
262 } finally {
263 mySavingInProgress.set(false);
268 public synchronized void dispose() {
269 ApplicationEx application = ApplicationManagerEx.getApplicationEx();
270 assert application.isHeadlessEnvironment() || application.isUnitTestMode() || application.isDispatchThread() || application.isInModalProgressThread();
271 LOG.assertTrue(!isDisposed());
272 if (myProjectManagerListener != null) {
273 myManager.removeProjectManagerListener(this, myProjectManagerListener);
276 disposeComponents();
277 Extensions.disposeArea(this);
278 myManager = null;
279 myProjectManagerListener = null;
281 super.dispose();
283 if (!application.isDisposed()) {
284 application.getMessageBus().syncPublisher(ProjectLifecycleListener.TOPIC).afterProjectClosed(this);
288 private void projectOpened() {
289 final ProjectComponent[] components = getComponents(ProjectComponent.class);
290 for (ProjectComponent component : components) {
291 try {
292 component.projectOpened();
294 catch (Throwable e) {
295 LOG.error(e);
301 private void projectClosed() {
302 List<ProjectComponent> components = new ArrayList<ProjectComponent>(Arrays.asList(getComponents(ProjectComponent.class)));
303 Collections.reverse(components);
304 for (ProjectComponent component : components) {
305 try {
306 component.projectClosed();
308 catch (Throwable e) {
309 LOG.error(e);
314 public <T> T[] getExtensions(final ExtensionPointName<T> extensionPointName) {
315 return Extensions.getArea(this).getExtensionPoint(extensionPointName).getExtensions();
318 public String getDefaultName() {
319 if (isDefault()) return TEMPLATE_PROJECT_NAME;
321 return getStateStore().getProjectName();
324 private class MyProjectManagerListener extends ProjectManagerAdapter {
325 public void projectOpened(Project project) {
326 LOG.assertTrue(project == ProjectImpl.this);
327 ProjectImpl.this.projectOpened();
330 public void projectClosed(Project project) {
331 LOG.assertTrue(project == ProjectImpl.this);
332 ProjectImpl.this.projectClosed();
336 protected MutablePicoContainer createPicoContainer() {
337 return Extensions.getArea(this).getPicoContainer();
340 public boolean isDefault() {
341 return false;
344 public void checkUnknownMacros() {
345 final IProjectStore stateStore = getStateStore();
347 final TrackingPathMacroSubstitutor[] substitutors = stateStore.getSubstitutors();
348 final Set<String> unknownMacros = new HashSet<String>();
349 for (final TrackingPathMacroSubstitutor substitutor : substitutors) {
350 unknownMacros.addAll(substitutor.getUnknownMacros(null));
353 if (!unknownMacros.isEmpty()) {
354 if (ProjectMacrosUtil.checkMacros(this, new HashSet<String>(unknownMacros))) {
355 final PathMacros pathMacros = PathMacros.getInstance();
356 final Set<String> macros2invalidate = new HashSet<String>(unknownMacros);
357 for (Iterator it = macros2invalidate.iterator(); it.hasNext();) {
358 final String macro = (String)it.next();
359 final String value = pathMacros.getValue(macro);
360 if (null == value || value.trim().length() == 0) {
361 it.remove();
365 if (!macros2invalidate.isEmpty()) {
366 final Set<String> components = new HashSet<String>();
367 for (TrackingPathMacroSubstitutor substitutor : substitutors) {
368 components.addAll(substitutor.getComponents(macros2invalidate));
371 if (stateStore.isReloadPossible(components)) {
372 ApplicationManager.getApplication().runWriteAction(new Runnable() {
373 public void run() {
374 stateStore.reinitComponents(components, true);
378 for (final TrackingPathMacroSubstitutor substitutor : substitutors) {
379 substitutor.invalidateUnknownMacros(macros2invalidate);
382 final UnknownMacroNotification[] notifications =
383 NotificationsManager.getNotificationsManager().getNotificationsOfType(UnknownMacroNotification.class, this);
384 for (final UnknownMacroNotification notification : notifications) {
385 if (macros2invalidate.containsAll(notification.getMacros())) notification.expire();
388 else {
389 if (Messages.showYesNoDialog(this, "Component could not be reloaded. Reload project?", "Configuration changed",
390 Messages.getQuestionIcon()) == 0) {
391 ProjectManagerEx.getInstanceEx().reloadProject(this);
399 @Override
400 public String toString() {
401 return "Project "
402 + (isDisposed() ? "(Disposed) " : "")
403 + (isDefault() ? "(Default) " : "'" + getLocation()+"'")