private static void doNotify(@NotNull final Set<String> macros, @NotNull final Project project, @NotNull final Map<TrackingPathMacroSubstitutor, IComponentStore> substitutorToStore) { String format = "<p><i>%s</i> %s undefined. <a href=\"define\">Fix it</a></p>"; String productName = ApplicationNamesInfo.getInstance().getProductName(); String content = String.format(format, StringUtil.join(macros, ", "), macros.size() == 1 ? "is" : "are") + "<br>Path variables are used to substitute absolute paths " + "in " + productName + " project files " + "and allow project file sharing in version control systems.<br>" + "Some of the files describing the current project settings contain unknown path variables " + "and " + productName + " cannot restore those paths."; new UnknownMacroNotification("Load Error", "Load error: undefined path variables", content, NotificationType.ERROR, new NotificationListener() { @Override public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { checkUnknownMacros(project, true, macros, substitutorToStore); } }, macros).notify(project); }
public static void checkUnknownMacros(@NotNull Project project, boolean notify) { // use linked set/map to get stable results Set<String> unknownMacros = new LinkedHashSet<String>(); Map<TrackingPathMacroSubstitutor, IComponentStore> substitutorToStore = ContainerUtil.newLinkedHashMap(); collect(project, unknownMacros, substitutorToStore); for (Module module : ModuleManager.getInstance(project).getModules()) { collect(module, unknownMacros, substitutorToStore); } if (unknownMacros.isEmpty()) { return; } if (notify) { doNotify(unknownMacros, project, substitutorToStore); return; } checkUnknownMacros(project, false, unknownMacros, substitutorToStore); }
private static void collect(@NotNull ComponentManager componentManager, @NotNull Set<String> unknownMacros, @NotNull Map<TrackingPathMacroSubstitutor, IComponentStore> substitutorToStore) { IComponentStore store = ServiceKt.getStateStore(componentManager); TrackingPathMacroSubstitutor substitutor = store.getStateStorageManager().getMacroSubstitutor(); if (substitutor == null) { return; } Set<String> macros = substitutor.getUnknownMacros(null); if (macros.isEmpty()) { return; } unknownMacros.addAll(macros); substitutorToStore.put(substitutor, store); }
@NotNull public static TreeMap<String, Element> load(@NotNull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { if (pathMacroSubstitutor != null) { pathMacroSubstitutor.expandPaths(rootElement); } StringInterner interner = intern ? new StringInterner() : null; List<Element> children = rootElement.getChildren(COMPONENT); TreeMap<String, Element> map = new TreeMap<String, Element>(); for (Element element : children) { String name = getComponentNameIfValid(element); if (name == null || !(element.getAttributes().size() > 1 || !element.getChildren().isEmpty())) { continue; } if (interner != null) { JDOMUtil.internElement(element, interner); } map.put(name, element); if (pathMacroSubstitutor instanceof TrackingPathMacroSubstitutor) { ((TrackingPathMacroSubstitutor)pathMacroSubstitutor).addUnknownMacros(name, PathMacrosCollector.getMacroNames(element)); } // remove only after "getMacroNames" - some PathMacroFilter requires element name attribute element.removeAttribute(NAME); } return map; }
public static void notifyUnknownMacros(@NotNull final IComponentStore store, @NotNull final Project project, @NotNull final String componentName) { final TrackingPathMacroSubstitutor substitutor = store.getStateStorageManager().getMacroSubstitutor(); if (substitutor == null) { return; } Set<String> immutableMacros = substitutor.getUnknownMacros(componentName); if (immutableMacros.isEmpty()) { return; } final Set<String> macros = new LinkedHashSet<String>(immutableMacros); AppUIUtil.invokeOnEdt(new Runnable() { @Override public void run() { List<String> notified = null; NotificationsManager manager = NotificationsManager.getNotificationsManager(); for (UnknownMacroNotification notification : manager.getNotificationsOfType(UnknownMacroNotification.class, project)) { if (notified == null) { notified = new SmartList<String>(); } notified.addAll(notification.getMacros()); } if (!ContainerUtil.isEmpty(notified)) { macros.removeAll(notified); } if (macros.isEmpty()) { return; } LOG.debug("Reporting unknown path macros " + macros + " in component " + componentName); doNotify(macros, project, Collections.singletonMap(substitutor, store)); } }, project.getDisposed()); }
public void checkUnknownMacros(TrackingPathMacroSubstitutor pathMacroSubstitutor) { if (pathMacroSubstitutor == null) return; for (String componentName : myComponentStates.keySet()) { final Set<String> unknownMacros = PathMacrosCollector.getMacroNames(myComponentStates.get(componentName)); if (!unknownMacros.isEmpty()) { pathMacroSubstitutor.addUnknownMacros(componentName, unknownMacros); } } }
public static void notifyUnknownMacros(@NotNull TrackingPathMacroSubstitutor substitutor, @NotNull final Project project, @Nullable String componentName) { final LinkedHashSet<String> macros = new LinkedHashSet<String>(substitutor.getUnknownMacros(componentName)); if (macros.isEmpty()) { return; } UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { macros.removeAll(getMacrosFromExistingNotifications(project)); if (!macros.isEmpty()) { String format = "<p><i>%s</i> %s undefined. <a href=\"define\">Fix it</a></p>"; String productName = ApplicationNamesInfo.getInstance().getProductName(); String content = String.format(format, StringUtil.join(macros, ", "), macros.size() == 1 ? "is" : "are") + "<br>Path variables are used to substitute absolute paths " + "in " + productName + " project files " + "and allow project file sharing in version control systems.<br>" + "Some of the files describing the current project settings contain unknown path variables " + "and " + productName + " cannot restore those paths."; new UnknownMacroNotification("Load Error", "Load error: undefined path variables", content, NotificationType.ERROR, new NotificationListener() { public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { ((ProjectEx)project).checkUnknownMacros(true); } }, macros).notify(project); } } }); }
@Override public TrackingPathMacroSubstitutor[] getSubstitutors() { final List<TrackingPathMacroSubstitutor> result = new ArrayList<TrackingPathMacroSubstitutor>(); result.add(getStateStorageManager().getMacroSubstitutor()); for (Module module : getPersistentModules()) { result.add(((ModuleImpl)module).getStateStore().getStateStorageManager().getMacroSubstitutor()); } return result.toArray(new TrackingPathMacroSubstitutor[result.size()]); }
public static void notifyUnknownMacros(@Nonnull TrackingPathMacroSubstitutor substitutor, @Nonnull final Project project, @Nullable final String componentName) { final LinkedHashSet<String> macros = new LinkedHashSet<String>(substitutor.getUnknownMacros(componentName)); if (macros.isEmpty()) { return; } UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { macros.removeAll(getMacrosFromExistingNotifications(project)); if (!macros.isEmpty()) { LOG.debug("Reporting unknown path macros " + macros + " in component " + componentName); String format = "<p><i>%s</i> %s undefined. <a href=\"define\">Fix it</a></p>"; String productName = ApplicationNamesInfo.getInstance().getProductName(); String content = String.format(format, StringUtil.join(macros, ", "), macros.size() == 1 ? "is" : "are") + "<br>Path variables are used to substitute absolute paths " + "in " + productName + " project files " + "and allow project file sharing in version control systems.<br>" + "Some of the files describing the current project settings contain unknown path variables " + "and " + productName + " cannot restore those paths."; new UnknownMacroNotification("Load Error", "Load error: undefined path variables", content, NotificationType.ERROR, (notification, event) -> ((ProjectEx)project).checkUnknownMacros(true), macros).notify(project); } } }); }
public void load(@Nonnull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { if (pathMacroSubstitutor != null) { pathMacroSubstitutor.expandPaths(rootElement); } StringInterner interner = intern ? new StringInterner() : null; for (Iterator<Element> iterator = rootElement.getChildren(COMPONENT).iterator(); iterator.hasNext(); ) { Element element = iterator.next(); String name = getComponentNameIfValid(element); if (name == null || !(element.getAttributes().size() > 1 || !element.getChildren().isEmpty())) { continue; } iterator.remove(); if (interner != null) { JDOMUtil.internStringsInElement(element, interner); } myStates.put(name, element); if (pathMacroSubstitutor instanceof TrackingPathMacroSubstitutor) { ((TrackingPathMacroSubstitutor)pathMacroSubstitutor).addUnknownMacros(name, PathMacrosService.getInstance().getMacroNames(element)); } // remove only after "getMacroNames" - some PathMacroFilter requires element name attribute element.removeAttribute(NAME); } }
protected XmlElementStorage(@Nonnull String fileSpec, @Nullable RoamingType roamingType, @Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor, @Nonnull String rootElementName, @Nullable StreamProvider streamProvider) { super(pathMacroSubstitutor); myFileSpec = fileSpec; myRoamingType = roamingType == null ? RoamingType.PER_USER : roamingType; myRootElementName = rootElementName; myStreamProvider = myRoamingType == RoamingType.DISABLED ? null : streamProvider; }
@NotNull public static Map<String, Element> loadFrom(@Nullable VirtualFile dir, @Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor) { if (dir == null || !dir.exists()) { return Collections.emptyMap(); } StringInterner interner = new StringInterner(); Map<String, Element> fileToState = new THashMap<String, Element>(); for (VirtualFile file : dir.getChildren()) { // ignore system files like .DS_Store on Mac if (!StringUtilRt.endsWithIgnoreCase(file.getNameSequence(), FileStorageCoreUtil.DEFAULT_EXT)) { continue; } try { if (file.getLength() == 0) { LOG.warn("Ignore empty file " + file.getPath()); continue; } Element element = JDOMUtil.load(file.getInputStream()); String componentName = FileStorageCoreUtil.getComponentNameIfValid(element); if (componentName == null) { continue; } if (!element.getName().equals(FileStorageCoreUtil.COMPONENT)) { LOG.error("Incorrect root tag name (" + element.getName() + ") in " + file.getPresentableUrl()); continue; } List<Element> elementChildren = element.getChildren(); if (elementChildren.isEmpty()) { continue; } Element state = (Element)elementChildren.get(0).detach(); if (JDOMUtil.isEmpty(state)) { continue; } JDOMUtil.internElement(state, interner); if (pathMacroSubstitutor != null) { pathMacroSubstitutor.expandPaths(state); pathMacroSubstitutor.addUnknownMacros(componentName, PathMacrosCollector.getMacroNames(state)); } fileToState.put(file.getName(), state); } catch (Throwable e) { LOG.warn("Unable to load state", e); } } return fileToState; }
@NotNull @Override public TrackingPathMacroSubstitutor createTrackingSubstitutor() { return new MyTrackingPathMacroSubstitutor(); }
private static void checkUnknownMacros(@NotNull Project project, boolean showDialog, @NotNull Set<String> unknownMacros, @NotNull Map<TrackingPathMacroSubstitutor, IComponentStore> substitutorToStore) { if (unknownMacros.isEmpty() || (showDialog && !ProjectMacrosUtil.checkMacros(project, new THashSet<String>(unknownMacros)))) { return; } PathMacros pathMacros = PathMacros.getInstance(); for (Iterator<String> it = unknownMacros.iterator(); it.hasNext(); ) { String macro = it.next(); if (StringUtil.isEmptyOrSpaces(pathMacros.getValue(macro)) && !pathMacros.isIgnoredMacroName(macro)) { it.remove(); } } if (unknownMacros.isEmpty()) { return; } for (Map.Entry<TrackingPathMacroSubstitutor, IComponentStore> entry : substitutorToStore.entrySet()) { TrackingPathMacroSubstitutor substitutor = entry.getKey(); Set<String> components = substitutor.getComponents(unknownMacros); IComponentStore store = entry.getValue(); if (store.isReloadPossible(components)) { substitutor.invalidateUnknownMacros(unknownMacros); for (UnknownMacroNotification notification : NotificationsManager.getNotificationsManager().getNotificationsOfType(UnknownMacroNotification.class, project)) { if (unknownMacros.containsAll(notification.getMacros())) { notification.expire(); } } store.reloadStates(components, project.getMessageBus()); } else if (Messages.showYesNoDialog(project, "Component could not be reloaded. Reload project?", "Configuration Changed", Messages.getQuestionIcon()) == Messages.YES) { ProjectManagerEx.getInstanceEx().reloadProject(project); } } }
@Override public TrackingPathMacroSubstitutor createTrackingSubstitutor() { return new MyTrackingPathMacroSubstitutor(); }
@Override public TrackingPathMacroSubstitutor[] getSubstitutors() { return new TrackingPathMacroSubstitutor[0]; }
@Override public boolean openProject(final Project project) { if (isLight(project)) { throw new AssertionError("must not open light project"); } final Application application = ApplicationManager.getApplication(); if (!application.isUnitTestMode() && !((ProjectEx)project).getStateStore().checkVersion()) { return false; } synchronized (myOpenProjects) { if (myOpenProjects.contains(project)) { return false; } myOpenProjects.add(project); cacheOpenProjects(); } fireProjectOpened(project); final StartupManagerImpl startupManager = (StartupManagerImpl)StartupManager.getInstance(project); waitForFileWatcher(project); boolean ok = myProgressManager.runProcessWithProgressSynchronously(new Runnable() { @Override public void run() { startupManager.runStartupActivities(); // dumb mode should start before post-startup activities // only when startCacheUpdate is called from UI thread, we can guarantee that // when the method returns, the application has entered dumb mode UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { startupManager.startCacheUpdate(); } }); startupManager.runPostStartupActivitiesFromExtensions(); UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { startupManager.runPostStartupActivities(); } }); } }, ProjectBundle.message("project.load.progress"), true, project); if (!ok) { closeProject(project, false, false, true); notifyProjectOpenFailed(); return false; } if (!application.isHeadlessEnvironment() && !application.isUnitTestMode()) { // should be invoked last startupManager.runWhenProjectIsInitialized(new Runnable() { @Override public void run() { final TrackingPathMacroSubstitutor macroSubstitutor = ((ProjectEx)project).getStateStore().getStateStorageManager().getMacroSubstitutor(); if (macroSubstitutor != null) { StorageUtil.notifyUnknownMacros(macroSubstitutor, project, null); } } }); } return true; }
public ModuleStateStorageManager(@Nullable final TrackingPathMacroSubstitutor pathMacroManager, final Module module) { super(pathMacroManager, ROOT_TAG_NAME, module, module.getPicoContainer()); myModule = module; }
public void loadFrom(@Nullable VirtualFile dir, @Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor) { if (dir == null || !dir.exists()) { return; } StringInterner interner = new StringInterner(); for (VirtualFile file : dir.getChildren()) { if (!isStorageFile(file)) { continue; } try { Element element = JDOMUtil.loadDocument(file.contentsToByteArray()).getRootElement(); String name = StorageData.getComponentNameIfValid(element); if (name == null) { continue; } if (!element.getName().equals(StorageData.COMPONENT)) { LOG.error("Incorrect root tag name (" + element.getName() + ") in " + file.getPresentableUrl()); continue; } List<Element> elementChildren = element.getChildren(); if (elementChildren.isEmpty()) { continue; } Element state = (Element)elementChildren.get(0).detach(); JDOMUtil.internStringsInElement(state, interner); if (pathMacroSubstitutor != null) { pathMacroSubstitutor.expandPaths(state); pathMacroSubstitutor.addUnknownMacros(name, PathMacrosService.getInstance().getMacroNames(state)); } setState(name, file.getName(), state); } catch (IOException | JDOMException e) { LOG.info("Unable to load state", e); } } }
@SuppressWarnings({"UnusedDeclaration"}) public ApplicationStoreImpl(final ApplicationEx2 application, PathMacroManager pathMacroManager) { myApplication = application; myStateStorageManager = new StateStorageManagerImpl(pathMacroManager.createTrackingSubstitutor(), ROOT_ELEMENT_NAME, application, application.getPicoContainer()) { private boolean myConfigDirectoryRefreshed; @Nonnull @Override protected String getConfigurationMacro(boolean directorySpec) { return directorySpec ? StoragePathMacros.ROOT_CONFIG : StoragePathMacros.APP_CONFIG; } @Override protected StorageData createStorageData(@Nonnull String fileSpec, @Nonnull String filePath) { return new StorageData(ROOT_ELEMENT_NAME); } @Override protected TrackingPathMacroSubstitutor getMacroSubstitutor(@Nonnull final String fileSpec) { if (fileSpec.equals(StoragePathMacros.APP_CONFIG + '/' + PathMacrosImpl.EXT_FILE_NAME + DirectoryStorageData.DEFAULT_EXT)) return null; return super.getMacroSubstitutor(fileSpec); } @Override protected boolean isUseXmlProlog() { return false; } @Override protected void beforeFileBasedStorageCreate() { if (!myConfigDirectoryRefreshed && (application.isUnitTestMode() || application.isDispatchThread())) { try { VirtualFile configDir = LocalFileSystem.getInstance().refreshAndFindFileByPath(getConfigPath()); if (configDir != null) { VfsUtil.markDirtyAndRefresh(false, true, true, configDir); } } finally { myConfigDirectoryRefreshed = true; } } } }; }
public ProjectStateStorageManager(final TrackingPathMacroSubstitutor macroSubstitutor, ProjectImpl project) { super(macroSubstitutor, ROOT_TAG_NAME, project, project.getPicoContainer()); myProject = project; }
protected StateStorageBase(@Nullable TrackingPathMacroSubstitutor trackingPathMacroSubstitutor) { myPathMacroSubstitutor = trackingPathMacroSubstitutor; }
TrackingPathMacroSubstitutor[] getSubstitutors();