From 8766282b8ed8a61dca6adfd1c27e249c98d63cc8 Mon Sep 17 00:00:00 2001 From: nik Date: Thu, 29 Sep 2011 15:16:02 +0400 Subject: [PATCH] project structure dialog: circular dependency checking implemented via daemon --- .../GeneralProjectSettingsElement.java | 88 +++++++++++ .../ui/configuration/ModulesConfigurator.java | 3 - .../ui/configuration/ProjectConfigurable.java | 147 ++++++++---------- .../ProjectStructureConfigurable.java | 12 +- 4 files changed, 162 insertions(+), 88 deletions(-) create mode 100644 java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/GeneralProjectSettingsElement.java diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/GeneralProjectSettingsElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/GeneralProjectSettingsElement.java new file mode 100644 index 000000000000..f617e748f080 --- /dev/null +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/GeneralProjectSettingsElement.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.roots.ui.configuration; + +import com.intellij.compiler.ModuleCompilerUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectBundle; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext; +import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.*; +import com.intellij.util.Chunk; +import com.intellij.util.graph.Graph; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * @author nik + */ +public class GeneralProjectSettingsElement extends ProjectStructureElement { + public GeneralProjectSettingsElement(@NotNull StructureConfigurableContext context) { + super(context); + } + + @Override + public void check(ProjectStructureProblemsHolder problemsHolder) { + final Graph> graph = ModuleCompilerUtil.toChunkGraph(myContext.getModulesConfigurator().createGraphGenerator()); + final Collection> chunks = graph.getNodes(); + String cycles = ""; + int count = 0; + for (Chunk chunk : chunks) { + final Set modules = chunk.getNodes(); + String cycle = ""; + for (ModifiableRootModel model : modules) { + cycle += ", " + model.getModule().getName(); + } + if (modules.size() > 1) { + @NonNls final String br = "
    "; + cycles += br + (++count) + ". " + cycle.substring(2); + } + } + if (count > 0) { + @NonNls final String leftBrace = ""; + @NonNls final String rightBrace = ""; + final String warningMessage = leftBrace + ProjectBundle.message("module.circular.dependency.warning", cycles, count) + rightBrace; + final Project project = myContext.getProject(); + final PlaceInProjectStructureBase place = new PlaceInProjectStructureBase(project, ProjectStructureConfigurable.getInstance(project).createModulesPlace()); + problemsHolder.registerWarning("Circular dependencies", warningMessage, place, null); + } + } + + @Override + public List getUsagesInElement() { + return Collections.emptyList(); + } + + @Override + public boolean highlightIfUnused() { + return false; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof GeneralProjectSettingsElement; + } + + @Override + public int hashCode() { + return 0; + } +} diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java index e917795b7827..f095f952392e 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java @@ -68,7 +68,6 @@ public class ModulesConfigurator implements ModulesProvider, ModuleEditor.Change private static final Logger LOG = Logger.getInstance("#" + ModulesConfigurator.class.getName()); private final Project myProject; - private final ProjectConfigurable myProjectConfigurable; private final List myModuleEditors = new ArrayList(); private final Comparator myModuleEditorComparator = new Comparator() { public int compare(ModuleEditor editor1, ModuleEditor editor2) { @@ -91,7 +90,6 @@ public class ModulesConfigurator implements ModulesProvider, ModuleEditor.Change public ModulesConfigurator(Project project, ProjectSdksModel projectJdksModel) { myProject = project; myModuleModel = ModuleManager.getInstance(myProject).getModifiableModel(); - myProjectConfigurable = new ProjectConfigurable(project, this, projectJdksModel); } public void setContext(final StructureConfigurableContext context) { @@ -202,7 +200,6 @@ public class ModulesConfigurator implements ModulesProvider, ModuleEditor.Change } public void moduleStateChanged(final ModifiableRootModel moduleRootModel) { - myProjectConfigurable.updateCircularDependencyWarning(); for (ModuleEditor.ChangeListener listener : myAllModulesChangeListeners) { listener.moduleStateChanged(moduleRootModel); } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectConfigurable.java index a6375ac57e35..b1aa213d4d7c 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectConfigurable.java @@ -16,7 +16,6 @@ package com.intellij.openapi.roots.ui.configuration; -import com.intellij.compiler.ModuleCompilerUtil; import com.intellij.ide.util.BrowseFilesListener; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.StorageScheme; @@ -31,10 +30,11 @@ import com.intellij.openapi.roots.CompilerProjectExtension; import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; -import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectConfigurationProblems; +import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectStructureElementConfigurable; +import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext; +import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.*; import com.intellij.openapi.ui.DetailsComponent; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.NamedConfigurable; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.util.IconLoader; @@ -44,30 +44,26 @@ import com.intellij.pom.java.LanguageLevel; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.FieldPanel; import com.intellij.ui.InsertPathAction; -import com.intellij.util.Alarm; -import com.intellij.util.Chunk; -import com.intellij.util.graph.Graph; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.DocumentEvent; import java.awt.*; import java.io.IOException; -import java.util.Collection; -import java.util.Set; /** * @author Eugene Zhuravlev * Date: Dec 15, 2003 */ -public class ProjectConfigurable extends NamedConfigurable implements DetailsComponent.Facade { +public class ProjectConfigurable extends ProjectStructureElementConfigurable implements DetailsComponent.Facade { private final Project myProject; private static final Icon PROJECT_ICON = IconLoader.getIcon("/nodes/project.png"); - private boolean myStartModuleWizardOnShow; private LanguageLevelCombo myLanguageLevelCombo; private ProjectJdkConfigurable myProjectJdkConfigurable; @@ -77,22 +73,69 @@ public class ProjectConfigurable extends NamedConfigurable implements D private JPanel myPanel; - private final Alarm myUpdateWarningAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); - private final JLabel myWarningLabel = new JLabel(""); + private final StructureConfigurableContext myContext; private final ModulesConfigurator myModulesConfigurator; private JPanel myWholePanel; private boolean myFreeze = false; private DetailsComponent myDetailsComponent; - private CircularDependencyWarning myCircularDependencyWarning; + private final GeneralProjectSettingsElement mySettingsElement; - public ProjectConfigurable(Project project, ModulesConfigurator configurator, ProjectSdksModel model) { + public ProjectConfigurable(Project project, + final StructureConfigurableContext context, + ModulesConfigurator configurator, + ProjectSdksModel model) { myProject = project; + myContext = context; myModulesConfigurator = configurator; + mySettingsElement = new GeneralProjectSettingsElement(context); + final ProjectStructureDaemonAnalyzer daemonAnalyzer = context.getDaemonAnalyzer(); + myModulesConfigurator.addAllModuleChangeListener(new ModuleEditor.ChangeListener() { + @Override + public void moduleStateChanged(ModifiableRootModel moduleRootModel) { + daemonAnalyzer.queueUpdate(mySettingsElement); + } + }); + daemonAnalyzer.addListener(new ProjectStructureDaemonAnalyzerListener() { + @Override + public void usagesCollected(@NotNull ProjectStructureElement containingElement) { + } + + @Override + public void problemsChanged(@NotNull ProjectStructureElement element) { + if (element instanceof GeneralProjectSettingsElement) { + updateCircularDependencyWarning(); + } + } + + @Override + public void allProblemsChanged() { + updateCircularDependencyWarning(); + } + }); init(model); } + private void updateCircularDependencyWarning() { + ProjectStructureProblemsHolderImpl holder = myContext.getDaemonAnalyzer().getProblemsHolder(mySettingsElement); + final ProjectStructureProblemDescription item = holder != null ? ContainerUtil.getFirstItem(holder.getProblemDescriptions()) : null; + if (item != null) { + myWarningLabel.setIcon(Messages.getWarningIcon()); + myWarningLabel.setText(item.getDescription()); + } + else { + myWarningLabel.setIcon(null); + myWarningLabel.setText(""); + } + myWarningLabel.repaint(); + } + + @Override + public ProjectStructureElement getProjectStructureElement() { + return mySettingsElement; + } + public DetailsComponent getDetailsComponent() { return myDetailsComponent; } @@ -155,7 +198,6 @@ public class ProjectConfigurable extends NamedConfigurable implements D } public void disposeUIResources() { - myUpdateWarningAlarm.cancelAllRequests(); if (myProjectJdkConfigurable != null) { myProjectJdkConfigurable.disposeUIResources(); } @@ -165,12 +207,11 @@ public class ProjectConfigurable extends NamedConfigurable implements D myFreeze = true; try { myProjectJdkConfigurable.reset(); - final String compilerOutput = CompilerProjectExtension.getInstance(myProject).getCompilerOutputUrl(); + final String compilerOutput = getOriginalCompilerOutputUrl(); if (compilerOutput != null) { myProjectCompilerOutput.setText(FileUtil.toSystemDependentName(VfsUtil.urlToPath(compilerOutput))); } myLanguageLevelCombo.reset(myProject); - updateCircularDependencyWarning(); if (myProjectName != null) { myProjectName.setText(myProject.getName()); @@ -179,55 +220,8 @@ public class ProjectConfigurable extends NamedConfigurable implements D finally { myFreeze = false; } - } - void updateCircularDependencyWarning() { - myUpdateWarningAlarm.cancelAllRequests(); - myUpdateWarningAlarm.addRequest(new Runnable() { - public void run() { - ApplicationManager.getApplication().runReadAction(new Runnable(){ - public void run() { - final Graph> graph = ModuleCompilerUtil.toChunkGraph(myModulesConfigurator.createGraphGenerator()); - final Collection> chunks = graph.getNodes(); - String cycles = ""; - int count = 0; - for (Chunk chunk : chunks) { - final Set modules = chunk.getNodes(); - String cycle = ""; - for (ModifiableRootModel model : modules) { - cycle += ", " + model.getModule().getName(); - } - if (modules.size() > 1) { - @NonNls final String br = "
    "; - cycles += br + (++count) + ". " + cycle.substring(2); - } - } - @NonNls final String leftBrace = ""; - @NonNls final String rightBrace = ""; - final String warningMessage = - leftBrace + (count > 0 ? ProjectBundle.message("module.circular.dependency.warning", cycles, count) : "") + rightBrace; - if (ProjectConfigurationProblems.isVisible()) { - if (myCircularDependencyWarning != null) { - ConfigurationErrors.Bus.removeError(myCircularDependencyWarning, myProject); - } - if (count > 0) { - myCircularDependencyWarning = new CircularDependencyWarning("Circular dependencies", warningMessage); - ConfigurationErrors.Bus.addError(myCircularDependencyWarning, myProject); - } - } - final Icon icon = count > 0 ? Messages.getWarningIcon() : null; - //noinspection SSBasedInspection - SwingUtilities.invokeLater(new Runnable() { - public void run() { - myWarningLabel.setIcon(icon); - myWarningLabel.setText(warningMessage); - myWarningLabel.repaint(); - } - }); - } - }); - } - }, 300); + myContext.getDaemonAnalyzer().queueUpdate(mySettingsElement); } @@ -307,8 +301,7 @@ public class ProjectConfigurable extends NamedConfigurable implements D if (!LanguageLevelProjectExtension.getInstance(myProject).getLanguageLevel().equals(myLanguageLevelCombo.getSelectedItem())) { return true; } - final CompilerProjectExtension compilerProjectExtension = CompilerProjectExtension.getInstance(myProject); - final String compilerOutput = compilerProjectExtension.getCompilerOutputUrl(); + final String compilerOutput = getOriginalCompilerOutputUrl(); if (!Comparing.strEqual(FileUtil.toSystemIndependentName(VfsUtil.urlToPath(compilerOutput)), FileUtil.toSystemIndependentName(myProjectCompilerOutput.getText()))) return true; if (myProjectJdkConfigurable.isModified()) return true; @@ -319,6 +312,12 @@ public class ProjectConfigurable extends NamedConfigurable implements D return false; } + @Nullable + private String getOriginalCompilerOutputUrl() { + final CompilerProjectExtension extension = CompilerProjectExtension.getInstance(myProject); + return extension != null ? extension.getCompilerOutputUrl() : null; + } + private void createUIComponents() { myLanguageLevelCombo = new LanguageLevelCombo(); final JTextField textField = new JTextField(); @@ -333,18 +332,4 @@ public class ProjectConfigurable extends NamedConfigurable implements D public String getCompilerOutputUrl() { return VfsUtil.pathToUrl(myProjectCompilerOutput.getText().trim()); } - - private static class CircularDependencyWarning extends ConfigurationError { - private CircularDependencyWarning(String plainTextTitle, String description) { - super(plainTextTitle, description); - } - - @Override - public void fix(JComponent contextComponent) { - } - - @Override - public void navigate() { - } - } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java index 622f65ae4194..8629c2194d18 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java @@ -248,7 +248,7 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se } private void addProjectConfig() { - myProjectConfig = new ProjectConfigurable(myProject, myModuleConfigurator, myProjectJdksModel); + myProjectConfig = new ProjectConfigurable(myProject, myContext, myModuleConfigurator, myProjectJdksModel); addConfigurable(myProjectConfig); } @@ -373,7 +373,7 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se } public ActionCallback select(@Nullable final String moduleToSelect, @Nullable String editorNameToSelect, final boolean requestFocus) { - Place place = createPlaceFor(myModulesConfig); + Place place = createModulesPlace(); if (moduleToSelect != null) { final Module module = ModuleManager.getInstance(myProject).findModuleByName(moduleToSelect); assert module != null; @@ -382,12 +382,16 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se return navigateTo(place, requestFocus); } + public Place createModulesPlace() { + return createPlaceFor(myModulesConfig); + } + public Place createModulePlace(@NotNull Module module) { - return createPlaceFor(myModulesConfig).putPath(ModuleStructureConfigurable.TREE_OBJECT, module); + return createModulesPlace().putPath(ModuleStructureConfigurable.TREE_OBJECT, module); } public ActionCallback select(@Nullable final Facet facetToSelect, final boolean requestFocus) { - Place place = createPlaceFor(myModulesConfig); + Place place = createModulesPlace(); if (facetToSelect != null) { place = place.putPath(ModuleStructureConfigurable.TREE_OBJECT, facetToSelect); }