mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
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
This commit is contained in:
parent
90376156be
commit
ae2c5f07ce
3 changed files with 95 additions and 24 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -274,6 +274,13 @@ public class Figure extends Properties.Entity implements Source.Provider, Vertex
|
||||||
public String[] getLines() {
|
public String[] getLines() {
|
||||||
if (lines == null) {
|
if (lines == null) {
|
||||||
updateLines();
|
updateLines();
|
||||||
|
// Set the "label" property of each input node, so that by default
|
||||||
|
// search is done on the node label (without line breaks). See also
|
||||||
|
// class NodeQuickSearch in the View module.
|
||||||
|
for (InputNode n : getSource().getSourceNodes()) {
|
||||||
|
String label = resolveString(diagram.getNodeText(), n.getProperties());
|
||||||
|
n.getProperties().setProperty("label", label);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
javac.source=1.7
|
javac.source=1.8
|
||||||
javac.compilerargs=-Xlint -Xlint:-serial
|
javac.compilerargs=-Xlint -Xlint:-serial
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -29,6 +29,7 @@ import com.sun.hotspot.igv.data.Properties;
|
||||||
import com.sun.hotspot.igv.data.Properties.RegexpPropertyMatcher;
|
import com.sun.hotspot.igv.data.Properties.RegexpPropertyMatcher;
|
||||||
import com.sun.hotspot.igv.data.services.InputGraphProvider;
|
import com.sun.hotspot.igv.data.services.InputGraphProvider;
|
||||||
import com.sun.hotspot.igv.util.LookupHistory;
|
import com.sun.hotspot.igv.util.LookupHistory;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -46,7 +47,7 @@ import org.openide.NotifyDescriptor.Message;
|
||||||
*/
|
*/
|
||||||
public class NodeQuickSearch implements SearchProvider {
|
public class NodeQuickSearch implements SearchProvider {
|
||||||
|
|
||||||
private static final String DEFAULT_PROPERTY = "name";
|
private static final String DEFAULT_PROPERTY = "label";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method is called by infrastructure when search operation was requested.
|
* Method is called by infrastructure when search operation was requested.
|
||||||
|
@ -66,18 +67,17 @@ public class NodeQuickSearch implements SearchProvider {
|
||||||
final String[] parts = query.split("=", 2);
|
final String[] parts = query.split("=", 2);
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
|
String rawValue;
|
||||||
String value;
|
String value;
|
||||||
|
|
||||||
if (parts.length == 1) {
|
if (parts.length == 1) {
|
||||||
name = DEFAULT_PROPERTY;
|
name = DEFAULT_PROPERTY;
|
||||||
value = ".*" + Pattern.quote(parts[0]) + ".*";
|
rawValue = parts[0];
|
||||||
|
value = ".*" + Pattern.quote(rawValue) + ".*";
|
||||||
} else {
|
} else {
|
||||||
name = parts[0];
|
name = parts[0];
|
||||||
value = parts[1];
|
rawValue = parts[1];
|
||||||
}
|
value = (rawValue.isEmpty() ? "" : Pattern.quote(rawValue)) + ".*";
|
||||||
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
value = ".*";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final InputGraphProvider p = LookupHistory.getLast(InputGraphProvider.class);
|
final InputGraphProvider p = LookupHistory.getLast(InputGraphProvider.class);
|
||||||
|
@ -109,25 +109,40 @@ public class NodeQuickSearch implements SearchProvider {
|
||||||
if (matches != null) {
|
if (matches != null) {
|
||||||
final Set<InputNode> set = new HashSet<>(matches);
|
final Set<InputNode> set = new HashSet<>(matches);
|
||||||
final InputGraph theGraph = p.getGraph() != matchGraph ? matchGraph : null;
|
final InputGraph theGraph = p.getGraph() != matchGraph ? matchGraph : null;
|
||||||
response.addResult(new Runnable() {
|
// Show "All N matching nodes" entry only if 1) there are
|
||||||
@Override
|
// multiple matches and 2) the query does not only contain
|
||||||
public void run() {
|
// digits (it is rare to select all nodes whose id contains a
|
||||||
final EditorTopComponent comp = EditorTopComponent.getActive();
|
// certain subsequence of digits).
|
||||||
if (comp != null) {
|
if (matches.size() > 1 && !rawValue.matches("\\d+")) {
|
||||||
if (theGraph != null) {
|
if (!response.addResult(new Runnable() {
|
||||||
comp.getDiagramModel().selectGraph(theGraph);
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final EditorTopComponent comp = EditorTopComponent.getActive();
|
||||||
|
if (comp != null) {
|
||||||
|
if (theGraph != null) {
|
||||||
|
comp.getDiagramModel().selectGraph(theGraph);
|
||||||
|
}
|
||||||
|
comp.setSelectedNodes(set);
|
||||||
|
comp.requestActive();
|
||||||
}
|
}
|
||||||
comp.setSelectedNodes(set);
|
|
||||||
comp.requestActive();
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"All " + matches.size() + " matching nodes (" + name + "=" + value + ")" + (theGraph != null ? " in " + theGraph.getName() : "")
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"All " + matches.size() + " matching nodes (" + name + "=" + value + ")" + (theGraph != null ? " in " + theGraph.getName() : "")
|
|
||||||
);
|
// Rank the matches.
|
||||||
|
Collections.sort(matches,
|
||||||
|
(InputNode a, InputNode b) ->
|
||||||
|
compareByRankThenNumVal(rawValue,
|
||||||
|
a.getProperties().get(name),
|
||||||
|
b.getProperties().get(name)));
|
||||||
|
|
||||||
// Single matches
|
// Single matches
|
||||||
for (final InputNode n : matches) {
|
for (final InputNode n : matches) {
|
||||||
response.addResult(new Runnable() {
|
if (!response.addResult(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final EditorTopComponent comp = EditorTopComponent.getActive();
|
final EditorTopComponent comp = EditorTopComponent.getActive();
|
||||||
|
@ -143,7 +158,9 @@ public class NodeQuickSearch implements SearchProvider {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
n.getProperties().get(name) + " (" + n.getId() + " " + n.getProperties().get("name") + ")" + (theGraph != null ? " in " + theGraph.getName() : "")
|
n.getProperties().get(name) + " (" + n.getId() + " " + n.getProperties().get("name") + ")" + (theGraph != null ? " in " + theGraph.getName() : "")
|
||||||
);
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -173,4 +190,51 @@ public class NodeQuickSearch implements SearchProvider {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two matches for a given query, first by rank (see rankMatch()
|
||||||
|
* below) and then by numeric value, if applicable.
|
||||||
|
*/
|
||||||
|
private int compareByRankThenNumVal(String qry, String prop1, String prop2) {
|
||||||
|
int key1 = rankMatch(qry, prop1);
|
||||||
|
int key2 = rankMatch(qry, prop2);
|
||||||
|
if (key1 == key2) {
|
||||||
|
// If the matches have the same rank, compare the numeric values of
|
||||||
|
// their first words, if applicable.
|
||||||
|
try {
|
||||||
|
key1 = Integer.parseInt(prop1.split("\\W+")[0]);
|
||||||
|
key2 = Integer.parseInt(prop2.split("\\W+")[0]);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Not applicable, return equality value.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Integer.compare(key1, key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rank a match by splitting the property into words. Full matches of a word
|
||||||
|
* rank highest, followed by partial matches at the word start, followed by
|
||||||
|
* the rest of matches in increasing size of the partially matched word, for
|
||||||
|
* example:
|
||||||
|
*
|
||||||
|
* rank("5", "5 AddI") = 1 (full match of first word)
|
||||||
|
* rank("5", "554 MulI") = 2 (start match of first word)
|
||||||
|
* rank("5", "25 AddL") = 3 (middle match of first word with excess 1)
|
||||||
|
* rank("5", "253 AddL") = 4 (middle match of first word with excess 2)
|
||||||
|
*/
|
||||||
|
private int rankMatch(String qry, String prop) {
|
||||||
|
String query = qry.toLowerCase();
|
||||||
|
String property = prop.toLowerCase();
|
||||||
|
for (String component : property.split("\\W+")) {
|
||||||
|
if (component.equals(query)) {
|
||||||
|
return 1;
|
||||||
|
} else if (component.startsWith(query)) {
|
||||||
|
return 2;
|
||||||
|
} else if (component.contains(query)) {
|
||||||
|
return component.length() - query.length() + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue