GT-3332 Fixed several issues with GTreeNodes

This commit is contained in:
ghidravore 2019-11-15 13:10:40 -05:00
parent 5feab045d2
commit b5a7246523
4 changed files with 80 additions and 12 deletions

View File

@ -91,6 +91,10 @@ public class CategoryNode extends DataTypeTreeNode {
return -1; // CategoryNodes are always come before ****everything else****
}
@Override
public int hashCode() {
return name.hashCode();
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@ -166,6 +170,11 @@ public class CategoryNode extends DataTypeTreeNode {
CategoryNode node = new CategoryNode(newCategory, filterState);
List<GTreeNode> allChildrenList = getChildren();
int index = Collections.binarySearch(allChildrenList, node);
if (index >= 0) {
if (node.getName().equals(allChildrenList.get(index).getName())) {
return;
}
}
if (index < 0) {
index = -index - 1;
}

View File

@ -31,13 +31,15 @@ import ghidra.util.task.TaskMonitor;
public class DataTypeTreeDeleteTask extends Task {
private static final int MAX_NODES = 10;
private Map<ArchiveNode, List<GTreeNode>> nodesByArchive;
private DataTypeManagerPlugin plugin;
private int nodeCount;
public DataTypeTreeDeleteTask(DataTypeManagerPlugin plugin, List<GTreeNode> nodes) {
super("Delete Nodes", true, true, true);
this.plugin = plugin;
nodeCount = nodes.size();
nodes = filterList(nodes);
nodesByArchive = groupNodeByArchive(nodes);
@ -105,7 +107,9 @@ public class DataTypeTreeDeleteTask extends Task {
DataTypeArchiveGTree tree = provider.getGTree();
GTreeState treeState = tree.getTreeState();
try {
collapseArchives(tree);
if (nodeCount > MAX_NODES) {
collapseArchives(tree);
}
Set<Entry<ArchiveNode, List<GTreeNode>>> entries = nodesByArchive.entrySet();
for (Entry<ArchiveNode, List<GTreeNode>> entry : entries) {

View File

@ -16,7 +16,6 @@
package docking.widgets.tree;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
import javax.swing.Icon;
@ -36,15 +35,40 @@ import util.CollectionUtils;
* <P>
* All methods in this class that mutate the children node must perform that operation in
* the swing thread.
* <P>
* To create a simple GTreeNode where nodes will be added immediately
* using the addNode() methods, simply extend this class and implement the following methods:
* <ul>
* <li>getName()</li>
* <li>getToolTip()</li>
* <li>isLeaf()</li>
* <li>getIcon()</li>
* </ul>
*
* <a name="usage"></a>Usage Notes:
* <ul>
* <li>The <b><tt>equals()</tt></b> method: The <tt>GTree</tt> has the ability to remember expanded and
* selected states. This will only work if the nodes in the saved state can be matched
* with the nodes in the <tt>GTree</tt>. Java will do this by using the <tt>equals()</tt> method.
* There is a potential problem with this usage. If nodes within the <tt>GTree</tt> get rebuilt (
* i.e., new nodes are created), then, by default, the expanded and selected state
* feature will be unable to find the correct nodes, since the default <tt>equals()</tt>
* method on <tt>GTreeNode</tt> performs a comparison based upon instances. To fix this problem,
* the {@link #equals()} method has been implemented such that nodes are considered equals if they have
* the same name. The {@link #hashCode()} method will return the hash of the name.
* <p><br>
* <p>
* There are two situations where the {@link #equals(Object)} and {@link #hashCode()} using the
* name are insufficient. One is if your tree implementation allows nodes with the same name
* with the same parent. The other possible situation is if your nodes can change their name,
* which may confuse the tree. If either of these situations apply, just override the
* {@link #equals(Object)} and {@link #hashCode()} methods to make them more robust.
* <p><br>
* </li>
* </ul>
*/
public abstract class GTreeNode extends CoreGTreeNode implements Comparable<GTreeNode> {
private static AtomicLong NEXT_ID = new AtomicLong();
private final long id;
protected GTreeNode() {
id = NEXT_ID.incrementAndGet();
}
@Override
protected List<GTreeNode> generateChildren() {
@ -170,6 +194,9 @@ public abstract class GTreeNode extends CoreGTreeNode implements Comparable<GTre
* @return the total number of leaf nodes in the subtree from this node
*/
public int getLeafCount() {
if (isLeaf() || !isLoaded()) {
return 1;
}
int count = 0;
for (GTreeNode node : children()) {
count += node.getLeafCount();
@ -349,7 +376,7 @@ public abstract class GTreeNode extends CoreGTreeNode implements Comparable<GTre
@Override
public int hashCode() {
return (int) id;
return getName().hashCode();
}
@Override
@ -364,7 +391,7 @@ public abstract class GTreeNode extends CoreGTreeNode implements Comparable<GTre
return false;
}
GTreeNode other = (GTreeNode) obj;
return id == other.id;
return getName().equals(other.getName());
}
/**

View File

@ -236,6 +236,16 @@ public class GTreeNodeTest {
assertEquals(4, root.getLeafCount()); // the total of all its children
}
@Test
public void testGetLeafCountOnLazyNodes() throws CancelledException {
LazyGTestNode node = new LazyGTestNode("Test", 3);
assertEquals(1, node.getLeafCount());
node.getChildren();// force load
assertEquals(3, node.getLeafCount());
node.loadAll(TaskMonitor.DUMMY);
assertEquals(27, node.getLeafCount());
}
@Test
public void testGetIndexInParent() {
assertEquals(0, node0.getIndexInParent());
@ -406,6 +416,24 @@ public class GTreeNodeTest {
assertEquals(node1_0, collect.get(6));
}
@Test
public void testEqualsAndHashCode() {
GTreeNode nodeA = new TestNode("AAA");
GTreeNode nodeB = new TestNode("BBB");
GTreeNode nodeAA = new TestNode("AAA");
assertEquals(nodeA, nodeAA);
assertNotEquals(nodeA, nodeB);
assertEquals(nodeA.hashCode(), nodeAA.hashCode());
assertNotEquals(nodeA.hashCode(), nodeB.hashCode());
}
@Test
public void testCloneEquals() throws CloneNotSupportedException {
GTreeNode nodeA = new TestNode("AAA");
assertEquals(nodeA, nodeA.clone());
assertEquals(nodeA.hashCode(), nodeA.clone().hashCode());
}
private class TestFilter implements GTreeFilter {
private String text;