Skip to content

Commit

Permalink
Support filtering nodes by function access level
Browse files Browse the repository at this point in the history
  • Loading branch information
Chentai-Kao committed May 8, 2019
1 parent bebec55 commit 648c291
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 39 deletions.
55 changes: 52 additions & 3 deletions src/main/java/CallGraphToolWindow.form
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@
</vspacer>
</children>
</grid>
<grid id="3fac4" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="3fac4" layout-manager="GridLayoutManager" row-count="4" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
Expand All @@ -445,7 +445,7 @@
<children>
<vspacer id="c2264">
<constraints>
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="3" column="0" row-span="1" col-span="3" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="c207b" class="javax.swing.JLabel">
Expand All @@ -459,7 +459,7 @@
</component>
<component id="265b4" class="javax.swing.JTextField" binding="searchTextField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="0" fill="1" indent="0" use-parent-layout="false">
<grid row="0" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="2" anchor="0" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
Expand All @@ -468,6 +468,55 @@
<toolTipText value="enter function name to search"/>
</properties>
</component>
<component id="4a601" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<icon value="icons/filter.png"/>
<text value="&lt;html&gt;&lt;b&gt;Access level&lt;/b&gt;&lt;/html&gt;"/>
</properties>
</component>
<component id="ce116" class="javax.swing.JCheckBox" binding="filterAccessPublicCheckbox">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<selected value="true"/>
<text value="P&amp;ublic"/>
</properties>
</component>
<component id="9ae3b" class="javax.swing.JCheckBox" binding="filterAccessProtectedCheckbox">
<constraints>
<grid row="1" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<selected value="true"/>
<text value="Pr&amp;otected"/>
</properties>
</component>
<component id="d5c1" class="javax.swing.JCheckBox" binding="filterAccessPackageLocalCheckbox">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<selected value="true"/>
<text value="P&amp;ackage local"/>
</properties>
</component>
<component id="fc6f6" class="javax.swing.JCheckBox" binding="filterAccessPrivateCheckbox">
<constraints>
<grid row="2" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<selected value="true"/>
<text value="&amp;Private"/>
</properties>
</component>
</children>
</grid>
<grid id="67170" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
Expand Down
91 changes: 65 additions & 26 deletions src/main/java/CallGraphToolWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,20 @@ public class CallGraphToolWindow {
private JComboBox<String> nodeSelectionComboBox;
private JTextField searchTextField;
private JComboBox<String> nodeColorComboBox;
private JCheckBox filterAccessPublicCheckbox;
private JCheckBox filterAccessProtectedCheckbox;
private JCheckBox filterAccessPackageLocalCheckbox;
private JCheckBox filterAccessPrivateCheckbox;

private final CanvasBuilder canvasBuilder = new CanvasBuilder();
private Canvas canvas;
private final Set<PsiMethod> focusedMethods = new HashSet<>();
private final List<JCheckBox> filterAccessCheckboxes = Arrays.asList(
this.filterAccessPublicCheckbox,
this.filterAccessProtectedCheckbox,
this.filterAccessPackageLocalCheckbox,
this.filterAccessPrivateCheckbox
);

public CallGraphToolWindow() {
// drop-down options
Expand Down Expand Up @@ -89,6 +99,8 @@ public void keyReleased(KeyEvent keyEvent) {
canvas.repaint();
}
});
this.filterAccessCheckboxes.forEach(checkbox ->
checkbox.addActionListener(e -> this.canvas.filterAccessChangeHandler()));

// click handlers for buttons
this.projectScopeButton.addActionListener(e -> projectScopeButtonHandler());
Expand Down Expand Up @@ -184,6 +196,22 @@ boolean isNodeColorByClassName() {
return getSelectedComboBoxOption(this.nodeColorComboBox) == ComboBoxOptions.NODE_COLOR_CLASS;
}

boolean isFilterAccessPublicChecked() {
return this.filterAccessPublicCheckbox.isSelected();
}

boolean isFilterAccessProtectedChecked() {
return this.filterAccessProtectedCheckbox.isSelected();
}

boolean isFilterAccessPackageLocalChecked() {
return this.filterAccessPackageLocalCheckbox.isSelected();
}

boolean isFilterAccessPrivateChecked() {
return this.filterAccessPrivateCheckbox.isSelected();
}

void run(@NotNull CanvasConfig.BuildType buildType) {
Project project = Utils.getActiveProject();
if (project != null) {
Expand Down Expand Up @@ -311,21 +339,28 @@ private void setupUiBeforeRun(@NotNull CanvasConfig canvasConfig) {
break;
}
// disable some checkboxes and buttons
this.viewPackageNameComboBox.setEnabled(false);
this.viewFilePathComboBox.setEnabled(false);
this.nodeSelectionComboBox.setEnabled(false);
this.nodeColorComboBox.setEnabled(false);
this.fitGraphToBestRatioButton.setEnabled(false);
this.fitGraphToViewButton.setEnabled(false);
this.increaseXGridButton.setEnabled(false);
this.decreaseXGridButton.setEnabled(false);
this.increaseYGridButton.setEnabled(false);
this.decreaseYGridButton.setEnabled(false);
this.viewSourceCodeButton.setEnabled(false);
this.showOnlyUpstreamButton.setEnabled(false);
this.showOnlyDownstreamButton.setEnabled(false);
this.showOnlyUpstreamDownstreamButton.setEnabled(false);
this.searchTextField.setEnabled(false);
Arrays.asList(
this.viewPackageNameComboBox,
this.viewFilePathComboBox,
this.nodeSelectionComboBox,
this.nodeColorComboBox,
this.fitGraphToBestRatioButton,
this.fitGraphToViewButton,
this.increaseXGridButton,
this.decreaseXGridButton,
this.increaseYGridButton,
this.decreaseYGridButton,
this.viewSourceCodeButton,
this.showOnlyUpstreamButton,
this.showOnlyDownstreamButton,
this.showOnlyUpstreamDownstreamButton,
this.searchTextField
).forEach(component -> component.setEnabled(false));
// filter-related checkboxes
this.filterAccessCheckboxes.forEach(checkbox -> {
checkbox.setEnabled(false);
checkbox.setSelected(true);
});
// progress bar
this.loadingProgressBar.setVisible(true);
// clear the canvas panel, ready for new graph
Expand All @@ -342,18 +377,22 @@ private void setupUiAfterRun() {
// hide progress bar
this.loadingProgressBar.setVisible(false);
// enable some checkboxes and buttons
this.viewPackageNameComboBox.setEnabled(true);
this.viewFilePathComboBox.setEnabled(true);
this.nodeSelectionComboBox.setEnabled(true);
this.nodeColorComboBox.setEnabled(true);
this.fitGraphToBestRatioButton.setEnabled(true);
this.fitGraphToViewButton.setEnabled(true);
this.increaseXGridButton.setEnabled(true);
this.decreaseXGridButton.setEnabled(true);
this.increaseYGridButton.setEnabled(true);
this.decreaseYGridButton.setEnabled(true);
this.searchTextField.setEnabled(true);
enableFocusedMethodButtons();
Arrays.asList(
this.viewPackageNameComboBox,
this.viewFilePathComboBox,
this.nodeSelectionComboBox,
this.nodeColorComboBox,
this.fitGraphToBestRatioButton,
this.fitGraphToViewButton,
this.increaseXGridButton,
this.decreaseXGridButton,
this.increaseYGridButton,
this.decreaseYGridButton,
this.searchTextField
).forEach(component -> component.setEnabled(true));
// filter-related checkboxes
this.filterAccessCheckboxes.forEach(checkbox -> checkbox.setEnabled(true));
}

@NotNull
Expand Down
45 changes: 35 additions & 10 deletions src/main/java/Canvas.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import com.google.common.collect.ImmutableMap;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiModifierList;
import org.jetbrains.annotations.NotNull;
Expand All @@ -21,6 +22,8 @@ class Canvas extends JPanel {
private JPanel canvasPanel;
private final CallGraphToolWindow callGraphToolWindow;
private Map<Shape, Node> nodeShapesMap = Collections.emptyMap();
private Collection<Node> visibleNodes;
private Collection<Edge> visibleEdges;
private Node hoveredNode;
private final Point2D defaultCameraOrigin = new Point2D.Float(0, 0);
private final float defaultZoomRatio = 1.0f;
Expand All @@ -41,6 +44,8 @@ class Canvas extends JPanel {
super();
this.callGraphToolWindow = callGraphToolWindow;
this.graph = graph;
this.visibleNodes = graph.getNodes();
this.visibleEdges = graph.getEdges();
}

@Override
Expand All @@ -57,21 +62,18 @@ public void paintComponent(Graphics graphics) {
graphics2D.fillRect(0, 0, this.getWidth(), this.getHeight());

// draw un-highlighted and highlighted self loops
this.graph.getEdges()
.stream()
this.visibleEdges.stream()
.filter(edge -> edge.getSourceNode() == edge.getTargetNode())
.forEach(edge -> drawSelfLoopEdge(graphics2D, edge, isNodeHighlighted(edge.getSourceNode())));

// draw un-highlighted edges
this.graph.getEdges()
.stream()
this.visibleEdges.stream()
.filter(edge -> edge.getSourceNode() != edge.getTargetNode() &&
!isNodeHighlighted(edge.getSourceNode()) && !isNodeHighlighted(edge.getTargetNode()))
.forEach(edge -> drawNonLoopEdge(graphics2D, edge, Colors.unHighlightedColor));

// draw upstream/downstream edges
Set<Node> highlightedNodes = this.graph.getNodes()
.stream()
Set<Node> highlightedNodes = this.visibleNodes.stream()
.filter(this::isNodeHighlighted)
.collect(Collectors.toSet());
Set<Edge> upstreamEdges = highlightedNodes.stream()
Expand All @@ -86,8 +88,7 @@ public void paintComponent(Graphics graphics) {
// draw un-highlighted labels
Set<Node> upstreamNodes = upstreamEdges.stream().map(Edge::getSourceNode).collect(Collectors.toSet());
Set<Node> downstreamNodes = downstreamEdges.stream().map(Edge::getTargetNode).collect(Collectors.toSet());
Set<Node> unHighlightedNodes = this.graph.getNodes()
.stream()
Set<Node> unHighlightedNodes = this.visibleNodes.stream()
.filter(node ->
!isNodeHighlighted(node) && !upstreamNodes.contains(node) && !downstreamNodes.contains(node)
)
Expand All @@ -107,8 +108,7 @@ public void paintComponent(Graphics graphics) {
downstreamNodes.forEach(node -> drawNode(graphics2D, node, Colors.downstreamColor));

// draw highlighted node and label
this.graph.getNodes()
.stream()
this.visibleNodes.stream()
.filter(this::isNodeHighlighted)
.forEach(node -> {
drawNode(graphics2D, node, Colors.highlightedColor);
Expand Down Expand Up @@ -199,6 +199,31 @@ int getNodesCount() {
return this.graph.getNodes().size();
}

void filterAccessChangeHandler() {
this.visibleNodes = this.graph.getNodes()
.stream()
.filter(node -> {
PsiMethod method = node.getMethod();
if (Utils.isPublic(method)) {
return this.callGraphToolWindow.isFilterAccessPublicChecked();
} else if (Utils.isProtected(method)) {
return this.callGraphToolWindow.isFilterAccessProtectedChecked();
} else if (Utils.isPackageLocal(method)) {
return this.callGraphToolWindow.isFilterAccessPackageLocalChecked();
} else if (Utils.isPrivate(method)) {
return this.callGraphToolWindow.isFilterAccessPrivateChecked();
}
return true;
})
.collect(Collectors.toSet());
this.visibleEdges = this.graph.getEdges()
.stream()
.filter(edge -> this.visibleNodes.contains(edge.getSourceNode()) &&
this.visibleNodes.contains(edge.getTargetNode()))
.collect(Collectors.toSet());
repaint();
}

@NotNull
private Point2D toCameraView(@NotNull Point2D point) {
Dimension canvasSize = this.canvasPanel.getSize();
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -538,4 +538,20 @@ static Set<PsiFile> getSourceCodeFiles(@NotNull CanvasConfig canvasConfig) {
})
.collect(Collectors.toSet());
}

static boolean isPublic(@NotNull PsiMethod method) {
return method.getModifierList().hasModifierProperty(PsiModifier.PUBLIC);
}

static boolean isProtected(@NotNull PsiMethod method) {
return method.getModifierList().hasModifierProperty(PsiModifier.PROTECTED);
}

static boolean isPackageLocal(@NotNull PsiMethod method) {
return method.getModifierList().hasModifierProperty(PsiModifier.PACKAGE_LOCAL);
}

static boolean isPrivate(@NotNull PsiMethod method) {
return method.getModifierList().hasModifierProperty(PsiModifier.PRIVATE);
}
}
3 changes: 3 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@
<li>
Support color-coding nodes by <b>function access level (public, protected, package local, private)</b> and <b>class name</b>.
</li>
<li>
Support filtering nodes by <b>function access level (public, protected, package local, private)</b>.
</li>
</ul>
]]></change-notes>

Expand Down
Binary file added src/main/resources/icons/filter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 648c291

Please sign in to comment.