8344204: IGV: Button to enable/disable cutting of long edges

Reviewed-by: rcastanedalo, chagedorn
This commit is contained in:
Tobias Holenstein 2024-11-18 08:35:12 +00:00
parent 4a7ce1d7c1
commit 6c2ae44c05
13 changed files with 176 additions and 106 deletions

View File

@ -45,6 +45,12 @@ public class HierarchicalCFGLayoutManager implements LayoutManager {
fontMetrics = canvas.getFontMetrics(font);
}
@Override
public void setCutEdges(boolean enable) {
subManager.setCutEdges(enable);
manager.setCutEdges(enable);
}
public void setSubManager(LayoutManager manager) {
this.subManager = manager;
}

View File

@ -43,6 +43,12 @@ public class HierarchicalClusterLayoutManager implements LayoutManager {
this.combine = combine;
}
@Override
public void setCutEdges(boolean enable) {
subManager.setCutEdges(enable);
manager.setCutEdges(enable);
}
public void doLayout(LayoutGraph graph, Set<? extends Link> importantLinks) {
doLayout(graph);
}

View File

@ -136,8 +136,9 @@ public class HierarchicalLayoutManager implements LayoutManager {
this.layerOffset = layerOffset;
}
public void setMaxLayerLength(int v) {
maxLayerLength = v;
@Override
public void setCutEdges(boolean enable) {
maxLayerLength = enable ? 10 : -1;
}
public void setMinLayerDifference(int v) {

View File

@ -38,8 +38,8 @@ public class HierarchicalStableLayoutManager {
public static final int X_OFFSET = 8;
public static final int LAYER_OFFSET = 8;
// Algorithm global data structures
private HashSet<? extends Vertex> currentVertices;
private HashSet<? extends Link> currentLinks;
private Set<? extends Vertex> currentVertices;
private Set<? extends Link> currentLinks;
private Set<Link> reversedLinks;
private List<LayoutNode> nodes;
private final HashMap<Vertex, LayoutNode> vertexToLayoutNode;
@ -51,10 +51,19 @@ public class HierarchicalStableLayoutManager {
private HashMap<Vertex, VertexAction> vertexToAction;
private List<VertexAction> vertexActions;
private List<LinkAction> linkActions;
private HashSet<? extends Vertex> oldVertices;
private HashSet<? extends Link> oldLinks;
private Set<? extends Vertex> oldVertices;
private Set<? extends Link> oldLinks;
private boolean shouldRedrawLayout = true;
private boolean shouldRemoveEmptyLayers = true;
private boolean cutEdges = false;
public void doLayout(LayoutGraph layoutGraph) {
boolean oldShouldRedrawLayout = shouldRedrawLayout;
setShouldRedrawLayout(true);
updateLayout(layoutGraph.getVertices(), layoutGraph.getLinks());
setShouldRedrawLayout(oldShouldRedrawLayout);
}
enum Action {
ADD,
@ -90,6 +99,15 @@ public class HierarchicalStableLayoutManager {
nodes = new ArrayList<>();
}
public void setCutEdges(boolean enable) {
cutEdges = enable;
manager.setCutEdges(enable);
}
public boolean getCutEdges() {
return cutEdges;
}
private int calculateOptimalBoth(LayoutNode n) {
if (n.preds.isEmpty() && n.succs.isEmpty()) {
return n.x;
@ -396,7 +414,7 @@ public class HierarchicalStableLayoutManager {
this.shouldRedrawLayout = shouldRedrawLayout;
}
public void updateLayout(HashSet<? extends Vertex> vertices, HashSet<? extends Link> links) {
public void updateLayout(Set<? extends Vertex> vertices, Set<? extends Link> links) {
currentVertices = vertices;
currentLinks = links;
reversedLinks = new HashSet<>();

View File

@ -39,6 +39,9 @@ public class LinearLayoutManager implements LayoutManager {
this.vertexRank = vertexRank;
}
@Override
public void setCutEdges(boolean enable) {}
@Override
public void doLayout(LayoutGraph graph) {
doLayout(graph, new HashSet<>());

View File

@ -31,6 +31,8 @@ import java.util.Set;
*/
public interface LayoutManager {
void setCutEdges(boolean enable);
void doLayout(LayoutGraph graph);
void doLayout(LayoutGraph graph, Set<? extends Link> importantLinks);

View File

@ -700,12 +700,19 @@ public class DiagramScene extends ObjectScene implements DiagramViewer, DoubleCl
}
private void doStableSeaLayout(HashSet<Figure> visibleFigures, HashSet<Connection> visibleConnections) {
hierarchicalStableLayoutManager.updateLayout(visibleFigures, visibleConnections);
boolean enable = model.getCutEdges();
boolean previous = hierarchicalStableLayoutManager.getCutEdges();
hierarchicalStableLayoutManager.setCutEdges(enable);
if (enable != previous) {
hierarchicalStableLayoutManager.doLayout(new LayoutGraph(visibleConnections, visibleFigures));
} else {
hierarchicalStableLayoutManager.updateLayout(visibleFigures, visibleConnections);
}
}
private void doSeaLayout(HashSet<Figure> figures, HashSet<Connection> edges) {
HierarchicalLayoutManager manager = new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS);
manager.setMaxLayerLength(10);
manager.setCutEdges(model.getCutEdges());
manager.doLayout(new LayoutGraph(edges, figures));
hierarchicalStableLayoutManager.setShouldRedrawLayout(true);
}
@ -713,7 +720,7 @@ public class DiagramScene extends ObjectScene implements DiagramViewer, DoubleCl
private void doClusteredLayout(HashSet<Connection> edges) {
HierarchicalClusterLayoutManager m = new HierarchicalClusterLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS);
HierarchicalLayoutManager manager = new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS);
manager.setMaxLayerLength(9);
manager.setCutEdges(model.getCutEdges());
manager.setMinLayerDifference(3);
m.setManager(manager);
m.setSubManager(new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS));
@ -724,7 +731,7 @@ public class DiagramScene extends ObjectScene implements DiagramViewer, DoubleCl
Diagram diagram = getModel().getDiagram();
HierarchicalCFGLayoutManager m = new HierarchicalCFGLayoutManager();
HierarchicalLayoutManager manager = new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS);
manager.setMaxLayerLength(9);
manager.setCutEdges(model.getCutEdges());
manager.setMinLayerDifference(1);
manager.setLayoutSelfEdges(true);
manager.setXOffset(25);

View File

@ -36,6 +36,7 @@ import com.sun.hotspot.igv.graph.Figure;
import com.sun.hotspot.igv.graph.MatcherSelector;
import com.sun.hotspot.igv.settings.Settings;
import com.sun.hotspot.igv.util.RangeSliderModel;
import com.sun.hotspot.igv.view.actions.CutEdgesAction;
import com.sun.hotspot.igv.view.actions.GlobalSelectionAction;
import java.awt.Color;
import java.util.*;
@ -68,8 +69,8 @@ public class DiagramViewModel extends RangeSliderModel implements ChangedListene
private boolean showCFG;
private boolean showNodeHull;
private boolean showEmptyBlocks;
private boolean hideDuplicates;
private static boolean globalSelection = false;
private static boolean cutEdges = false;
private final ChangedListener<FilterChain> filterChainChangedListener = changedFilterChain -> {
assert filterChain == changedFilterChain;
@ -80,6 +81,18 @@ public class DiagramViewModel extends RangeSliderModel implements ChangedListene
return group;
}
public boolean getCutEdges() {
return cutEdges;
}
public void setCutEdges(boolean enable, boolean fire) {
boolean prevEnable = cutEdges;
cutEdges = enable;
if (fire && prevEnable != enable) {
diagramChangedEvent.fire();
}
}
public boolean getGlobalSelection() {
return globalSelection;
}
@ -154,25 +167,6 @@ public class DiagramViewModel extends RangeSliderModel implements ChangedListene
diagramChangedEvent.fire();
}
public void setHideDuplicates(boolean hideDuplicates) {
this.hideDuplicates = hideDuplicates;
InputGraph currentGraph = getFirstGraph();
if (hideDuplicates) {
// Back up to the unhidden equivalent graph
int index = graphs.indexOf(currentGraph);
while (graphs.get(index).getProperties().get("_isDuplicate") != null) {
index--;
}
currentGraph = graphs.get(index);
}
filterGraphs();
selectGraph(currentGraph);
}
public boolean getHideDuplicates() {
return hideDuplicates;
}
private void initGroup() {
group.getChangedEvent().addListener(g -> {
assert g == group;
@ -191,6 +185,7 @@ public class DiagramViewModel extends RangeSliderModel implements ChangedListene
public DiagramViewModel(DiagramViewModel model) {
super(model);
globalSelection = false;
cutEdges = false;
group = model.getGroup();
initGroup();
graphs = new ArrayList<>(model.graphs);
@ -205,12 +200,12 @@ public class DiagramViewModel extends RangeSliderModel implements ChangedListene
filtersOrder = provider.getAllFiltersOrdered();
globalSelection = GlobalSelectionAction.get(GlobalSelectionAction.class).isSelected();
cutEdges = CutEdgesAction.get(CutEdgesAction.class).isSelected();
showCFG = model.getShowCFG();
showSea = model.getShowSea();
showBlocks = model.getShowBlocks();
showNodeHull = model.getShowNodeHull();
showEmptyBlocks = model.getShowEmptyBlocks();
hideDuplicates = model.getHideDuplicates();
hiddenNodes = new HashSet<>(model.getHiddenNodes());
selectedNodes = new HashSet<>();
@ -228,13 +223,13 @@ public class DiagramViewModel extends RangeSliderModel implements ChangedListene
filtersOrder = provider.getAllFiltersOrdered();
globalSelection = GlobalSelectionAction.get(GlobalSelectionAction.class).isSelected();
cutEdges = CutEdgesAction.get(CutEdgesAction.class).isSelected();
showStableSea = Settings.get().getInt(Settings.DEFAULT_VIEW, Settings.DEFAULT_VIEW_DEFAULT) == Settings.DefaultView.STABLE_SEA_OF_NODES;
showSea = Settings.get().getInt(Settings.DEFAULT_VIEW, Settings.DEFAULT_VIEW_DEFAULT) == Settings.DefaultView.SEA_OF_NODES;
showBlocks = Settings.get().getInt(Settings.DEFAULT_VIEW, Settings.DEFAULT_VIEW_DEFAULT) == Settings.DefaultView.CLUSTERED_SEA_OF_NODES;
showCFG = Settings.get().getInt(Settings.DEFAULT_VIEW, Settings.DEFAULT_VIEW_DEFAULT) == Settings.DefaultView.CONTROL_FLOW_GRAPH;
showNodeHull = true;
showEmptyBlocks = true;
hideDuplicates = false;
hiddenNodes = new HashSet<>();
selectedNodes = new HashSet<>();
@ -422,11 +417,8 @@ public class DiagramViewModel extends RangeSliderModel implements ChangedListene
ArrayList<InputGraph> result = new ArrayList<>();
List<String> positions = new ArrayList<>();
for (InputGraph graph : group.getGraphs()) {
String duplicate = graph.getProperties().get("_isDuplicate");
if (duplicate == null || !hideDuplicates) {
result.add(graph);
positions.add(graph.getName());
}
result.add(graph);
positions.add(graph.getName());
}
this.graphs = result;
setPositions(positions);
@ -460,22 +452,12 @@ public class DiagramViewModel extends RangeSliderModel implements ChangedListene
public void selectGraph(InputGraph graph) {
int index = graphs.indexOf(graph);
if (index == -1 && hideDuplicates) {
// A graph was selected that's currently hidden, so unhide and select it.
setHideDuplicates(false);
index = graphs.indexOf(graph);
}
assert index != -1;
setPositions(index, index);
}
public void selectDiffGraph(InputGraph graph) {
int index = graphs.indexOf(graph);
if (index == -1 && hideDuplicates) {
// A graph was selected that's currently hidden, so unhide and select it.
setHideDuplicates(false);
index = graphs.indexOf(graph);
}
assert index != -1;
int firstIndex = getFirstPosition();
int secondIndex = getSecondPosition();

View File

@ -210,7 +210,6 @@ public final class EditorTopComponent extends TopComponent implements TopCompone
toolBar.addSeparator();
toolBar.add(new JToggleButton(new PredSuccAction(diagramViewModel.getShowNodeHull())));
toolBar.add(new JToggleButton(new ShowEmptyBlocksAction(cfgLayoutAction, diagramViewModel.getShowEmptyBlocks())));
toolBar.add(new JToggleButton(new HideDuplicatesAction(diagramViewModel.getHideDuplicates())));
toolBar.addSeparator();
UndoAction undoAction = UndoAction.get(UndoAction.class);
@ -221,6 +220,11 @@ public final class EditorTopComponent extends TopComponent implements TopCompone
toolBar.add(redoAction);
toolBar.addSeparator();
JToggleButton cutEdgesButton = new JToggleButton(CutEdgesAction.get(CutEdgesAction.class));
cutEdgesButton.setHideActionText(true);
toolBar.add(cutEdgesButton);
JToggleButton globalSelectionButton = new JToggleButton(GlobalSelectionAction.get(GlobalSelectionAction.class));
globalSelectionButton.setHideActionText(true);
toolBar.add(globalSelectionButton);
@ -453,6 +457,7 @@ public final class EditorTopComponent extends TopComponent implements TopCompone
}
etc.addSelectedNodes(selectedNodes, false);
model.setGlobalSelection(GlobalSelectionAction.get(GlobalSelectionAction.class).isSelected(), false);
model.setCutEdges(CutEdgesAction.get(CutEdgesAction.class).isSelected(), false);
etc.resetUndoRedo();
int currentZoomLevel = scene.getZoomPercentage();

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package com.sun.hotspot.igv.view.actions;
import com.sun.hotspot.igv.view.EditorTopComponent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.util.HelpCtx;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
@ActionID(category = "View", id = "com.sun.hotspot.igv.view.actions.CutEdgesAction")
@ActionRegistration(displayName = "#CTL_CutEdgesAction")
@ActionReferences({
@ActionReference(path = "Menu/Options", position = 350),
})
@NbBundle.Messages({
"CTL_CutEdgesAction=Cut long edges",
"HINT_CutEdgesAction=Cut long edges"
})
public final class CutEdgesAction extends CallableSystemAction {
private boolean isSelected;
public CutEdgesAction() {
putValue(AbstractAction.SMALL_ICON, new ImageIcon(ImageUtilities.loadImage(iconResource())));
putValue(Action.SHORT_DESCRIPTION, getDescription());
putValue(SELECTED_KEY, false);
isSelected = false;
}
@Override
public String getName() {
return NbBundle.getMessage(CutEdgesAction.class, "CTL_CutEdgesAction");
}
@Override
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
@Override
public void performAction() {
isSelected = !isSelected;
putValue(SELECTED_KEY, isSelected);
EditorTopComponent editor = EditorTopComponent.getActive();
if (editor != null) {
SwingUtilities.invokeLater(() -> editor.getModel().setCutEdges(isSelected, true));
}
}
public boolean isSelected() {
return isSelected;
}
private String getDescription() {
return NbBundle.getMessage(CutEdgesAction.class, "HINT_CutEdgesAction");
}
@Override
protected boolean asynchronous() {
return false;
}
@Override
public String iconResource() {
return "com/sun/hotspot/igv/view/images/cut.png";
}
}

View File

@ -1,57 +0,0 @@
/*
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package com.sun.hotspot.igv.view.actions;
import com.sun.hotspot.igv.view.EditorTopComponent;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import org.openide.util.ImageUtilities;
/**
*
* @author Tom Rodriguez
*/
public class HideDuplicatesAction extends AbstractAction {
public HideDuplicatesAction(boolean selected) {
putValue(AbstractAction.SMALL_ICON, new ImageIcon(ImageUtilities.loadImage(iconResource())));
putValue(Action.SELECTED_KEY, selected);
putValue(Action.SHORT_DESCRIPTION, "Hide graphs which are the same as the previous graph");
}
@Override
public void actionPerformed(ActionEvent ev) {
EditorTopComponent editor = EditorTopComponent.getActive();
if (editor != null) {
boolean selected = (boolean)getValue(SELECTED_KEY);
editor.getModel().setHideDuplicates(selected);
}
}
protected String iconResource() {
return "com/sun/hotspot/igv/view/images/hideDuplicates.png";
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB