mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
project structure dialog: circular dependency checking implemented via daemon
This commit is contained in:
+88
@@ -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<Chunk<ModifiableRootModel>> graph = ModuleCompilerUtil.toChunkGraph(myContext.getModulesConfigurator().createGraphGenerator());
|
||||
final Collection<Chunk<ModifiableRootModel>> chunks = graph.getNodes();
|
||||
String cycles = "";
|
||||
int count = 0;
|
||||
for (Chunk<ModifiableRootModel> chunk : chunks) {
|
||||
final Set<ModifiableRootModel> modules = chunk.getNodes();
|
||||
String cycle = "";
|
||||
for (ModifiableRootModel model : modules) {
|
||||
cycle += ", " + model.getModule().getName();
|
||||
}
|
||||
if (modules.size() > 1) {
|
||||
@NonNls final String br = "<br> ";
|
||||
cycles += br + (++count) + ". " + cycle.substring(2);
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
@NonNls final String leftBrace = "<html>";
|
||||
@NonNls final String rightBrace = "</html>";
|
||||
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<ProjectStructureElementUsage> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<ModuleEditor> myModuleEditors = new ArrayList<ModuleEditor>();
|
||||
private final Comparator<ModuleEditor> myModuleEditorComparator = new Comparator<ModuleEditor>() {
|
||||
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);
|
||||
}
|
||||
|
||||
+66
-81
@@ -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<Project> implements DetailsComponent.Facade {
|
||||
public class ProjectConfigurable extends ProjectStructureElementConfigurable<Project> 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<Project> 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<Project> implements D
|
||||
}
|
||||
|
||||
public void disposeUIResources() {
|
||||
myUpdateWarningAlarm.cancelAllRequests();
|
||||
if (myProjectJdkConfigurable != null) {
|
||||
myProjectJdkConfigurable.disposeUIResources();
|
||||
}
|
||||
@@ -165,12 +207,11 @@ public class ProjectConfigurable extends NamedConfigurable<Project> 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<Project> 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<Chunk<ModifiableRootModel>> graph = ModuleCompilerUtil.toChunkGraph(myModulesConfigurator.createGraphGenerator());
|
||||
final Collection<Chunk<ModifiableRootModel>> chunks = graph.getNodes();
|
||||
String cycles = "";
|
||||
int count = 0;
|
||||
for (Chunk<ModifiableRootModel> chunk : chunks) {
|
||||
final Set<ModifiableRootModel> modules = chunk.getNodes();
|
||||
String cycle = "";
|
||||
for (ModifiableRootModel model : modules) {
|
||||
cycle += ", " + model.getModule().getName();
|
||||
}
|
||||
if (modules.size() > 1) {
|
||||
@NonNls final String br = "<br> ";
|
||||
cycles += br + (++count) + ". " + cycle.substring(2);
|
||||
}
|
||||
}
|
||||
@NonNls final String leftBrace = "<html>";
|
||||
@NonNls final String rightBrace = "</html>";
|
||||
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<Project> 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<Project> 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<Project> 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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user