From 5a0f59add844d852e78acdd8544b45e99bf25ed5 Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Tue, 6 Oct 2009 13:01:45 +0400 Subject: [PATCH] notifications for unknown macros --- .../compiler/ant/GenerationOptionsImpl.java | 2 +- .../projectWizard/ExistingModuleLoader.java | 8 +- .../testFramework/ModuleTestCase.java | 2 +- .../impl/ConversionContextImpl.java | 2 +- .../impl/stores/ModuleStoreImpl.java | 60 +++++++++++- .../stores/ProjectWithModulesStoreImpl.java | 10 +- .../roots/impl/storage/ClasspathStorage.java | 9 +- .../openapi/components/StateStorage.java | 2 +- .../StorageAnnotationsDefaultValues.java | 2 +- .../options/PathMacrosCollector.java | 4 +- .../options/ReplacePathToMacroMap.java | 41 ++------ .../pathMacros/PathMacroListEditor.java | 4 +- .../options/pathMacros/PathMacroTable.java | 6 +- .../components/ExpandMacroToPathMap.java | 3 +- .../openapi/components/PathMacroMap.java | 34 +++---- .../TrackingPathMacroSubstitutor.java | 9 +- .../components/ex/ComponentManagerEx.java | 5 + .../components/impl/BasePathMacroManager.java | 93 ++++++++++++++----- .../components/impl/ComponentManagerImpl.java | 57 ++++++++++-- .../components/impl/ServiceManagerImpl.java | 6 +- .../impl/stores/ApplicationStoreImpl.java | 2 +- .../stores/BaseFileConfigurableStoreImpl.java | 2 +- .../impl/stores/ComponentStoreImpl.java | 36 ++++--- .../impl/stores/DefaultProjectStoreImpl.java | 5 + .../impl/stores/DefaultsStateStorage.java | 2 +- .../impl/stores/DirectoryBasedStorage.java | 9 +- .../impl/stores/IComponentStore.java | 7 +- .../stores/ProjectStateStorageManager.java | 2 +- .../impl/stores/ProjectStoreImpl.java | 34 ++++++- .../impl/stores/StateStorageManager.java | 7 +- .../impl/stores/StateStorageManagerImpl.java | 5 + .../impl/stores/XmlElementStorage.java | 33 ++++--- .../project/impl/ProjectMacrosUtil.java | 10 +- .../project/impl/ProjectManagerImpl.java | 29 ++++++ .../impl/UndefinedMacrosConfigurable.java | 6 +- .../src/com/intellij/mock/MockProject.java | 4 + .../com/intellij/mock/MockProjectStore.java | 10 +- .../importWizard/EclipseImportBuilder.java | 7 +- .../javaee/ExternalResourceManagerImpl.java | 4 +- 39 files changed, 383 insertions(+), 190 deletions(-) rename {java/java-impl => platform/platform-impl}/src/com/intellij/application/options/PathMacrosCollector.java (87%) diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/GenerationOptionsImpl.java b/java/compiler/impl/src/com/intellij/compiler/ant/GenerationOptionsImpl.java index c4adfee634f4..c995139dce1e 100644 --- a/java/compiler/impl/src/com/intellij/compiler/ant/GenerationOptionsImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/ant/GenerationOptionsImpl.java @@ -131,7 +131,7 @@ public class GenerationOptionsImpl extends GenerationOptions { if (myMacroReplacementMap.size() == 0) { return path; // optimization } - return myMacroReplacementMap.substitute(path, SystemInfo.isFileSystemCaseSensitive, null); + return myMacroReplacementMap.substitute(path, SystemInfo.isFileSystemCaseSensitive); } public String getPropertyRefForUrl(String url) { diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ExistingModuleLoader.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ExistingModuleLoader.java index 53ffe47fb57e..69442ef7de05 100644 --- a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ExistingModuleLoader.java +++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ExistingModuleLoader.java @@ -40,7 +40,6 @@ import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; -import java.util.HashMap; import java.util.Set; /** @@ -87,13 +86,8 @@ public class ExistingModuleLoader extends ModuleBuilder { usedMacros.remove("$" + PathMacrosImpl.MODULE_DIR_MACRO_NAME + "$"); usedMacros.removeAll(definedMacros); - final HashMap map = new HashMap(); - for (String v : usedMacros) { - map.put(v, null); - } - if (usedMacros.size() > 0) { - final boolean ok = ProjectMacrosUtil.showMacrosConfigurationDialog(current, map); + final boolean ok = ProjectMacrosUtil.showMacrosConfigurationDialog(current, usedMacros); if (!ok) { return false; } diff --git a/java/testFramework/src/com/intellij/testFramework/ModuleTestCase.java b/java/testFramework/src/com/intellij/testFramework/ModuleTestCase.java index 839da0fcc102..00ed66fd78fe 100644 --- a/java/testFramework/src/com/intellij/testFramework/ModuleTestCase.java +++ b/java/testFramework/src/com/intellij/testFramework/ModuleTestCase.java @@ -122,7 +122,7 @@ public abstract class ModuleTestCase extends IdeaTestCase { final ProjectImpl project = (ProjectImpl)myProject; project.setOptimiseTestLoadSpeed(false); final ModuleRootManagerImpl moduleRootManager = (ModuleRootManagerImpl)ModuleRootManager.getInstance(module); - module.getStateStore().initComponent(moduleRootManager); + module.getStateStore().initComponent(moduleRootManager, false); project.setOptimiseTestLoadSpeed(true); } diff --git a/platform/lang-impl/src/com/intellij/conversion/impl/ConversionContextImpl.java b/platform/lang-impl/src/com/intellij/conversion/impl/ConversionContextImpl.java index 6064149856bb..917d30d7465a 100644 --- a/platform/lang-impl/src/com/intellij/conversion/impl/ConversionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/conversion/impl/ConversionContextImpl.java @@ -116,7 +116,7 @@ public class ConversionContextImpl implements ConversionContext { List files = new ArrayList(); for (Element module : JDomConvertingUtil.getChildren(modules, ModuleManagerImpl.ELEMENT_MODULE)) { String filePath = module.getAttributeValue(ModuleManagerImpl.ATTRIBUTE_FILEPATH); - filePath = macros.substitute(filePath, true, null); + filePath = macros.substitute(filePath, true); files.add(new File(FileUtil.toSystemDependentName(filePath))); } return files.toArray(new File[files.size()]); diff --git a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java index ee64cb2ceaf1..ad8bb819bec0 100644 --- a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java @@ -1,14 +1,20 @@ package com.intellij.openapi.components.impl.stores; +import com.intellij.notification.*; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PathMacroManager; import com.intellij.openapi.components.PathMacroSubstitutor; import com.intellij.openapi.components.StateStorage; +import com.intellij.openapi.components.TrackingPathMacroSubstitutor; import com.intellij.openapi.components.impl.ComponentManagerImpl; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleTypeManager; import com.intellij.openapi.module.impl.ModuleImpl; +import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ex.ProjectEx; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import org.jdom.Attribute; @@ -17,12 +23,10 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.event.HyperlinkEvent; import java.io.File; import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; +import java.util.*; public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IModuleStore { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.ModuleStoreImpl"); @@ -45,7 +49,32 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM return storage; } + @Override + public String initComponent(@NotNull Object component, boolean service) { + final String componentName = super.initComponent(component, service); + if (!ApplicationManager.getApplication().isHeadlessEnvironment() && !ApplicationManager.getApplication().isUnitTestMode()) { + if (service && componentName != null) { + final TrackingPathMacroSubstitutor substitutor = getStateStorageManager().getMacroSubstitutor(); + if (substitutor != null) { + final Collection macros = substitutor.getUnknownMacros(componentName); + if (!macros.isEmpty()) { + Notifications.Bus.notify(new Notification("Load Error", "Error loading component", + String.format("

Undefined Path Variables: %s. Fix it!

", + StringUtil.join(macros, ", ")), NotificationType.ERROR, + new NotificationListener() { + public void hyperlinkUpdate(@NotNull Notification notification, + @NotNull HyperlinkEvent event) { + myModule.checkUnknownMacros(myModule.getProject(), notification); + } + }), NotificationDisplayType.STICKY_BALLOON, myModule.getProject()); + } + } + } + } + + return componentName; + } @Override public void load() throws IOException, StateStorage.StateStorageException { @@ -54,8 +83,29 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM final ModuleFileData storageData = getMainStorageData(); final String moduleTypeId = storageData.myOptions.get(ModuleImpl.ELEMENT_TYPE); myModule.setModuleType(ModuleTypeManager.getInstance().findByID(moduleTypeId)); - } + if (ApplicationManager.getApplication().isHeadlessEnvironment() || ApplicationManager.getApplication().isUnitTestMode()) return; + + final TrackingPathMacroSubstitutor substitutor = getStateStorageManager().getMacroSubstitutor(); + if (substitutor != null) { + final Collection macros = substitutor.getUnknownMacros(null); + if (!macros.isEmpty()) { + final Project project = myModule.getProject(); + StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { + public void run() { + Notifications.Bus.notify(new Notification("Load Error", String.format("Error loading module '%s':", myModule.getName()), + String.format( + "

Undefined Path Variable(s): %s. Fix it!

", + StringUtil.join(macros, ", ")), NotificationType.ERROR, new NotificationListener() { + public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { + myModule.checkUnknownMacros(myModule.getProject(), notification); + } + }), NotificationDisplayType.STICKY_BALLOON, project); + } + }); + } + } + } public ModuleFileData getMainStorageData() throws StateStorage.StateStorageException { return (ModuleFileData)super.getMainStorageData(); diff --git a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java index 4cdd698ec6b3..4aa23cb1e6c4 100644 --- a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java @@ -21,19 +21,19 @@ public class ProjectWithModulesStoreImpl extends ProjectStoreImpl { super(project); } - protected void reinitComponents(final Set componentNames) { - super.reinitComponents(componentNames); + public void reinitComponents(final Set componentNames, final boolean reloadData) { + super.reinitComponents(componentNames, reloadData); for (Module module : getPersistentModules()) { - ((ComponentStoreImpl)((ModuleImpl)module).getStateStore()).reinitComponents(componentNames); + ((ModuleImpl)module).getStateStore().reinitComponents(componentNames, reloadData); } } - protected boolean isReloadPossible(final Set componentNames) { + public boolean isReloadPossible(final Set componentNames) { if (!super.isReloadPossible(componentNames)) return false; for (Module module : getPersistentModules()) { - if (!((ComponentStoreImpl)((ModuleImpl)module).getStateStore()).isReloadPossible(componentNames)) return false; + if (!((ModuleImpl)module).getStateStore().isReloadPossible(componentNames)) return false; } return true; diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java index 5647c14cabc3..082d83283f63 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java @@ -106,11 +106,8 @@ public class ClasspathStorage implements StateStorage { model.dispose(); } } - final HashMap map = new HashMap(); - for (String v : macros) { - map.put(v, null); - } - final boolean macrosOk = ProjectMacrosUtil.checkMacros(module.getProject(), map); + + final boolean macrosOk = ProjectMacrosUtil.checkMacros(module.getProject(), macros); PathMacroManager.getInstance(module).expandPaths(element); ModuleRootManagerImpl.ModuleRootManagerState moduleRootManagerState = new ModuleRootManagerImpl.ModuleRootManagerState(); moduleRootManagerState.readExternal(element); @@ -128,7 +125,7 @@ public class ClasspathStorage implements StateStorage { } } - public boolean hasState(final Object component, final String componentName, final Class aClass) + public boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { return true; } diff --git a/platform/platform-api/src/com/intellij/openapi/components/StateStorage.java b/platform/platform-api/src/com/intellij/openapi/components/StateStorage.java index 272c127fe082..85c421eb3782 100644 --- a/platform/platform-api/src/com/intellij/openapi/components/StateStorage.java +++ b/platform/platform-api/src/com/intellij/openapi/components/StateStorage.java @@ -33,7 +33,7 @@ public interface StateStorage { @Nullable T getState(final Object component, final String componentName, Class stateClass, @Nullable T mergeInto) throws StateStorageException; - boolean hasState(final Object component, final String componentName, final Class aClass) throws StateStorageException; + boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException; @NotNull ExternalizationSession startExternalization(); diff --git a/platform/platform-api/src/com/intellij/openapi/components/StorageAnnotationsDefaultValues.java b/platform/platform-api/src/com/intellij/openapi/components/StorageAnnotationsDefaultValues.java index b04f58563038..ec42b3d2be3c 100644 --- a/platform/platform-api/src/com/intellij/openapi/components/StorageAnnotationsDefaultValues.java +++ b/platform/platform-api/src/com/intellij/openapi/components/StorageAnnotationsDefaultValues.java @@ -32,7 +32,7 @@ public interface StorageAnnotationsDefaultValues { throw new UnsupportedOperationException("Method getState is not supported in " + getClass()); } - public boolean hasState(final Object component, final String componentName, final Class aClass) throws StateStorageException { + public boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { throw new UnsupportedOperationException("Method hasState not implemented in " + getClass()); } diff --git a/java/java-impl/src/com/intellij/application/options/PathMacrosCollector.java b/platform/platform-impl/src/com/intellij/application/options/PathMacrosCollector.java similarity index 87% rename from java/java-impl/src/com/intellij/application/options/PathMacrosCollector.java rename to platform/platform-impl/src/com/intellij/application/options/PathMacrosCollector.java index 58c2acbf94a6..053bc5536e3e 100644 --- a/java/java-impl/src/com/intellij/application/options/PathMacrosCollector.java +++ b/platform/platform-impl/src/com/intellij/application/options/PathMacrosCollector.java @@ -26,11 +26,11 @@ public class PathMacrosCollector extends PathMacroMap { public static Set getMacroNames(Element root) { final PathMacrosCollector collector = new PathMacrosCollector(); - collector.substitute(root, true, null); + collector.substitute(root, true); return new HashSet(collector.myMacroMap.keySet()); } - public String substitute(String text, boolean caseSensitive, final Set usedMacros) { + public String substitute(String text, boolean caseSensitive) { myMatcher.reset(text); while (myMatcher.find()) { final String macroName = myMatcher.group(1); diff --git a/platform/platform-impl/src/com/intellij/application/options/ReplacePathToMacroMap.java b/platform/platform-impl/src/com/intellij/application/options/ReplacePathToMacroMap.java index f26160a7727a..267dab6127e4 100644 --- a/platform/platform-impl/src/com/intellij/application/options/ReplacePathToMacroMap.java +++ b/platform/platform-impl/src/com/intellij/application/options/ReplacePathToMacroMap.java @@ -51,10 +51,10 @@ public class ReplacePathToMacroMap extends PathMacroMap { } } - public String substitute(String text, boolean caseSensitive, final Set usedMacros) { + public String substitute(String text, boolean caseSensitive) { for (final String path : getPathIndex()) { final String macro = get(path); - text = replacePathMacro(text, path, macro, caseSensitive, usedMacros); + text = replacePathMacro(text, path, macro, caseSensitive); } return text; } @@ -62,8 +62,7 @@ public class ReplacePathToMacroMap extends PathMacroMap { private static String replacePathMacro(String text, String path, final String macro, - boolean caseSensitive, - final Set usedMacros) { + boolean caseSensitive) { if (text.length() < path.length() || path.length() == 0) { return text; } @@ -93,7 +92,6 @@ public class ReplacePathToMacroMap extends PathMacroMap { newText.append(macro); newText.append(text.substring(endOfOccurence)); - logUsage(macro, usedMacros); return newText.toString(); } @@ -103,10 +101,10 @@ public class ReplacePathToMacroMap extends PathMacroMap { } @Override - public String substituteRecursively(String text, final boolean caseSensitive, final Set usedMacros) { + public String substituteRecursively(String text, final boolean caseSensitive) { for (final String path : getPathIndex()) { final String macro = get(path); - text = replacePathMacroRecursively(text, path, macro, caseSensitive, usedMacros); + text = replacePathMacroRecursively(text, path, macro, caseSensitive); } return text; } @@ -114,8 +112,7 @@ public class ReplacePathToMacroMap extends PathMacroMap { private static String replacePathMacroRecursively(String text, String path, final String macro, - boolean caseSensitive, - final Set usedMacros) { + boolean caseSensitive) { if (text.length() < path.length()) { return text; } @@ -151,7 +148,6 @@ public class ReplacePathToMacroMap extends PathMacroMap { else { newText.append(text.substring(i, occurrenceOfPath)); newText.append(macro); - logUsage(macro, usedMacros); i = occurrenceOfPath + path.length(); } } @@ -162,31 +158,6 @@ public class ReplacePathToMacroMap extends PathMacroMap { } } - - private static void logUsage(String macroReplacement, final Set usedMacros) { - if (usedMacros == null) return; - - int idx = 0; - for (String protocol : PROTOCOLS) { - if (StringUtil.startsWithConcatenationOf(macroReplacement, protocol, "://")) { - idx = protocol.length() + 3; - } - else if (StringUtil.startsWithConcatenationOf(macroReplacement, protocol, ":/")) { - idx = protocol.length() + 2; - } - else if (StringUtil.startsWithConcatenationOf(macroReplacement, protocol, ":")) { - idx = protocol.length() + 1; - } - } - - macroReplacement = macroReplacement.substring(idx); - if (macroReplacement.length() >= 2 && macroReplacement.startsWith("$") && macroReplacement.endsWith("$")) { - macroReplacement = macroReplacement.substring(1, macroReplacement.length() - 1); - } - - usedMacros.add(macroReplacement); - } - public List getPathIndex() { if (myPathsIndex == null || myPathsIndex.size() != size()) { diff --git a/platform/platform-impl/src/com/intellij/application/options/pathMacros/PathMacroListEditor.java b/platform/platform-impl/src/com/intellij/application/options/pathMacros/PathMacroListEditor.java index 9ba8e966e2e7..3b81d3094542 100644 --- a/platform/platform-impl/src/com/intellij/application/options/pathMacros/PathMacroListEditor.java +++ b/platform/platform-impl/src/com/intellij/application/options/pathMacros/PathMacroListEditor.java @@ -7,7 +7,7 @@ import com.intellij.openapi.options.ConfigurationException; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.Map; +import java.util.Collection; /** * @author dsl @@ -24,7 +24,7 @@ public class PathMacroListEditor { this(null); } - public PathMacroListEditor(Map undefinedMacroNames) { + public PathMacroListEditor(final Collection undefinedMacroNames) { myPathMacroTable = undefinedMacroNames != null ? new PathMacroTable(undefinedMacroNames) : new PathMacroTable(); myScrollPane.setViewportView(myPathMacroTable); myAddButton.addActionListener(new ActionListener() { diff --git a/platform/platform-impl/src/com/intellij/application/options/pathMacros/PathMacroTable.java b/platform/platform-impl/src/com/intellij/application/options/pathMacros/PathMacroTable.java index 783dc51d9d3c..6d18697f2054 100644 --- a/platform/platform-impl/src/com/intellij/application/options/pathMacros/PathMacroTable.java +++ b/platform/platform-impl/src/com/intellij/application/options/pathMacros/PathMacroTable.java @@ -33,13 +33,13 @@ public class PathMacroTable extends Table { } }; - private final Map myUndefinedMacroNames; + private final Collection myUndefinedMacroNames; public PathMacroTable() { this(null); } - public PathMacroTable(Map undefinedMacroNames) { + public PathMacroTable(final Collection undefinedMacroNames) { myUndefinedMacroNames = undefinedMacroNames; setModel(myTableModel); TableColumn column = getColumnModel().getColumn(NAME_COLUMN); @@ -147,7 +147,7 @@ public class PathMacroTable extends Table { } if (myUndefinedMacroNames != null) { - for (String undefinedMacroName : myUndefinedMacroNames.keySet()) { + for (String undefinedMacroName : myUndefinedMacroNames) { macros.add(new Pair(undefinedMacroName, "")); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/ExpandMacroToPathMap.java b/platform/platform-impl/src/com/intellij/openapi/components/ExpandMacroToPathMap.java index a295341c7beb..defdfccf4d72 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/ExpandMacroToPathMap.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/ExpandMacroToPathMap.java @@ -7,7 +7,6 @@ package com.intellij.openapi.components; import com.intellij.openapi.util.text.StringUtil; import java.util.Map; -import java.util.Set; /** * @author Eugene Zhuravlev @@ -19,7 +18,7 @@ public class ExpandMacroToPathMap extends PathMacroMap { put("$" + macroName + "$", quotePath(path)); } - public String substitute(String text, boolean caseSensitive, final Set usedMacros) { + public String substitute(String text, boolean caseSensitive) { for (Map.Entry entry : entries()) { // when replacing macros with actual paths the replace utility may be used as always 'case-sensitive' // for case-insensitive file systems there will be no unnecesary toLowerCase() transforms. diff --git a/platform/platform-impl/src/com/intellij/openapi/components/PathMacroMap.java b/platform/platform-impl/src/com/intellij/openapi/components/PathMacroMap.java index 3761e5a479ff..46844d1153d1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/PathMacroMap.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/PathMacroMap.java @@ -9,7 +9,6 @@ import org.jdom.Attribute; import org.jdom.Comment; import org.jdom.Element; import org.jdom.Text; -import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.LinkedHashMap; @@ -41,34 +40,22 @@ public abstract class PathMacroMap { myMacroMap.put(fromText, toText); } - @SuppressWarnings({"WeakerAccess"}) - public abstract String substitute(String text, boolean caseSensitive, @Nullable final Set usedMacros); + public abstract String substitute(String text, boolean caseSensitive); - public final void substitute(Element e, boolean caseSensitive, @Nullable final Set usedMacros) { - substitute(e, caseSensitive, usedMacros, false); + public final void substitute(Element e, boolean caseSensitive) { + substitute(e, caseSensitive, false); } - public final void substitute(Element e, boolean caseSensitive, @Nullable final Set usedMacros, final boolean recursively) { + public final void substitute(Element e, boolean caseSensitive, final boolean recursively) { List content = e.getContent(); for (Object child : content) { if (child instanceof Element) { Element element = (Element)child; - - //mike - //dirty hack: do not substitute macroses in path macroses declarations. - //I can't find a way to disable macro saving in one component (yet). - - if (element.getName().equals("macro") && - element.getAttributes().size() == 2 && - element.getAttributeValue("name") != null && - element.getAttributeValue("value") != null && - element.getChildren().isEmpty()) continue; - - substitute(element, caseSensitive, usedMacros, recursively); + substitute(element, caseSensitive, recursively); } else if (child instanceof Text) { Text t = (Text)child; - t.setText(recursively ? substituteRecursively(t.getText(), caseSensitive, usedMacros) : substitute(t.getText(), caseSensitive, usedMacros)); + t.setText(recursively ? substituteRecursively(t.getText(), caseSensitive) : substitute(t.getText(), caseSensitive)); } else if (child instanceof Comment) { /*do not substitute in comments @@ -84,12 +71,15 @@ public abstract class PathMacroMap { List attributes = e.getAttributes(); for (final Object attribute1 : attributes) { Attribute attribute = (Attribute)attribute1; - attribute.setValue(recursively? substituteRecursively(attribute.getValue(), caseSensitive, usedMacros) : substitute(attribute.getValue(), caseSensitive, usedMacros)); + final String value = recursively + ? substituteRecursively(attribute.getValue(), caseSensitive) + : substitute(attribute.getValue(), caseSensitive); + attribute.setValue(value); } } - public String substituteRecursively(String text, boolean caseSensitive, Set usedMacros) { - return substitute(text, caseSensitive, usedMacros); + public String substituteRecursively(String text, boolean caseSensitive) { + return substitute(text, caseSensitive); } public int size() { diff --git a/platform/platform-impl/src/com/intellij/openapi/components/TrackingPathMacroSubstitutor.java b/platform/platform-impl/src/com/intellij/openapi/components/TrackingPathMacroSubstitutor.java index 49c55093624e..1d8452d18953 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/TrackingPathMacroSubstitutor.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/TrackingPathMacroSubstitutor.java @@ -1,9 +1,14 @@ package com.intellij.openapi.components; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; import java.util.Set; public interface TrackingPathMacroSubstitutor extends PathMacroSubstitutor { - Set getUsedMacros(); + Collection getUnknownMacros(@Nullable String componentName); + Collection getComponents(final Collection macros); + void addUnknownMacros(String componentName, Collection unknownMacros); + void invalidateUnknownMacros(Set macros); void reset(); - void reset(Set usedMacros); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/ex/ComponentManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/components/ex/ComponentManagerEx.java index ce05e2cf8af1..e7d9c15eacc0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/ex/ComponentManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/ex/ComponentManagerEx.java @@ -1,9 +1,12 @@ package com.intellij.openapi.components.ex; import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.notification.Notification; import com.intellij.openapi.components.ComponentConfig; import com.intellij.openapi.components.ComponentManager; import com.intellij.openapi.components.impl.stores.IComponentStore; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -25,4 +28,6 @@ public interface ComponentManagerEx extends ComponentManager { void registerComponent(ComponentConfig config, IdeaPluginDescriptor pluginDescriptor); IComponentStore getComponentStore(); + + void checkUnknownMacros(@NotNull Project project, @NotNull Notification notification); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java index 509f88c0927f..42786531ccc1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java @@ -7,12 +7,11 @@ import com.intellij.openapi.components.ExpandMacroToPathMap; import com.intellij.openapi.components.PathMacroManager; import com.intellij.openapi.components.TrackingPathMacroSubstitutor; import com.intellij.openapi.util.SystemInfo; +import com.intellij.util.containers.FactoryMap; import org.jdom.Element; import org.jetbrains.annotations.NonNls; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.*; public class BasePathMacroManager extends PathMacroManager { private PathMacrosImpl myPathMacros; @@ -49,28 +48,28 @@ public class BasePathMacroManager extends PathMacroManager { } public TrackingPathMacroSubstitutor createTrackingSubstitutor() { - return new MyTrackingPathMacroSubstitutor(new HashSet()); + return new MyTrackingPathMacroSubstitutor(); } public String expandPath(final String path) { - return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive, null); + return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive); } public String collapsePath(final String path) { - return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive, null); + return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive); } public void collapsePathsRecursively(final Element element) { - getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, null, true); + getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, true); } public void expandPaths(final Element element) { - getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, null); + getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive); } public void collapsePaths(final Element element) { - getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, null); + getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive); } public PathMacrosImpl getPathMacros() { @@ -88,43 +87,87 @@ public class BasePathMacroManager extends PathMacroManager { } private class MyTrackingPathMacroSubstitutor implements TrackingPathMacroSubstitutor { - private final Set myUsedMacros; + private final Map> myMacroToComponentNames = new FactoryMap>() { + @Override + protected Set create(String key) { + return new HashSet(); + } + }; - public MyTrackingPathMacroSubstitutor(final Set usedMacros) { - myUsedMacros = usedMacros; - } + private final Map> myComponentNameToMacros = new FactoryMap>() { + @Override + protected Set create(String key) { + return new HashSet(); + } + }; - public Set getUsedMacros() { - return Collections.unmodifiableSet(myUsedMacros); + public MyTrackingPathMacroSubstitutor() { } public void reset() { - myUsedMacros.clear(); + myMacroToComponentNames.clear(); + myComponentNameToMacros.clear(); } public String expandPath(final String path) { - return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive, myUsedMacros); + return getExpandMacroMap().substitute(path, SystemInfo.isFileSystemCaseSensitive); } public String collapsePath(final String path) { - return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive, myUsedMacros); + return getReplacePathMap().substitute(path, SystemInfo.isFileSystemCaseSensitive); } public void expandPaths(final Element element) { - getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, myUsedMacros); + getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive); } public void collapsePaths(final Element element) { - getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, myUsedMacros); - } - - public void reset(final Set usedMacros) { - reset(); - myUsedMacros.addAll(usedMacros); + getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive); } public int hashCode() { return getExpandMacroMap().hashCode(); } + + public void invalidateUnknownMacros(final Set macros) { + for (final String macro : macros) { + final Set components = myMacroToComponentNames.get(macro); + for (final String component : components) { + myComponentNameToMacros.remove(component); + } + + myMacroToComponentNames.remove(macro); + } + } + + public Collection getComponents(final Collection macros) { + final Set result = new HashSet(); + for (String macro : myMacroToComponentNames.keySet()) { + if (macros.contains(macro)) { + result.addAll(myMacroToComponentNames.get(macro)); + } + } + + return result; + } + + public Collection getUnknownMacros(final String componentName) { + if (componentName == null) { + return Collections.unmodifiableSet(myMacroToComponentNames.keySet()); + } else { + return Collections.unmodifiableSet(myComponentNameToMacros.get(componentName)); + } + } + + public void addUnknownMacros(final String componentName, final Collection unknownMacros) { + if (unknownMacros.isEmpty()) return; + + for (String unknownMacro : unknownMacros) { + final Set stringList = myMacroToComponentNames.get(unknownMacro); + stringList.add(componentName); + } + + myComponentNameToMacros.get(componentName).addAll(unknownMacros); + } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/ComponentManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/ComponentManagerImpl.java index d266f32cd5fa..4284363f5ae8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/ComponentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/ComponentManagerImpl.java @@ -2,19 +2,22 @@ package com.intellij.openapi.components.impl; import com.intellij.diagnostic.PluginException; import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.notification.Notification; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.PathMacros; import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.application.impl.ApplicationInfoImpl; -import com.intellij.openapi.components.BaseComponent; -import com.intellij.openapi.components.ComponentConfig; -import com.intellij.openapi.components.ComponentManager; -import com.intellij.openapi.components.StateStorage; +import com.intellij.openapi.components.*; import com.intellij.openapi.components.ex.ComponentManagerEx; import com.intellij.openapi.components.impl.stores.IComponentStore; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ex.ProjectManagerEx; +import com.intellij.openapi.project.impl.ProjectMacrosUtil; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.util.ArrayUtil; @@ -34,9 +37,7 @@ import org.picocontainer.defaults.ConstructorInjectionComponentAdapter; import java.io.IOException; import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; /** * @author mike @@ -217,7 +218,7 @@ public abstract class ComponentManagerImpl extends UserDataHolderBase implements } try { - getStateStore().initComponent(component); + getStateStore().initComponent(component, false); if (component instanceof BaseComponent) { ((BaseComponent)component).initComponent(); } @@ -233,6 +234,46 @@ public abstract class ComponentManagerImpl extends UserDataHolderBase implements } } + public void checkUnknownMacros(@NotNull final Project project, @NotNull final Notification notification) { + final IComponentStore stateStore = getStateStore(); + final TrackingPathMacroSubstitutor substitutor = stateStore.getStateStorageManager().getMacroSubstitutor(); + if (substitutor != null) { + final Collection macros = substitutor.getUnknownMacros(null); + if (!macros.isEmpty()) { + if (ProjectMacrosUtil.checkMacros(project, new HashSet(macros))) { + final PathMacros pathMacros = PathMacros.getInstance(); + boolean expire = true; + final Set macros2invalidate = new HashSet(macros); + for (Iterator it = macros2invalidate.iterator(); it.hasNext();) { + final String macro = (String)it.next(); + if (null == pathMacros.getValue(macro)) { + it.remove(); + expire = false; + } + } + + final Set components = new HashSet(substitutor.getComponents(macros2invalidate)); + if (stateStore.isReloadPossible(components)) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + stateStore.reinitComponents(components, true); + } + }); + + substitutor.invalidateUnknownMacros(macros2invalidate); + if (expire) notification.expire(); + } + else { + if (Messages.showYesNoDialog(project, "Component could not be reloaded. Reload project?", "Configuration changed", + Messages.getQuestionIcon()) == 0) { + ProjectManagerEx.getInstanceEx().reloadProject(project); + } + } + } + } + } + } + protected void handleInitComponentError(final Throwable ex, final boolean fatal, final String componentClassName) { LOG.error(ex); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java index bcaa1f7ea387..ecb418d67cbc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java @@ -116,12 +116,14 @@ public class ServiceManagerImpl implements BaseComponent { }); } - private Object initializeInstance(final PicoContainer container) { + protected Object initializeInstance(final PicoContainer container) { final Object serviceInstance = getDelegate().getComponentInstance(container); if (serviceInstance instanceof Disposable) { Disposer.register(myComponentManager, (Disposable)serviceInstance); } - myComponentManager.getComponentStore().initComponent(serviceInstance); + + myComponentManager.getComponentStore().initComponent(serviceInstance, true); + return serviceInstance; } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java index ab45d1295635..229196c3de08 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java @@ -120,7 +120,7 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto try { doReload(changedFiles, componentNames); - reinitComponents(componentNames); + reinitComponents(componentNames, false); } finally { myApplication.getMessageBus().syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateFinished(); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java index d73270429ee7..2f7e44c12b1e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java @@ -98,7 +98,7 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { } public BaseStorageData getMainStorageData() throws StateStorage.StateStorageException { - return (BaseStorageData) getMainStorage().getStorageData(); + return (BaseStorageData) getMainStorage().getStorageData(false); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java index 9b69c6e1a4df..c7be8edf990b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java @@ -47,25 +47,26 @@ abstract class ComponentStoreImpl implements IComponentStore { throw new UnsupportedOperationException("Method getDefaultsStorage is not supported in " + getClass()); } - public void initComponent(@NotNull final Object component) { + public String initComponent(@NotNull final Object component, final boolean service) { boolean isSerializable = component instanceof JDOMExternalizable || component instanceof PersistentStateComponent || component instanceof SettingsSavingComponent; - if (!isSerializable) return; + if (!isSerializable) return null; if (component instanceof SettingsSavingComponent) { SettingsSavingComponent settingsSavingComponent = (SettingsSavingComponent)component; mySettingsSavingComponents.add(settingsSavingComponent); } + final String[] componentName = {null}; final Runnable r = new Runnable() { public void run() { if (component instanceof PersistentStateComponent) { - initPersistentComponent((PersistentStateComponent)component); + componentName[0] = initPersistentComponent((PersistentStateComponent)component, false); } else if (component instanceof JDOMExternalizable) { - initJdomExternalizable((JDOMExternalizable)component); + componentName[0] = initJdomExternalizable((JDOMExternalizable)component); } } }; @@ -77,6 +78,8 @@ abstract class ComponentStoreImpl implements IComponentStore { else { applicationEx.runReadAction(r); } + + return componentName[0]; } public boolean isSaving() { @@ -146,22 +149,23 @@ abstract class ComponentStoreImpl implements IComponentStore { session.setStateInOldStorage(component, componentName, component); } - void initJdomExternalizable(@NotNull JDOMExternalizable component) { + @Nullable + String initJdomExternalizable(@NotNull JDOMExternalizable component) { final String componentName = getComponentName(component); myComponents.put(componentName, component); - if (optimizeTestLoading()) return; + if (optimizeTestLoading()) return componentName; loadJdomDefaults(component, componentName); Element element = null; StateStorage stateStorage = getOldStorage(component, componentName, StateStorageOperation.READ); - if (stateStorage == null) return; + if (stateStorage == null) return null; element = getJdomState(component, componentName, stateStorage); - if (element == null) return; + if (element == null) return null; try { if (LOG.isDebugEnabled()) { @@ -172,6 +176,8 @@ abstract class ComponentStoreImpl implements IComponentStore { catch (InvalidDataException e) { throw new InvalidComponentDataException(e); } + + return componentName; } private static String getComponentName(@NotNull final JDOMExternalizable component) { @@ -216,7 +222,7 @@ abstract class ComponentStoreImpl implements IComponentStore { return defaultsStorage.getState(component, componentName, Element.class, null); } - private void initPersistentComponent(@NotNull final PersistentStateComponent component) { + private String initPersistentComponent(@NotNull final PersistentStateComponent component, final boolean reloadData) { final String name = getComponentName(component); RoamingType roamingTypeFromComponent = getRoamingType(component); @@ -226,7 +232,7 @@ abstract class ComponentStoreImpl implements IComponentStore { } myComponents.put(name, component); - if (optimizeTestLoading()) return; + if (optimizeTestLoading()) return name; Class stateClass = getComponentStateClass(component); @@ -242,13 +248,15 @@ abstract class ComponentStoreImpl implements IComponentStore { for (Storage storageSpec : storageSpecs) { StateStorage stateStorage = getStateStorage(storageSpec); - if (stateStorage == null || !stateStorage.hasState(component, name, stateClass)) continue; + if (stateStorage == null || !stateStorage.hasState(component, name, stateClass, reloadData)) continue; state = stateStorage.getState(component, name, stateClass, state); } if (state != null) { component.loadState(state); } + + return name; } private static RoamingType getRoamingType(final PersistentStateComponent component) { @@ -455,7 +463,7 @@ abstract class ComponentStoreImpl implements IComponentStore { } - protected boolean isReloadPossible(final Set componentNames) { + public boolean isReloadPossible(final Set componentNames) { for (String componentName : componentNames) { final Object component = myComponents.get(componentName); @@ -470,11 +478,11 @@ abstract class ComponentStoreImpl implements IComponentStore { return true; } - protected void reinitComponents(final Set componentNames) { + public void reinitComponents(final Set componentNames, final boolean reloadData) { for (String componentName : componentNames) { final PersistentStateComponent component = (PersistentStateComponent)myComponents.get(componentName); if (component != null) { - initPersistentComponent(component); + initPersistentComponent(component, reloadData); } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java index 8349345bac93..732e648bc873 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java @@ -94,6 +94,11 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { throw new UnsupportedOperationException("Method addMacro not implemented in " + getClass()); } + @Nullable + public TrackingPathMacroSubstitutor getMacroSubstitutor() { + return null; + } + @Nullable public StateStorage getStateStorage(@NotNull Storage storageSpec) throws StateStorage.StateStorageException { return storage; diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java index eb474efebcee..e992df3d4b0a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java @@ -54,7 +54,7 @@ class DefaultsStateStorage implements StateStorage { return DefaultStateSerializer.deserializeState(getState(component, componentName), stateClass, mergeInto); } - public boolean hasState(final Object component, final String componentName, final Class aClass) throws StateStorageException { + public boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { final URL url = DecodeDefaultsUtil.getDefaults(component, componentName); return url != null; } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java index 94398328df58..ac6922a10e58 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java @@ -1,5 +1,6 @@ package com.intellij.openapi.components.impl.stores; +import com.intellij.application.options.PathMacrosCollector; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.StateSplitter; @@ -146,6 +147,9 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { if (myPathMacroSubstitutor != null) { myPathMacroSubstitutor.expandPaths(element); + + final Set unknownMacros = PathMacrosCollector.getMacroNames(element); + myPathMacroSubstitutor.addUnknownMacros(componentName, unknownMacros); } storageData.put(componentName, file, element, true); @@ -162,7 +166,7 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { } - public boolean hasState(final Object component, final String componentName, final Class aClass) throws StateStorageException { + public boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { if (!myDir.exists()) return false; return true; } @@ -432,13 +436,10 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { } private class MyExternalizationSession implements ExternalizationSession { - private final TrackingPathMacroSubstitutor myPathMacroSubstitutor; private final MyStorageData myStorageData; private MyExternalizationSession(final TrackingPathMacroSubstitutor pathMacroSubstitutor, final MyStorageData storageData) { myStorageData = storageData; - myPathMacroSubstitutor = pathMacroSubstitutor; - myPathMacroSubstitutor.reset(); } public void setState(final Object component, final String componentName, final Object state, final Storage storageSpec) diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java index 4ff773f25cc5..072988b3f465 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java @@ -12,7 +12,12 @@ import java.util.List; import java.util.Set; public interface IComponentStore { - void initComponent(Object component); + @Nullable + String initComponent(Object component, boolean service); + void reinitComponents(Set componentNames, boolean reloadData); + boolean isReloadPossible(Set componentNames); + + void load() throws IOException, StateStorage.StateStorageException; boolean isSaving(); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java index bc9e16ed7c07..fd45dc3527c8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java @@ -51,7 +51,7 @@ class ProjectStateStorageManager extends StateStorageManagerImpl { StateStorage storage = getFileStateStorage(name); - if (operation == StateStorageOperation.READ && storage != null && workspace && !storage.hasState(component, componentName, Element.class)) { + if (operation == StateStorageOperation.READ && storage != null && workspace && !storage.hasState(component, componentName, Element.class, false)) { name = "$" + ProjectStoreImpl.PROJECT_FILE_MACRO + "$"; } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java index bbc59a047f94..0ff486c0d150 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java @@ -3,9 +3,11 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.CommonBundle; import com.intellij.ide.highlighter.ProjectFileType; import com.intellij.ide.highlighter.WorkspaceFileType; +import com.intellij.notification.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.components.*; +import com.intellij.openapi.components.ex.ComponentManagerEx; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.project.Project; @@ -19,6 +21,7 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.openapi.vfs.VfsUtil; @@ -32,12 +35,14 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.event.HyperlinkEvent; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -137,6 +142,33 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject return myProject.isOptimiseTestLoadSpeed(); } + @Override + public String initComponent(@NotNull Object component, boolean service) { + final String componentName = super.initComponent(component, service); + + if (!ApplicationManager.getApplication().isHeadlessEnvironment() && !ApplicationManager.getApplication().isUnitTestMode()) { + if (service && componentName != null && myProject.isInitialized()) { + final TrackingPathMacroSubstitutor substitutor = getStateStorageManager().getMacroSubstitutor(); + if (substitutor != null) { + final Collection macros = substitutor.getUnknownMacros(componentName); + if (!macros.isEmpty()) { + Notifications.Bus.notify(new Notification("Load Error", "Error loading component", + String.format("

Undefined Path Variables: %s. Fix it!

", + StringUtil.join(macros, ", ")), NotificationType.ERROR, + new NotificationListener() { + public void hyperlinkUpdate(@NotNull Notification notification, + @NotNull HyperlinkEvent event) { + ((ComponentManagerEx)myProject).checkUnknownMacros(myProject, notification); + } + }), NotificationDisplayType.STICKY_BALLOON, myProject); + } + } + } + } + + return componentName; + } + public void setProjectFilePath(final String filePath) { if (filePath == null) { return; @@ -595,7 +627,7 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject try { doReload(changedFiles, componentNames); - reinitComponents(componentNames); + reinitComponents(componentNames, false); } finally { myProject.getMessageBus().syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateFinished(); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java index 2a73b6d16d53..b583603b7d9c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java @@ -1,9 +1,6 @@ package com.intellij.openapi.components.impl.stores; -import com.intellij.openapi.components.RoamingType; -import com.intellij.openapi.components.StateStorage; -import com.intellij.openapi.components.StateStorageOperation; -import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.*; import com.intellij.openapi.options.StreamProvider; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; @@ -20,6 +17,8 @@ import java.util.Set; */ public interface StateStorageManager { void addMacro(String macro, String expansion); + @Nullable + TrackingPathMacroSubstitutor getMacroSubstitutor(); @Nullable StateStorage getStateStorage(@NotNull Storage storageSpec) throws StateStorage.StateStorageException; diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java index 04d1e792314b..7bc0664ec12a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java @@ -59,6 +59,11 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } } + @SuppressWarnings({"unchecked"}) + public TrackingPathMacroSubstitutor getMacroSubstitutor() { + return myPathMacroSubstitutor; + } + public synchronized void addMacro(String macro, String expansion) { myMacros.put("$" + macro + "$", expansion); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java index 75701d0bf819..9cb39d0a2e0e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java @@ -1,5 +1,6 @@ package com.intellij.openapi.components.impl.stores; +import com.intellij.application.options.PathMacrosCollector; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; @@ -102,7 +103,7 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @Nullable public synchronized Element getState(final String componentName) throws StateStorageException { - final StorageData storageData = getStorageData(); + final StorageData storageData = getStorageData(false); final Element state = storageData.getState(componentName); @@ -117,8 +118,8 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { return state; } - public boolean hasState(final Object component, final String componentName, final Class aClass) throws StateStorageException { - final StorageData storageData = getStorageData(); + public boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { + final StorageData storageData = getStorageData(reloadData); return storageData.hasState(componentName); } @@ -129,8 +130,8 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @NotNull - protected StorageData getStorageData() throws StateStorageException { - if (myLoadedData != null) return myLoadedData; + protected StorageData getStorageData(final boolean reloadData) throws StateStorageException { + if (myLoadedData != null && !reloadData) return myLoadedData; myLoadedData = loadData(true, myListener); @@ -178,7 +179,6 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } protected void loadState(final StorageData result, final Element element) throws StateStorageException { - // TODO: notification should be created here if (myPathMacroSubstitutor != null) { myPathMacroSubstitutor.expandPaths(element); } @@ -187,6 +187,7 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { try { result.load(element); + result.checkUnknownMacros(myPathMacroSubstitutor); } catch (IOException e) { throw new StateStorageException(e); @@ -211,7 +212,7 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @NotNull public ExternalizationSession startExternalization() { try { - final ExternalizationSession session = new MyExternalizationSession(getStorageData().clone(), myListener); + final ExternalizationSession session = new MyExternalizationSession(getStorageData(false).clone(), myListener); mySession = session; return session; @@ -305,17 +306,13 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } protected Document getDocument(StorageData data) { - final Element element = data.save(); if (myPathMacroSubstitutor != null) { - Set usedMacros = myPathMacroSubstitutor.getUsedMacros(); try { - myPathMacroSubstitutor.reset(); myPathMacroSubstitutor.collapsePaths(element); - } - finally { - myPathMacroSubstitutor.reset(usedMacros); + } finally { + myPathMacroSubstitutor.reset(); } } @@ -463,7 +460,6 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { myDocumentToSave = new Document(element); if (myPathMacroSubstitutor != null) { - myPathMacroSubstitutor.reset(); myPathMacroSubstitutor.collapsePaths(element); } @@ -697,6 +693,15 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { public boolean hasState(final String componentName) { return myComponentStates.containsKey(componentName); } + + public void checkUnknownMacros(TrackingPathMacroSubstitutor pathMacroSubstitutor) { + for (String componentName : myComponentStates.keySet()) { + final Set unknownMacros = PathMacrosCollector.getMacroNames(myComponentStates.get(componentName)); + if (!unknownMacros.isEmpty()) { + pathMacroSubstitutor.addUnknownMacros(componentName, unknownMacros); + } + } + } } public void resetData(){ diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectMacrosUtil.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectMacrosUtil.java index e8012d793927..d487438889c0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectMacrosUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectMacrosUtil.java @@ -25,11 +25,11 @@ public class ProjectMacrosUtil { private ProjectMacrosUtil() { } - public static boolean showMacrosConfigurationDialog(Project project, final Map undefinedMacros) { + public static boolean showMacrosConfigurationDialog(Project project, final Collection undefinedMacros) { final String text = ProjectBundle.message("project.load.undefined.path.variables.message"); final Application application = ApplicationManager.getApplication(); if (application.isHeadlessEnvironment() || application.isUnitTestMode()) { - throw new RuntimeException(text + ": " + StringUtil.join(undefinedMacros.keySet(), ", ")); + throw new RuntimeException(text + ": " + StringUtil.join(undefinedMacros, ", ")); } final UndefinedMacrosConfigurable configurable = new UndefinedMacrosConfigurable(text, undefinedMacros); @@ -47,13 +47,13 @@ public class ProjectMacrosUtil { return editor.isOK(); } - public static boolean checkMacros(final Project project, final Map usedMacros) { + public static boolean checkMacros(final Project project, final Set usedMacros) { final Set defined = getDefinedMacros(); - usedMacros.keySet().removeAll(defined); + usedMacros.removeAll(defined); // try to lookup values in System properties @NonNls final String pathMacroSystemPrefix = "path.macro."; - for (Iterator it = usedMacros.keySet().iterator(); it.hasNext();) { + for (Iterator it = usedMacros.iterator(); it.hasNext();) { final String macro = (String)it.next(); final String value = System.getProperty(pathMacroSystemPrefix + macro, null); if (value != null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index 7d46ea1aa366..d30898d779ab 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -4,6 +4,7 @@ import com.intellij.ide.AppLifecycleListener; import com.intellij.ide.highlighter.WorkspaceFileType; import com.intellij.ide.impl.ProjectUtil; import com.intellij.ide.startup.impl.StartupManagerImpl; +import com.intellij.notification.*; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; @@ -13,6 +14,8 @@ import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.application.impl.ApplicationImpl; import com.intellij.openapi.components.ExportableApplicationComponent; import com.intellij.openapi.components.StateStorage; +import com.intellij.openapi.components.TrackingPathMacroSubstitutor; +import com.intellij.openapi.components.ex.ComponentManagerEx; import com.intellij.openapi.components.impl.stores.IComponentStore; import com.intellij.openapi.components.impl.stores.IProjectStore; import com.intellij.openapi.components.impl.stores.XmlElementStorage; @@ -31,6 +34,7 @@ import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileEvent; @@ -51,6 +55,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +import javax.swing.event.HyperlinkEvent; import java.io.File; import java.io.IOException; import java.util.*; @@ -376,6 +381,30 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt startupManager.runPostStartupActivities(); + if (!ApplicationManager.getApplication().isHeadlessEnvironment() && !ApplicationManager.getApplication().isUnitTestMode()) { + // should be invoked last + StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { + public void run() { + final TrackingPathMacroSubstitutor macroSubstitutor = + ((ProjectEx)project).getStateStore().getStateStorageManager().getMacroSubstitutor(); + if (macroSubstitutor != null) { + final Collection macros = macroSubstitutor.getUnknownMacros(null); + if (!macros.isEmpty()) { + Notifications.Bus.notify(new Notification("Load Error", "Error loading project", + String.format("

Undefined Path Variables: %s. Fix it!

", + StringUtil.join(macros, ", ")), NotificationType.ERROR, + new NotificationListener() { + public void hyperlinkUpdate(@NotNull Notification notification, + @NotNull HyperlinkEvent event) { + ((ComponentManagerEx)project).checkUnknownMacros(project, notification); + } + }), NotificationDisplayType.STICKY_BALLOON, project); + } + } + } + }); + } + return true; } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/UndefinedMacrosConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/UndefinedMacrosConfigurable.java index 6268c9e0179f..dde9aac0798f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/UndefinedMacrosConfigurable.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/UndefinedMacrosConfigurable.java @@ -14,7 +14,7 @@ import com.intellij.ui.IdeBorderFactory; import javax.swing.*; import java.awt.*; -import java.util.Map; +import java.util.Collection; /** * @author Eugene Zhuravlev @@ -23,9 +23,9 @@ import java.util.Map; public class UndefinedMacrosConfigurable implements Configurable{ private PathMacroListEditor myEditor; private final String myText; - private final Map myUndefinedMacroNames; + private final Collection myUndefinedMacroNames; - public UndefinedMacrosConfigurable(String text, Map undefinedMacroNames) { + public UndefinedMacrosConfigurable(String text, Collection undefinedMacroNames) { myText = text; myUndefinedMacroNames = undefinedMacroNames; } diff --git a/platform/testFramework/src/com/intellij/mock/MockProject.java b/platform/testFramework/src/com/intellij/mock/MockProject.java index 429d9a9fd9b5..66585bbda569 100644 --- a/platform/testFramework/src/com/intellij/mock/MockProject.java +++ b/platform/testFramework/src/com/intellij/mock/MockProject.java @@ -1,6 +1,7 @@ package com.intellij.mock; import com.intellij.application.options.ReplacePathToMacroMap; +import com.intellij.notification.Notification; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ExpandMacroToPathMap; import com.intellij.openapi.components.ServiceManager; @@ -23,6 +24,9 @@ public class MockProject extends MockComponentManager implements ProjectEx { return false; } + public void checkMacros(Notification notification, @Nullable String componentName) { + } + @NotNull public PomModel getModel() { return ServiceManager.getService(this, PomModel.class); diff --git a/platform/testFramework/src/com/intellij/mock/MockProjectStore.java b/platform/testFramework/src/com/intellij/mock/MockProjectStore.java index 25130e7c1125..e8b07eadca18 100644 --- a/platform/testFramework/src/com/intellij/mock/MockProjectStore.java +++ b/platform/testFramework/src/com/intellij/mock/MockProjectStore.java @@ -33,6 +33,14 @@ public class MockProjectStore implements IProjectStore { throw new UnsupportedOperationException("Method setProjectFilePath is not yet implemented in " + getClass().getName()); } + public void reinitComponents(Set componentNames, boolean reloadData) { + throw new UnsupportedOperationException("Method reinitComponents is not yet implemented in " + getClass().getName()); + } + + public boolean isReloadPossible(Set componentNames) { + throw new UnsupportedOperationException("Method isReloadPossible is not yet implemented in " + getClass().getName()); + } + @Nullable public VirtualFile getProjectBaseDir() { throw new UnsupportedOperationException("Method getProjectBaseDir is not yet implemented in " + getClass().getName()); @@ -95,7 +103,7 @@ public class MockProjectStore implements IProjectStore { throw new UnsupportedOperationException("Method initStore is not yet implemented in " + getClass().getName()); } - public void initComponent(Object component) { + public String initComponent(Object component, final boolean service) { throw new UnsupportedOperationException("Method initComponent is not yet implemented in " + getClass().getName()); } diff --git a/plugins/eclipse/src/org/jetbrains/idea/eclipse/importWizard/EclipseImportBuilder.java b/plugins/eclipse/src/org/jetbrains/idea/eclipse/importWizard/EclipseImportBuilder.java index be7510d77fd3..9c5b4009939f 100644 --- a/plugins/eclipse/src/org/jetbrains/idea/eclipse/importWizard/EclipseImportBuilder.java +++ b/plugins/eclipse/src/org/jetbrains/idea/eclipse/importWizard/EclipseImportBuilder.java @@ -155,12 +155,7 @@ public class EclipseImportBuilder extends ProjectImportBuilder implement return false; } - final HashMap map = new HashMap(); - for (String v : variables) { - map.put(v, null); - } - - if (!ProjectMacrosUtil.checkMacros(dstProject, map)) { + if (!ProjectMacrosUtil.checkMacros(dstProject, variables)) { return false; } diff --git a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java index f5f7560cbb97..5db3a1d64723 100644 --- a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java +++ b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java @@ -292,7 +292,7 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple public void readExternal(Element element) throws InvalidDataException { final ExpandMacroToPathMap macroExpands = new ExpandMacroToPathMap(); myPathMacros.addMacroExpands(macroExpands); - macroExpands.substitute(element, SystemInfo.isFileSystemCaseSensitive, null); + macroExpands.substitute(element, SystemInfo.isFileSystemCaseSensitive); myModificationCount++; for (final Object o1 : element.getChildren(RESOURCE_ELEMENT)) { @@ -329,7 +329,7 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple final ReplacePathToMacroMap macroReplacements = new ReplacePathToMacroMap(); PathMacrosImpl.getInstanceEx().addMacroReplacements(macroReplacements); - macroReplacements.substitute(element, SystemInfo.isFileSystemCaseSensitive, null); + macroReplacements.substitute(element, SystemInfo.isFileSystemCaseSensitive); } public void addExternalResourceListener(ExternalResourceListener listener) {