From f23f0b718f66231d5b6eda5b1ad4041eaef836af Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 7 Oct 2011 10:22:58 +0400 Subject: [PATCH] project structure error highlighting: show warnings with quickfixes for unused project libraries --- .../GeneralProjectSettingsElement.java | 5 -- .../ui/configuration/ModulesConfigurator.java | 2 +- .../artifacts/ArtifactErrorPanel.java | 4 +- .../ArtifactProjectStructureElement.java | 5 -- .../ArtifactValidationManagerImpl.java | 2 +- .../ArtifactsStructureConfigurable.java | 2 +- ...factsStructureConfigurableContextImpl.java | 2 +- .../libraries/LibraryEditingUtil.java | 12 +++++ .../libraryEditor/CreateNewLibraryAction.java | 13 +---- .../BaseLibrariesConfigurable.java | 15 +++++- .../projectRoot/ChangeLibraryLevelAction.java | 16 +++--- .../ModuleStructureConfigurable.java | 9 ---- .../daemon/FacetProjectStructureElement.java | 5 -- .../LibraryProjectStructureElement.java | 53 +++++++++++++++++-- .../daemon/ModuleProjectStructureElement.java | 5 -- .../ProjectStructureDaemonAnalyzer.java | 45 +++++++++++++++- .../daemon/ProjectStructureElement.java | 12 ++++- .../ProjectStructureProblemDescription.java | 6 +-- .../ProjectStructureProblemsHolderImpl.java | 6 +++ .../daemon/SdkProjectStructureElement.java | 5 -- .../src/messages/ProjectBundle.properties | 2 + 21 files changed, 154 insertions(+), 72 deletions(-) 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 index 3b5f30ab9c48..ce2d0bf3b107 100644 --- 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 @@ -86,11 +86,6 @@ public class GeneralProjectSettingsElement extends ProjectStructureElement { return Collections.emptyList(); } - @Override - public boolean highlightIfUnused() { - return false; - } - @Override public String getId() { return "project:general"; 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 f095f952392e..bf53ba837116 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 @@ -552,7 +552,7 @@ public class ModulesConfigurator implements ModulesProvider, ModuleEditor.Change if (module == moduleEditor.getModule() && Comparing.strEqual(moduleEditor.getName(), oldName)) { moduleEditor.setModuleName(name); moduleEditor.updateCompilerOutputPathChanged(ProjectStructureConfigurable.getInstance(myProject).getProjectConfig().getCompilerOutputUrl(), name); - myContext.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(myContext, module), true, false); + myContext.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(myContext, module)); return; } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactErrorPanel.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactErrorPanel.java index a728d3e4c86a..2c309f36d958 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactErrorPanel.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactErrorPanel.java @@ -37,7 +37,7 @@ public class ArtifactErrorPanel { private JPanel myMainPanel; private JButton myFixButton; private JLabel myErrorLabel; - private List myCurrentQuickFixes; + private List myCurrentQuickFixes; private String myErrorText; public ArtifactErrorPanel(final ArtifactEditorImpl artifactEditor) { @@ -83,7 +83,7 @@ public class ArtifactErrorPanel { artifactEditor.queueValidation(); } - public void showError(@NotNull String message, @NotNull List quickFixes) { + public void showError(@NotNull String message, @NotNull List quickFixes) { myErrorLabel.setVisible(true); final String errorText = "" + message + ""; if (myErrorLabel.isShowing()) { diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProjectStructureElement.java index 0005bfc3d0cc..2d28cae26162 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProjectStructureElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactProjectStructureElement.java @@ -128,11 +128,6 @@ public class ArtifactProjectStructureElement extends ProjectStructureElement { return myOriginalArtifact.hashCode(); } - @Override - public boolean highlightIfUnused() { - return false; - } - @Override public String getPresentableName() { return "Artifact '" + getActualArtifactName() + "'"; diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactValidationManagerImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactValidationManagerImpl.java index e7220350e294..9c411ca11fa1 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactValidationManagerImpl.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactValidationManagerImpl.java @@ -73,7 +73,7 @@ public class ArtifactValidationManagerImpl implements Disposable { if (problemDescriptions != null) { for (ProjectStructureProblemDescription description : problemDescriptions) { final String message = description.getMessage(); - List quickFixes = Collections.emptyList(); + List quickFixes = Collections.emptyList(); if (description instanceof ArtifactProblemDescription) { final ArtifactProblemDescription artifactProblem = (ArtifactProblemDescription)description; quickFixes = artifactProblem.getFixes(); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurable.java index 5e1087318155..3981663cee12 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurable.java @@ -82,7 +82,7 @@ public class ArtifactsStructureConfigurable extends BaseStructureConfigurable { context.getModulesConfigurator().addAllModuleChangeListener(new ModuleEditor.ChangeListener() { public void moduleStateChanged(ModifiableRootModel moduleRootModel) { for (ProjectStructureElement element : getProjectStructureElements()) { - myContext.getDaemonAnalyzer().queueUpdate(element, true, false); + myContext.getDaemonAnalyzer().queueUpdate(element); } } }); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurableContextImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurableContextImpl.java index 1eb26f9c27bc..a07554770ee3 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurableContextImpl.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/ArtifactsStructureConfigurableContextImpl.java @@ -110,7 +110,7 @@ public class ArtifactsStructureConfigurableContextImpl implements ArtifactsStruc } public void queueValidation(Artifact artifact) { - myContext.getDaemonAnalyzer().queueUpdate(getOrCreateArtifactElement(artifact), true, false); + myContext.getDaemonAnalyzer().queueUpdate(getOrCreateArtifactElement(artifact)); } public CompositePackagingElement getRootElement(@NotNull Artifact artifact) { diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/LibraryEditingUtil.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/LibraryEditingUtil.java index 7f4b3b52ebed..8612f0518e3f 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/LibraryEditingUtil.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraries/LibraryEditingUtil.java @@ -32,6 +32,7 @@ import com.intellij.openapi.roots.libraries.*; import com.intellij.openapi.roots.libraries.ui.OrderRoot; import com.intellij.openapi.roots.ui.configuration.classpath.ClasspathPanel; import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesModifiableModel; +import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable; import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.util.io.FileUtil; @@ -41,6 +42,7 @@ import com.intellij.util.ParameterizedRunnable; import com.intellij.util.PathUtil; import com.intellij.util.PlatformIcons; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.*; @@ -199,4 +201,14 @@ public class LibraryEditingUtil { } }; } + + public static List getSuitableModules(@NotNull ModuleStructureConfigurable rootConfigurable, final @Nullable LibraryType type) { + final List modules = new ArrayList(); + for (Module module : rootConfigurable.getModules()) { + if (type == null || type.isSuitableModule(module, rootConfigurable.getFacetConfigurator())) { + modules.add(module); + } + } + return modules; + } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java index 198b7d0a2e62..fc1eda9cdf4c 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java @@ -21,7 +21,6 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; -import com.intellij.openapi.module.Module; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.libraries.Library; @@ -111,23 +110,13 @@ public class CreateNewLibraryAction extends DumbAwareAction { rootConfigurable.selectNodeInTree(libraryNode); } - private static List getSuitableModules(@NotNull ModuleStructureConfigurable rootConfigurable, final @Nullable LibraryType type) { - final List modules = new ArrayList(); - for (Module module : rootConfigurable.getModules()) { - if (type == null || type.isSuitableModule(module, rootConfigurable.getFacetConfigurator())) { - modules.add(module); - } - } - return modules; - } - public static AnAction[] createActionOrGroup(@NotNull String text, @NotNull BaseLibrariesConfigurable librariesConfigurable, final @NotNull Project project) { final LibraryType[] extensions = LibraryType.EP_NAME.getExtensions(); List> suitableTypes = new ArrayList>(); if (librariesConfigurable instanceof ProjectLibrariesConfigurable) { final ModuleStructureConfigurable configurable = ModuleStructureConfigurable.getInstance(project); for (LibraryType extension : extensions) { - if (!getSuitableModules(configurable, extension).isEmpty()) { + if (!LibraryEditingUtil.getSuitableModules(configurable, extension).isEmpty()) { suitableTypes.add(extension); } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java index 56efdfc8766d..3a195859f0c3 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java @@ -26,6 +26,7 @@ import com.intellij.openapi.roots.impl.libraries.LibraryImpl; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.roots.libraries.LibraryTablePresentation; +import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; import com.intellij.openapi.roots.ui.configuration.artifacts.UsageInArtifact; import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil; import com.intellij.openapi.roots.ui.configuration.libraryEditor.CreateNewLibraryAction; @@ -51,6 +52,14 @@ public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurabl super(project); } + public static BaseLibrariesConfigurable getInstance(@NotNull Project project, @NotNull String tableLevel) { + if (tableLevel.equals(LibraryTablesRegistrar.PROJECT_LEVEL)) { + return ProjectLibrariesConfigurable.getInstance(project); + } + else { + return GlobalLibrariesConfigurable.getInstance(project); + } + } public abstract LibraryTablePresentation getLibraryTablePresentation(); @@ -208,8 +217,10 @@ public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurabl } } - public void removeLibraryNode(@NotNull Library library) { - final MyNode node = findNodeByObject(myRoot, library); + public void removeLibrary(@NotNull LibraryProjectStructureElement element) { + getModelProvider().getModifiableModel().removeLibrary(element.getLibrary()); + myContext.getDaemonAnalyzer().removeElement(element); + final MyNode node = findNodeByObject(myRoot, element.getLibrary()); if (node != null) { removePaths(TreeUtil.getPathFromRoot(node)); } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ChangeLibraryLevelAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ChangeLibraryLevelAction.java index 1d6e9f2f95e5..2403269c1dcf 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ChangeLibraryLevelAction.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ChangeLibraryLevelAction.java @@ -61,14 +61,14 @@ public class ChangeLibraryLevelAction extends ChangeLibraryLevelActionBase { final ProjectStructureElement selectedElement = mySourceConfigurable.getSelectedElement(); if (!(selectedElement instanceof LibraryProjectStructureElement)) return; final StructureConfigurableContext context = mySourceConfigurable.myContext; - final Library originalLibrary = ((LibraryProjectStructureElement)selectedElement).getLibrary(); - final LibraryEx oldLibrary = (LibraryEx)context.getLibrary(originalLibrary.getName(), mySourceConfigurable.getLevel()); + final LibraryProjectStructureElement libraryElement = (LibraryProjectStructureElement)selectedElement; + final LibraryEx oldLibrary = (LibraryEx)context.getLibrary(libraryElement.getLibrary().getName(), mySourceConfigurable.getLevel()); LOG.assertTrue(oldLibrary != null); final Library newLibrary = doCopy(oldLibrary); if (newLibrary == null) return; final ModulesConfigurator configurator = context.getModulesConfigurator(); - final Collection usages = context.getDaemonAnalyzer().getUsages(selectedElement); + final Collection usages = context.getDaemonAnalyzer().getUsages(libraryElement); for (ProjectStructureElementUsage usage : usages) { if (usage instanceof UsageInModuleClasspath) { final Module module = ((UsageInModuleClasspath)usage).getModule(); @@ -80,16 +80,14 @@ public class ChangeLibraryLevelAction extends ChangeLibraryLevelActionBase { } } else if (usage instanceof UsageInArtifact) { - final PackagingElement libraryElement = PackagingElementFactory.getInstance().createLibraryFiles(newLibrary.getName(), - newLibrary.getTable().getTableLevel(), null); - ((UsageInArtifact)usage).replaceElement(libraryElement); + final PackagingElement newLibraryElement = PackagingElementFactory.getInstance().createLibraryFiles(newLibrary.getName(), + newLibrary.getTable().getTableLevel(), null); + ((UsageInArtifact)usage).replaceElement(newLibraryElement); } } if (!myCopy) { - mySourceConfigurable.getModelProvider().getModifiableModel().removeLibrary(originalLibrary); - context.getDaemonAnalyzer().removeElement(selectedElement); - mySourceConfigurable.removeLibraryNode(originalLibrary); + mySourceConfigurable.removeLibrary(libraryElement); } ProjectStructureConfigurable.getInstance(myProject).selectProjectOrGlobalLibrary(newLibrary, true); } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java index 64d0862e26a2..ab6d2c53c5e1 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ModuleStructureConfigurable.java @@ -323,7 +323,6 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple ((DefaultTreeModel)myTree.getModel()).reload(parent); } myContext.getDaemonAnalyzer().removeElement(new LibraryProjectStructureElement(myContext, library)); - // TODO: myContext.invalidateModules(myContext.myLibraryDependencyCache.get(library.getName())); } } @@ -469,14 +468,6 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple } modelProxy.addLibraryEntry(library); myContext.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(myContext, module)); - /* TODO - Set modules = myContext.myLibraryDependencyCache.get(library.getName()); - if (modules == null) { - modules = new HashSet(); - myContext.myLibraryDependencyCache.put(library.getName(), modules); - } - modules.add(module.getName()); - */ myTree.repaint(); } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/FacetProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/FacetProjectStructureElement.java index 43e79bc7bc30..38b94f9e387c 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/FacetProjectStructureElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/FacetProjectStructureElement.java @@ -43,11 +43,6 @@ public class FacetProjectStructureElement extends ProjectStructureElement { return Collections.emptyList(); } - @Override - public boolean highlightIfUnused() { - return false; - } - @Override public String getPresentableName() { return "Facet '" + myFacet.getName() + "' in module '" + myFacet.getModule().getName() + "'"; diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java index 9417597aebfa..894fd6556ddb 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java @@ -1,6 +1,8 @@ package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon; +import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.roots.JavadocOrderRootType; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.impl.libraries.LibraryEx; @@ -8,18 +10,19 @@ import com.intellij.openapi.roots.impl.libraries.LibraryImpl; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; +import com.intellij.openapi.roots.ui.configuration.ChooseModulesDialog; import com.intellij.openapi.roots.ui.configuration.ModuleEditor; import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable; +import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil; import com.intellij.openapi.roots.ui.configuration.libraryEditor.ExistingLibraryEditor; -import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesModifiableModel; -import com.intellij.openapi.roots.ui.configuration.projectRoot.LibraryConfigurable; -import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext; +import com.intellij.openapi.roots.ui.configuration.projectRoot.*; import com.intellij.openapi.ui.NamedConfigurable; import com.intellij.openapi.vfs.VfsUtil; import org.jetbrains.annotations.NotNull; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -126,11 +129,18 @@ public class LibraryProjectStructureElement extends ProjectStructureElement { } @Override - public boolean highlightIfUnused() { + public boolean shouldShowWarningIfUnused() { final LibraryTable libraryTable = myLibrary.getTable(); return libraryTable != null && LibraryTablesRegistrar.PROJECT_LEVEL.equals(libraryTable.getTableLevel()); } + @Override + public ProjectStructureProblemDescription createUnusedElementWarning() { + final List fixes = Arrays.asList(new AddLibraryToDependenciesFix(), new RemoveLibraryFix()); + return new ProjectStructureProblemDescription(getPresentableName() + " is not used", null, createPlace(), fixes, + ProjectStructureProblemType.warning("unused-library")); + } + @Override public String getPresentableName() { return "Library '" + myLibrary.getName() + "'"; @@ -175,4 +185,39 @@ public class LibraryProjectStructureElement extends ProjectStructureElement { } } } + + private class AddLibraryToDependenciesFix extends ConfigurationErrorQuickFix { + private AddLibraryToDependenciesFix() { + super("Add to Dependencies..."); + } + + @Override + public void performFix() { + final Project project = myContext.getProject(); + final ModuleStructureConfigurable moduleStructureConfigurable = ModuleStructureConfigurable.getInstance(project); + final List modules = LibraryEditingUtil.getSuitableModules(moduleStructureConfigurable, ((LibraryEx)myLibrary).getType()); + if (modules.isEmpty()) return; + final ChooseModulesDialog dlg = new ChooseModulesDialog(project, modules, ProjectBundle.message("choose.modules.dialog.title"), + ProjectBundle + .message("choose.modules.dialog.description", myLibrary.getName())); + dlg.show(); + if (dlg.isOK()) { + final List chosenModules = dlg.getChosenElements(); + for (Module module : chosenModules) { + moduleStructureConfigurable.addLibraryOrderEntry(module, myLibrary); + } + } + } + } + + private class RemoveLibraryFix extends ConfigurationErrorQuickFix { + private RemoveLibraryFix() { + super("Remove Library"); + } + + @Override + public void performFix() { + BaseLibrariesConfigurable.getInstance(myContext.getProject(), myLibrary.getTable().getTableLevel()).removeLibrary(LibraryProjectStructureElement.this); + } + } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ModuleProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ModuleProjectStructureElement.java index 5915083f67d5..22f4752597c5 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ModuleProjectStructureElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ModuleProjectStructureElement.java @@ -129,11 +129,6 @@ public class ModuleProjectStructureElement extends ProjectStructureElement { return myModule.hashCode(); } - @Override - public boolean highlightIfUnused() { - return false; - } - @Override public String getPresentableName() { return "Module '" + myModule.getName() + "'"; diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java index 9a063d6f9d72..a19db243dbd0 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java @@ -26,6 +26,8 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { private final MultiValuesMap mySourceElement2Usages = new MultiValuesMap(); private final MultiValuesMap myContainingElement2Usages = new MultiValuesMap(); private final Set myElementWithNotCalculatedUsages = new HashSet(); + private final Set myElementsToShowWarningIfUnused = new HashSet(); + private final Map myWarningsAboutUnused = new HashMap(); private final MergingUpdateQueue myAnalyzerQueue; private final EventDispatcher myDispatcher = EventDispatcher.create(ProjectStructureDaemonAnalyzerListener.class); private final AtomicBoolean myStopped = new AtomicBoolean(false); @@ -71,6 +73,9 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { if (LOG.isDebugEnabled()) { LOG.debug("updating problems for " + element); } + final ProjectStructureProblemDescription warning = myWarningsAboutUnused.get(element); + if (warning != null) + problemsHolder.registerProblem(warning); myProblemHolders.put(element, problemsHolder); myDispatcher.getMulticaster().problemsChanged(element); } @@ -108,6 +113,7 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { addUsage(usage); } myElementWithNotCalculatedUsages.remove(element); + reportUnusedElements(); } private static void invokeLater(Runnable runnable) { @@ -125,11 +131,16 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { if (collectUsages) { myElementWithNotCalculatedUsages.add(element); } + if (element.shouldShowWarningIfUnused()) { + myElementsToShowWarningIfUnused.add(element); + } myAnalyzerQueue.queue(new AnalyzeElementUpdate(element, check, collectUsages)); } public void removeElement(ProjectStructureElement element) { myElementWithNotCalculatedUsages.remove(element); + myElementsToShowWarningIfUnused.remove(element); + myWarningsAboutUnused.remove(element); myProblemHolders.remove(element); final Collection usages = mySourceElement2Usages.removeAll(element); if (usages != null) { @@ -139,10 +150,42 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { } removeUsagesInElement(element); myDispatcher.getMulticaster().problemsChanged(element); + reportUnusedElements(); + } + + private void reportUnusedElements() { + if (!myElementWithNotCalculatedUsages.isEmpty()) return; + + for (ProjectStructureElement element : myElementsToShowWarningIfUnused) { + final ProjectStructureProblemDescription warning; + final Collection usages = mySourceElement2Usages.get(element); + if (usages == null || usages.isEmpty()) { + warning = element.createUnusedElementWarning(); + } + else { + warning = null; + } + + final ProjectStructureProblemDescription old = myWarningsAboutUnused.put(element, warning); + ProjectStructureProblemsHolderImpl holder = myProblemHolders.get(element); + if (holder == null) { + holder = new ProjectStructureProblemsHolderImpl(); + myProblemHolders.put(element, holder); + } + if (old != null) { + holder.removeProblem(old); + } + if (warning != null) { + holder.registerProblem(warning); + } + if (old != null || warning != null) { + myDispatcher.getMulticaster().problemsChanged(element); + } + } } public boolean isUnused(ProjectStructureElement element) { - if (!element.highlightIfUnused()) { + if (!element.shouldShowWarningIfUnused()) { return false; } if (!myElementWithNotCalculatedUsages.isEmpty()) { diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java index 40884089009a..76fec66e165c 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java @@ -2,6 +2,7 @@ package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon; import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -23,7 +24,16 @@ public abstract class ProjectStructureElement { public abstract List getUsagesInElement(); - public abstract boolean highlightIfUnused(); + + public boolean shouldShowWarningIfUnused() { + return false; + } + + @Nullable + public ProjectStructureProblemDescription createUnusedElementWarning() { + return null; + } + @Override public abstract boolean equals(Object obj); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemDescription.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemDescription.java index b4d3b63b25f1..e69093deb714 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemDescription.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemDescription.java @@ -27,13 +27,13 @@ public class ProjectStructureProblemDescription { private final String myMessage; private final String myDescription; private final PlaceInProjectStructure myPlace; - private final List myFixes; + private final List myFixes; private final ProjectStructureProblemType myProblemType; public ProjectStructureProblemDescription(@NotNull String message, @Nullable String description, @NotNull PlaceInProjectStructure place, - @NotNull List fixes, + @NotNull List fixes, @NotNull ProjectStructureProblemType problemType) { myMessage = message; myDescription = description; @@ -51,7 +51,7 @@ public class ProjectStructureProblemDescription { return myDescription; } - public List getFixes() { + public List getFixes() { return myFixes; } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java index f8d8836bbfec..9c795bb7ac1a 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureProblemsHolderImpl.java @@ -70,6 +70,12 @@ public class ProjectStructureProblemsHolderImpl implements ProjectStructureProbl return myProblemDescriptions != null && !myProblemDescriptions.isEmpty(); } + public void removeProblem(@NotNull ProjectStructureProblemDescription description) { + if (myProblemDescriptions != null) { + myProblemDescriptions.remove(description); + } + } + @Nullable public List getProblemDescriptions() { return myProblemDescriptions; diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/SdkProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/SdkProjectStructureElement.java index 79a4713459a5..3e7e332355a1 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/SdkProjectStructureElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/SdkProjectStructureElement.java @@ -43,11 +43,6 @@ public class SdkProjectStructureElement extends ProjectStructureElement { return mySdk.hashCode(); } - @Override - public boolean highlightIfUnused() { - return false; - } - @Override public String getPresentableName() { return "SDK '" + mySdk.getName() + "'"; diff --git a/platform/platform-resources-en/src/messages/ProjectBundle.properties b/platform/platform-resources-en/src/messages/ProjectBundle.properties index e49f1296655c..cf3e1051171a 100644 --- a/platform/platform-resources-en/src/messages/ProjectBundle.properties +++ b/platform/platform-resources-en/src/messages/ProjectBundle.properties @@ -312,6 +312,8 @@ project.roots.plain.mode.action.text.enabled=Show Module Groups project.roots.tooltip.library.misconfigured=Library ''{0}'' has broken paths. project.roots.error.message.invalid.classes.roots=invalid classes {0, choice, 1#root|2#roots} project.roots.error.message.invalid.source.javadoc.roots=invalid source/javadoc {0, choice, 1#root|2#roots} +choose.modules.dialog.title=Choose modules +choose.modules.dialog.description=Library '' {0}'' will be added to the selected modules. project.roots.tooltip.unused=''{0}'' is unused. project.roots.javadoc.tab.description=Manage external JavaDocs attached to this module. External JavaDoc override JavaDoc annotations you might have in your module. project.roots.output.compiler.title=Compiler output