From bfd13f90d3f02009d8b22296eb3fc9f149c73b63 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 12 Nov 2015 21:14:09 +0100 Subject: [PATCH] [platform] module dependency analysis rewritten; test scope added --- .../DependenciesAnalyzeManager.java | 34 +- .../ModulesDependenciesPanel.java | 675 ++++++++---------- .../ShowModuleDependenciesAction.java | 84 +-- .../openapi/actionSystem/IdeActions.java | 1 + .../messages/AnalysisScopeBundle.properties | 12 +- 5 files changed, 359 insertions(+), 447 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/moduleDependencies/DependenciesAnalyzeManager.java b/platform/lang-impl/src/com/intellij/moduleDependencies/DependenciesAnalyzeManager.java index f9c19d400cbb..91916bb00c29 100644 --- a/platform/lang-impl/src/com/intellij/moduleDependencies/DependenciesAnalyzeManager.java +++ b/platform/lang-impl/src/com/intellij/moduleDependencies/DependenciesAnalyzeManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.moduleDependencies; import com.intellij.icons.AllIcons; @@ -27,35 +26,31 @@ import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManager; +import org.jetbrains.annotations.NotNull; /** - * User: anna - * Date: Feb 10, 2005 + * @author anna + * @since Feb 10, 2005 */ -@State( - name = "DependenciesAnalyzeManager", - storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)} -) +@State(name = "DependenciesAnalyzeManager", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}) public class DependenciesAnalyzeManager implements PersistentStateComponent { private final Project myProject; private ContentManager myContentManager; public static class State { - public boolean myForwardDirection; + public boolean forwardDirection = true; + public boolean includeTests = false; } - private State myState; + private State myState = new State(); public DependenciesAnalyzeManager(final Project project) { myProject = project; StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() { @Override public void run() { - ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject); - ToolWindow toolWindow = toolWindowManager.registerToolWindow(ToolWindowId.MODULES_DEPENDENCIES, - true, - ToolWindowAnchor.RIGHT, - project); + ToolWindowManager manager = ToolWindowManager.getInstance(myProject); + ToolWindow toolWindow = manager.registerToolWindow(ToolWindowId.MODULES_DEPENDENCIES, true, ToolWindowAnchor.RIGHT, project); myContentManager = toolWindow.getContentManager(); toolWindow.setIcon(AllIcons.Toolwindows.ToolWindowModuleDependencies); new ContentManagerWatcher(toolWindow, myContentManager); @@ -63,7 +58,7 @@ public class DependenciesAnalyzeManager implements PersistentStateComponent NODE_COMPARATOR = new Comparator() { + @Override + public int compare(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) { + if (!(o1.getUserObject() instanceof MyUserObject)) return 1; + if (!(o2.getUserObject() instanceof MyUserObject)) return -1; + return (o1.getUserObject().toString().compareToIgnoreCase(o2.getUserObject().toString())); + } + }; + + private static final ColoredTreeCellRenderer NODE_RENDERER = new ColoredTreeCellRenderer() { + @Override + public void customizeCellRenderer(@NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + Object userObject = ((DefaultMutableTreeNode)value).getUserObject(); + if (userObject instanceof MyUserObject) { + MyUserObject node = (MyUserObject)userObject; + setIcon(ModuleType.get(node.myModule).getIcon()); + append(node.myModule.getName(), node.myInCycle ? SimpleTextAttributes.ERROR_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + else if (userObject != null) { + append(userObject.toString(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + } + } + }; + private final Project myProject; - private Tree myLeftTree; - private DefaultTreeModel myLeftTreeModel; - - private final Tree myRightTree; - private final DefaultTreeModel myRightTreeModel; - - private Graph myModulesGraph; private final Module[] myModules; + private final DependenciesAnalyzeManager.State myState; - private final JTextField myPathField = new JTextField(); - + private final Tree myLeftTree; + private final Tree myRightTree; private final Splitter mySplitter; - @NonNls private static final String ourHelpID = "module.dependencies.tool.window"; + private final JTextComponent myPathField; - public ModulesDependenciesPanel(final Project project, final Module[] modules) { + private Content myContent; + private Graph myModuleGraph; + private final Map myCycleMap = ContainerUtil.newHashMap(); + + public ModulesDependenciesPanel(@NotNull Project project, @Nullable Module[] modules) { super(new BorderLayout()); + myProject = project; - myModules = modules; + myModules = modules != null ? modules : ModuleManager.getInstance(project).getModules(); + myState = DependenciesAnalyzeManager.getInstance(project).getState(); + updateModuleGraph(); - //noinspection HardCodedStringLiteral - myRightTreeModel = new DefaultTreeModel(new DefaultMutableTreeNode("Root")); - myRightTree = new Tree(myRightTreeModel); - initTree(myRightTree, true); + myLeftTree = new Tree(new DefaultTreeModel(new DefaultMutableTreeNode("Root"))); + installLeftTreeListeners(); + installTreeActions(myLeftTree, false); - initLeftTree(); + myRightTree = new Tree(new DefaultTreeModel(new DefaultMutableTreeNode("Root"))); + installTreeActions(myRightTree, true); mySplitter = new Splitter(); mySplitter.setFirstComponent(new MyTreePanel(myLeftTree, myProject)); mySplitter.setSecondComponent(new MyTreePanel(myRightTree, myProject)); - - setSplitterProportion(); add(mySplitter, BorderLayout.CENTER); + + myPathField = new JTextField(); + myPathField.setEditable(false); add(createNorthPanel(), BorderLayout.NORTH); - project.getMessageBus().connect(this).subscribe(ProjectTopics.PROJECT_ROOTS, this); - } - - private void setSplitterProportion() { - if (mySplitter == null){ - return; - } - myModulesGraph = buildGraph(); - DFSTBuilder builder = new DFSTBuilder(myModulesGraph); - if (builder.isAcyclic()){ - mySplitter.setProportion(1.f); - } else { - mySplitter.setProportion(0.5f); - } - } - - @Override - public void dispose() { - } - - public ModulesDependenciesPanel(final Project project) { - this(project, ModuleManager.getInstance(project).getModules()); - } - - private JComponent createNorthPanel(){ - DefaultActionGroup group = new DefaultActionGroup(); - - group.add(new AnAction(CommonBundle.message("action.close"), AnalysisScopeBundle.message("action.close.modules.dependencies.description"), - AllIcons.Actions.Cancel){ + project.getMessageBus().connect(this).subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() { @Override - public void actionPerformed(AnActionEvent e) { - DependenciesAnalyzeManager.getInstance(myProject).closeContent(myContent); + public void rootsChanged(ModuleRootEvent event) { + updateModuleGraph(); + updateSplitterProportion(); + updateLeftTree(); } }); - appendDependenciesAction(group); - - group.add(new ToggleAction(AnalysisScopeBundle.message("action.module.dependencies.direction"), "", isForwardDirection() ? AllIcons.Actions.SortAsc - : AllIcons.Actions.SortDesc){ - @Override - public boolean isSelected(AnActionEvent e) { - return isForwardDirection(); - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - PropertiesComponent.getInstance(myProject).setValue(DIRECTION, String.valueOf(state)); - initLeftTreeModel(); - } - - @Override - public void update(final AnActionEvent e) { - e.getPresentation().setIcon(isForwardDirection() ? AllIcons.Actions.SortAsc : AllIcons.Actions.SortDesc); - } - }); - - group.add(new ContextHelpAction(ourHelpID)); - - ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true); - final JPanel panel = new JPanel(new BorderLayout()); - panel.add(toolbar.getComponent(), BorderLayout.NORTH); - panel.add(myPathField, BorderLayout.SOUTH); - myPathField.setEditable(false); - return panel; + updateSplitterProportion(); + updateLeftTree(); } - private boolean isForwardDirection() { - final String value = PropertiesComponent.getInstance(myProject).getValue(DIRECTION); - return value == null || Boolean.parseBoolean(value); + private void updateModuleGraph() { + myModuleGraph = ModuleManager.getInstance(myProject).moduleGraph(myState.includeTests); + myCycleMap.clear(); } - private static void appendDependenciesAction(final DefaultActionGroup group) { - final AnAction analyzeDepsAction = ActionManager.getInstance().getAction(IdeActions.ACTION_ANALYZE_DEPENDENCIES); - group.add(new AnAction(analyzeDepsAction.getTemplatePresentation().getText(), - analyzeDepsAction.getTemplatePresentation().getDescription(), - AllIcons.Toolwindows.ToolWindowInspection){ - + private Iterable getModuleDependencies(Module module) { + final Iterator iterator = myState.forwardDirection ? myModuleGraph.getIn(module) : myModuleGraph.getOut(module); + return new Iterable() { @Override - public void actionPerformed(AnActionEvent e) { - analyzeDepsAction.actionPerformed(e); + public Iterator iterator() { + return iterator; } - - - @Override - public void update(AnActionEvent e) { - analyzeDepsAction.update(e); - } - }); + }; } - private void buildRightTree(Module module){ - final DefaultMutableTreeNode root = (DefaultMutableTreeNode)myRightTreeModel.getRoot(); - root.removeAllChildren(); - final Set> cycles = GraphAlgorithms.getInstance().findCycles(myModulesGraph, module); - int index = 1; - for (List modules : cycles) { - final DefaultMutableTreeNode cycle = new DefaultMutableTreeNode( - AnalysisScopeBundle.message("module.dependencies.cycle.node.text", Integer.toString(index++).toUpperCase())); - root.add(cycle); - cycle.add(new DefaultMutableTreeNode(new MyUserObject(false, module))); - for (Module moduleInCycle : modules) { - cycle.add(new DefaultMutableTreeNode(new MyUserObject(false, moduleInCycle))); - } - } - ((DefaultTreeModel)myRightTree.getModel()).reload(); - TreeUtil.expandAll(myRightTree); - } - - private void initLeftTreeModel(){ - final DefaultMutableTreeNode root = (DefaultMutableTreeNode)myLeftTreeModel.getRoot(); - root.removeAllChildren(); - myModulesGraph = buildGraph(); - setSplitterProportion(); - ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { - @Override - public void run() { - final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); - final Map inCycle = new HashMap(); - for (Module module : myModules) { - if (progressIndicator != null) { - if (progressIndicator.isCanceled()) return; - progressIndicator.setText(AnalysisScopeBundle.message("update.module.tree.progress.text", module.getName())); - } - if (!module.isDisposed()) { - Boolean isInCycle = inCycle.get(module); - if (isInCycle == null) { - isInCycle = !GraphAlgorithms.getInstance().findCycles(myModulesGraph, module).isEmpty(); - inCycle.put(module, isInCycle); - } - final DefaultMutableTreeNode moduleNode = new DefaultMutableTreeNode(new MyUserObject(isInCycle.booleanValue(), module)); - root.add(moduleNode); - final Iterator out = myModulesGraph.getOut(module); - while (out.hasNext()) { - moduleNode.add(new DefaultMutableTreeNode(new MyUserObject(false, out.next()))); - } - } + private boolean isInCycle(Module module) { + Boolean inCycle = myCycleMap.get(module); + if (inCycle == null) { + Set> cycles = GraphAlgorithms.getInstance().findCycles(myModuleGraph, module); + inCycle = !cycles.isEmpty(); + myCycleMap.put(module, inCycle); + for (List cycle : cycles) { + for (Module moduleInCycle : cycle) { + myCycleMap.put(moduleInCycle, true); } } - }, AnalysisScopeBundle.message("update.module.tree.progress.title"), true, myProject); - sortSubTree(root); - myLeftTreeModel.reload(); + } + return inCycle; } - private static void sortSubTree(final DefaultMutableTreeNode root) { - TreeUtil.sort(root, new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - DefaultMutableTreeNode node1 = (DefaultMutableTreeNode)o1; - DefaultMutableTreeNode node2 = (DefaultMutableTreeNode)o2; - if (!(node1.getUserObject() instanceof MyUserObject)){ - return 1; - } - else if (!(node2.getUserObject() instanceof MyUserObject)){ - return -1; - } - return (node1.getUserObject().toString().compareToIgnoreCase(node2.getUserObject().toString())); - } - }); - } - - private void selectCycleUpward(final DefaultMutableTreeNode selection){ - ArrayList selectionNodes = new ArrayList(); - selectionNodes.add(selection); - DefaultMutableTreeNode current = (DefaultMutableTreeNode)selection.getParent(); - boolean flag = false; - while (current != null && current.getUserObject() != null){ - if (current.getUserObject().equals(selection.getUserObject())){ - flag = true; - selectionNodes.add(current); - break; - } - selectionNodes.add(current); - current = (DefaultMutableTreeNode)current.getParent(); - } - if (flag){ - for (DefaultMutableTreeNode node : selectionNodes) { - ((MyUserObject)node.getUserObject()).setInCycle(true); - } - } - if (current != null) current = (DefaultMutableTreeNode)current.getParent(); - while (current != null) { - final Object userObject = current.getUserObject(); - if (userObject instanceof MyUserObject) { - ((MyUserObject)userObject).setInCycle(false); - } - current = (DefaultMutableTreeNode)current.getParent(); - } - myLeftTree.repaint(); - } - - private void initLeftTree(){ - //noinspection HardCodedStringLiteral - final DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root"); - myLeftTreeModel = new DefaultTreeModel(root); - initLeftTreeModel(); - myLeftTree = new Tree(myLeftTreeModel); - initTree(myLeftTree, false); - + private void installLeftTreeListeners() { myLeftTree.addTreeExpansionListener(new TreeExpansionListener() { @Override - public void treeCollapsed(TreeExpansionEvent event) { - } + public void treeCollapsed(TreeExpansionEvent event) { } @Override public void treeExpanded(TreeExpansionEvent event) { - final DefaultMutableTreeNode expandedNode = (DefaultMutableTreeNode)event.getPath().getLastPathComponent(); - for(int i = 0; i < expandedNode.getChildCount(); i++){ + DefaultMutableTreeNode expandedNode = (DefaultMutableTreeNode)event.getPath().getLastPathComponent(); + for (int i = 0; i < expandedNode.getChildCount(); i++) { DefaultMutableTreeNode child = (DefaultMutableTreeNode)expandedNode.getChildAt(i); - if (child.getChildCount() == 0){ - Module module = ((MyUserObject)child.getUserObject()).getModule(); - final Iterator out = myModulesGraph.getOut(module); - while (out.hasNext()) { - final Module nextModule = out.next(); - child.add(new DefaultMutableTreeNode(new MyUserObject(false, nextModule))); + if (child.getChildCount() == 0 && !isLooped(event.getPath(), child)) { + Module module = ((MyUserObject)child.getUserObject()).myModule; + for (Module dependency : getModuleDependencies(module)) { + child.add(new DefaultMutableTreeNode(new MyUserObject(isInCycle(dependency), dependency))); } - sortSubTree(child); + TreeUtil.sort(child, NODE_COMPARATOR); } } } + + private boolean isLooped(TreePath path, DefaultMutableTreeNode child) { + for (Object o : path.getPath()) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode)o; + if (node != child && Comparing.equal(node.getUserObject(), child.getUserObject())) { + return true; + } + } + return false; + } }); myLeftTree.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { - final TreePath selectionPath = myLeftTree.getSelectionPath(); + TreePath selectionPath = myLeftTree.getSelectionPath(); if (selectionPath != null) { - myPathField.setText(StringUtil.join(selectionPath.getPath(), new Function() { @Override public String fun(Object o) { - final Object userObject = ((DefaultMutableTreeNode)o).getUserObject(); - if (userObject instanceof MyUserObject) { - return ((MyUserObject)userObject).getModule().getName(); - } - return ""; + Object userObject = ((DefaultMutableTreeNode)o).getUserObject(); + return userObject instanceof MyUserObject ? ((MyUserObject)userObject).myModule.getName() : ""; } - }, ":")); + }, " : ")); - final DefaultMutableTreeNode selection = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); - if (selection != null){ - TreeUtil.traverseDepth(selection, new TreeUtil.Traverse() { - @Override - public boolean accept(Object node) { - DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)node; - if (treeNode.getUserObject() instanceof MyUserObject){ - ((MyUserObject)treeNode.getUserObject()).setInCycle(false); - } - return true; - } - }); - selectCycleUpward(selection); - buildRightTree(((MyUserObject)selection.getUserObject()).getModule()); + DefaultMutableTreeNode selection = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); + if (selection != null) { + updateRightTree(((MyUserObject)selection.getUserObject()).myModule); } } } }); - TreeUtil.selectFirstNode(myLeftTree); } - private static ActionGroup createTreePopupActions(final boolean isRightTree, final Tree tree) { - DefaultActionGroup group = new DefaultActionGroup(); - final TreeExpander treeExpander = new TreeExpander() { - @Override - public void expandAll() { - TreeUtil.expandAll(tree); - } - - @Override - public boolean canExpand() { - return isRightTree; - } - - @Override - public void collapseAll() { - TreeUtil.collapseAll(tree, 3); - } - - @Override - public boolean canCollapse() { - return true; - } - }; - - final CommonActionsManager actionManager = CommonActionsManager.getInstance(); - if (isRightTree){ - group.add(actionManager.createExpandAllAction(treeExpander, tree)); - } - group.add(actionManager.createCollapseAllAction(treeExpander, tree)); - final ActionManager globalActionManager = ActionManager.getInstance(); - group.add(globalActionManager.getAction(IdeActions.ACTION_EDIT_SOURCE)); - group.add(Separator.getInstance()); - group.add(globalActionManager.getAction(IdeActions.ACTION_ANALYZE_DEPENDENCIES)); - group.add(globalActionManager.getAction(IdeActions.ACTION_ANALYZE_BACK_DEPENDENCIES)); - group.add(globalActionManager.getAction(IdeActions.ACTION_ANALYZE_CYCLIC_DEPENDENCIES)); - return group; - } - - private static void initTree(Tree tree, boolean isRightTree) { + private static void installTreeActions(Tree tree, boolean enableExpandAll) { tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - tree.setCellRenderer(new MyTreeCellRenderer()); + tree.setCellRenderer(NODE_RENDERER); tree.setRootVisible(false); tree.setShowsRootHandles(true); UIUtil.setLineStyleAngled(tree); TreeUtil.installActions(tree); + new TreeSpeedSearch(tree, new Convertor() { @Override public String convert(TreePath o) { return o.getLastPathComponent().toString(); } }, true); - PopupHandler.installUnknownPopupHandler(tree, createTreePopupActions(isRightTree, tree), ActionManager.getInstance()); + + DefaultActionGroup group = new DefaultActionGroup(); + CommonActionsManager commonActionManager = CommonActionsManager.getInstance(); + ActionManager globalActionManager = ActionManager.getInstance(); + + TreeExpander treeExpander = new MyTreeExpander(tree, enableExpandAll); + group.add(commonActionManager.createExpandAllAction(treeExpander, tree)); + group.add(commonActionManager.createCollapseAllAction(treeExpander, tree)); + group.add(globalActionManager.getAction(IdeActions.ACTION_EDIT_SOURCE)); + group.add(Separator.getInstance()); + group.add(globalActionManager.getAction(IdeActions.ACTION_ANALYZE_DEPENDENCIES)); + group.add(globalActionManager.getAction(IdeActions.ACTION_ANALYZE_BACK_DEPENDENCIES)); + group.add(globalActionManager.getAction(IdeActions.ACTION_ANALYZE_CYCLIC_DEPENDENCIES)); + group.add(globalActionManager.getAction(IdeActions.ACTION_ANALYZE_MODULE_DEPENDENCIES)); + + PopupHandler.installUnknownPopupHandler(tree, group, ActionManager.getInstance()); } - - private Graph buildGraph() { - final Graph graph = ModuleManager.getInstance(myProject).moduleGraph(); - if (isForwardDirection()) { - return graph; - } - else { - return GraphAlgorithms.getInstance().invertEdgeDirections(graph); - } + private void updateSplitterProportion() { + DFSTBuilder builder = new DFSTBuilder(myModuleGraph); + mySplitter.setProportion(builder.isAcyclic() ? 1.0f : 0.6f); } - public void setContent(final Content content) { + private JComponent createNorthPanel() { + DefaultActionGroup group = new DefaultActionGroup(); + + group.add(new AnAction(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel) { + @Override + public void actionPerformed(AnActionEvent e) { + DependenciesAnalyzeManager.getInstance(myProject).closeContent(myContent); + } + }); + + final AnAction analyzeDepsAction = ActionManager.getInstance().getAction(IdeActions.ACTION_ANALYZE_DEPENDENCIES); + group.add(new AnAction(analyzeDepsAction.getTemplatePresentation().getText(), null, AllIcons.Toolwindows.ToolWindowInspection) { + @Override + public void actionPerformed(AnActionEvent e) { + analyzeDepsAction.actionPerformed(e); + } + + @Override + public void update(AnActionEvent e) { + analyzeDepsAction.update(e); + } + }); + + group.add(new ToggleAction(AnalysisScopeBundle.message("action.module.dependencies.direction")) { + @Override + public boolean isSelected(AnActionEvent e) { + return !myState.forwardDirection; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + myState.forwardDirection = !state; + updateLeftTree(); + } + + @Override + public void update(@NotNull AnActionEvent e) { + e.getPresentation().setIcon(myState.forwardDirection ? AllIcons.Hierarchy.Subtypes : AllIcons.Hierarchy.Supertypes); + super.update(e); + } + }); + + group.add(new ToggleAction(AnalysisScopeBundle.message("action.module.dependencies.tests"), null, AllIcons.Modules.TestSourceFolder) { + @Override + public boolean isSelected(AnActionEvent e) { + return myState.includeTests; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + myState.includeTests = state; + updateModuleGraph(); + updateLeftTree(); + } + }); + + group.add(new ContextHelpAction(HELP_ID)); + + ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true); + JPanel panel = new JPanel(new BorderLayout()); + panel.add(toolbar.getComponent(), BorderLayout.WEST); + panel.add(myPathField, BorderLayout.CENTER); + return panel; + } + + private void updateLeftTree() { + final DefaultMutableTreeNode root = (DefaultMutableTreeNode)myLeftTree.getModel().getRoot(); + root.removeAllChildren(); + + ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { + @Override + public void run() { + for (Module module : myModules) { + if (module.isDisposed()) continue; + ProgressManager.progress(AnalysisScopeBundle.message("update.module.tree.progress.text", module.getName())); + + DefaultMutableTreeNode moduleNode = new DefaultMutableTreeNode(new MyUserObject(isInCycle(module), module)); + root.add(moduleNode); + for (Module dependency : getModuleDependencies(module)) { + moduleNode.add(new DefaultMutableTreeNode(new MyUserObject(isInCycle(dependency), dependency))); + } + } + } + }, AnalysisScopeBundle.message("update.module.tree.progress.title"), true, myProject); + + TreeUtil.sort(root, NODE_COMPARATOR); + ((DefaultTreeModel)myLeftTree.getModel()).reload(); + TreeUtil.selectFirstNode(myLeftTree); + } + + private void updateRightTree(Module module) { + DefaultMutableTreeNode root = (DefaultMutableTreeNode)myRightTree.getModel().getRoot(); + root.removeAllChildren(); + + Set> cycles = GraphAlgorithms.getInstance().findCycles(myModuleGraph, module); + int index = 1; + for (List modules : cycles) { + DefaultMutableTreeNode cycle = new DefaultMutableTreeNode(AnalysisScopeBundle.message("module.dependencies.cycle.node.text", index++)); + root.add(cycle); + cycle.add(new DefaultMutableTreeNode(new MyUserObject(false, module))); + for (Module moduleInCycle : modules) { + cycle.add(new DefaultMutableTreeNode(new MyUserObject(false, moduleInCycle))); + } + } + + ((DefaultTreeModel)myRightTree.getModel()).reload(); + TreeUtil.expandAll(myRightTree); + } + + public void setContent(Content content) { myContent = content; } @Override - public void beforeRootsChange(ModuleRootEvent event) { - } + public void dispose() { } - @Override - public void rootsChanged(ModuleRootEvent event) { - initLeftTreeModel(); - TreeUtil.selectFirstNode(myLeftTree); - } - private static class MyUserObject implements NavigatableWithText{ - private boolean myInCycle; + private static class MyUserObject implements NavigatableWithText { private final Module myModule; + private final boolean myInCycle; - public MyUserObject(final boolean inCycle, final Module module) { + public MyUserObject(boolean inCycle, Module module) { myInCycle = inCycle; myModule = module; } - public boolean isInCycle() { - return myInCycle; - } - - public void setInCycle(final boolean inCycle) { - myInCycle = inCycle; - } - - public Module getModule() { - return myModule; - } - - public boolean equals(Object object) { - return object instanceof MyUserObject && myModule.equals(((MyUserObject)object).getModule()); - } - - public int hashCode() { - return myModule.hashCode(); - } - - public String toString() { - return myModule.getName(); - } - @Override public void navigate(boolean requestFocus) { ProjectSettingsService.getInstance(myModule.getProject()).openModuleSettings(myModule); @@ -485,7 +400,7 @@ public class ModulesDependenciesPanel extends JPanel implements ModuleRootListen @Override public boolean canNavigate() { - return myModule != null && !myModule.isDisposed(); + return !myModule.isDisposed(); } @Override @@ -495,14 +410,30 @@ public class ModulesDependenciesPanel extends JPanel implements ModuleRootListen @Override public String getNavigateActionText(boolean focusEditor) { - return "Open Module Settings"; + return ActionsBundle.message("action.ModuleSettings.navigate"); + } + + @Override + public boolean equals(Object o) { + return o == this || o instanceof MyUserObject && myModule.equals(((MyUserObject)o).myModule); + } + + @Override + public int hashCode() { + return myModule.hashCode(); + } + + @Override + public String toString() { + return myModule.getName(); } } - private static class MyTreePanel extends JPanel implements DataProvider{ + private static class MyTreePanel extends JPanel implements DataProvider { private final Tree myTree; private final Project myProject; - public MyTreePanel(final Tree tree, Project project) { + + public MyTreePanel(Tree tree, Project project) { super(new BorderLayout()); myTree = tree; myProject = project; @@ -511,26 +442,26 @@ public class ModulesDependenciesPanel extends JPanel implements ModuleRootListen @Override public Object getData(String dataId) { - if (CommonDataKeys.PROJECT.is(dataId)){ + if (CommonDataKeys.PROJECT.is(dataId)) { return myProject; } - if (LangDataKeys.MODULE_CONTEXT.is(dataId)){ - final TreePath selectionPath = myTree.getLeadSelectionPath(); - if (selectionPath != null && selectionPath.getLastPathComponent() instanceof DefaultMutableTreeNode){ + if (LangDataKeys.MODULE_CONTEXT.is(dataId)) { + TreePath selectionPath = myTree.getLeadSelectionPath(); + if (selectionPath != null && selectionPath.getLastPathComponent() instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode node = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); - if (node.getUserObject() instanceof MyUserObject){ - return ((MyUserObject)node.getUserObject()).getModule(); + if (node.getUserObject() instanceof MyUserObject) { + return ((MyUserObject)node.getUserObject()).myModule; } } } if (PlatformDataKeys.HELP_ID.is(dataId)) { - return ourHelpID; + return HELP_ID; } if (CommonDataKeys.NAVIGATABLE.is(dataId)) { - final TreePath selectionPath = myTree.getLeadSelectionPath(); - if (selectionPath != null && selectionPath.getLastPathComponent() instanceof DefaultMutableTreeNode){ + TreePath selectionPath = myTree.getLeadSelectionPath(); + if (selectionPath != null && selectionPath.getLastPathComponent() instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode node = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); - if (node.getUserObject() instanceof MyUserObject){ + if (node.getUserObject() instanceof MyUserObject) { return node.getUserObject(); } } @@ -538,32 +469,34 @@ public class ModulesDependenciesPanel extends JPanel implements ModuleRootListen return null; } } - private static class MyTreeCellRenderer extends ColoredTreeCellRenderer { + + private static class MyTreeExpander implements TreeExpander { + private final Tree myTree; + private final boolean myEnableExpandAll; + + public MyTreeExpander(Tree tree, boolean enableExpandAll) { + myTree = tree; + myEnableExpandAll = enableExpandAll; + } + @Override - public void customizeCellRenderer( - JTree tree, - Object value, - boolean selected, - boolean expanded, - boolean leaf, - int row, - boolean hasFocus - ){ - final Object userObject = ((DefaultMutableTreeNode)value).getUserObject(); - if (!(userObject instanceof MyUserObject)){ - if (userObject != null){ - append(userObject.toString(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); - } - return; - } - MyUserObject node = (MyUserObject)userObject; - Module module = node.getModule(); - setIcon(ModuleType.get(module).getIcon()); - if (node.isInCycle()){ - append(module.getName(), SimpleTextAttributes.ERROR_ATTRIBUTES); - } else { - append(module.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - } + public void expandAll() { + TreeUtil.expandAll(myTree); + } + + @Override + public boolean canExpand() { + return myEnableExpandAll; + } + + @Override + public void collapseAll() { + TreeUtil.collapseAll(myTree, 3); + } + + @Override + public boolean canCollapse() { + return true; } } -} +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/moduleDependencies/ShowModuleDependenciesAction.java b/platform/lang-impl/src/com/intellij/moduleDependencies/ShowModuleDependenciesAction.java index e4bc7db8b6bd..1de7690c3f25 100644 --- a/platform/lang-impl/src/com/intellij/moduleDependencies/ShowModuleDependenciesAction.java +++ b/platform/lang-impl/src/com/intellij/moduleDependencies/ShowModuleDependenciesAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -13,85 +13,69 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.moduleDependencies; import com.intellij.analysis.AnalysisScope; import com.intellij.analysis.AnalysisScopeBundle; -import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; /** - * User: anna - * Date: Feb 9, 2005 + * @author anna + * @since Feb 9, 2005 */ -public class ShowModuleDependenciesAction extends AnAction{ +public class ShowModuleDependenciesAction extends AnAction { @Override - public void actionPerformed(AnActionEvent e) { - final DataContext dataContext = e.getDataContext(); - final Project project = CommonDataKeys.PROJECT.getData(dataContext); - if (project == null){ - return; - } - ModulesDependenciesPanel panel; - AnalysisScope scope = new AnalysisScope(project); - final Module[] modules = LangDataKeys.MODULE_CONTEXT_ARRAY.getData(dataContext); - if (modules != null){ - panel = new ModulesDependenciesPanel(project, modules); - scope = new AnalysisScope(modules); - } else { - final PsiElement element = CommonDataKeys.PSI_FILE.getData(dataContext); - final Module module = element != null ? ModuleUtil.findModuleForPsiElement(element) : null; + public void update(@NotNull AnActionEvent e) { + e.getPresentation().setEnabled(e.getProject() != null); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + Project project = e.getProject(); + if (project == null) return; + + Module[] modules = LangDataKeys.MODULE_CONTEXT_ARRAY.getData(e.getDataContext()); + if (modules == null) { + PsiElement element = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); + Module module = element != null ? ModuleUtilCore.findModuleForPsiElement(element) : null; if (module != null && ModuleManager.getInstance(project).getModules().length > 1) { MyModuleOrProjectScope dlg = new MyModuleOrProjectScope(module.getName()); - if (dlg.showAndGet()) { - if (!dlg.useProjectScope()) { - panel = new ModulesDependenciesPanel(project, new Module[]{module}); - scope = new AnalysisScope(module); - } - else { - panel = new ModulesDependenciesPanel(project); - } - } - else { + if (!dlg.showAndGet()) { return; } - } else { - panel = new ModulesDependenciesPanel(project); + if (!dlg.useProjectScope()) { + modules = new Module[]{module}; + } } } - Content content = ContentFactory.SERVICE.getInstance().createContent(panel, - AnalysisScopeBundle.message( - "module.dependencies.toolwindow.title", - StringUtil.capitalize(scope.getDisplayName())), - false); + ModulesDependenciesPanel panel = new ModulesDependenciesPanel(project, modules); + AnalysisScope scope = modules != null ? new AnalysisScope(modules) : new AnalysisScope(project); + Content content = ContentFactory.SERVICE.getInstance().createContent(panel, scope.getDisplayName(), false); content.setDisposer(panel); panel.setContent(content); DependenciesAnalyzeManager.getInstance(project).addContent(content); } - @Override - public void update(AnActionEvent e) { - final DataContext dataContext = e.getDataContext(); - final Project project = CommonDataKeys.PROJECT.getData(dataContext); - e.getPresentation().setEnabled(project != null); - } - - private static class MyModuleOrProjectScope extends DialogWrapper{ + private static class MyModuleOrProjectScope extends DialogWrapper { private final JRadioButton myProjectScope; private final JRadioButton myModuleScope; + protected MyModuleOrProjectScope(String moduleName) { super(false); setTitle(AnalysisScopeBundle.message("module.dependencies.scope.dialog.title")); @@ -112,8 +96,8 @@ public class ShowModuleDependenciesAction extends AnAction{ return panel; } - public boolean useProjectScope(){ + public boolean useProjectScope() { return myProjectScope.isSelected(); } } -} +} \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java index 1e99dade5d6a..c784a8fccd74 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java @@ -287,6 +287,7 @@ public interface IdeActions { @NonNls String ACTION_ANALYZE_DEPENDENCIES = "ShowPackageDeps"; @NonNls String ACTION_ANALYZE_BACK_DEPENDENCIES = "ShowBackwardPackageDeps"; @NonNls String ACTION_ANALYZE_CYCLIC_DEPENDENCIES = "ShowPackageCycles"; + @NonNls String ACTION_ANALYZE_MODULE_DEPENDENCIES = "ShowModulesDependencies"; @NonNls String GROUP_MOVE_MODULE_TO_GROUP = "MoveModuleToGroup"; @NonNls String ACTION_CLEAR_TEXT = "TextComponent.ClearAction"; @NonNls String ACTION_HIGHLIGHT_USAGES_IN_FILE = "HighlightUsagesInFile"; diff --git a/platform/platform-resources-en/src/messages/AnalysisScopeBundle.properties b/platform/platform-resources-en/src/messages/AnalysisScopeBundle.properties index 0ec0bb4caa1a..15b14276c187 100644 --- a/platform/platform-resources-en/src/messages/AnalysisScopeBundle.properties +++ b/platform/platform-resources-en/src/messages/AnalysisScopeBundle.properties @@ -21,8 +21,8 @@ action.group.by.scope.type.description=Group by Scope Type (production, test, li action.show.files=Show Files action.show.files.description=Show/Hide Files action.close.dependency.description=Close Dependency Viewer -action.close.modules.dependencies.description=Close Modules Dependencies Viewer -action.module.dependencies.direction=Dependencies Direction +action.module.dependencies.direction=Backward Dependencies (Usages) +action.module.dependencies.tests=Include Test Dependencies action.analyzing.cyclic.dependencies.in.scope=Cyclic Dependencies of {0} action.analyze.verb=Analyze action.analysis.noun=Analysis @@ -39,6 +39,8 @@ action.select.in.left.tree.description=Select in left tree (to browse dependenci action.forward.dependency.analysis=Dependency Analysis action.backward.dependency.analysis=Backward Dependency Analysis +update.module.tree.progress.title=Updating Module Tree +update.module.tree.progress.text=Processing module ''{0}'' cycles cyclic.dependencies.scope.dialog.title=Specify {0} Scope cyclic.dependencies.scope.dialog.project.button={0} the whole &project @@ -48,11 +50,10 @@ cyclic.dependencies.usage.view.root.node.text=Usages of package ''{0}'' in packa cyclic.dependencies.usage.view.initial.text=Select package to analyze from the left tree cyclic.dependencies.progress.text=Building dependencies graph -module.dependencies.cycle.node.text=Cycle{0} +module.dependencies.cycle.node.text=Cycle {0} module.dependencies.scope.dialog.module.button=Inspect &module ''{0}'' module.dependencies.scope.dialog.project.button=Inspect the whole &project module.dependencies.scope.dialog.title=Specify Analysis Scope -module.dependencies.toolwindow.title={0} backward.dependencies.usage.view.root.node.text=Usages of the left tree scope selection in the right tree scope selection backward.dependencies.usage.view.initial.text=Select where to search in right tree and what to search in left tree. @@ -83,7 +84,6 @@ dependencies.tree.node.default.package.abbreviation= #scope selector dependencies.libraries.node.text=Libraries -error.packageset.common.expectations=(test|lib|src)[modulename] expected before : error.packageset.token.expectations=Unexpected ''{0}'' error.packageset.pattern.expectations=Package pattern expected error.packageset.rparen.expected=')' expected @@ -110,6 +110,4 @@ project.is.out.of.date=Project Is Out Of Date skip.import.statements.checkbox.title=Skip import statements mark.dependency.illegal.text=Mark Illegal scope.selected.files=Selected files -update.module.tree.progress.title=Update modules tree -update.module.tree.progress.text=Process module ''{0}'' cycles scope.virtual.files=selection \ No newline at end of file