Skip to content

Commit ae2c5f0

Browse files
robcaslozchhagedorn
authored andcommitted
8260581: IGV: enhance node search
Allow users to search by node id or name by default, show partial matches when searching for a specific property, show 'All N matching nodes' entry only if relevant, and rank results by level of matching. Co-authored-by: Christian Hagedorn <chagedorn@openjdk.org> Reviewed-by: chagedorn, vlivanov, xliu
1 parent 9037615 commit ae2c5f0

File tree

3 files changed

+95
-24
lines changed

3 files changed

+95
-24
lines changed

src/utils/IdealGraphVisualizer/Graph/src/com/sun/hotspot/igv/graph/Figure.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -274,6 +274,13 @@ void removeOutputSlot(OutputSlot s) {
274274
public String[] getLines() {
275275
if (lines == null) {
276276
updateLines();
277+
// Set the "label" property of each input node, so that by default
278+
// search is done on the node label (without line breaks). See also
279+
// class NodeQuickSearch in the View module.
280+
for (InputNode n : getSource().getSourceNodes()) {
281+
String label = resolveString(diagram.getNodeText(), n.getProperties());
282+
n.getProperties().setProperty("label", label);
283+
}
277284
}
278285
return lines;
279286
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
javac.source=1.7
1+
javac.source=1.8
22
javac.compilerargs=-Xlint -Xlint:-serial

src/utils/IdealGraphVisualizer/View/src/com/sun/hotspot/igv/view/NodeQuickSearch.java

Lines changed: 86 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
2929
import com.sun.hotspot.igv.data.Properties.RegexpPropertyMatcher;
3030
import com.sun.hotspot.igv.data.services.InputGraphProvider;
3131
import com.sun.hotspot.igv.util.LookupHistory;
32+
import java.util.Collections;
3233
import java.util.HashSet;
3334
import java.util.List;
3435
import java.util.Set;
@@ -46,7 +47,7 @@
4647
*/
4748
public class NodeQuickSearch implements SearchProvider {
4849

49-
private static final String DEFAULT_PROPERTY = "name";
50+
private static final String DEFAULT_PROPERTY = "label";
5051

5152
/**
5253
* Method is called by infrastructure when search operation was requested.
@@ -66,18 +67,17 @@ public void evaluate(SearchRequest request, SearchResponse response) {
6667
final String[] parts = query.split("=", 2);
6768

6869
String name;
70+
String rawValue;
6971
String value;
7072

7173
if (parts.length == 1) {
7274
name = DEFAULT_PROPERTY;
73-
value = ".*" + Pattern.quote(parts[0]) + ".*";
75+
rawValue = parts[0];
76+
value = ".*" + Pattern.quote(rawValue) + ".*";
7477
} else {
7578
name = parts[0];
76-
value = parts[1];
77-
}
78-
79-
if (value.isEmpty()) {
80-
value = ".*";
79+
rawValue = parts[1];
80+
value = (rawValue.isEmpty() ? "" : Pattern.quote(rawValue)) + ".*";
8181
}
8282

8383
final InputGraphProvider p = LookupHistory.getLast(InputGraphProvider.class);
@@ -109,25 +109,40 @@ public void evaluate(SearchRequest request, SearchResponse response) {
109109
if (matches != null) {
110110
final Set<InputNode> set = new HashSet<>(matches);
111111
final InputGraph theGraph = p.getGraph() != matchGraph ? matchGraph : null;
112-
response.addResult(new Runnable() {
113-
@Override
114-
public void run() {
115-
final EditorTopComponent comp = EditorTopComponent.getActive();
116-
if (comp != null) {
117-
if (theGraph != null) {
118-
comp.getDiagramModel().selectGraph(theGraph);
112+
// Show "All N matching nodes" entry only if 1) there are
113+
// multiple matches and 2) the query does not only contain
114+
// digits (it is rare to select all nodes whose id contains a
115+
// certain subsequence of digits).
116+
if (matches.size() > 1 && !rawValue.matches("\\d+")) {
117+
if (!response.addResult(new Runnable() {
118+
@Override
119+
public void run() {
120+
final EditorTopComponent comp = EditorTopComponent.getActive();
121+
if (comp != null) {
122+
if (theGraph != null) {
123+
comp.getDiagramModel().selectGraph(theGraph);
124+
}
125+
comp.setSelectedNodes(set);
126+
comp.requestActive();
119127
}
120-
comp.setSelectedNodes(set);
121-
comp.requestActive();
122128
}
129+
},
130+
"All " + matches.size() + " matching nodes (" + name + "=" + value + ")" + (theGraph != null ? " in " + theGraph.getName() : "")
131+
)) {
132+
return;
123133
}
124-
},
125-
"All " + matches.size() + " matching nodes (" + name + "=" + value + ")" + (theGraph != null ? " in " + theGraph.getName() : "")
126-
);
134+
}
135+
136+
// Rank the matches.
137+
Collections.sort(matches,
138+
(InputNode a, InputNode b) ->
139+
compareByRankThenNumVal(rawValue,
140+
a.getProperties().get(name),
141+
b.getProperties().get(name)));
127142

128143
// Single matches
129144
for (final InputNode n : matches) {
130-
response.addResult(new Runnable() {
145+
if (!response.addResult(new Runnable() {
131146
@Override
132147
public void run() {
133148
final EditorTopComponent comp = EditorTopComponent.getActive();
@@ -143,7 +158,9 @@ public void run() {
143158
}
144159
},
145160
n.getProperties().get(name) + " (" + n.getId() + " " + n.getProperties().get("name") + ")" + (theGraph != null ? " in " + theGraph.getName() : "")
146-
);
161+
)) {
162+
return;
163+
}
147164
}
148165
}
149166
} else {
@@ -173,4 +190,51 @@ public void run() {
173190
}
174191
return null;
175192
}
193+
194+
/**
195+
* Compare two matches for a given query, first by rank (see rankMatch()
196+
* below) and then by numeric value, if applicable.
197+
*/
198+
private int compareByRankThenNumVal(String qry, String prop1, String prop2) {
199+
int key1 = rankMatch(qry, prop1);
200+
int key2 = rankMatch(qry, prop2);
201+
if (key1 == key2) {
202+
// If the matches have the same rank, compare the numeric values of
203+
// their first words, if applicable.
204+
try {
205+
key1 = Integer.parseInt(prop1.split("\\W+")[0]);
206+
key2 = Integer.parseInt(prop2.split("\\W+")[0]);
207+
} catch (Exception e) {
208+
// Not applicable, return equality value.
209+
return 0;
210+
}
211+
}
212+
return Integer.compare(key1, key2);
213+
}
214+
215+
/**
216+
* Rank a match by splitting the property into words. Full matches of a word
217+
* rank highest, followed by partial matches at the word start, followed by
218+
* the rest of matches in increasing size of the partially matched word, for
219+
* example:
220+
*
221+
* rank("5", "5 AddI") = 1 (full match of first word)
222+
* rank("5", "554 MulI") = 2 (start match of first word)
223+
* rank("5", "25 AddL") = 3 (middle match of first word with excess 1)
224+
* rank("5", "253 AddL") = 4 (middle match of first word with excess 2)
225+
*/
226+
private int rankMatch(String qry, String prop) {
227+
String query = qry.toLowerCase();
228+
String property = prop.toLowerCase();
229+
for (String component : property.split("\\W+")) {
230+
if (component.equals(query)) {
231+
return 1;
232+
} else if (component.startsWith(query)) {
233+
return 2;
234+
} else if (component.contains(query)) {
235+
return component.length() - query.length() + 2;
236+
}
237+
}
238+
return Integer.MAX_VALUE;
239+
}
176240
}

0 commit comments

Comments
 (0)