Changes to the GTree code to improve performance. Completely changed

GTreeNode base implementations and filtering code.
This commit is contained in:
ghidravore 2019-09-11 15:54:23 -04:00
parent 04f7366a62
commit 5c6b32714c
137 changed files with 2834 additions and 3081 deletions

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +15,13 @@
*/
package ghidra.app.plugin.core.calltree;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.tree.TreePath;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeSlowLoadingNode;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
@ -25,15 +31,6 @@ import ghidra.program.util.ProgramLocation;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.tree.TreePath;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeSlowLoadingNode;
import docking.widgets.tree.support.GTreeFilter;
public abstract class CallNode extends GTreeSlowLoadingNode {
private boolean allowDuplicates;
@ -124,13 +121,11 @@ public abstract class CallNode extends GTreeSlowLoadingNode {
}
@Override
public void filter(GTreeFilter filter, TaskMonitor monitor, int min, int max)
throws CancelledException {
public int loadAll(TaskMonitor monitor) throws CancelledException {
if (depth() > filterDepth.get()) {
doSetActiveChildren(new ArrayList<GTreeNode>());
return;
return 1;
}
super.filter(filter, monitor, min, max);
return super.loadAll(monitor);
}
private int depth() {

View file

@ -182,7 +182,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
public void actionPerformed(ActionContext context) {
Object contextObject = context.getContextObject();
GTree gTree = (GTree) contextObject;
GTreeRootNode rootNode = gTree.getRootNode();
GTreeNode rootNode = gTree.getViewRoot();
List<GTreeNode> children = rootNode.getChildren();
for (GTreeNode child : children) {
gTree.collapseAll(child);
@ -283,7 +283,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
for (TreePath path : selectionPaths) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (node instanceof GTreeRootNode) {
if (node instanceof GTreeNode) {
return false;
}
}
@ -343,7 +343,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
for (TreePath path : selectionPaths) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (node instanceof GTreeRootNode) {
if (node.isRoot()) {
return false;
}
}
@ -496,7 +496,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
for (TreePath path : selectionPaths) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (node instanceof GTreeRootNode) {
if (node.isRoot()) {
return false;
}
}
@ -551,7 +551,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
for (TreePath path : selectionPaths) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (node instanceof GTreeRootNode) {
if (node.isRoot()) {
return false;
}
}
@ -660,7 +660,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
for (TreePath path : selectionPaths) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (node instanceof GTreeRootNode) {
if (node.isRoot()) {
return false;
}
}
@ -915,7 +915,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
}
private void clearTrees() {
if (incomingTree.getRootNode() instanceof EmptyRootNode) {
if (incomingTree.getModelRoot() instanceof EmptyRootNode) {
// already empty
return;
}
@ -937,7 +937,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
}
private void updateIncomingReferencs(Function function) {
GTreeRootNode rootNode = null;
GTreeNode rootNode = null;
if (function == null) {
rootNode = new EmptyRootNode();
}
@ -949,7 +949,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
}
private void updateOutgoingReferences(Function function) {
GTreeRootNode rootNode = null;
GTreeNode rootNode = null;
if (function == null) {
rootNode = new EmptyRootNode();
}
@ -1083,12 +1083,12 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
}
private boolean isEmpty() {
GTreeRootNode rootNode = incomingTree.getRootNode();
GTreeNode rootNode = incomingTree.getModelRoot();
return rootNode instanceof EmptyRootNode;
}
private boolean updateRootNodes(Function function) {
CallNode callNode = (CallNode) incomingTree.getRootNode();
CallNode callNode = (CallNode) incomingTree.getModelRoot();
Function nodeFunction = callNode.getContainingFunction();
if (nodeFunction.equals(function)) {
reloadUpdateManager.update();
@ -1109,7 +1109,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
@Override
public void run(TaskMonitor monitor) throws CancelledException {
CallNode rootNode = (CallNode) tree.getRootNode();
CallNode rootNode = (CallNode) tree.getModelRoot();
List<GTreeNode> children = rootNode.getChildren();
for (GTreeNode node : children) {
updateFunction((CallNode) node);
@ -1117,7 +1117,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
}
private void updateFunction(CallNode node) {
if (!node.isChildrenLoadedOrInProgress()) {
if (!node.isLoaded()) {
// children not loaded, don't force a load by asking for them
return;
}
@ -1189,6 +1189,8 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
this.recurseIcon.setNumber(depth);
removeFilterCache();
incomingTree.refilterLater();
outgoingTree.refilterLater();
saveRecurseDepth();
}
@ -1203,9 +1205,9 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
// you have done. Normally this is not that big of a problem. However, if the loading
// takes a long time, then you lose some work.
//
GTreeRootNode rootNode = incomingTree.getRootNode();
GTreeNode rootNode = incomingTree.getModelRoot();
rootNode.removeAll();
rootNode = outgoingTree.getRootNode();
rootNode = outgoingTree.getModelRoot();
rootNode.removeAll();
}
@ -1364,7 +1366,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
}
}
private class PendingRootNode extends AbstractGTreeRootNode {
private class PendingRootNode extends GTreeNode {
@Override
public Icon getIcon(boolean expanded) {
@ -1387,7 +1389,7 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
}
}
private class EmptyRootNode extends AbstractGTreeRootNode {
private class EmptyRootNode extends GTreeNode {
@Override
public Icon getIcon(boolean expanded) {

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,20 +15,15 @@
*/
package ghidra.app.plugin.core.calltree;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.Icon;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeRootNode;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
public class IncomingCallsRootNode extends IncomingCallNode implements GTreeRootNode {
private GTree tree;
public class IncomingCallsRootNode extends IncomingCallNode {
IncomingCallsRootNode(Program program, Function function, Address sourceAddress,
boolean filterDuplicates, AtomicInteger filterDepth) {
@ -52,14 +46,4 @@ public class IncomingCallsRootNode extends IncomingCallNode implements GTreeRoot
public String getName() {
return "Incoming References - " + name;
}
@Override
public GTree getGTree() {
return tree;
}
@Override
public void setGTree(GTree tree) {
this.tree = tree;
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,20 +15,15 @@
*/
package ghidra.app.plugin.core.calltree;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.Icon;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeRootNode;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
public class OutgoingCallsRootNode extends OutgoingCallNode implements GTreeRootNode {
private GTree tree;
public class OutgoingCallsRootNode extends OutgoingCallNode {
OutgoingCallsRootNode(Program program, Function function, Address sourceAddress,
boolean filterDuplicates, AtomicInteger filterDepth) {
@ -62,14 +56,4 @@ public class OutgoingCallsRootNode extends OutgoingCallNode implements GTreeRoot
public String getToolTip() {
return null;
}
@Override
public GTree getGTree() {
return tree;
}
@Override
public void setGTree(GTree tree) {
this.tree = tree;
}
}

View file

@ -454,7 +454,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
previewScrollPane = new JScrollPane(previewPane);
DockingWindowManager.getHelpService().registerHelp(previewScrollPane,
DockingWindowManager.getHelpService()
.registerHelp(previewScrollPane,
new HelpLocation("DataTypeManagerPlugin", "Preview_Window"));
}
@ -701,8 +702,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
}
private ArchiveNode getProgramArchiveNode() {
GTreeNode rootNode = getGTree().getRootNode();
List<GTreeNode> children = rootNode.getAllChildren();
GTreeNode rootNode = getGTree().getModelRoot();
List<GTreeNode> children = rootNode.getChildren();
for (GTreeNode node : children) {
ArchiveNode archiveNode = (ArchiveNode) node;
Archive archive = archiveNode.getArchive();
@ -714,8 +715,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
}
private ArchiveNode getDataTypeArchiveNode(DataTypeArchive dataTypeArchive) {
GTreeNode rootNode = getGTree().getRootNode();
List<GTreeNode> children = rootNode.getAllChildren();
GTreeNode rootNode = getGTree().getModelRoot();
List<GTreeNode> children = rootNode.getChildren();
for (GTreeNode node : children) {
ArchiveNode archiveNode = (ArchiveNode) node;
Archive archive = archiveNode.getArchive();
@ -754,7 +755,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
}
Category category = dataTypeManager.getCategory(dataType.getCategoryPath());
ArchiveRootNode rootNode = (ArchiveRootNode) gTree.getRootNode();
ArchiveRootNode rootNode = (ArchiveRootNode) gTree.getViewRoot();
ArchiveNode archiveNode = rootNode.getNodeForManager(dataTypeManager);
if (archiveNode == null) {
plugin.setStatus("Cannot find archive '" + dataTypeManager.getName() + "'. It may " +
@ -844,8 +845,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
}
void programRenamed() {
ArchiveRootNode rootNode = (ArchiveRootNode) archiveGTree.getRootNode();
List<GTreeNode> allChildren = rootNode.getAllChildren();
ArchiveRootNode rootNode = (ArchiveRootNode) archiveGTree.getModelRoot();
List<GTreeNode> allChildren = rootNode.getChildren();
for (GTreeNode node : allChildren) {
ArchiveNode archiveNode = (ArchiveNode) node;
if (archiveNode.getArchive() instanceof ProgramArchive) {

View file

@ -219,8 +219,8 @@ class OpenDomainFileTask extends Task {
}
private GTreeNode getNodeForArchive(GTree tree, Archive archive) {
GTreeNode rootNode = tree.getRootNode();
for (GTreeNode node : rootNode) {
GTreeNode rootNode = tree.getModelRoot();
for (GTreeNode node : rootNode.getChildren()) {
if (node instanceof ArchiveNode) {
ArchiveNode archiveNode = (ArchiveNode) node;
if (archiveNode.getArchive() == archive) {

View file

@ -154,7 +154,7 @@ public class CollapseAllArchivesAction extends DockingAction {
}
private void collapseAll(GTree archiveGTree) {
GTreeNode rootNode = archiveGTree.getRootNode();
GTreeNode rootNode = archiveGTree.getViewRoot();
List<GTreeNode> children = rootNode.getChildren();
for (GTreeNode childNode : children) {
archiveGTree.collapseAll(childNode);

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,19 +15,18 @@
*/
package ghidra.app.plugin.core.datamgr.actions;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.archive.ArchiveFileChooser;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.util.Msg;
import java.io.File;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.OptionDialog;
import docking.widgets.tree.GTreeRootNode;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.archive.ArchiveFileChooser;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.util.Msg;
public class CreateArchiveAction extends DockingAction {
private final DataTypeManagerPlugin plugin;
@ -64,7 +62,8 @@ public class CreateArchiveAction extends DockingAction {
Msg.trace(this, "Need to overwrite--showing dialog");
if (OptionDialog.showYesNoDialogWithNoAsDefaultButton(gTree,
"Overwrite Existing File?",
"Do you want to overwrite existing file\n" + file.getAbsolutePath()) != OptionDialog.OPTION_ONE) {
"Do you want to overwrite existing file\n" +
file.getAbsolutePath()) != OptionDialog.OPTION_ONE) {
Msg.trace(this, "\tdo not overwrite was chosen");
return;
}
@ -79,7 +78,7 @@ public class CreateArchiveAction extends DockingAction {
}
private void selectNewArchive(final Archive archive, final DataTypeArchiveGTree gTree) {
GTreeRootNode rootNode = gTree.getRootNode();
GTreeNode rootNode = gTree.getModelRoot();
gTree.setSelectedNodeByNamePath(new String[] { rootNode.getName(), archive.getName() });
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +15,15 @@
*/
package ghidra.app.plugin.core.datamgr.actions;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreePath;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.dialogs.InputDialog;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
@ -27,15 +35,6 @@ import ghidra.program.model.data.Enum;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreePath;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.dialogs.InputDialog;
import docking.widgets.tree.*;
public class CreateEnumFromSelectionAction extends DockingAction {
private final DataTypeManagerPlugin plugin;
@ -131,7 +130,7 @@ public class CreateEnumFromSelectionAction extends DockingAction {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
GTreeRootNode rootNode = gTree.getRootNode();
GTreeNode rootNode = gTree.getViewRoot();
gTree.setSelectedNodeByNamePath(new String[] { rootNode.getName(), parentNodeName,
newNodeName });
}
@ -174,9 +173,9 @@ public class CreateEnumFromSelectionAction extends DockingAction {
// figure out size of the new enum using the max size of the selected enums
int maxEnumSize = 1;
for (int i = 0; i < enumArray.length; i++) {
if (maxEnumSize < enumArray[i].getLength()) {
maxEnumSize = enumArray[i].getLength();
for (Enum element : enumArray) {
if (maxEnumSize < element.getLength()) {
maxEnumSize = element.getLength();
}
}
SourceArchive sourceArchive = category.getDataTypeManager().getLocalSourceArchive();
@ -184,17 +183,18 @@ public class CreateEnumFromSelectionAction extends DockingAction {
new EnumDataType(category.getCategoryPath(), newName, maxEnumSize,
category.getDataTypeManager());
for (int i = 0; i < enumArray.length; i++) {
String[] names = enumArray[i].getNames();
for (int j = 0; j < names.length; j++) {
dataType.add(names[j], enumArray[i].getValue(names[j]));
for (Enum element : enumArray) {
String[] names = element.getNames();
for (String name : names) {
dataType.add(name, element.getValue(name));
}
}
dataType.setSourceArchive(sourceArchive);
int id = category.getDataTypeManager().startTransaction("Create New Enum Data Type");
category.getDataTypeManager().addDataType(dataType, DataTypeConflictHandler.REPLACE_HANDLER);
category.getDataTypeManager()
.addDataType(dataType, DataTypeConflictHandler.REPLACE_HANDLER);
category.getDataTypeManager().endTransaction(id, true);
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,12 +15,6 @@
*/
package ghidra.app.plugin.core.datamgr.actions;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.util.exception.CancelledException;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreePath;
@ -29,6 +22,10 @@ import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.util.exception.CancelledException;
public class CreateProjectArchiveAction extends DockingAction {
private final DataTypeManagerPlugin plugin;
@ -37,7 +34,7 @@ public class CreateProjectArchiveAction extends DockingAction {
super("New Project Data Type Archive", plugin.getName());
this.plugin = plugin;
setMenuBarData( new MenuData( new String[]{"New Project Archive..."}, null, "Archive" ) );
setMenuBarData(new MenuData(new String[] { "New Project Archive..." }, null, "Archive"));
setDescription("Creates a new project data type archive in this data type manager.");
setEnabled(true);
}
@ -47,21 +44,22 @@ public class CreateProjectArchiveAction extends DockingAction {
try {
Archive newArchive = plugin.getDataTypeManagerHandler().createProjectArchive();
DataTypeArchiveGTree gTree = plugin.getProvider().getGTree();
selectNewArchive( newArchive, gTree );
selectNewArchive(newArchive, gTree);
}
catch (CancelledException ce) {
plugin.getTool().setStatusInfo("Create project archive was cancelled.");
}
}
private void selectNewArchive( final Archive archive, final DataTypeArchiveGTree gTree ) {
SwingUtilities.invokeLater( new Runnable() {
private void selectNewArchive(final Archive archive, final DataTypeArchiveGTree gTree) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// start an edit on the new temporary node name
GTreeNode node = gTree.getRootNode();
final GTreeNode child = node.getChild( archive.getName() );
if ( child != null ) {
gTree.expandPath( node );
GTreeNode node = gTree.getViewRoot();
final GTreeNode child = node.getChild(archive.getName());
if (child != null) {
gTree.expandPath(node);
TreePath path = child.getTreePath();
gTree.scrollPathToVisible(path);
gTree.setSelectedNode(child);

View file

@ -84,7 +84,7 @@ public class DeleteAction extends DockingAction {
private boolean containsUndeletableNodes(TreePath[] selectionPaths) {
for (TreePath path : selectionPaths) {
DataTypeTreeNode node = (DataTypeTreeNode) path.getLastPathComponent();
if (node.isSystemNode() || (node instanceof ArchiveNode)) {
if (!node.canDelete() || (node instanceof ArchiveNode)) {
return true;
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +15,9 @@
*/
package ghidra.app.plugin.core.datamgr.actions;
import java.util.List;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
@ -24,11 +26,6 @@ import ghidra.program.model.data.*;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.AssertException;
import java.util.List;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeRootNode;
/**
* A class to 1) hold related data for creating a new data type and 2) to validate the given
* data when the requested info is based upon the a disallowed condition (e.g., creating a data
@ -82,7 +79,7 @@ class DerivativeDataTypeInfo {
private ArchiveNode getProgramArchiveNode(DataTypeArchiveGTree tree) {
DataTypeManager manager = plugin.getProgramDataTypeManager();
GTreeRootNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
List<GTreeNode> children = rootNode.getChildren();
for (GTreeNode node : children) {
ArchiveNode archiveNode = (ArchiveNode) node;

View file

@ -149,8 +149,8 @@ public class DisassociateDataTypeAction extends DockingAction {
private void collapseArchiveNodes(DataTypeArchiveGTree tree) {
// Note: collapsing archive nodes will actually remove all the children of the archive
// which means no event processing and less memory consumption.
GTreeRootNode root = tree.getRootNode();
List<GTreeNode> archives = root.getAllChildren();
GTreeNode root = tree.getViewRoot();
List<GTreeNode> archives = root.getChildren();
archives.forEach(archive -> tree.collapseAll(archive));
}

View file

@ -210,7 +210,8 @@ public class ExportToHeaderAction extends DockingAction {
finally {
writer.close();
}
plugin.getTool().setStatusInfo(
plugin.getTool()
.setStatusInfo(
"Successfully exported data type(s) to " + file.getAbsolutePath());
}
catch (CancelledException e) {
@ -242,7 +243,7 @@ public class ExportToHeaderAction extends DockingAction {
}
else if (last instanceof CategoryNode) {
CategoryNode node = (CategoryNode) last;
List<GTreeNode> children = node.getAllChildren();
List<GTreeNode> children = node.getChildren();
for (GTreeNode cnode : children) {
addToManager(cnode.getTreePath(), managersToDataTypesMap);
}

View file

@ -15,20 +15,19 @@
*/
package ghidra.app.plugin.core.datamgr.actions;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.util.HTMLUtilities;
import java.util.List;
import javax.swing.Icon;
import javax.swing.tree.TreePath;
import resources.ResourceManager;
import docking.ActionContext;
import docking.action.ToggleDockingAction;
import docking.action.ToolBarData;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.util.HTMLUtilities;
import resources.ResourceManager;
public class FilterArraysAction extends ToggleDockingAction {
@ -41,8 +40,8 @@ public class FilterArraysAction extends ToggleDockingAction {
this.setToolBarData(new ToolBarData(FILTER_ARRAYS_ICON, "filters"));
setDescription(HTMLUtilities.toHTML("Toggle whether or not Arrays are\n"
+ "displayed in the Data Type Manager tree."));
setDescription(HTMLUtilities.toHTML(
"Toggle whether or not Arrays are\n" + "displayed in the Data Type Manager tree."));
setSelected(true);
setEnabled(true);
}
@ -58,7 +57,7 @@ public class FilterArraysAction extends ToggleDockingAction {
@Override
public void actionPerformed(ActionContext context) {
DataTypeArchiveGTree gtree = (DataTypeArchiveGTree) context.getContextObject();
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getRootNode());
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getViewRoot());
TreePath[] selectionPaths = gtree.getSelectionPaths();
gtree.enableArrayFilter(isSelected());
gtree.expandPaths(expandedPaths);

View file

@ -15,20 +15,19 @@
*/
package ghidra.app.plugin.core.datamgr.actions;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.util.HTMLUtilities;
import java.util.List;
import javax.swing.Icon;
import javax.swing.tree.TreePath;
import resources.ResourceManager;
import docking.ActionContext;
import docking.action.ToggleDockingAction;
import docking.action.ToolBarData;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.util.HTMLUtilities;
import resources.ResourceManager;
public class FilterPointersAction extends ToggleDockingAction {
private static final Icon FILTER_POINTERS_ICON =
@ -40,8 +39,8 @@ public class FilterPointersAction extends ToggleDockingAction {
this.setToolBarData(new ToolBarData(FILTER_POINTERS_ICON, "filters"));
setDescription(HTMLUtilities.toHTML("Toggle whether or not Pointers are\n"
+ "displayed in the Data Type Manager tree."));
setDescription(HTMLUtilities.toHTML(
"Toggle whether or not Pointers are\n" + "displayed in the Data Type Manager tree."));
setSelected(true);
setEnabled(true);
}
@ -56,8 +55,9 @@ public class FilterPointersAction extends ToggleDockingAction {
@Override
public void actionPerformed(ActionContext context) {
// this doesn't actually cause a filter task to happen, so we need to save and restore the state here
DataTypeArchiveGTree gtree = (DataTypeArchiveGTree) context.getContextObject();
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getRootNode());
List<TreePath> expandedPaths = gtree.getExpandedPaths(gtree.getViewRoot());
TreePath[] selectionPaths = gtree.getSelectionPaths();
gtree.enablePointerFilter(isSelected());
gtree.expandPaths(expandedPaths);

View file

@ -91,8 +91,8 @@ public class OpenArchiveAction extends DockingAction {
}
private GTreeNode getNodeForArchive(GTree tree, Archive archive) {
GTreeNode rootNode = tree.getRootNode();
List<GTreeNode> allChildren = rootNode.getAllChildren();
GTreeNode rootNode = tree.getModelRoot();
List<GTreeNode> allChildren = rootNode.getChildren();
for (GTreeNode node : allChildren) {
ArchiveNode archiveNode = (ArchiveNode) node;
if (archiveNode.getArchive() == archive) {

View file

@ -32,15 +32,17 @@ public class ArchiveNode extends CategoryNode {
protected ArchiveNodeCategoryChangeListener listener;
private DataTypeManager dataTypeManager; // may be null
public ArchiveNode(Archive archive) {
public ArchiveNode(Archive archive, ArrayPointerFilterState filterState) {
this(archive, archive.getDataTypeManager() == null ? null
: archive.getDataTypeManager().getRootCategory());
: archive.getDataTypeManager().getRootCategory(),
filterState);
this.dataTypeManager = archive.getDataTypeManager();
installDataTypeManagerListener();
}
protected ArchiveNode(Archive archive, Category rootCategory) {
super(rootCategory);
protected ArchiveNode(Archive archive, Category rootCategory,
ArrayPointerFilterState filterState) {
super(rootCategory, filterState);
this.archive = archive;
}
@ -51,7 +53,7 @@ public class ArchiveNode extends CategoryNode {
protected void dataTypeManagerChanged() {
installDataTypeManagerListener();
// old children are no longer valid--clear the cache and fire a node structure changed event
removeAll();
setChildren(null);
nodeChanged(); // notify that this nodes display data has changed
structureChanged(); // notify that his children have been refreshed and the tree cache needs to be wiped.
@ -112,8 +114,7 @@ public class ArchiveNode extends CategoryNode {
}
public void structureChanged() {
removeAll();
getTree().scheduleFilterTask(this);
setChildren(null);
}
public void nodeChanged() {
@ -206,7 +207,7 @@ public class ArchiveNode extends CategoryNode {
public CategoryNode findCategoryNode(Category localCategory, boolean loadChildren) {
// if we don't have to loadChildren and we are not loaded get out.
if (!loadChildren && !isChildrenLoadedOrInProgress()) {
if (!loadChildren && !isLoaded()) {
return null;
}
@ -228,7 +229,7 @@ public class ArchiveNode extends CategoryNode {
return null;
}
List<GTreeNode> children = node.getAllChildren();
List<GTreeNode> children = node.getChildren();
for (GTreeNode child : children) {
if (!(child instanceof CategoryNode)) {
continue;

View file

@ -19,21 +19,21 @@ import java.util.*;
import javax.swing.Icon;
import docking.widgets.tree.*;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.archive.*;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.program.model.data.Category;
import ghidra.program.model.data.DataTypeManager;
import ghidra.util.exception.AssertException;
public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, DataTypeTreeNode {
public class ArchiveRootNode extends DataTypeTreeNode {
private static final String NAME = "Data Types";
private GTree tree;
private final DataTypeManagerHandler archiveManager;
private RootNodeListener archiveListener;
private ArrayPointerFilterState filterState = new ArrayPointerFilterState();
ArchiveRootNode(DataTypeManagerHandler archiveManager) {
this.archiveManager = archiveManager;
archiveListener = new RootNodeListener();
@ -76,21 +76,22 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
}
// a factory method to isolate non-OO inheritance checks
private static final GTreeNode createArchiveNode(Archive archive) {
private static final GTreeNode createArchiveNode(Archive archive,
ArrayPointerFilterState filterState) {
if (archive instanceof FileArchive) {
return new FileArchiveNode((FileArchive) archive);
return new FileArchiveNode((FileArchive) archive, filterState);
}
else if (archive instanceof ProjectArchive) {
return new ProjectArchiveNode((ProjectArchive) archive);
return new ProjectArchiveNode((ProjectArchive) archive, filterState);
}
else if (archive instanceof InvalidFileArchive) {
return new InvalidArchiveNode((InvalidFileArchive) archive);
}
else if (archive instanceof ProgramArchive) {
return new ProgramArchiveNode((ProgramArchive) archive);
return new ProgramArchiveNode((ProgramArchive) archive, filterState);
}
else if (archive instanceof BuiltInArchive) {
return new BuiltInArchiveNode((BuiltInArchive) archive);
return new BuiltInArchiveNode((BuiltInArchive) archive, filterState);
}
return null;
}
@ -137,12 +138,12 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
}
@Override
public boolean isSystemNode() {
return true;
public boolean canDelete() {
return false;
}
public CategoryNode findCategoryNode(Category category) {
Iterator<GTreeNode> iterator = getAllChildren().iterator();
Iterator<GTreeNode> iterator = getChildren().iterator();
while (iterator.hasNext()) {
GTreeNode node = iterator.next();
ArchiveNode archiveNode = (ArchiveNode) node;
@ -155,7 +156,7 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
}
public ArchiveNode getNodeForManager(DataTypeManager dtm) {
Iterator<GTreeNode> iterator = getAllChildren().iterator();
Iterator<GTreeNode> iterator = getChildren().iterator();
while (iterator.hasNext()) {
GTreeNode node = iterator.next();
ArchiveNode archiveNode = (ArchiveNode) node;
@ -170,17 +171,17 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
@Override
public List<GTreeNode> generateChildren() {
List<GTreeNode> children = new ArrayList<>();
List<GTreeNode> list = new ArrayList<>();
Iterator<Archive> iterator = archiveManager.getAllArchives().iterator();
while (iterator.hasNext()) {
children.add(createArchiveNode(iterator.next()));
list.add(createArchiveNode(iterator.next(), filterState));
}
Collections.sort(children);
return children;
Collections.sort(list);
return list;
}
private ArchiveNode getArchiveNode(Archive archive) {
List<GTreeNode> allChildrenList = getAllChildren();
List<GTreeNode> allChildrenList = getChildren();
for (GTreeNode node : allChildrenList) {
if (node instanceof ArchiveNode) {
ArchiveNode archiveNode = (ArchiveNode) node;
@ -200,10 +201,10 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
@Override
public void archiveClosed(Archive archive) {
if (!isChildrenLoadedOrInProgress()) {
if (!isLoaded()) {
return;
}
List<GTreeNode> allChildrenList = getAllChildren();
List<GTreeNode> allChildrenList = getChildren();
Iterator<GTreeNode> iterator = allChildrenList.iterator();
while (iterator.hasNext()) {
GTreeNode node = iterator.next();
@ -218,9 +219,9 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
@Override
public void archiveOpened(Archive archive) {
if (isChildrenLoadedOrInProgress()) {
GTreeNode node = createArchiveNode(archive);
List<GTreeNode> allChildrenList = getAllChildren();
if (isLoaded()) {
GTreeNode node = createArchiveNode(archive, filterState);
List<GTreeNode> allChildrenList = getChildren();
int index = Collections.binarySearch(allChildrenList, node);
if (index < 0) {
index = -index - 1;
@ -228,7 +229,7 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
addNode(index, node);
// kick tree to refilter if filter in place so that nodes will stay expaned see
// SCR #7895
tree.setFilterText(tree.getFilterText());
// tree.setFilterText(tree.getFilterText());
}
}
@ -249,14 +250,12 @@ public class ArchiveRootNode extends GTreeLazyNode implements GTreeRootNode, Dat
}
}
@Override
public GTree getGTree() {
return tree;
public void setFilterArray(boolean enabled) {
filterState.setFilterArrays(enabled);
}
@Override
public void setGTree(GTree tree) {
this.tree = tree;
public void setFilterPointer(boolean enabled) {
filterState.setFilterPointers(enabled);
}
}

View file

@ -0,0 +1,63 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.datamgr.tree;
/**
* Class to maintain the state of whether or not the Data Type Manager Tree is showing
* pointers and/or arrays.
*
* Note: Not sure if this needs to be synchronized or not. The set methods are called
* from the awt thread, but the getter methods are often called by a background worker
* thread. The theory is that we don't need to synchronize this because if the state
* of this object changes, the tree will later be rebuilt in a follow-up task.
*/
public class ArrayPointerFilterState {
private boolean filterArrays;
private boolean filterPointers;
/**
* Returns true if the tree should NOT show arrays
* @return true if the tree should NOT show arrays
*/
public boolean filterArrays() {
return filterArrays;
}
/**
* Sets whether the tree should show arrays.
* @param filterArrays if true the tree will NOT show arrays.
*/
public void setFilterArrays(boolean filterArrays) {
this.filterArrays = filterArrays;
}
/**
* Returns true if the tree should NOT show pointers
* @return true if the tree should NOT show pointers
*/
public boolean filterPointers() {
return filterPointers;
}
/**
* Sets whether the tree should show pointers.
* @param filterPointers if true the tree will NOT show pointers.
*/
public void setFilterPointers(boolean filterPointers) {
this.filterPointers = filterPointers;
}
}

View file

@ -22,8 +22,8 @@ import resources.MultiIcon;
public class BuiltInArchiveNode extends ArchiveNode {
public BuiltInArchiveNode(BuiltInArchive archive) {
super(archive);
public BuiltInArchiveNode(BuiltInArchive archive, ArrayPointerFilterState filterState) {
super(archive, filterState);
}
@Override

View file

@ -19,7 +19,6 @@ import java.util.*;
import javax.swing.Icon;
import docking.widgets.tree.GTreeLazyNode;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.program.model.data.*;
@ -27,14 +26,16 @@ import ghidra.util.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.DuplicateNameException;
public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
public class CategoryNode extends DataTypeTreeNode {
private Category category;
private String name;
private boolean isCut;
private ArrayPointerFilterState filterState;
public CategoryNode(Category category) {
public CategoryNode(Category category, ArrayPointerFilterState filterState) {
this.filterState = filterState;
setCategory(category);
}
@ -45,49 +46,35 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
}
}
@Override
protected synchronized int doGetIndexOfChild(GTreeNode node, List<GTreeNode> children) {
if (children == null) {
return -1;
}
return Collections.binarySearch(children, node);
}
@Override
protected List<GTreeNode> generateChildren() {
if (category == null) {
return Collections.emptyList();
}
DataTypeArchiveGTree tree = (DataTypeArchiveGTree) getTree();
if (tree == null) {
return Collections.emptyList(); // must have been disposed
}
Category[] subCategories = category.getCategories();
DataType[] dataTypes = category.getDataTypes();
List<GTreeNode> children = new ArrayList<>(subCategories.length + dataTypes.length);
List<GTreeNode> list = new ArrayList<>(subCategories.length + dataTypes.length);
for (Category subCategory : subCategories) {
children.add(new CategoryNode(subCategory));
list.add(new CategoryNode(subCategory, filterState));
}
for (DataType dataType : dataTypes) {
if (!isFilteredType(tree, dataType)) {
children.add(new DataTypeNode(dataType));
if (!isFilteredType(dataType)) {
list.add(new DataTypeNode(dataType));
}
}
Collections.sort(children);
Collections.sort(list);
return children;
return list;
}
private boolean isFilteredType(DataTypeArchiveGTree tree, DataType dataType) {
if (tree.isFilterArrays() && dataType instanceof Array) {
private boolean isFilteredType(DataType dataType) {
if (filterState.filterArrays() && dataType instanceof Array) {
return true;
}
if (tree.isFilterPointers() && (dataType instanceof Pointer) &&
if (filterState.filterPointers() && (dataType instanceof Pointer) &&
!(dataType.getDataTypeManager() instanceof BuiltInDataTypeManager)) {
return true;
}
@ -172,12 +159,12 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
public void categoryAdded(Category newCategory) {
if (!isChildrenLoadedOrInProgress()) {
if (!isLoaded()) {
return;
}
CategoryNode node = new CategoryNode(newCategory);
List<GTreeNode> allChildrenList = getAllChildren();
CategoryNode node = new CategoryNode(newCategory, filterState);
List<GTreeNode> allChildrenList = getChildren();
int index = Collections.binarySearch(allChildrenList, node);
if (index < 0) {
index = -index - 1;
@ -186,7 +173,7 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
}
public void dataTypeAdded(DataType dataType) {
if (!isChildrenLoadedOrInProgress()) {
if (!isLoaded()) {
return;
}
@ -196,12 +183,12 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
return;
}
if (isFilteredType(tree, dataType)) {
if (isFilteredType(dataType)) {
return;
}
DataTypeNode node = new DataTypeNode(dataType);
List<GTreeNode> allChildrenList = getAllChildren();
List<GTreeNode> allChildrenList = getChildren();
int index = Collections.binarySearch(allChildrenList, node);
if (index >= 0) {
if (node.getName().equals(allChildrenList.get(index).getName())) {
@ -215,7 +202,10 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
}
public void categoryRemoved(String categoryName) {
for (GTreeNode node : getAllChildrenIfLoaded()) {
if (!isLoaded()) {
return;
}
for (GTreeNode node : getChildren()) {
if ((node instanceof CategoryNode) && node.getName().equals(categoryName)) {
removeNode(node);
return;
@ -224,8 +214,11 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
}
public void dataTypeRemoved(String dataTypeName) {
if (!isLoaded()) {
return;
}
for (GTreeNode node : getAllChildrenIfLoaded()) {
for (GTreeNode node : getChildren()) {
if ((node instanceof DataTypeNode) && node.getName().equals(dataTypeName)) {
removeNode(node);
return;
@ -234,8 +227,12 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
}
public void dataTypeChanged(DataType dataType) {
if (!isLoaded()) {
return;
}
String dataTypeName = dataType.getName();
for (GTreeNode node : getAllChildrenIfLoaded()) {
for (GTreeNode node : getChildren()) {
if ((node instanceof DataTypeNode) && node.getName().equals(dataTypeName)) {
((DataTypeNode) node).dataTypeChanged();
return;
@ -347,7 +344,7 @@ public class CategoryNode extends GTreeLazyNode implements DataTypeTreeNode {
}
@Override
public boolean isSystemNode() {
return false;
public boolean canDelete() {
return true;
}
}

View file

@ -53,9 +53,6 @@ public class DataTypeArchiveGTree extends GTree {
private MyFolderListener folderListener;
private DataTypeTreeExpansionListener cleanupListener = new DataTypeTreeExpansionListener();
private boolean filterArrays = true;
private boolean filterPointers = true;
public DataTypeArchiveGTree(DataTypeManagerPlugin dataTypeManagerPlugin) {
super(new ArchiveRootNode(dataTypeManagerPlugin.getDataTypeManagerHandler()));
@ -65,7 +62,7 @@ public class DataTypeArchiveGTree extends GTree {
// setting the row height may provide speed improvements, as the tree does not have to
// ask for the height for each cell from the renderer.
setRowHeight(getHeight(getRootNode(), renderer));
setRowHeight(getHeight(getViewRoot(), renderer));
setCellRenderer(renderer);
Project project = plugin.getTool().getProject();
if (project != null) {
@ -79,7 +76,7 @@ public class DataTypeArchiveGTree extends GTree {
addTreeExpansionListener(cleanupListener);
}
private int getHeight(GTreeRootNode rootNode, DataTypeTreeRenderer renderer) {
private int getHeight(GTreeNode rootNode, DataTypeTreeRenderer renderer) {
Component c = renderer.getTreeCellRendererComponent(getJTree(), rootNode, false, false,
false, 0, false);
Dimension size = c.getPreferredSize();
@ -89,7 +86,7 @@ public class DataTypeArchiveGTree extends GTree {
@Override
public void expandedStateRestored(TaskMonitor monitor) {
// walk all of our nodes and reclaim any that aren't expanded
GTreeRootNode rootNode = getRootNode();
GTreeNode rootNode = getViewRoot();
if (rootNode == null) {
return; // in a state of flux; been disposed
}
@ -108,7 +105,7 @@ public class DataTypeArchiveGTree extends GTree {
if (!isExpanded(node.getTreePath())) {
int leafCount = node.getLeafCount();
if (node instanceof GTreeLazyNode) {
((GTreeLazyNode) node).removeAll();
((GTreeLazyNode) node).unloadChildren();
}
monitor.incrementProgress(leafCount);
return;
@ -122,7 +119,7 @@ public class DataTypeArchiveGTree extends GTree {
@Override
public void dispose() {
((ArchiveRootNode) getRootNode()).dispose();
((ArchiveRootNode) getModelRoot()).dispose();
PluginTool tool = plugin.getTool();
if (tool == null) {
return; // this can happen when the plugin is disposed off the swing thread
@ -141,12 +138,14 @@ public class DataTypeArchiveGTree extends GTree {
}
public void enableArrayFilter(boolean enabled) {
this.filterArrays = enabled;
ArchiveRootNode root = (ArchiveRootNode) getModelRoot();
root.setFilterArray(enabled);
reloadTree();
}
public void enablePointerFilter(boolean enabled) {
this.filterPointers = enabled;
ArchiveRootNode root = (ArchiveRootNode) getModelRoot();
root.setFilterPointer(enabled);
reloadTree();
}
@ -159,22 +158,12 @@ public class DataTypeArchiveGTree extends GTree {
private void reloadTree() {
GTreeState treeState = getTreeState();
ArchiveRootNode rootNode = (ArchiveRootNode) getRootNode();
rootNode.removeAll();
// calling getChildCount() will "kick" the root node to reload its children
rootNode.getChildCount();
ArchiveRootNode rootNode = (ArchiveRootNode) getModelRoot();
rootNode.unloadChildren();
updateModelFilter();
restoreTreeState(treeState);
}
public boolean isFilterPointers() {
return filterPointers;
}
public boolean isFilterArrays() {
return filterArrays;
}
@Override
public void setNodeEditable(GTreeNode node) {
armedNode = node;
@ -314,7 +303,7 @@ public class DataTypeArchiveGTree extends GTree {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if ((node instanceof CategoryNode)) {
CategoryNode categoryNode = (CategoryNode) node;
categoryNode.removeAll();
categoryNode.setChildren(null);
}
}
@ -462,7 +451,7 @@ public class DataTypeArchiveGTree extends GTree {
@Override
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
List<GTreeNode> archiveNodes = getRootNode().getChildren();
List<GTreeNode> archiveNodes = getModelRoot().getChildren();
for (GTreeNode treeNode : archiveNodes) {
if (treeNode instanceof ProjectArchiveNode) {
ProjectArchiveNode projectArchiveNode = (ProjectArchiveNode) treeNode;

View file

@ -15,13 +15,15 @@
*/
package ghidra.app.plugin.core.datamgr.tree;
import java.util.Collections;
import java.util.List;
import javax.swing.Icon;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.tree.*;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.util.ToolTipUtils;
@ -30,7 +32,7 @@ import ghidra.program.model.data.Enum;
import ghidra.util.*;
import ghidra.util.exception.DuplicateNameException;
public class DataTypeNode extends AbstractGTreeNode implements DataTypeTreeNode {
public class DataTypeNode extends DataTypeTreeNode {
private final DataType dataType;
private final String name;
private String displayName;
@ -227,8 +229,8 @@ public class DataTypeNode extends AbstractGTreeNode implements DataTypeTreeNode
}
@Override
public boolean isSystemNode() {
return false;
public boolean canDelete() {
return true;
}
public void dataTypeStatusChanged() {
@ -268,4 +270,9 @@ public class DataTypeNode extends AbstractGTreeNode implements DataTypeTreeNode
return baseDisplayName;
}
@Override
protected List<GTreeNode> generateChildren() {
return Collections.emptyList();
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,53 +17,56 @@ package ghidra.app.plugin.core.datamgr.tree;
import java.util.List;
import docking.widgets.tree.GTreeLazyNode;
import docking.widgets.tree.GTreeNode;
/**
* A single interface for unifying the handling of nodes that can be manipulated during
* cut/copy/paste operations.
*/
public interface DataTypeTreeNode {
public abstract class DataTypeTreeNode extends GTreeLazyNode {
/**
* Returns true if this node can be cut and moved to a different location.
* @return true if this node can be cut and moved to a different location.
*/
public boolean canCut();
public abstract boolean canCut();
/**
* Returns true if this nodes handles paste operations
* @return true if this nodes handles paste operations
*/
public boolean canPaste(List<GTreeNode> pastedNodes);
public abstract boolean canPaste(List<GTreeNode> pastedNodes);
/**
* Signals to this node that it has been cut during a cut operation, for example, like during
* a cut/paste operation.
* @param isCut true signals that the node has been cut; false that it is not cut.
*/
public void setNodeCut(boolean isCut);
public abstract void setNodeCut(boolean isCut);
/**
* Return true if the node has been cut.
* @return true if the node has been cut.
*/
public boolean isCut();
public abstract boolean isCut();
/**
* Returns the ArchiveNode for this tree node.
* @return the ArchiveNode for this tree node.
*/
public ArchiveNode getArchiveNode();
public abstract ArchiveNode getArchiveNode();
/**
* Returns true if this node is from an archive that can be modified.
* @return true if this node is from an archive that can be modified.
*/
public boolean isModifiable();
public abstract boolean isModifiable();
// TODO: bad name; can't rename it, can't delete it...it's special
public boolean isSystemNode();
/**
* Returns true if this node can be deleted
* @return true if this node can be deleted
*/
public abstract boolean canDelete();
public String getName();
}

View file

@ -49,8 +49,8 @@ public class DomainFileArchiveNode extends ArchiveNode {
private String domainFileInfoString;
public DomainFileArchiveNode(DomainFileArchive archive) {
super(archive);
public DomainFileArchiveNode(DomainFileArchive archive, ArrayPointerFilterState filterState) {
super(archive, filterState);
updateDomainFileInfo();
}
@ -109,8 +109,8 @@ public class DomainFileArchiveNode extends ArchiveNode {
}
@Override
public boolean isSystemNode() {
return true;
public boolean canDelete() {
return false;
}
@Override

View file

@ -32,8 +32,8 @@ public class FileArchiveNode extends ArchiveNode {
FileArchive fileArchive; // casted reference for easy access
public FileArchiveNode(FileArchive archive) {
super(archive);
public FileArchiveNode(FileArchive archive, ArrayPointerFilterState filterState) {
super(archive, filterState);
this.fileArchive = archive;
}

View file

@ -26,7 +26,7 @@ import ghidra.util.HTMLUtilities;
public class InvalidArchiveNode extends ArchiveNode {
public InvalidArchiveNode(InvalidFileArchive archive) {
super(archive);
super(archive, new ArrayPointerFilterState());
}
@Override
@ -73,8 +73,8 @@ public class InvalidArchiveNode extends ArchiveNode {
}
@Override
public boolean isSystemNode() {
return true;
public boolean canDelete() {
return false;
}
@Override

View file

@ -21,8 +21,8 @@ import ghidra.util.HTMLUtilities;
public class ProgramArchiveNode extends DomainFileArchiveNode {
public ProgramArchiveNode(ProgramArchive archive) {
super(archive);
public ProgramArchiveNode(ProgramArchive archive, ArrayPointerFilterState filterState) {
super(archive, filterState);
}
@Override
@ -33,5 +33,4 @@ public class ProgramArchiveNode extends DomainFileArchiveNode {
}
return "[Unsaved New Program Archive]";
}
}

View file

@ -21,16 +21,15 @@ import ghidra.util.HTMLUtilities;
public class ProjectArchiveNode extends DomainFileArchiveNode {
public ProjectArchiveNode(ProjectArchive archive) {
super(archive);
public ProjectArchiveNode(ProjectArchive archive, ArrayPointerFilterState filterState) {
super(archive, filterState);
}
@Override
protected void dataTypeManagerChanged() {
removeAll(); // old children are no longer valid.
setChildren(null); // old children are no longer valid.
installDataTypeManagerListener();
nodeChanged();
fireNodeStructureChanged(this); // all the children have changed.
}
@Override

View file

@ -165,7 +165,7 @@ public class DataTypeChooserDialog extends DialogComponentProvider {
@Override
public void run(TaskMonitor monitor) throws CancelledException {
GTreeRootNode root = tree.getRootNode();
GTreeNode root = tree.getViewRoot();
List<GTreeNode> dtNodes = new ArrayList<>();
getDataTypeNodes(root, dtNodes);

View file

@ -18,7 +18,8 @@ package ghidra.app.plugin.core.datamgr.util;
import java.util.*;
import java.util.Map.Entry;
import docking.widgets.tree.*;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeState;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
import ghidra.app.plugin.core.datamgr.archive.Archive;
@ -122,8 +123,8 @@ public class DataTypeTreeDeleteTask extends Task {
}
private void collapseArchives(DataTypeArchiveGTree tree) {
GTreeRootNode root = tree.getRootNode();
List<GTreeNode> children = root.getAllChildren();
GTreeNode root = tree.getModelRoot();
List<GTreeNode> children = root.getChildren();
for (GTreeNode archive : children) {
tree.collapseAll(archive);
}

View file

@ -37,7 +37,7 @@ public class RegisterTree extends GTree {
public RegisterTree() {
super(new RegisterTreeRootNode());
root = (RegisterTreeRootNode) getRootNode();
root = (RegisterTreeRootNode) getModelRoot();
setEditable(false);
@ -138,9 +138,9 @@ public class RegisterTree extends GTree {
}
}
abstract class SearchableRegisterTreeNode extends AbstractGTreeNode {
abstract class SearchableRegisterTreeNode extends GTreeNode {
public GTreeNode findNode(Register register) {
List<GTreeNode> allChildren = getAllChildren();
List<GTreeNode> allChildren = getChildren();
for (GTreeNode child : allChildren) {
if (!(child instanceof RegisterTreeNode)) {
continue;
@ -160,9 +160,8 @@ abstract class SearchableRegisterTreeNode extends AbstractGTreeNode {
}
}
class RegisterTreeRootNode extends SearchableRegisterTreeNode implements GTreeRootNode {
class RegisterTreeRootNode extends SearchableRegisterTreeNode {
private Register[] lastRegisters;
private GTree tree;
@Override
public Icon getIcon(boolean expanded) {
@ -197,7 +196,8 @@ class RegisterTreeRootNode extends SearchableRegisterTreeNode implements GTreeRo
List<GTreeNode> nodes = new ArrayList<GTreeNode>();
for (Register register : registers) {
if (register.getBaseRegister() != register && !register.getParentRegister().isHidden()) {
if (register.getBaseRegister() != register &&
!register.getParentRegister().isHidden()) {
continue;
}
String groupName = register.getGroup();
@ -217,17 +217,6 @@ class RegisterTreeRootNode extends SearchableRegisterTreeNode implements GTreeRo
Collections.sort(nodes);
setChildren(nodes);
}
@Override
public GTree getGTree() {
return tree;
}
@Override
public void setGTree(GTree tree) {
this.tree = tree;
}
}
class RegisterTreeNode extends SearchableRegisterTreeNode {

View file

@ -37,7 +37,8 @@ import docking.widgets.filechooser.GhidraFileChooserMode;
import docking.widgets.pathmanager.PathManager;
import docking.widgets.pathmanager.PathManagerListener;
import docking.widgets.table.*;
import docking.widgets.tree.*;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.support.BreadthFirstIterator;
import generic.jar.ResourceFile;
import generic.util.Path;
@ -619,7 +620,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
List<GTreeNode> toDelete = new LinkedList<>();
Iterator<GTreeNode> nodes = new BreadthFirstIterator(scriptCategoryTree, scriptRoot);
Iterator<GTreeNode> nodes = new BreadthFirstIterator(scriptRoot);
for (GTreeNode node : CollectionUtils.asIterable(nodes)) {
String[] path = getCategoryPath(node);
List<String> category = Arrays.asList(path);
@ -780,7 +781,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
});
scriptCategoryTree.getSelectionModel().setSelectionMode(
scriptCategoryTree.getSelectionModel()
.setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
tableModel = new GhidraScriptTableModel(this);
@ -1135,7 +1137,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
// with a filter, only things in the available children match the root node (this is
// so filtering in the tree will show all matching results when the
// root is selected, instead of all results).
GTreeRootNode rootNode = scriptCategoryTree.getRootNode();
GTreeNode rootNode = scriptCategoryTree.getViewRoot();
List<GTreeNode> children = rootNode.getChildren();
for (GTreeNode node : children) {
String[] path = getCategoryPath(node);

View file

@ -19,14 +19,14 @@ import java.util.List;
import javax.swing.Icon;
import docking.widgets.tree.AbstractGTreeRootNode;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeNode;
import generic.jar.ResourceFile;
import ghidra.app.script.GhidraScriptUtil;
import ghidra.app.script.ScriptInfo;
import resources.ResourceManager;
class RootNode extends AbstractGTreeRootNode {
class RootNode extends GTreeNode {
private static Icon icon = ResourceManager.loadImage("images/play.png");
@Override
@ -65,7 +65,7 @@ class RootNode extends AbstractGTreeRootNode {
}
private GTreeNode getChildRegardlessOfFilter(GTreeNode parent, String name) {
List<GTreeNode> children = parent.getAllChildren();
List<GTreeNode> children = parent.getChildren();
for (GTreeNode child : children) {
if (child.getName().equals(name)) {
return child;
@ -75,7 +75,7 @@ class RootNode extends AbstractGTreeRootNode {
}
private void insertSorted(GTreeNode parent, GTreeNode newChild) {
List<GTreeNode> allChildren = parent.getAllChildren();
List<GTreeNode> allChildren = parent.getChildren();
for (GTreeNode child : allChildren) {
String nodeName = child.getName();
String newNodeName = newChild.getName();

View file

@ -17,10 +17,10 @@ package ghidra.app.plugin.core.script;
import javax.swing.Icon;
import docking.widgets.tree.AbstractGTreeNode;
import docking.widgets.tree.GTreeNode;
import resources.ResourceManager;
public class ScriptCategoryNode extends AbstractGTreeNode {
public class ScriptCategoryNode extends GTreeNode {
private static Icon OPEN_FOLDER = ResourceManager.loadImage("images/openSmallFolder.png");
private static Icon CLOSED_FOLDER = ResourceManager.loadImage("images/closedSmallFolder.png");
@ -49,7 +49,7 @@ public class ScriptCategoryNode extends AbstractGTreeNode {
@Override
public boolean isLeaf() {
return getAllChildCount() == 0;
return getChildCount() == 0;
}
}

View file

@ -21,7 +21,8 @@ import java.awt.Component;
import javax.swing.*;
import javax.swing.tree.TreePath;
import docking.widgets.tree.*;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.support.GTreeRenderer;
import ghidra.app.plugin.core.symboltree.nodes.SymbolNode;
import ghidra.app.util.SymbolInspector;
@ -34,7 +35,7 @@ public class SymbolGTree extends GTree {
private GTreeNode armedNode;
private SymbolInspector symbolInspector;
public SymbolGTree(GTreeRootNode root, SymbolTreePlugin plugin) {
public SymbolGTree(GTreeNode root, SymbolTreePlugin plugin) {
super(root);
symbolInspector = new SymbolInspector(plugin.getTool(), this);
setShowsRootHandles(true);

View file

@ -137,7 +137,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
private SymbolGTree createTree(SymbolTreeRootNode rootNode) {
if (tree != null) {
GTreeNode oldRootNode = tree.getRootNode();
GTreeNode oldRootNode = tree.getModelRoot();
tree.setProgram(rootNode.getProgram());
tree.setRootNode(rootNode);
@ -362,8 +362,9 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
}
private void rebuildTree() {
GTreeNode node = tree.getRootNode();
node.removeAll();
SymbolTreeRootNode node = (SymbolTreeRootNode) tree.getModelRoot();
node.setChildren(null);
tree.refilterLater();
}
private void symbolChanged(Symbol symbol, String oldName) {
@ -446,8 +447,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
return;
}
GTreeNode node = tree.getRootNode();
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) tree.getViewRoot();
tree.runTask(new SearchTask(tree, rootNode, symbol));
}
@ -535,12 +535,12 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
@Override
void doRun(TaskMonitor monitor) throws CancelledException {
GTreeNode node = tree.getRootNode();
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) tree.getModelRoot();
// the symbol may have been deleted while we are processing bulk changes
if (symbol.checkIsValid()) {
rootNode.symbolAdded(symbol);
GTreeNode newNode = rootNode.symbolAdded(symbol);
tree.refilterLater(newNode);
}
}
}
@ -562,15 +562,14 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
@Override
void doRun(TaskMonitor monitor) throws CancelledException {
GTreeNode node = tree.getRootNode();
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
rootNode.symbolRemoved(symbol, oldName, monitor);
SymbolTreeRootNode root = (SymbolTreeRootNode) tree.getModelRoot();
root.symbolRemoved(symbol, monitor);
// the symbol may have been deleted while we are processing bulk changes
if (symbol.checkIsValid()) {
rootNode.symbolAdded(symbol);
root.symbolAdded(symbol);
}
tree.refilterLater();
}
}
@ -582,9 +581,9 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
@Override
void doRun(TaskMonitor monitor) throws CancelledException {
GTreeNode node = tree.getRootNode();
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) node;
rootNode.symbolRemoved(symbol, monitor);
SymbolTreeRootNode root = (SymbolTreeRootNode) tree.getModelRoot();
root.symbolRemoved(symbol, monitor);
tree.refilterLater();
}
}

View file

@ -57,18 +57,17 @@ public class ClassCategoryNode extends SymbolCategoryNode {
}
@Override
public void symbolAdded(Symbol symbol) {
if (!isChildrenLoadedOrInProgress()) {
return;
public SymbolNode symbolAdded(Symbol symbol) {
if (!isLoaded()) {
return null;
}
if (!supportsSymbol(symbol)) {
return;
return null;
}
if (symbol.getSymbolType() == symbolCategory.getSymbolType()) {
doAddSymbol(symbol, this); // add new Class symbol
return;
return doAddSymbol(symbol, this); // add new Class symbol
}
// see if the symbol is in a class namespace
@ -77,9 +76,9 @@ public class ClassCategoryNode extends SymbolCategoryNode {
SymbolNode key = SymbolNode.createNode(namespaceSymbol, program);
GTreeNode parentNode = findSymbolTreeNode(key, false, TaskMonitorAdapter.DUMMY_MONITOR);
if (parentNode == null) {
return;
return null;
}
doAddSymbol(symbol, parentNode);
return doAddSymbol(symbol, parentNode);
}
@Override
@ -87,6 +86,7 @@ public class ClassCategoryNode extends SymbolCategoryNode {
throws CancelledException {
List<GTreeNode> list = new ArrayList<GTreeNode>();
monitor.initialize(symbolTable.getNumSymbols());
SymbolType symbolType = symbolCategory.getSymbolType();
SymbolIterator it = symbolTable.getDefinedSymbols();
while (it.hasNext()) {
@ -95,6 +95,7 @@ public class ClassCategoryNode extends SymbolCategoryNode {
monitor.checkCanceled();
list.add(SymbolNode.createNode(s, program));
}
monitor.incrementProgress(1);
}
Collections.sort(list, getChildrenComparator());
return list;

View file

@ -27,10 +27,10 @@ import ghidra.program.model.symbol.Symbol;
import resources.ResourceManager;
public class ClassSymbolNode extends SymbolNode {
static final DataFlavor LOCAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
"Symbol Tree Data Flavor - Local Classes");
static final DataFlavor GLOBAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
"Symbol Tree Data Flavor - Global Classes");
static final DataFlavor LOCAL_DATA_FLAVOR =
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Local Classes");
static final DataFlavor GLOBAL_DATA_FLAVOR =
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Global Classes");
private static Icon CLASS_ICON = ResourceManager.loadImage("images/class.png");
private static Icon DISABLED_CLASS_ICONDISABLED_CLASS_ICON =

View file

@ -27,12 +27,12 @@ import ghidra.program.model.symbol.Symbol;
import resources.ResourceManager;
public class CodeSymbolNode extends SymbolNode {
static final DataFlavor LOCAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
"Symbol Tree Data Flavor - Local Labels");
static final DataFlavor GLOBAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
"Symbol Tree Data Flavor - Global Labels");
static final DataFlavor EXTERNAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
"Symbol Tree Data Flavor - External Data");
static final DataFlavor LOCAL_DATA_FLAVOR =
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Local Labels");
static final DataFlavor GLOBAL_DATA_FLAVOR =
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Global Labels");
static final DataFlavor EXTERNAL_DATA_FLAVOR =
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - External Data");
private static final Icon CODE_ICON = ResourceManager.loadImage("images/label.png");
private static final Icon PINNED_ICON = ResourceManager.loadImage("images/pin.png");

View file

@ -102,31 +102,30 @@ class FunctionCategoryNode extends SymbolCategoryNode {
}
@Override
public void symbolAdded(Symbol symbol) {
if (!isChildrenLoadedOrInProgress()) {
return;
public SymbolNode symbolAdded(Symbol symbol) {
if (!isLoaded()) {
return null;
}
if (!supportsSymbol(symbol)) {
return;
return null;
}
// variables and parameters will be beneath function nodes, and our parent method
// will find them
if (isVariableParameterOrCodeSymbol(symbol)) {
super.symbolAdded(symbol);
return;
return super.symbolAdded(symbol);
}
// this namespace will be beneath function nodes, and our parent method will find them
if (isChildNamespaceOfFunction(symbol)) {
super.symbolAdded(symbol);
return;
return super.symbolAdded(symbol);
}
// ...otherwise, we have a function and we need to add it as a child of our parent node
GTreeNode newNode = SymbolNode.createNode(symbol, program);
SymbolNode newNode = SymbolNode.createNode(symbol, program);
doAddNode(this, newNode);
return newNode;
}
private boolean isChildNamespaceOfFunction(Symbol symbol) {
@ -171,7 +170,7 @@ class FunctionCategoryNode extends SymbolCategoryNode {
public GTreeNode findSymbolTreeNode(SymbolNode key, boolean loadChildren, TaskMonitor monitor) {
// if we don't have to loadChildren and we are not loaded get out.
if (!loadChildren && !isChildrenLoadedOrInProgress()) {
if (!loadChildren && !isLoaded()) {
return null;
}

View file

@ -85,21 +85,22 @@ public class LabelCategoryNode extends SymbolCategoryNode {
}
@Override
public void symbolAdded(Symbol symbol) {
if (!isChildrenLoadedOrInProgress()) {
return;
public SymbolNode symbolAdded(Symbol symbol) {
if (!isLoaded()) {
return null;
}
// only include global symbols
if (!symbol.isGlobal()) {
return;
return null;
}
if (!supportsSymbol(symbol)) {
return;
return null;
}
GTreeNode newNode = SymbolNode.createNode(symbol, program);
SymbolNode newNode = SymbolNode.createNode(symbol, program);
doAddNode(this, newNode);
return newNode;
}
}

View file

@ -121,5 +121,4 @@ public class LibrarySymbolNode extends SymbolNode {
public Namespace getNamespace() {
return (Library) symbol.getObject();
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,20 +15,19 @@
*/
package ghidra.app.plugin.core.symboltree.nodes;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import javax.swing.Icon;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import resources.ResourceManager;
public class LocalVariableSymbolNode extends SymbolNode {
public static final Icon LOCAL_VARIABLE_ICON = ResourceManager.loadImage("images/LocalVariable.gif");
public static final Icon LOCAL_VARIABLE_ICON =
ResourceManager.loadImage("images/LocalVariable.gif");
LocalVariableSymbolNode(Program program, Symbol symbol) {
super( program, symbol );
super(program, symbol);
}
@Override
@ -39,6 +37,6 @@ public class LocalVariableSymbolNode extends SymbolNode {
@Override
public void setNodeCut(boolean isCut) {
throw new UnsupportedOperationException( "Cannot cut a local variable node" );
throw new UnsupportedOperationException("Cannot cut a local variable node");
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,23 +15,22 @@
*/
package ghidra.app.plugin.core.symboltree.nodes;
import ghidra.app.util.SelectionTransferData;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Symbol;
import java.awt.datatransfer.DataFlavor;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import ghidra.app.util.SelectionTransferData;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Symbol;
import resources.ResourceManager;
public class NamespaceSymbolNode extends SymbolNode {
static final DataFlavor LOCAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
"Symbol Tree Data Flavor - Local Namespaces");
static final DataFlavor GLOBAL_DATA_FLAVOR = new SymbolTreeDataFlavor(
"Symbol Tree Data Flavor - Global Namespaces");
static final DataFlavor LOCAL_DATA_FLAVOR =
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Local Namespaces");
static final DataFlavor GLOBAL_DATA_FLAVOR =
new SymbolTreeDataFlavor("Symbol Tree Data Flavor - Global Namespaces");
public static final Icon NAMESPACE_ICON = ResourceManager.loadImage("images/Namespace.gif");
public static final Icon DISABLED_NAMESPACE_ICON =

View file

@ -20,7 +20,6 @@ import java.util.*;
import javax.swing.Icon;
import docking.widgets.tree.AbstractGTreeNode;
import docking.widgets.tree.GTreeNode;
import ghidra.program.model.symbol.Namespace;
import ghidra.util.datastruct.IntArray;
@ -32,7 +31,7 @@ import resources.ResourceManager;
* See {@link #computeChildren(List, int, GTreeNode, int, TaskMonitor)} for details on
* how this class works.
*/
public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNode {
public class OrganizationNode extends SymbolTreeNode {
static final Comparator<GTreeNode> COMPARATOR = new OrganizationNodeComparator();
private static Icon OPEN_FOLDER_GROUP_ICON =
@ -44,27 +43,28 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
/**
* You cannot instantiate this class directly, instead use the factory method below
* {@link #organize(List, int)}.
* @throws CancelledException
* {@link #organize(List, int, TaskMonitor)}
* @throws CancelledException if the operation is cancelled
*/
private OrganizationNode(List<GTreeNode> list, int max, int parentLevel, GTreeNode parent,
int indexInParent, TaskMonitor monitor) throws CancelledException {
private OrganizationNode(List<GTreeNode> list, int max, int parentLevel, TaskMonitor monitor)
throws CancelledException {
setChildren(computeChildren(list, max, this, parentLevel, monitor));
GTreeNode child = getChild(0);
baseName = child.getName().substring(0, getPrefixSizeForGrouping(getAllChildren(), 1) + 1);
baseName = child.getName().substring(0, getPrefixSizeForGrouping(getChildren(), 1) + 1);
}
/**
* A factory method for creating OrganizationNode objects. See
* {@link #computeChildren(List, int, GTreeNode, int)} for details .
* A factory method for creating OrganizationNode objects.
* See {@link #computeChildren(List, int, GTreeNode, int, TaskMonitor)}
*
* @param nodes the original list of child nodes to be subdivided.
* @param max The max number of child nodes per parent node at any node level.
* @param monitor the task monitor used to cancel this operation
* @return A list of nodes that is based upon the given list, but subdivided as needed.
* @throws CancelledException
* @see #computeChildren(List, int, GTreeNode, int)
* @throws CancelledException if the operation is cancelled
* @see #computeChildren(List, int, GTreeNode, int, TaskMonitor)
*/
public static List<GTreeNode> organize(List<GTreeNode> nodes, int max, TaskMonitor monitor)
throws CancelledException {
@ -106,19 +106,14 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
* @param maxNodes The max number of child nodes per parent node at any node level.
* @param parent The parent of the given <tt>children</tt>
* @param parentLevel node depth in the tree of <b>Organization</b> nodes.
* @return the given <tt>list</tt> subgrouped as outlined above.
* @throws CancelledException
* @return the given <tt>list</tt> sub-grouped as outlined above.
* @throws CancelledException if the operation is cancelled
*/
private static List<GTreeNode> computeChildren(List<GTreeNode> list, int maxNodes,
GTreeNode parent, int parentLevel, TaskMonitor monitor) throws CancelledException {
List<GTreeNode> children;
if (list.size() <= maxNodes) {
children = new ArrayList<>(list);
Iterator<GTreeNode> it = list.iterator();
while (it.hasNext()) {
monitor.checkCanceled();
parent.addNode(it.next());
}
}
else {
int characterOffset = getPrefixSizeForGrouping(list, maxNodes);
@ -133,13 +128,13 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
monitor.checkCanceled();
String str = list.get(i).getName();
if (stringsDiffer(prevStr, str, characterOffset)) {
addNode(children, list, start, i - 1, maxNodes, parent, characterOffset,
addNode(children, list, start, i - 1, maxNodes, characterOffset,
monitor);
start = i;
}
prevStr = str;
}
addNode(children, list, start, end - 1, maxNodes, parent, characterOffset, monitor);
addNode(children, list, start, end - 1, maxNodes, characterOffset, monitor);
}
return children;
}
@ -148,22 +143,20 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
if (s1.length() <= diffLevel || s2.length() <= diffLevel) {
return true;
}
return s1.substring(0, diffLevel + 1).compareToIgnoreCase(
return s1.substring(0, diffLevel + 1)
.compareToIgnoreCase(
s2.substring(0, diffLevel + 1)) != 0;
}
private static void addNode(List<GTreeNode> children, List<GTreeNode> list, int start, int end,
int max, GTreeNode parent, int diffLevel, TaskMonitor monitor)
int max, int diffLevel, TaskMonitor monitor)
throws CancelledException {
if (end - start > 0) {
children.add(new OrganizationNode(list.subList(start, end + 1), max, diffLevel, parent,
children.size(), monitor));
children.add(new OrganizationNode(list.subList(start, end + 1), max, diffLevel,
monitor));
}
else {
GTreeNode node = list.get(start);
if (parent != null) {
parent.addNode(node);
}
children.add(node);
}
}
@ -292,7 +285,7 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
* @param newNode the node to insert.
*/
public void insertNode(GTreeNode newNode) {
int index = Collections.binarySearch(getAllChildren(), newNode, getChildrenComparator());
int index = Collections.binarySearch(getChildren(), newNode, getChildrenComparator());
if (index >= 0) {
// found a match
GTreeNode matchingNode = getChild(index);
@ -326,7 +319,7 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
// the old name will find the right parent, but not the actual current node, as
// it has a new name.
//
return SymbolTreeNode.super.findSymbolTreeNode(key, loadChildren, taskMonitor);
return super.findSymbolTreeNode(key, loadChildren, taskMonitor);
}
@Override
@ -365,4 +358,9 @@ public class OrganizationNode extends AbstractGTreeNode implements SymbolTreeNod
}
}
@Override
public List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException {
// not used, children generated in constructor
return null;
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +15,10 @@
*/
package ghidra.app.plugin.core.symboltree.nodes;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import javax.swing.Icon;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import resources.ResourceManager;
public class ParameterSymbolNode extends SymbolNode {

View file

@ -18,7 +18,7 @@ package ghidra.app.plugin.core.symboltree.nodes;
import java.awt.datatransfer.DataFlavor;
import java.util.*;
import docking.widgets.tree.*;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.symboltree.SymbolCategory;
import ghidra.program.model.address.GlobalNamespace;
import ghidra.program.model.listing.Program;
@ -27,7 +27,7 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
public abstract class SymbolCategoryNode extends SymbolTreeNode {
protected SymbolCategory symbolCategory;
protected SymbolTable symbolTable;
protected GlobalNamespace globalNamespace;
@ -73,10 +73,12 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
List<GTreeNode> list = new ArrayList<>();
SymbolType symbolType = symbolCategory.getSymbolType();
monitor.initialize(symbolTable.getNumSymbols());
SymbolIterator it =
globalOnly ? symbolTable.getSymbols(globalNamespace) : symbolTable.getSymbolIterator();
while (it.hasNext()) {
Symbol s = it.next();
monitor.incrementProgress(1);
if (s != null && (s.getSymbolType() == symbolType)) {
monitor.checkCanceled();
list.add(SymbolNode.createNode(s, program));
@ -151,20 +153,19 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
dataFlavor == ClassSymbolNode.LOCAL_DATA_FLAVOR;
}
public void symbolAdded(Symbol symbol) {
public SymbolNode symbolAdded(Symbol symbol) {
if (!isChildrenLoadedOrInProgress()) {
return;
if (!isLoaded()) {
return null;
}
if (!supportsSymbol(symbol)) {
return;
return null;
}
GTreeNode parentNode = this;
if (symbol.isGlobal()) {
doAddSymbol(symbol, parentNode);
return;
return doAddSymbol(symbol, parentNode);
}
Namespace parentNamespace = symbol.getParentNamespace();
@ -172,26 +173,27 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
SymbolNode key = SymbolNode.createNode(namespaceSymbol, program);
parentNode = findSymbolTreeNode(key, false, TaskMonitorAdapter.DUMMY_MONITOR);
if (parentNode == null) {
return;
return null;
}
doAddSymbol(symbol, parentNode);
return doAddSymbol(symbol, parentNode);
}
protected void doAddSymbol(Symbol symbol, GTreeNode parentNode) {
if (!((AbstractGTreeNode) parentNode).isChildrenLoadedOrInProgress()) {
return; // the node's not open, we don't care
protected SymbolNode doAddSymbol(Symbol symbol, GTreeNode parentNode) {
if (!parentNode.isLoaded()) {
return null; // the node's not open, we don't care
}
SymbolNode newNode = SymbolNode.createNode(symbol, program);
doAddNode(parentNode, newNode);
return newNode;
}
protected void doAddNode(GTreeNode parentNode, GTreeNode newNode) {
SymbolTreeNode symbolTreeNode = (SymbolTreeNode) parentNode;
Comparator<GTreeNode> comparator = symbolTreeNode.getChildrenComparator();
List<GTreeNode> children = parentNode.getAllChildren();
List<GTreeNode> children = parentNode.getChildren();
int index = Collections.binarySearch(children, newNode, comparator);
if (index >= 0) { // found a match
GTreeNode matchingNode = getChild(index);
@ -215,7 +217,7 @@ public abstract class SymbolCategoryNode extends GTreeSlowLoadingNode implements
}
public void symbolRemoved(Symbol symbol, String oldName, TaskMonitor monitor) {
if (!isChildrenLoadedOrInProgress()) {
if (!isLoaded()) {
return;
}

View file

@ -21,7 +21,6 @@ import java.util.*;
import javax.swing.Icon;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeSlowLoadingNode;
import ghidra.app.cmd.label.CreateNamespacesCmd;
import ghidra.app.util.SymbolPath;
import ghidra.program.model.address.GlobalNamespace;
@ -34,7 +33,7 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
public class SymbolNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
public class SymbolNode extends SymbolTreeNode {
protected final Program program;
protected final Symbol symbol;
@ -42,6 +41,7 @@ public class SymbolNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
private boolean isCut;
SymbolNode(Program program, Symbol symbol) {
super();
this.program = program;
this.symbol = symbol;
}
@ -170,7 +170,7 @@ public class SymbolNode extends GTreeSlowLoadingNode implements SymbolTreeNode {
return this;
}
return SymbolTreeNode.super.findSymbolTreeNode(key, loadChildren, taskMonitor);
return super.findSymbolTreeNode(key, loadChildren, taskMonitor);
}
public static SymbolNode createKeyNode(Symbol symbol, String searchSymbolName,

View file

@ -19,6 +19,7 @@ import java.awt.datatransfer.DataFlavor;
import java.util.*;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeSlowLoadingNode;
import ghidra.app.plugin.core.symboltree.SymbolTreeProvider;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
@ -27,10 +28,10 @@ import ghidra.program.model.symbol.Symbol;
import ghidra.util.task.TaskMonitor;
/**
* Interface for all nodes that live in the {@link SymbolTreeProvider Symbol Tree}.
* Base class for all nodes that live in the {@link SymbolTreeProvider Symbol Tree}.
*
* <p>All nodes will provide a way to search for the node that represents a given symbol. The
* 'find' logic lives in this interface so all nodes have this capability. Some subclasses
* 'find' logic lives in this class so all nodes have this capability. Some subclasses
* of this interface, those with the potential for thousands of children, will break their
* children up into subgroups by name. The search algorithm in this class will uses each
* node's {@link #getChildrenComparator()} method in order to be able to find the correct
@ -38,7 +39,7 @@ import ghidra.util.task.TaskMonitor;
* to keep its default {@link GTreeNode#compareTo(GTreeNode)} method, while allow each
* parent node to sort its children differently.
*/
public interface SymbolTreeNode {
public abstract class SymbolTreeNode extends GTreeSlowLoadingNode {
public static final int MAX_CHILD_NODES = 40;
@ -94,39 +95,39 @@ public interface SymbolTreeNode {
* Returns true if this node can be cut and moved to a different location.
* @return true if this node can be cut and moved to a different location.
*/
public boolean canCut();
public abstract boolean canCut();
/**
* Returns true if this nodes handles paste operations
* @return true if this nodes handles paste operations
*/
public boolean canPaste(List<GTreeNode> pastedNodes);
public abstract boolean canPaste(List<GTreeNode> pastedNodes);
/**
* Signals to this node that it has been cut during a cut operation, for example, like during
* a cut/paste operation.
* @param isCut true signals that the node has been cut; false that it is not cut.
*/
public void setNodeCut(boolean isCut);
public abstract void setNodeCut(boolean isCut);
/**
* Return true if the node has been cut.
* @return true if the node has been cut.
*/
public boolean isCut();
public abstract boolean isCut();
/**
* Gets the data flavor that this node supports for dragging.
* @return the data flavor that this node supports for dragging.
*/
public DataFlavor getNodeDataFlavor();
public abstract DataFlavor getNodeDataFlavor();
/**
* Returns true if this node can accept any of the given data flavors for dropping.
* @param dataFlavors the data flavors of an object being dragged.
* @return true if this node can accept any of the given data flavors for dropping.
*/
public boolean supportsDataFlavors(DataFlavor[] dataFlavors);
public abstract boolean supportsDataFlavors(DataFlavor[] dataFlavors);
/**
* Returns the namespace for this symbol tree node. Not all implementations contain symbols,
@ -134,7 +135,7 @@ public interface SymbolTreeNode {
* namespace.
* @return the namespace for this symbol tree node.
*/
public Namespace getNamespace();
public abstract Namespace getNamespace();
/**
* Returns the comparator used to sort the children of this node. This node will still
@ -143,7 +144,7 @@ public interface SymbolTreeNode {
*
* @return the comparator used to sort this node's children
*/
default public Comparator<GTreeNode> getChildrenComparator() {
public Comparator<GTreeNode> getChildrenComparator() {
return DEFAULT_NODE_COMPARATOR;
}
@ -152,7 +153,7 @@ public interface SymbolTreeNode {
*
* @return the symbol for this node; null if it not associated with a symbol
*/
default public Symbol getSymbol() {
public Symbol getSymbol() {
// We use an odd inheritance hierarchy, where all nodes share this interface. Not all
// nodes have symbols, like a category node. Stub this method out here and allow
// symbol nodes to return their value.
@ -162,13 +163,9 @@ public interface SymbolTreeNode {
/**
* Locates the node that contains the given symbol.
*
* <p><b>Note: </b>This is can degenerate into a brute-force search algorithm, but works in
* <p><b>Note: </b>This can degenerate into a brute-force search algorithm, but works in
* all normal cases using a binary search.
*
* <p>Implementation Note: It is a bit unusual to put this logic into a <code>default</code>
* methods. However, this code needed to be shared by classes that do not share a
* concrete hierarchy, but do share this interface.
*
* @param key the node used to find an existing node. This node is a node created that is
* used by the Comparators to perform binary searches. These can be fabricated
* by using {@link SymbolNode#createNode(Symbol, Program)}
@ -177,15 +174,15 @@ public interface SymbolTreeNode {
* @param monitor the task monitor
* @return the node that contains the given symbol.
*/
default public GTreeNode findSymbolTreeNode(SymbolNode key, boolean loadChildren,
public GTreeNode findSymbolTreeNode(SymbolNode key, boolean loadChildren,
TaskMonitor monitor) {
// if we don't have to loadChildren and we are not loaded get out.
if (!loadChildren && !isChildrenLoadedOrInProgress()) {
if (!loadChildren && !isLoaded()) {
return null;
}
List<GTreeNode> children = getAllChildren();
List<GTreeNode> children = getChildren();
int index = Collections.binarySearch(children, key, getChildrenComparator());
if (index >= 0) {
GTreeNode node = children.get(index);
@ -222,12 +219,4 @@ public interface SymbolTreeNode {
return null;
}
//==================================================================================================
// CoreGTreeNode Methods - redefined here so this class can use them
//==================================================================================================
public boolean isChildrenLoadedOrInProgress();
public List<GTreeNode> getAllChildren();
}

View file

@ -22,7 +22,7 @@ import java.util.*;
import javax.swing.Icon;
import docking.widgets.tree.*;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.symboltree.SymbolCategory;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
@ -30,10 +30,9 @@ import ghidra.program.model.symbol.SymbolType;
import ghidra.util.task.TaskMonitor;
import resources.ResourceManager;
public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootNode {
public class SymbolTreeRootNode extends SymbolCategoryNode {
private static Icon GLOBAL_ICON = ResourceManager.loadImage("images/bullet_green.png");
private final String name;
private GTree tree;
public SymbolTreeRootNode() {
name = "No Symbol Tree";
@ -229,19 +228,24 @@ public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootN
}
@Override
public void symbolAdded(Symbol symbol) {
List<GTreeNode> allChildren = getAllChildren();
public SymbolNode symbolAdded(Symbol symbol) {
SymbolNode returnNode = null;
List<GTreeNode> allChildren = getChildren();
for (GTreeNode gNode : allChildren) {
SymbolCategoryNode symbolNode = (SymbolCategoryNode) gNode;
symbolNode.symbolAdded(symbol);
SymbolNode newNode = symbolNode.symbolAdded(symbol);
if (newNode != null) {
returnNode = newNode; // doesn't matter which one we return
}
}
return returnNode;
}
@Override
public void symbolRemoved(Symbol symbol, String oldName, TaskMonitor monitor) {
// we have to loop--the symbol may exist in more than one category
List<GTreeNode> allChildren = getAllChildren();
List<GTreeNode> allChildren = getChildren();
for (GTreeNode gNode : allChildren) {
SymbolCategoryNode symbolNode = (SymbolCategoryNode) gNode;
symbolNode.symbolRemoved(symbol, oldName, monitor);
@ -249,8 +253,7 @@ public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootN
}
public void rebuild() {
removeAll();
fireNodeStructureChanged(this);
setChildren(null);
}
@Override
@ -302,14 +305,4 @@ public class SymbolTreeRootNode extends SymbolCategoryNode implements GTreeRootN
public void setNodeCut(boolean isCut) {
throw new UnsupportedOperationException("Cannot cut the symbol tree root node");
}
@Override
public GTree getGTree() {
return tree;
}
@Override
public void setGTree(GTree tree) {
this.tree = tree;
}
}

View file

@ -944,7 +944,8 @@ class FSBActionManager {
private void doOpenFileSystem(FSRL containerFSRL, TaskMonitor monitor) {
try {
monitor.setMessage("Probing " + containerFSRL.getName() + " for filesystems");
FileSystemRef ref = FileSystemService.getInstance().probeFileForFilesystem(
FileSystemRef ref = FileSystemService.getInstance()
.probeFileForFilesystem(
containerFSRL, monitor, FileSystemProbeConflictResolver.GUI_PICKER);
if (ref == null) {
Msg.showWarn(this, plugin.getTool().getActiveWindow(), "Open Filesystem",
@ -1051,7 +1052,7 @@ class FSBActionManager {
return;
}
FSBRootNode node = (FSBRootNode) context.getContextObject();
if (node == gTree.getRootNode()) {
if (node.equals(gTree.getModelRoot())) {
// Close entire window
FileSystemRef fsRef = node.getFSRef();
if (fsRef != null && !fsRef.isClosed() &&
@ -1174,7 +1175,8 @@ class FSBActionManager {
gTree.runTask(monitor -> {
try {
FileSystemRef fsRef =
FileSystemService.getInstance().probeFileForFilesystem(
FileSystemService.getInstance()
.probeFileForFilesystem(
containerFSRL, monitor,
FileSystemProbeConflictResolver.GUI_PICKER);
if (fsRef == null) {
@ -1183,7 +1185,7 @@ class FSBActionManager {
return;
}
FSBRootNode nestedRootNode = new FSBRootNode(fsRef, gTree, fileNode);
FSBRootNode nestedRootNode = new FSBRootNode(fsRef, fileNode);
FSBRootNode containingFSBRootNode =
FSBNode.findContainingFileSystemFSBRootNode(fileNode);
if (containingFSBRootNode != null) {

View file

@ -22,7 +22,6 @@ import java.util.List;
import javax.swing.Icon;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeSlowLoadingNode;
import ghidra.formats.gfilesystem.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
@ -33,7 +32,7 @@ import ghidra.util.task.TaskMonitor;
* <p>
* Visible to just this package.
*/
public class FSBDirNode extends GTreeSlowLoadingNode implements FSBNode {
public class FSBDirNode extends FSBNode {
private FSRL fsrl;
FSBDirNode(FSRL fsrl) {

View file

@ -15,17 +15,22 @@
*/
package ghidra.plugins.fsbrowser;
import java.util.Collections;
import java.util.List;
import javax.swing.Icon;
import docking.widgets.tree.AbstractGTreeNode;
import docking.widgets.tree.GTreeNode;
import ghidra.formats.gfilesystem.FSRL;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* GTreeNode that represents a file on a filesystem.
* <p>
* Visible to just this package.
*/
public class FSBFileNode extends AbstractGTreeNode implements FSBNode {
public class FSBFileNode extends FSBNode {
protected FSRL fsrl;
FSBFileNode(FSRL fsrl) {
@ -62,4 +67,9 @@ public class FSBFileNode extends AbstractGTreeNode implements FSBNode {
return fsrl.hashCode();
}
@Override
public List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException {
return Collections.emptyList();
}
}

View file

@ -18,12 +18,13 @@ package ghidra.plugins.fsbrowser;
import java.util.*;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeSlowLoadingNode;
import ghidra.formats.gfilesystem.*;
/**
* Base interface for all filesystem browser gtree nodes.
*/
public interface FSBNode extends GTreeNode {
public abstract class FSBNode extends GTreeSlowLoadingNode {
/**
* Returns the {@link FSRL} of the filesystem object that this node represents.
@ -32,7 +33,7 @@ public interface FSBNode extends GTreeNode {
*
* @return {@link FSRL} of the filesystem object.
*/
FSRL getFSRL();
public abstract FSRL getFSRL();
/**
* Returns the {@link FSBRootNode} that represents the root of the file system that
@ -61,7 +62,7 @@ public interface FSBNode extends GTreeNode {
Collections.sort(files, FSUtilities.GFILE_NAME_TYPE_COMPARATOR);
for (GFile child : files) {
nodes.add(getNodeFromFile(child));
nodes.add((GTreeNode) getNodeFromFile(child));
}
return nodes;
}

View file

@ -20,7 +20,7 @@ import java.util.*;
import javax.swing.Icon;
import docking.widgets.tree.*;
import docking.widgets.tree.GTreeNode;
import ghidra.formats.gfilesystem.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -33,10 +33,9 @@ import ghidra.util.task.TaskMonitor;
* <p>
* Visible to just this package.
*/
public class FSBRootNode extends GTreeSlowLoadingNode implements GTreeRootNode, FSBNode {
public class FSBRootNode extends FSBNode {
private FileSystemRef fsRef;
private GTree tree;
private FSBFileNode prevNode;
private List<FSBRootNode> subRootNodes = new ArrayList<>();
@ -44,9 +43,8 @@ public class FSBRootNode extends GTreeSlowLoadingNode implements GTreeRootNode,
this.fsRef = fsRef;
}
FSBRootNode(FileSystemRef fsRef, GTree gTree, FSBFileNode prevNode) {
FSBRootNode(FileSystemRef fsRef, FSBFileNode prevNode) {
this.fsRef = fsRef;
this.tree = gTree;
this.prevNode = prevNode;
}
@ -106,16 +104,6 @@ public class FSBRootNode extends GTreeSlowLoadingNode implements GTreeRootNode,
return Collections.emptyList();
}
@Override
public void setGTree(GTree tree) {
this.tree = tree;
}
@Override
public GTree getGTree() {
return tree;
}
@Override
public FSRL getFSRL() {
return fsRef.getFilesystem().getFSRL();

View file

@ -60,7 +60,7 @@ public class FSBUtils {
}
public static FSBRootNode getNodesRoot(FSBNode node) {
GTreeNode tmp = node;
GTreeNode tmp = (GTreeNode) node;
while (tmp != null && !(tmp instanceof FSBRootNode)) {
tmp = tmp.getParent();
}

View file

@ -15,7 +15,7 @@
*/
package docking.widgets.tree;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
@ -36,13 +36,11 @@ public class GTreeFilterTest extends AbstractDockingTest {
private GTree gTree;
private FilterTextField filterField;
private GTreeRootNode root;
private DockingWindowManager winMgr;
@Before
public void setUp() throws Exception {
root = new TestRootNode();
GTreeNode root = new TestRootNode();
gTree = new GTree(root);
filterField = (FilterTextField) gTree.getFilterField();
@ -63,10 +61,11 @@ public class GTreeFilterTest extends AbstractDockingTest {
public void testContains() {
setFilterOptions(TextFilterStrategy.CONTAINS, false);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("ABC");
assertEquals("Expected 4 of nodes to be in filtered tree!", 4, root.getChildCount());
assertEquals("Expected 4 of nodes to be in filtered tree!", 4,
viewRoot().getChildCount());
checkContainsNode("ABC");
checkContainsNode("XABC");
@ -74,34 +73,38 @@ public class GTreeFilterTest extends AbstractDockingTest {
checkContainsNode("XABCX");
setFilterText("MMM");
assertEquals("Expected 4 of nodes to be in filtered tree!", 0, root.getChildCount());
assertEquals("Expected 4 of nodes to be in filtered tree!", 0, viewRoot().getChildCount());
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
private GTreeNode viewRoot() {
return gTree.getViewRoot();
}
@Test
public void testMultiWordContains() {
setFilterOptions(TextFilterStrategy.CONTAINS, false);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, ' ',
MultitermEvaluationMode.AND);
setFilterText("CX AB");
assertEquals(2, root.getChildCount());
assertEquals(2, viewRoot().getChildCount());
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, ' ', MultitermEvaluationMode.OR);
setFilterText("CX AB");
assertEquals(4, root.getChildCount());
assertEquals(4, viewRoot().getChildCount());
checkContainsNode("ABCX");
checkContainsNode("XABCX");
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
@Test
@ -109,26 +112,26 @@ public class GTreeFilterTest extends AbstractDockingTest {
setFilterOptions(TextFilterStrategy.CONTAINS, false);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
for (char delim : FilterOptions.VALID_MULTITERM_DELIMITERS.toCharArray()) {
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
MultitermEvaluationMode.AND);
setFilterText("CX" + delim + "AB");
assertEquals(2, root.getChildCount());
assertEquals(2, viewRoot().getChildCount());
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
MultitermEvaluationMode.OR);
setFilterText("CX" + delim + "AB");
assertEquals(4, root.getChildCount());
assertEquals(4, viewRoot().getChildCount());
checkContainsNode("ABCX");
checkContainsNode("XABCX");
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
}
@ -138,7 +141,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
setFilterOptions(TextFilterStrategy.CONTAINS, false);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
String delimPad = StringUtilities.pad("", ' ', 1);
@ -149,19 +152,19 @@ public class GTreeFilterTest extends AbstractDockingTest {
String delimStr = delimPad + delim;
setFilterText("CX" + delimStr + "AB");
assertEquals(2, root.getChildCount());
assertEquals(2, viewRoot().getChildCount());
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
MultitermEvaluationMode.OR);
setFilterText("CX" + delimStr + "AB");
assertEquals(4, root.getChildCount());
assertEquals(4, viewRoot().getChildCount());
checkContainsNode("ABCX");
checkContainsNode("XABCX");
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
}
@ -171,7 +174,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
setFilterOptions(TextFilterStrategy.CONTAINS, false);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
String delimPad = StringUtilities.pad("", ' ', 1);
@ -182,19 +185,19 @@ public class GTreeFilterTest extends AbstractDockingTest {
String delimStr = delim + delimPad;
setFilterText("CX" + delimStr + "AB");
assertEquals(2, root.getChildCount());
assertEquals(2, viewRoot().getChildCount());
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
MultitermEvaluationMode.OR);
setFilterText("CX" + delimStr + "AB");
assertEquals(4, root.getChildCount());
assertEquals(4, viewRoot().getChildCount());
checkContainsNode("ABCX");
checkContainsNode("XABCX");
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
}
@ -204,7 +207,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
setFilterOptions(TextFilterStrategy.CONTAINS, false);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
String delimPad = StringUtilities.pad("", ' ', 1);
@ -215,19 +218,19 @@ public class GTreeFilterTest extends AbstractDockingTest {
String delimStr = delimPad + delim + delimPad;
setFilterText("CX" + delimStr + "AB");
assertEquals(2, root.getChildCount());
assertEquals(2, viewRoot().getChildCount());
setFilterOptions(TextFilterStrategy.CONTAINS, false, true, delim,
MultitermEvaluationMode.OR);
setFilterText("CX" + delimStr + "AB");
assertEquals(4, root.getChildCount());
assertEquals(4, viewRoot().getChildCount());
checkContainsNode("ABCX");
checkContainsNode("XABCX");
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
}
@ -236,10 +239,10 @@ public class GTreeFilterTest extends AbstractDockingTest {
public void testInvertedContains() {
setFilterOptions(TextFilterStrategy.CONTAINS, true);
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("ABC");
assertEquals(1, root.getChildCount());
assertEquals(1, viewRoot().getChildCount());
checkDoesNotContainsNode("ABC");
checkDoesNotContainsNode("XABC");
@ -247,167 +250,168 @@ public class GTreeFilterTest extends AbstractDockingTest {
checkDoesNotContainsNode("XABCX");
setFilterText("MMM");
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
@Test
public void testInvertedMultiWordContains() {
setFilterOptions(TextFilterStrategy.CONTAINS, true, true, ' ', MultitermEvaluationMode.AND);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("CX AB");
checkDoesNotContainsNode("ABCX");
checkDoesNotContainsNode("XABCX");
assertEquals(3, root.getChildCount());
assertEquals(3, viewRoot().getChildCount());
setFilterOptions(TextFilterStrategy.CONTAINS, true, true, ' ', MultitermEvaluationMode.OR);
setFilterText("");
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("CX AB");
checkDoesNotContainsNode("ABCX");
checkDoesNotContainsNode("XABCX");
assertEquals(1, root.getChildCount());
assertEquals(1, viewRoot().getChildCount());
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
@Test
public void testStartsWith() {
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("ABC");
checkContainsNode("ABC");
checkContainsNode("ABCX");
assertEquals(2, root.getChildCount());
assertEquals(2, viewRoot().getChildCount());
setFilterText("MMM");
assertEquals(0, root.getChildCount());
assertEquals(0, viewRoot().getChildCount());
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
@Test
public void testInvertedStartsWith() {
setFilterOptions(TextFilterStrategy.STARTS_WITH, true);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("ABC");
checkDoesNotContainsNode("ABC");
checkDoesNotContainsNode("ABCX");
assertEquals(3, root.getChildCount());
assertEquals(3, viewRoot().getChildCount());
setFilterText("MMM");
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
@Test
public void testExactMatch() {
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("ABC");
checkContainsNode("ABC");
assertEquals(1, root.getChildCount());
assertEquals(1, viewRoot().getChildCount());
setFilterText("MMM");
assertEquals(0, root.getChildCount());
assertEquals(0, viewRoot().getChildCount());
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
@Test
public void testInvertedExactMatch() {
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, true);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("ABC");
checkDoesNotContainsNode("ABC");
assertEquals(4, root.getChildCount());
assertEquals(4, viewRoot().getChildCount());
setFilterText("MMM");
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
@Test
public void testRegExMatch() {
setFilterOptions(TextFilterStrategy.REGULAR_EXPRESSION, false);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("^ABC$");
checkContainsNode("ABC");
assertEquals("Expected 1 node match exacly match ABC!", 1, root.getChildCount());
assertEquals("Expected 1 node match exacly match ABC!", 1, viewRoot().getChildCount());
setFilterText("ABC");
checkContainsNode("ABC");
checkContainsNode("XABC");
checkContainsNode("ABCX");
checkContainsNode("XABCX");
assertEquals("Expected 4 of nodes that contain the text ABC!", 4, root.getChildCount());
assertEquals("Expected 4 of nodes that contain the text ABC!", 4,
viewRoot().getChildCount());
setFilterText("XA.{0,2}X");
checkContainsNode("XABCX");
assertEquals(1, root.getChildCount());
assertEquals(1, viewRoot().getChildCount());
setFilterText("X{0,1}A.{0,2}X");
checkContainsNode("XABCX");
checkContainsNode("ABCX");
assertEquals(2, root.getChildCount());
assertEquals(2, viewRoot().getChildCount());
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
@Test
public void testInvertedRegExMatch() {
setFilterOptions(TextFilterStrategy.REGULAR_EXPRESSION, true);
// no filter text - make sure all 5 nodes are there
assertEquals(5, root.getChildCount());
assertEquals(5, viewRoot().getChildCount());
setFilterText("^ABC$");
checkDoesNotContainsNode("ABC");
assertEquals(4, root.getChildCount());
assertEquals(4, viewRoot().getChildCount());
setFilterText("ABC");
checkDoesNotContainsNode("ABC");
checkDoesNotContainsNode("XABC");
checkDoesNotContainsNode("ABCX");
checkDoesNotContainsNode("XABCX");
assertEquals(1, root.getChildCount());
assertEquals(1, viewRoot().getChildCount());
setFilterText("XA.{0,2}X");
checkDoesNotContainsNode("XABCX");
assertEquals(4, root.getChildCount());
assertEquals(4, viewRoot().getChildCount());
setFilterText("X{0,1}A.{0,2}X");
checkDoesNotContainsNode("XABCX");
checkDoesNotContainsNode("ABCX");
assertEquals(3, root.getChildCount());
assertEquals(3, viewRoot().getChildCount());
setFilterText("");
assertEquals("Expected all 5 nodes to be back", 5, root.getChildCount());
assertEquals("Expected all 5 nodes to be back", 5, viewRoot().getChildCount());
}
@Test
@ -416,14 +420,14 @@ public class GTreeFilterTest extends AbstractDockingTest {
setFilterText("ABC");
checkContainsNode("ABC");
checkContainsNode("ABCX");
assertEquals(2, root.getChildCount());
assertEquals(2, viewRoot().getChildCount());
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
checkContainsNode("ABC");
assertEquals(1, root.getChildCount());
assertEquals(1, viewRoot().getChildCount());
setFilterOptions(TextFilterStrategy.CONTAINS, false);
assertEquals("Expected 4 of nodes to be in filtered tree!", 4, root.getChildCount());
assertEquals("Expected 4 of nodes to be in filtered tree!", 4, viewRoot().getChildCount());
checkContainsNode("ABC");
checkContainsNode("XABC");
checkContainsNode("ABCX");
@ -436,20 +440,20 @@ public class GTreeFilterTest extends AbstractDockingTest {
setFilterOptions(TextFilterStrategy.MATCHES_EXACTLY, false);
setFilterText("ABC");
checkContainsNode("ABC");
assertEquals(1, root.getChildCount());
assertEquals(1, viewRoot().getChildCount());
Object originalValue = getInstanceField("uniquePreferenceKey", gTree);
setInstanceField("preferenceKey", gTree.getFilterProvider(), "XYZ");
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
checkContainsNode("ABC");
checkContainsNode("ABCX");
assertEquals(2, root.getChildCount());
assertEquals(2, viewRoot().getChildCount());
setInstanceField("preferenceKey", gTree.getFilterProvider(), originalValue);
setInstanceField("optionsSet", gTree.getFilterProvider(), false);
restorePreferences();
checkContainsNode("ABC");
assertEquals(1, root.getChildCount());
assertEquals(1, viewRoot().getChildCount());
}
@ -465,7 +469,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
}
private void checkContainsNode(String string) {
List<GTreeNode> children = root.getChildren();
List<GTreeNode> children = viewRoot().getChildren();
for (GTreeNode gTreeNode : children) {
if (gTreeNode.getName().equals(string)) {
return;
@ -475,7 +479,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
}
private void checkDoesNotContainsNode(String string) {
List<GTreeNode> children = root.getChildren();
List<GTreeNode> children = viewRoot().getChildren();
for (GTreeNode gTreeNode : children) {
if (gTreeNode.getName().equals(string)) {
Assert.fail("Expected node " + string +
@ -517,7 +521,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
waitForTree(gTree);
}
private class TestRootNode extends AbstractGTreeRootNode {
private class TestRootNode extends GTreeNode {
TestRootNode() {
List<GTreeNode> children = new ArrayList<>();
@ -553,7 +557,7 @@ public class GTreeFilterTest extends AbstractDockingTest {
/**
* A basic leaf node
*/
private class LeafNode extends AbstractGTreeNode {
private class LeafNode extends GTreeNode {
private final String name;

View file

@ -215,16 +215,16 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
String existingCaller = "Function_1000";// four levels back
setIncomingFilter(existingCaller);
assertIncomingMaxDepth(depth);
assertIncomingMaxDepth(depth, true);
assertIncomingNode(existingCaller, depth);
depth = 3;
setDepth(depth);
setIncomingFilter(existingCaller);
assertIncomingMaxDepth(0);// filter no longer matches
assertIncomingMaxDepth(0, true);// filter no longer matches
assertIncomingNoNode(existingCaller, depth);
assertIncomingNoNode(existingCaller, depth, true);
}
@Test
@ -236,16 +236,16 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
String existingCallee = "Function_8000";
setOutgoingFilter(existingCallee);
assertOutgoingMaxDepth(depth);
assertOutgoingMaxDepth(depth, true);
assertOutgoingNode(existingCallee, depth);
depth = 2;
setDepth(depth);
setOutgoingFilter(existingCallee);
assertOutgoingMaxDepth(0);// filter no longer matches
assertOutgoingMaxDepth(0, true);// filter no longer matches
assertOutgoingNoNode(existingCallee, depth);
assertOutgoingNoNode(existingCallee, depth, true);
}
@Test
@ -261,7 +261,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
String existingCaller = "2000";// at depth 3
setIncomingFilter(existingCaller);
assertIncomingMaxDepth(depth);
assertIncomingMaxDepth(depth, true);
assertIncomingNode(existingCaller, depth);
@ -271,7 +271,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
existingCaller = "1000";// at depth 4
setIncomingFilter(existingCaller);
assertIncomingMaxDepth(depth);
assertIncomingMaxDepth(depth, true);
assertIncomingNode(existingCaller, depth);
}
@ -331,7 +331,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
fullyExpandIncomingNode(node);
assertIncomingMaxDepth(currentDepthSetting(provider));
assertIncomingMaxDepth(currentDepthSetting(provider), false);
}
@Test
@ -347,7 +347,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
fullyExpandOutgoingNode(node);
assertOutgoingMaxDepth(currentDepthSetting(provider));
assertOutgoingMaxDepth(currentDepthSetting(provider), false);
}
@Test
@ -365,7 +365,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
int nodeDepth = node.getTreePath().getPathCount() - 1;// -1 for root node
int depth = 4 + nodeDepth;
assertIncomingMaxDepth(depth);
assertIncomingMaxDepth(depth, false);
assertDepth(node, depth);
}
@ -383,7 +383,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
fullyExpandOutgoingNode(node);
int depth = currentDepthSetting(provider);
assertOutgoingMaxDepth(depth);
assertOutgoingMaxDepth(depth, false);
assertDepth(node, depth);
}
@ -422,7 +422,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
//
myWaitForTree(incomingTree, provider);
GTreeRootNode rootNode = getRootNode(incomingTree);
GTreeNode rootNode = getRootNode(incomingTree);
List<GTreeNode> children = rootNode.getChildren();
assertTrue(
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
@ -442,7 +442,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
setProviderFunction("0x5000");
myWaitForTree(outgoingTree, provider);
GTreeRootNode rootNode = getRootNode(outgoingTree);
GTreeNode rootNode = getRootNode(outgoingTree);
List<GTreeNode> children = rootNode.getChildren();
assertTrue(
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
@ -462,7 +462,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
setProviderFunction("0x5000");
myWaitForTree(outgoingTree, provider);
GTreeRootNode rootNode = getRootNode(outgoingTree);
GTreeNode rootNode = getRootNode(outgoingTree);
List<GTreeNode> children = rootNode.getChildren();
assertTrue(
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
@ -503,7 +503,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(currentSelection.isEmpty());
myWaitForTree(outgoingTree, provider);
GTreeRootNode rootNode = getRootNode(outgoingTree);
GTreeNode rootNode = getRootNode(outgoingTree);
List<GTreeNode> children = rootNode.getChildren();
assertTrue(
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
@ -588,7 +588,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
}
myWaitForTree(outgoingTree, provider);
GTreeRootNode rootNode = getRootNode(outgoingTree);
GTreeNode rootNode = getRootNode(outgoingTree);
List<GTreeNode> children = rootNode.getChildren();
assertTrue(
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
@ -628,7 +628,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
}
myWaitForTree(outgoingTree, provider);
GTreeRootNode rootNode = getRootNode(outgoingTree);
GTreeNode rootNode = getRootNode(outgoingTree);
List<GTreeNode> children = rootNode.getChildren();
assertTrue(
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
@ -698,7 +698,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
setProviderFunction(addrString);
myWaitForTree(incomingTree, provider);
GTreeRootNode rootNode = getRootNode(incomingTree);
GTreeNode rootNode = getRootNode(incomingTree);
List<GTreeNode> children = rootNode.getChildren();
assertTrue("Incoming tree does not have callers as expected for function: " + addrString,
children.size() > 0);
@ -713,7 +713,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
setProviderFunction("0x5000");
myWaitForTree(incomingTree, provider);
GTreeRootNode rootNode = getRootNode(incomingTree);
GTreeNode rootNode = getRootNode(incomingTree);
List<GTreeNode> children = rootNode.getChildren();
assertTrue(
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
@ -744,7 +744,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
setProviderFunction("0x5000");
myWaitForTree(incomingTree, provider);
GTreeRootNode rootNode = getRootNode(incomingTree);
GTreeNode rootNode = getRootNode(incomingTree);
List<GTreeNode> children = rootNode.getChildren();
assertTrue(
"Incoming tree does not have callers as expected for function: " + getListingFunction(),
@ -799,10 +799,14 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
}
}
private GTreeRootNode getRootNode(final GTree tree) {
private GTreeNode getRootNode(GTree tree) {
return getRootNode(tree, false);
}
private GTreeNode getRootNode(final GTree tree, boolean filtered) {
myWaitForTree(tree, provider);
final AtomicReference<GTreeRootNode> ref = new AtomicReference<>();
runSwing(() -> ref.set(tree.getRootNode()));
final AtomicReference<GTreeNode> ref = new AtomicReference<>();
runSwing(() -> ref.set(filtered ? tree.getViewRoot() : tree.getModelRoot()));
return ref.get();
}
@ -830,8 +834,8 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
return functionManager.getFunctionAt(address);
}
private void assertOutgoingNoNode(String name, int depth) {
List<NodeDepthInfo> nodes = getNodesByDepth(false);
private void assertOutgoingNoNode(String name, int depth, boolean filtered) {
List<NodeDepthInfo> nodes = getNodesByDepth(false, filtered);
for (NodeDepthInfo info : nodes) {
String nodeName = info.node.getName();
if (nodeName.indexOf(name) != -1) {
@ -861,8 +865,8 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
Assert.fail("Unable to find a node by name: " + name + " at depth: " + depth);
}
private void assertOutgoingMaxDepth(int depth) {
List<NodeDepthInfo> nodes = getNodesByDepth(false);
private void assertOutgoingMaxDepth(int depth, boolean filtered) {
List<NodeDepthInfo> nodes = getNodesByDepth(false, filtered);
NodeDepthInfo maxDepthNode = nodes.get(nodes.size() - 1);
assertEquals("Node max depth does not match: " + maxDepthNode, depth, maxDepthNode.depth);
@ -888,7 +892,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
}
private GTreeNode selectIncomingNode(String text) {
GTreeRootNode rootNode = getRootNode(incomingTree);
GTreeNode rootNode = getRootNode(incomingTree);
GTreeNode node = findNode(rootNode, text);
assertNotNull(node);
incomingTree.setSelectedNode(node);
@ -897,7 +901,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
}
private GTreeNode selectOutgoingNode(String text) {
GTreeRootNode rootNode = getRootNode(outgoingTree);
GTreeNode rootNode = getRootNode(outgoingTree);
GTreeNode node = findNode(rootNode, text);
assertNotNull(node);
outgoingTree.setSelectedNode(node);
@ -912,7 +916,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
}
if (node instanceof GTreeSlowLoadingNode) {
boolean loaded = ((GTreeSlowLoadingNode) node).isChildrenLoadedOrInProgress();
boolean loaded = ((GTreeSlowLoadingNode) node).isLoaded();
if (!loaded) {
return null;// children not loaded--don't load
}
@ -929,23 +933,15 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
}
private void assertDepth(GTreeNode node, int depth) {
int currentDepth = 0;
GTreeNode parent = node.getParent();
while (parent != null) {
currentDepth++;
parent = parent.getParent();
}
currentDepth--;// the root is considered depth 0, so we have to subtract one
int currentDepth = node.getTreePath().getPathCount() - 1;
int maxNodeDepth = getMaxNodeDepth(node, currentDepth);
assertEquals("Node depth is not correct " + node, depth, maxNodeDepth);
}
private int getMaxNodeDepth(GTreeNode node, int currentDepth) {
int maxDepth = currentDepth + 1;
int maxDepth = currentDepth;
if (node instanceof GTreeSlowLoadingNode) {
boolean loaded = ((GTreeSlowLoadingNode) node).isChildrenLoadedOrInProgress();
boolean loaded = ((GTreeSlowLoadingNode) node).isLoaded();
if (!loaded) {
return maxDepth;// children not loaded--don't load
}
@ -991,8 +987,8 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
Assert.fail("Unable to find a node by name: " + name + " at depth: " + depth);
}
private void assertIncomingNoNode(String name, int depth) {
List<NodeDepthInfo> nodes = getNodesByDepth(true);
private void assertIncomingNoNode(String name, int depth, boolean filtered) {
List<NodeDepthInfo> nodes = getNodesByDepth(true, filtered);
for (NodeDepthInfo info : nodes) {
String nodeName = info.node.getName();
if (nodeName.indexOf(name) != -1) {
@ -1002,16 +998,21 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
}
}
private void assertIncomingMaxDepth(int depth) {
List<NodeDepthInfo> nodes = getNodesByDepth(true);
private void assertIncomingMaxDepth(int depth, boolean filtered) {
List<NodeDepthInfo> nodes = getNodesByDepth(true, filtered);
NodeDepthInfo maxDepthNode = nodes.get(nodes.size() - 1);
assertEquals("Node max depth does not match: " + maxDepthNode, depth, maxDepthNode.depth);
}
private List<NodeDepthInfo> getNodesByDepth(boolean incoming) {
return getNodesByDepth(incoming, false);
}
private List<NodeDepthInfo> getNodesByDepth(boolean incoming, boolean filtered) {
List<NodeDepthInfo> list = new ArrayList<>();
GTreeRootNode root = incoming ? getRootNode(incomingTree) : getRootNode(outgoingTree);
GTreeNode root =
incoming ? getRootNode(incomingTree, filtered) : getRootNode(outgoingTree, filtered);
accumulateNodeDepths(list, root, 0);
Collections.sort(list);
return list;
@ -1025,7 +1026,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
}
if (node instanceof GTreeSlowLoadingNode) {
boolean loaded = ((GTreeSlowLoadingNode) node).isChildrenLoadedOrInProgress();
boolean loaded = ((GTreeSlowLoadingNode) node).isLoaded();
if (!loaded) {
return;// children not loaded--don't load
}

View file

@ -139,7 +139,7 @@ public class StructureEditorArchiveTest extends AbstractStructureEditorTest {
// openArchive = getDockingAction(plugin, "Open Data Type Archive");
closeArchive = getAction(plugin, "Close Archive");
waitForTree(dtTree);
GTreeNode rootNode = dtTree.getRootNode();
GTreeNode rootNode = dtTree.getModelRoot();
GTreeNode newNode = rootNode.getChild("New Archive");
selectNode(newNode);
}
@ -189,7 +189,7 @@ public class StructureEditorArchiveTest extends AbstractStructureEditorTest {
assertTrue(comp0.getDataType().isEquivalent(DataType.DEFAULT));
assertTrue(comp1.getDataType().isEquivalent(new WordDataType()));
GTreeNode rootNode = dtTree.getRootNode();
GTreeNode rootNode = dtTree.getModelRoot();
GTreeNode child = rootNode.getChild("New Archive");
selectNode(child);

View file

@ -89,7 +89,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT
conflictHandlerModesAction);
tree = provider.getGTree();
waitForTree();
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
archiveRootNode = (ArchiveRootNode) tree.getViewRoot();
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
assertNotNull("Did not successfully wait for the program node to load", programNode);

View file

@ -15,8 +15,7 @@
*/
package ghidra.app.plugin.core.datamgr;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.*;
import java.io.*;
@ -180,7 +179,7 @@ public abstract class AbstractCreateArchiveTest extends AbstractGhidraHeadedInte
tree = provider.getGTree();
treeModelModListener = new TreeModelModCounter();
tree.addGTModelListener(treeModelModListener);
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
archiveRootNode = (ArchiveRootNode) tree.getViewRoot();
tool.showComponentProvider(provider, true);
}

View file

@ -234,7 +234,7 @@ public class ArchiveRemappedHeadedTest extends AbstractGhidraHeadedIntegrationTe
assertNotNull(archiveDtm);
assertEquals("windows_vs12_32", archiveDtm.getName());
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getRootNode();
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
ArchiveNode archiveNode = (ArchiveNode) archiveRootNode.getChild("windows_vs12_32");
assertNotNull(archiveNode);
ArchiveNode programNode = (ArchiveNode) archiveRootNode.getChild(program.getName());

View file

@ -79,7 +79,7 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
provider = plugin.getProvider();
tree = provider.getGTree();
waitForTree();
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
assertNotNull("Did not successfully wait for the program node to load", programNode);

View file

@ -89,7 +89,7 @@ public class CreateLabelsFromEnumsTest extends AbstractGhidraHeadedIntegrationTe
provider = plugin.getProvider();
tree = provider.getGTree();
waitForTree();
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
programNode = (ArchiveNode) archiveRootNode.getChild(testName.getMethodName());
assertNotNull("Did not successfully wait for the program node to load", programNode);

View file

@ -90,7 +90,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
conflictHandlerModesAction);
tree = provider.getGTree();
waitForTree();
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
assertNotNull("Did not successfully wait for the program node to load", programNode);
@ -144,7 +144,8 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
private ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> findConflictHandlerActionState(
DataTypeConflictHandler.ConflictResolutionPolicy conflictMode) {
for (ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> actionState : conflictHandlerModesAction.getAllActionStates()) {
for (ActionState<DataTypeConflictHandler.ConflictResolutionPolicy> actionState : conflictHandlerModesAction
.getAllActionStates()) {
if (actionState.getUserData() == conflictMode) {
return actionState;
}

View file

@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.datamgr;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.awt.Container;
@ -110,7 +110,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
tree = provider.getGTree();
jTree = (JTree) invokeInstanceMethod("getJTree", tree);
waitForTree();
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getRootNode();
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_FILENAME);
assertNotNull("Did not successfully wait for the program node to load", programNode);
@ -187,7 +187,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
runSwing(() -> invokeInstanceMethod("openArchives", managerHandler,
new Class[] { String[].class }, new Object[] { invalidNames }));
GTreeNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
GTreeNode invalidChild = rootNode.getChild("BADARCHIVENAME");
assertNull("Tree did not close invalid archive.", invalidChild);
}
@ -523,7 +523,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
DataTypeNode myStructNode = (DataTypeNode) cat2Node.getChild("MyStruct");
GTreeNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
selectNode(myStructNode);
@ -550,7 +550,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
GTreeNode cat2Node = cat1Node.getChild("Category2");
expandNode(cat2Node);
GTreeNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
selectNode(cat2Node);
@ -575,14 +575,14 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
ProgramManager pm = tool.getService(ProgramManager.class);
pm.closeProgram();
});
GTreeNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
assertEquals(1, rootNode.getChildCount());
}
@Test
public void testExpandAll() throws Exception {
GTreeNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
selectNode(rootNode);
DockingActionIf expandAction = getAction(plugin, "Expand All");
assertTrue(expandAction.isEnabledForContext(treeContext));
@ -608,7 +608,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
@Test
public void testCollapseAll() throws Exception {
GTreeNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
selectNode(rootNode);
DockingActionIf collapseAction = getAction(plugin, "Collapse All");
assertTrue(collapseAction.isEnabledForContext(treeContext));
@ -645,7 +645,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
@Test
public void testRefreshBuiltins() throws Exception {
GTreeNode treeRoot = tree.getRootNode();
GTreeNode treeRoot = tree.getModelRoot();
GTreeNode builtInNode = treeRoot.getChild("BuiltInTypes");
assertNull("Test setup Error: ghidra.app.test.TestDataType was not removed!",
@ -850,7 +850,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
}
private ArchiveNode getBuiltInNode() {
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getRootNode();
ArchiveRootNode archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
ArchiveNode builtinNode = (ArchiveNode) archiveRootNode.getChild(BUILTIN_NAME);
assertNotNull(builtinNode);
return builtinNode;
@ -936,7 +936,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
}
private void assertSingleFilterMatch(String[] path) {
GTreeNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getViewRoot();
GTreeNode node = rootNode;
for (int i = 0; i < path.length; i++) {
@ -962,7 +962,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
}
private void assertEmptyTree() {
final GTreeNode rootNode = tree.getRootNode();
final GTreeNode rootNode = tree.getViewRoot();
final Integer[] box = new Integer[1];
runSwing(() -> box[0] = rootNode.getChildCount());
assertEquals("Root node is not empty as expected", 0, (int) box[0]);
@ -1061,7 +1061,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
}
private void checkNodesCollapsed(GTreeNode parent) {
if (parent != tree.getRootNode()) {
if (parent != tree.getModelRoot()) {
assertTrue(!tree.isExpanded(parent.getTreePath()));
}

View file

@ -103,7 +103,7 @@ public class DataTypeTestUtils {
waitForTree(plugin);
GTree tree = plugin.getProvider().getGTree();
GTreeNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
return (ArchiveNode) rootNode.getChild(trimFullArchiveName(archiveName));
}
@ -138,7 +138,7 @@ public class DataTypeTestUtils {
dataTypeManagerHandler.openArchive(file, checkout, isUserAction);
archiveTree = plugin.getProvider().getGTree();
GTreeNode rootNode = archiveTree.getRootNode();
GTreeNode rootNode = archiveTree.getViewRoot();
waitForTree(plugin);
return (ArchiveNode) rootNode.getChild(trimFullArchiveName(archiveName));
}
@ -196,7 +196,7 @@ public class DataTypeTestUtils {
String archiveNodeName = trimFullArchiveName(archiveName);
GTree tree = plugin.getProvider().getGTree();
GTreeNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
ArchiveNode archiveNode = (ArchiveNode) rootNode.getChild(archiveNodeName);
if (archiveNode == null) {
throw new IllegalArgumentException(

View file

@ -69,7 +69,7 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest {
tree = provider.getGTree();
waitForTree();
archiveRootNode = (ArchiveRootNode) tree.getRootNode();
archiveRootNode = (ArchiveRootNode) tree.getModelRoot();
builtInNode = (ArchiveNode) archiveRootNode.getChild("BuiltInTypes");
programNode = (ArchiveNode) archiveRootNode.getChild(PROGRAM_NAME);
@ -349,7 +349,7 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest {
GTree gtree = (GTree) getInstanceField("tree", d);
waitForTree(gtree);
ArchiveRootNode root = (ArchiveRootNode) gtree.getRootNode();
ArchiveRootNode root = (ArchiveRootNode) gtree.getModelRoot();
ArchiveNode programNode1 = (ArchiveNode) root.getChild(PROGRAM_NAME);
assertNotNull("could not find " + PROGRAM_NAME + " in " + root, programNode1);

View file

@ -40,7 +40,8 @@ import docking.widgets.filter.FilterTextField;
import docking.widgets.pathmanager.PathManager;
import docking.widgets.table.GDynamicColumnTableModel;
import docking.widgets.table.RowObjectTableModel;
import docking.widgets.tree.*;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import generic.jar.ResourceFile;
import generic.test.TestUtils;
import generic.util.Path;
@ -194,7 +195,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
waitForTree(categoryTree);
JTree jTree = (JTree) invokeInstanceMethod("getJTree", categoryTree);
assertNotNull(jTree);
GTreeNode child = categoryTree.getRootNode().getChild(category);
GTreeNode child = categoryTree.getModelRoot().getChild(category);
categoryTree.setSelectedNode(child);
waitForTree(categoryTree);
TreePath path = child.getTreePath();
@ -716,7 +717,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
GTree tree = (GTree) getInstanceField("scriptCategoryTree", provider);
waitForTree(tree);
GTreeNode parentNode = tree.getRootNode();
GTreeNode parentNode = tree.getModelRoot();
String[] parts = newCategory.split("\\.");
for (String category : parts) {
@ -726,7 +727,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
}
protected GTreeNode findChildByName(GTreeNode node, String name) {
List<GTreeNode> children = node.getAllChildren();
List<GTreeNode> children = node.getChildren();
for (GTreeNode child : children) {
if (child.getName().equals(name)) {
return child;
@ -739,8 +740,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
GTree tree = (GTree) getInstanceField("scriptCategoryTree", provider);
waitForTree(tree);
GTreeRootNode rootNode = tree.getRootNode();
List<GTreeNode> children = rootNode.getAllChildren();
GTreeNode rootNode = tree.getModelRoot();
List<GTreeNode> children = rootNode.getChildren();
for (GTreeNode node : children) {
if (node.getName().equals(oldCategory)) {
Assert.fail("Category in tree when expected not to be: " + oldCategory);

View file

@ -297,7 +297,7 @@ public class SymbolTreePlugin3Test extends AbstractGhidraHeadedIntegrationTest {
}
}
List<GTreeNode> children = nsParentNode.getAllChildren();
List<GTreeNode> children = nsParentNode.getChildren();
//@formatter:off
List<String> symbolNames =
@ -342,7 +342,7 @@ public class SymbolTreePlugin3Test extends AbstractGhidraHeadedIntegrationTest {
util.waitForTree();
GTreeNode cnode = rootNode.getChild(4);
List<GTreeNode> children = cnode.getAllChildren();
List<GTreeNode> children = cnode.getChildren();
//@formatter:off
List<String> symbolNames =

View file

@ -15,11 +15,10 @@
*/
package ghidra.app.plugin.core.symboltree;
import static generic.test.AbstractGTest.waitForCondition;
import static generic.test.AbstractGTest.*;
import static generic.test.AbstractGenericTest.*;
import static ghidra.test.AbstractGhidraHeadedIntegrationTest.getAction;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static ghidra.test.AbstractGhidraHeadedIntegrationTest.*;
import static org.junit.Assert.*;
import java.awt.Container;
import java.awt.datatransfer.Clipboard;
@ -38,7 +37,8 @@ import docking.ActionContext;
import docking.action.DockingActionIf;
import docking.action.ToggleDockingAction;
import docking.test.AbstractDockingTest;
import docking.widgets.tree.*;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.support.GTreeNodeTransferable;
import ghidra.app.plugin.core.symboltree.nodes.*;
import ghidra.app.services.ProgramManager;
@ -58,7 +58,7 @@ class SymbolTreeTestUtils {
private SymbolTreePlugin plugin;
private DockingActionIf symTreeAction;
private SymbolGTree tree;
private GTreeRootNode rootGTreeNode;
private GTreeNode rootGTreeNode;
private SymbolTreeProvider provider;
private DockingActionIf renameAction;
private DockingActionIf cutAction;
@ -224,7 +224,7 @@ class SymbolTreeTestUtils {
}
void collapseTree() {
GTreeRootNode root = tree.getRootNode();
GTreeNode root = tree.getViewRoot();
List<GTreeNode> topLevelNodes = root.getChildren();
topLevelNodes.forEach(n -> tree.collapseAll(n));
waitForTree();
@ -334,7 +334,7 @@ class SymbolTreeTestUtils {
provider = plugin.getProvider();
tree = findComponent(provider.getComponent(), SymbolGTree.class);
waitForTree();
rootGTreeNode = tree.getRootNode();
rootGTreeNode = tree.getViewRoot();
renameAction = getAction(plugin, "Rename Symbol");
assertNotNull(renameAction);
cutAction = getAction(plugin, "Cut SymbolTree Node");
@ -406,7 +406,7 @@ class SymbolTreeTestUtils {
}
public static GTreeNode getNode(GTree tree, String... path) {
GTreeRootNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
String rootName = path[0];
if (!rootNode.getName().equals(rootName)) {
throw new RuntimeException(

View file

@ -188,7 +188,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
assertTrue("Did not find the data type chooser tree",
(provider instanceof DataTypeChooserDialog));
GTree gTree = (GTree) getInstanceField("tree", provider);
GTreeNode rootNode = gTree.getRootNode();
GTreeNode rootNode = gTree.getModelRoot();
waitForTree(gTree);
final GTreeNode builtInNode = rootNode.getChild("BuiltInTypes");
final DataTypeNode doubleNode = (DataTypeNode) builtInNode.getChild("double");
@ -743,7 +743,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
private void pickSingleDataType(DataTypeChooserDialog chooserDialog) {
GTree gTree = (GTree) getInstanceField("tree", chooserDialog);
GTreeNode rootNode = gTree.getRootNode();
GTreeNode rootNode = gTree.getModelRoot();
waitForTree(gTree);
List<GTreeNode> children = rootNode.getChildren();
assertEquals(1, children.size());// one archive
@ -764,7 +764,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
private void pickFromMultipleDataTypes(DataTypeChooserDialog chooserDialog) {
GTree gTree = (GTree) getInstanceField("tree", chooserDialog);
GTreeNode rootNode = gTree.getRootNode();
GTreeNode rootNode = gTree.getModelRoot();
waitForTree(gTree);
List<GTreeNode> children = rootNode.getChildren();
assertEquals(2, children.size());// two archives

View file

@ -15,8 +15,7 @@
*/
package ghidra.framework.main;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.*;
import java.util.concurrent.atomic.AtomicBoolean;
@ -25,7 +24,8 @@ import javax.swing.tree.TreeModel;
import org.junit.*;
import docking.widgets.tree.*;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import ghidra.framework.main.datatree.ProjectDataTreePanel;
import ghidra.framework.model.*;
import ghidra.program.database.ProgramBuilder;
@ -62,8 +62,8 @@ public class DataTreeDialogTest extends AbstractGhidraHeadedIntegrationTest {
Program p = builder.getProgram();
rootFolder.createFile("notepad", p, TaskMonitorAdapter.DUMMY_MONITOR);
rootFolder.createFile("XNotepad", p, TaskMonitorAdapter.DUMMY_MONITOR);
for (int i = 0; i < names.length; i++) {
rootFolder.createFile(names[i], p, TaskMonitorAdapter.DUMMY_MONITOR);
for (String name : names) {
rootFolder.createFile(name, p, TaskMonitorAdapter.DUMMY_MONITOR);
}
builder.dispose();
@ -249,7 +249,7 @@ public class DataTreeDialogTest extends AbstractGhidraHeadedIntegrationTest {
final AtomicBoolean result = new AtomicBoolean(false);
final GTree gTree = getGTree();
runSwing(() -> {
GTreeRootNode node = gTree.getRootNode();
GTreeNode node = gTree.getViewRoot();
if (node != null) {
gTree.expandPath(node);
gTree.setSelectedNode(node);
@ -307,7 +307,7 @@ public class DataTreeDialogTest extends AbstractGhidraHeadedIntegrationTest {
final AtomicBoolean result = new AtomicBoolean(false);
final GTree gTree = getGTree();
runSwing(() -> {
GTreeRootNode root = gTree.getRootNode();
GTreeNode root = gTree.getModelRoot();
GTreeNode node = root.getChild(name);
if (node != null) {
gTree.expandPath(node);

View file

@ -34,7 +34,6 @@ import docking.action.ToggleDockingAction;
import docking.test.AbstractDockingTest;
import docking.widgets.OptionDialog;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeRootNode;
import docking.widgets.tree.support.*;
import ghidra.framework.data.DomainObjectAdapter;
import ghidra.framework.main.FrontEndTool;
@ -58,7 +57,7 @@ public class FrontEndPluginActionsTest extends AbstractGhidraHeadedIntegrationTe
private TestEnv env;
private DataTree tree;
private DomainFolder rootFolder;
private GTreeRootNode rootNode;
private GTreeNode rootNode;
@Before
public void setUp() throws Exception {
@ -82,7 +81,7 @@ public class FrontEndPluginActionsTest extends AbstractGhidraHeadedIntegrationTe
rootFolder.createFile("tms", p, TaskMonitor.DUMMY);
p.release(this);
rootNode = tree.getRootNode();
rootNode = tree.getModelRoot();
waitForSwing();
}
@ -717,7 +716,7 @@ public class FrontEndPluginActionsTest extends AbstractGhidraHeadedIntegrationTe
performAction(selectAction, getDomainFileActionContext(), true);
waitForTree();
BreadthFirstIterator it = new BreadthFirstIterator(tree, rootNode);
BreadthFirstIterator it = new BreadthFirstIterator(rootNode);
while (it.hasNext()) {
GTreeNode node = it.next();
assertTrue(tree.isPathSelected(node.getTreePath()));

View file

@ -37,6 +37,7 @@ import docking.options.editor.OptionsPanel;
import docking.tool.util.DockingToolConstants;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import generic.io.NullWriter;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.data.DataPlugin;
@ -367,7 +368,7 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
// this is an instance of OptionsNode
GTree tree = (GTree) getInstanceField("gTree", optionsPanel);
Object keyBindingsNode = getGTreeNode(tree.getRootNode(), "Key Bindings");
Object keyBindingsNode = getGTreeNode(tree.getModelRoot(), "Key Bindings");
selectNode(tree, keyBindingsNode);
debug("ee");
@ -407,17 +408,18 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
SwingUtilities.invokeAndWait(() -> tree.setSelectionPath(path));
}
private Object getGTreeNode(Object parent, String nodeName) throws Exception {
List<?> children = (List<?>) getInstanceField("allChildrenList", parent);
if (children == null) {
private GTreeNode getGTreeNode(GTreeNode parent, String nodeName) throws Exception {
if (!parent.isLoaded()) {
return null;
}
for (Object rootChild : children) {
List<GTreeNode> children = parent.getChildren();
for (GTreeNode rootChild : children) {
String name = (String) invokeInstanceMethod("getName", rootChild);
if (nodeName.equals(name)) {
return rootChild;
}
Object foundNode = getGTreeNode(rootChild, nodeName);
GTreeNode foundNode = getGTreeNode(rootChild, nodeName);
if (foundNode != null) {
return foundNode;
}

View file

@ -40,7 +40,8 @@ import docking.tool.ToolConstants;
import docking.widgets.MultiLineLabel;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.table.RowObjectFilterModel;
import docking.widgets.tree.*;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import generic.test.TestUtils;
import ghidra.GhidraOptions;
import ghidra.app.plugin.core.console.ConsolePlugin;
@ -836,8 +837,8 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
}
private void waitForThreadedModel() throws InterruptedException {
GTreeRootNode root = (GTreeRootNode) treeModel.getRoot();
GTree gTree = root.getGTree();
GTreeNode root = (GTreeNode) treeModel.getRoot();
GTree gTree = root.getTree();
while (gTree.isBusy()) {
Thread.sleep(50);
}

View file

@ -15,12 +15,11 @@
*/
package ghidra.bitpatterns.info;
import java.util.Collections;
import java.util.List;
import java.util.*;
import javax.swing.Icon;
import docking.widgets.tree.AbstractGTreeNode;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeNode;
import resources.ResourceManager;
@ -29,7 +28,7 @@ import resources.ResourceManager;
* An object of this class represents a node in a tree of instruction sequences.
*
*/
public class FunctionBitPatternsGTreeNode extends AbstractGTreeNode {
public class FunctionBitPatternsGTreeNode extends GTreeNode {
private static final Icon DISABLED_ICON = ResourceManager.loadImage("images/ledred.png");
@ -65,7 +64,7 @@ public class FunctionBitPatternsGTreeNode extends AbstractGTreeNode {
for (GTreeNode node : getChildren()) {
((FunctionBitPatternsGTreeNode) node).sortAndSetFields();
}
List<GTreeNode> children = getChildren();
List<GTreeNode> children = new ArrayList<>(getChildren());
Collections.sort(children);
setChildren(children);
//now set isLeaf

View file

@ -15,12 +15,11 @@
*/
package ghidra.bitpatterns.info;
import java.util.Collections;
import java.util.List;
import java.util.*;
import javax.swing.Icon;
import docking.widgets.tree.AbstractGTreeRootNode;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeNode;
/**
@ -30,7 +29,7 @@ import docking.widgets.tree.GTreeNode;
*
*/
public class FunctionBitPatternsGTreeRootNode extends AbstractGTreeRootNode {
public class FunctionBitPatternsGTreeRootNode extends GTreeNode {
@Override
public String getName() {
@ -54,7 +53,7 @@ public class FunctionBitPatternsGTreeRootNode extends AbstractGTreeRootNode {
for (GTreeNode node : getChildren()) {
((FunctionBitPatternsGTreeNode) node).sortAndSetFields();
}
List<GTreeNode> children = getChildren();
List<GTreeNode> children = new ArrayList<>(getChildren());
Collections.sort(children);
setChildren(children);
}

View file

@ -115,7 +115,8 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
if (outputDirectory == null) {
return;
}
fsbContext.getTree().runTask(
fsbContext.getTree()
.runTask(
monitor -> doExportToEclipse(fsrl, outputDirectory, monitor));
}
}
@ -278,7 +279,7 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
writer.open();
try {
// gTree.expandAll( node );
writeFile(writer, node.getAllChildren());
writeFile(writer, node.getChildren());
}
finally {
writer.close();
@ -303,7 +304,7 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
writer.write(childFSRL.getName());
}
else {
writeFile(writer, child.getAllChildren());
writeFile(writer, child.getChildren());
}
}
}

View file

@ -35,7 +35,7 @@ public class TreeTestUtils {
if (text.equals(node.getName())) {
return node.getTreePath();
}
List<GTreeNode> allChildren = node.getAllChildren();
List<GTreeNode> allChildren = node.getChildren();
for (GTreeNode childNode : allChildren) {
TreePath treePath = findPathToText(tree, childNode, text);
if (treePath != null) {

View file

@ -35,6 +35,7 @@ import ghidra.util.*;
import ghidra.util.exception.*;
import ghidra.util.html.HTMLElement;
import resources.ResourceManager;
import util.CollectionUtils;
public class ErrLogExpandableDialog extends DialogComponentProvider {
public static ImageIcon IMG_REPORT = ResourceManager.loadImage("images/report.png");
@ -246,10 +247,9 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
}
};
for (TreePath path : root.allPaths()) {
Object last = path.getLastPathComponent();
if (last instanceof ReportExceptionNode) {
excTree.expandTree((GTreeNode) last);
for (GTreeNode node : CollectionUtils.asIterable(root.iterator(true))) {
if (node instanceof ReportExceptionNode) {
excTree.expandTree(node);
}
}
@ -406,8 +406,8 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
indent += 1;
}
}
boolean doAll = (included == null || !containsAny(included, cur.getAllChildren()));
for (GTreeNode node : cur.getAllChildren()) {
boolean doAll = (included == null || !containsAny(included, cur.getChildren()));
for (GTreeNode node : cur.getChildren()) {
if (node instanceof NodeWithText && (doAll || included.contains(node))) {
NodeWithText nwt = (NodeWithText) node;
@ -427,7 +427,7 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
}
}
static class ReportRootNode extends AbstractGTreeRootNode implements NodeWithText {
static class ReportRootNode extends GTreeNode implements NodeWithText {
protected Collection<? extends Throwable> report;
protected String title;
protected boolean loaded = false;
@ -435,6 +435,9 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
public ReportRootNode(String title, Collection<? extends Throwable> report) {
this.title = title;
this.report = report;
for (Throwable exc : report) {
addNode(new ReportExceptionNode(exc));
}
}
@Override
@ -442,16 +445,6 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
return title;
}
@Override
protected void loadChildren() {
if (!loaded) {
loaded = true;
for (Throwable exc : report) {
addNode(new ReportExceptionNode(exc));
}
}
}
@Override
public Icon getIcon(boolean expanded) {
return IMG_REPORT;
@ -483,7 +476,7 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
}
}
static class ReportExceptionNode extends AbstractGTreeNode implements NodeWithText {
static class ReportExceptionNode extends GTreeLazyNode implements NodeWithText {
protected Throwable exc;
protected boolean loaded = false;
@ -497,22 +490,21 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
}
@Override
protected void loadChildren() {
if (!loaded) {
loaded = true;
addNode(new ReportStackTraceNode(exc));
protected List<GTreeNode> generateChildren() {
List<GTreeNode> list = new ArrayList<GTreeNode>();
list.add(new ReportStackTraceNode(exc));
Throwable c = exc.getCause();
if (c != null) {
if (c instanceof MultipleCauses) {
for (Throwable t : ((MultipleCauses) c).getCauses()) {
addNode(new ReportExceptionNode(t));
list.add(new ReportExceptionNode(t));
}
}
else {
addNode(new ReportCauseNode(c));
}
list.add(new ReportCauseNode(c));
}
}
return list;
}
@Override
@ -553,7 +545,7 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
}
}
static class ReportStackTraceNode extends AbstractGTreeNode implements NodeWithText {
static class ReportStackTraceNode extends GTreeLazyNode implements NodeWithText {
protected Throwable exc;
protected boolean loaded = false;
@ -567,13 +559,12 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
}
@Override
public void loadChildren() {
if (!loaded) {
loaded = true;
protected List<GTreeNode> generateChildren() {
List<GTreeNode> list = new ArrayList<>();
for (StackTraceElement te : exc.getStackTrace()) {
addNode(new ReportStackFrameNode(te));
}
list.add(new ReportStackFrameNode(te));
}
return list;
}
@Override
@ -628,7 +619,7 @@ public class ErrLogExpandableDialog extends DialogComponentProvider {
}
}
static class ReportStackFrameNode extends AbstractGTreeNode implements NodeWithText {
static class ReportStackFrameNode extends GTreeNode implements NodeWithText {
private StackTraceElement te;
public ReportStackFrameNode(StackTraceElement te) {

View file

@ -32,7 +32,8 @@ import docking.help.HelpService;
import docking.widgets.MultiLineLabel;
import docking.widgets.OptionDialog;
import docking.widgets.label.GIconLabel;
import docking.widgets.tree.*;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.internal.DefaultGTreeDataTransformer;
import ghidra.framework.options.*;
import ghidra.util.*;
@ -231,7 +232,7 @@ public class OptionsPanel extends JPanel {
public void displayCategory(String category, String filterText) {
String escapedDelimiter = Pattern.quote(Options.DELIMITER_STRING);
GTreeRootNode root = gTree.getRootNode();
GTreeNode root = gTree.getModelRoot();
category = root.getName() + Options.DELIMITER_STRING + category;
String[] categories = category.split(escapedDelimiter);
gTree.setFilterText(filterText);

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,17 +15,15 @@
*/
package docking.options.editor;
import ghidra.framework.options.Options;
import java.util.*;
import javax.swing.JComponent;
import docking.widgets.tree.*;
import docking.widgets.tree.GTreeNode;
import ghidra.framework.options.Options;
class OptionsRootTreeNode extends OptionsTreeNode implements GTreeRootNode {
class OptionsRootTreeNode extends OptionsTreeNode {
private Options[] options;
private GTree tree;
OptionsRootTreeNode(String name, Options[] options) {
super(name, null);
@ -55,14 +52,4 @@ class OptionsRootTreeNode extends OptionsTreeNode implements GTreeRootNode {
protected JComponent getEditorComponent() {
return null;
}
@Override
public GTree getGTree() {
return tree;
}
@Override
public void setGTree(GTree tree) {
this.tree = tree;
}
}

View file

@ -83,7 +83,7 @@ class OptionsTreeNode extends GTreeLazyNode {
@Override
public boolean isLeaf() {
return getAllChildCount() == 0;
return getChildCount() == 0;
}
@Override

View file

@ -2071,7 +2071,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
}
public static GTreeNode getNode(GTree tree, String... path) {
GTreeRootNode rootNode = tree.getRootNode();
GTreeNode rootNode = tree.getModelRoot();
String rootName = path[0];
if (!rootNode.getName().equals(rootName)) {
throw new RuntimeException(

View file

@ -1,484 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.widgets.tree;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.tree.TreePath;
import docking.widgets.tree.support.GTreeFilter;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Base class for GTNodes. To create a simple GTNode 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 you
* must override the <tt>equals()</tt> method that can find the same logical nodes when
* the instances in memory have changed; typically this is done by overriding <tt>equals()</tt>
* to compare by node name.
* <p><br>
* <p>
* The <tt>GTreeNode</tt> has already overridden {@link #hashCode()} so that the node name is
* used to generate the correct value. If you override the {@link #equals(Object)} method,
* <b>and you do not compare only by {@link #getName()}, then you must also override the
* {@link #hashCode()} method to generate a value based upon the same algorithm used by the
* new <tt>equals()</tt> method.</b>
* <p><br>
* <p>
* As a rule of thumb, unless you want to allow multiple nodes under one parent with the
* same name, then it is a swell idea to override the <tt>equals()</tt> method to compare on
* {@link #getName()}, as outlined above.
* </li>
* </ul>
*/
public abstract class AbstractGTreeNode extends CoreGTreeNode {
private AtomicBoolean isFiltering = new AtomicBoolean();
/**
* This will be called when it is time to load children. Some subclasses may not use this
* method, but may instead have children externally added.
*/
protected void loadChildren() {
// I don't use this...subclasses might
}
@Override
public void addNode(GTreeNode node) {
addNode(-1, node);
}
@Override
public void addNode(int index, GTreeNode node) {
doAddNode(index, node);
GTreeFilter filter = getFilter();
if (filter != null) {
GTree tree = getTree();
tree.scheduleFilterTask(this);
}
}
@Override
public int compareTo(GTreeNode node) {
return getName().compareToIgnoreCase(node.getName());
}
@Override
public List<GTreeNode> getAllChildren() {
// TODO this seemed like an unnecessary and inconsistent optimization, as
// the loadChildren() call will not perform excess work when called repeatedly, even
// if the children are empty (i.e., isLeaf()); remove after a bit
// if (isLeaf()) {
// return Collections.emptyList();
// }
loadChildren();
return doGetAllChildren();
}
@Override
public List<GTreeNode> getChildren() {
// TODO this seemed like an unnecessary and inconsistent optimization, as
// the loadChildren() call will not perform excess work when called repeatedly, even
// if the children are empty (i.e., isLeaf()); remove after a bit
// if (isLeaf()) {
// return Collections.emptyList();
// }
loadChildren();
return doGetActiveChildren();
}
@Override
public int getChildCount() {
loadChildren();
return doGetChildCount();
}
@Override
public int getAllChildCount() {
loadChildren();
return doGetAllChildCount();
}
@Override
public GTreeNode getChild(String name) {
List<GTreeNode> children = getChildren();
for (GTreeNode child : children) {
if (child.getName().equals(name)) {
return child;
}
}
return null;
}
@Override
public GTreeNode getChild(int index) {
loadChildren();
return doGetChild(index);
}
@Override
public int getNodeCount() {
List<GTreeNode> children = getChildren();
int count = 1;
for (GTreeNode child : children) {
count += child.getNodeCount();
}
return count;
}
@Override
public int getLeafCount() {
if (isLeaf()) {
return 1;
}
if (!isChildrenLoadedOrInProgress()) {
return 0;
}
List<GTreeNode> children = getChildren();
int count = 0;
for (GTreeNode child : children) {
count += child.getLeafCount();
}
return count;
}
@Override
public int getIndexInParent() {
GTreeNode myParent = getParent();
if (myParent != null) {
return myParent.getIndexOfChild(this);
}
return -1;
}
@Override
public int getIndexOfChild(GTreeNode node) {
loadChildren();
return doGetIndexOfChild(node);
}
@Override
public TreePath getTreePath() {
return new TreePath(getPathToRoot(this, 0));
}
@Override
public void removeAll() {
if (!isChildrenLoadedOrInProgress()) {
return;
}
List<GTreeNode> allChildren = getAllChildren();
doSetChildren(null, true);
for (GTreeNode gTreeNode : allChildren) {
gTreeNode.dispose();
}
}
@Override
public void setChildren(List<GTreeNode> childList) {
doSetChildren(childList, true);
if (isFiltering.get()) {
return;
}
GTreeFilter filter = getFilter();
if (filter != null) {
GTree tree = getTree();
tree.scheduleFilterTask(this);
}
}
@Override
public boolean isAncestor(GTreeNode node) {
GTreeNode nodeParent = node.getParent();
while (nodeParent != null) {
if (nodeParent.equals(this)) {
return true;
}
nodeParent = nodeParent.getParent();
}
return false;
}
public static class AllPathsIterator implements Iterator<TreePath> {
private TreePath ancestry;
private TreePath nextPath;
private Iterator<GTreeNode> childIt = null;
private Iterator<TreePath> childPathIt = null;
public AllPathsIterator(TreePath path) {
ancestry = path;
nextPath = path;
}
@Override
public boolean hasNext() {
return nextPath != null;
}
@Override
public TreePath next() {
TreePath n = nextPath;
loadNext();
return n;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
protected void loadNext() {
if (childIt == null) {
Object obj = ancestry.getLastPathComponent();
assert obj instanceof GTreeNode;
childIt = ((GTreeNode) obj).iterator();
}
if (childPathIt == null || !childPathIt.hasNext()) {
if (childIt.hasNext()) {
childPathIt = new AllPathsIterator(ancestry.pathByAddingChild(childIt.next()));
}
else {
childPathIt = null;
}
}
if (childPathIt != null && childPathIt.hasNext()) {
nextPath = childPathIt.next();
}
else {
nextPath = null;
}
}
}
public Iterable<TreePath> allPaths() {
return new AllPathsIterable(new TreePath(this));
}
@Override
public Iterator<GTreeNode> iterator() {
return getChildren().iterator();
}
@Override
public void filter(GTreeFilter filter, TaskMonitor monitor, int min, int max)
throws CancelledException {
if (isFiltering.get()) {
stopCurrentFilterAndRestart();
return;
}
isFiltering.set(true);
try {
doFilter(filter, monitor, min, max);
}
finally {
isFiltering.set(false);
}
}
private void stopCurrentFilterAndRestart() {
GTree tree = getTree();
if (tree != null) {
// assume that filtering will be done later when we are made be part of a tree
tree.refilter();
}
}
private void doFilter(GTreeFilter filter, TaskMonitor monitor, int min, int max)
throws CancelledException {
if (isLeaf()) {
return;
}
List<GTreeNode> allChildren = getAllChildren();
if (allChildren.size() == 0) {
return;
}
List<GTreeNode> newChildren = allChildren;
try {
setInProgress();
List<GTreeNode> filteredChildren = new ArrayList<GTreeNode>();
monitor.setProgress(min);
int progressChunkSize = (max - min) / (allChildren.size());
int childMin = min;
for (GTreeNode child : allChildren) {
monitor.checkCanceled();
child.filter(filter, monitor, childMin, childMin + progressChunkSize);
if (filter.acceptsNode(child) || child.getChildCount() > 0) {
filteredChildren.add(child);
}
childMin += progressChunkSize;
}
newChildren = filteredChildren;
}
finally {
// if an exception occurs, then the default children will be restored
doSetActiveChildren(newChildren);
}
monitor.setProgress(max);
}
@Override
public void clearFilter() {
if (isLeaf()) {
return;
}
if (!isChildrenLoaded()) {
return;
}
List<GTreeNode> allChildren = getAllChildren();
if (allChildren.size() == 0) {
return;
}
setInProgress();
for (GTreeNode child : allChildren) {
child.clearFilter();
}
doResetActiveChildren();
}
@Override
public boolean isFilteredOut() {
if (getParent() == null) {
return false;
}
return getIndexInParent() < 0;
}
@Override
public void valueChanged(Object newValue) {
// Overridden in subclasses
}
@Override
public boolean isEditable() {
return false;
}
@Override
/**
* The hashCode() method has been overridden so that it will work in the hashtables inside of JTree.
* This assumes that if the .equals method is overridden, then the names will match which will
* make this hashCode implementation valid. If for some reason .equals is overriden such that
* two node may be equal even if their names don't match, then the hashCode method must also be
* overridden.
*
* @see <a href="#usage">GTreeNode Usage</a>
*/
public int hashCode() {
return getName().hashCode();
}
@Override
public GTreeRootNode getRoot() {
GTreeNode myParent = getParent();
if (myParent == null) {
if (this instanceof GTreeRootNode) {
return (GTreeRootNode) this;
}
throw new AssertException(
"Found a root node that is not an instance of GTreeRootNode--stop it!");
}
return myParent.getRoot();
}
@Override
public String toString() {
return getName();
}
@Override
public GTree getTree() {
GTreeNode myParent = getParent();
if (myParent != null) {
return myParent.getTree();
}
if (this instanceof GTreeRootNode) {
return ((GTreeRootNode) this).getGTree();
}
return null;
}
private GTreeNode[] getPathToRoot(GTreeNode node, int depth) {
GTreeNode[] returnNodes;
/* Check for null, in case someone passed in a null node, or
they passed in an element that isn't rooted at root. */
if (node == null) {
if (depth == 0) {
return null;
}
returnNodes = new GTreeNode[depth];
}
else {
depth++;
returnNodes = getPathToRoot(node.getParent(), depth);
returnNodes[returnNodes.length - depth] = node;
}
return returnNodes;
}
protected GTreeFilter getFilter() {
GTree tree = getTree();
if (tree != null) {
return tree.getFilter();
}
return null;
}
private static class AllPathsIterable implements Iterable<TreePath> {
private TreePath path;
public AllPathsIterable(TreePath path) {
this.path = path;
}
@Override
public Iterator<TreePath> iterator() {
return new AllPathsIterator(path);
}
}
}

View file

@ -15,385 +15,199 @@
*/
package docking.widgets.tree;
import ghidra.util.SystemUtilities;
import java.util.*;
import javax.swing.SwingUtilities;
import java.util.concurrent.CopyOnWriteArrayList;
import docking.widgets.tree.internal.InProgressGTreeNode;
import ghidra.util.Swing;
/**
* This class is not meant to be subclassed directly. Instead, you should extend
* {@link AbstractGTreeNode}.
* <p>
* This class is responsible for mutating/managing the children and parent of this node. These
* items are sensitive to threading issues, which this class is designed to handle.
* <p>
* The pattern used by this class is to create <tt>doXXX</tt> methods for the public mutator
* methods of the {@link GTreeNode} interface.
* This class exists to help prevent threading errors in {@link GTreeNode} and subclasses,
* by privately maintaining synchronous access to the parent and children of a node.
* <P>
* This implementation uses a {@link CopyOnWriteArrayList} to store its children. The theory is
* that this will allow direct thread-safe access to the children without having to worry about
* {@link ConcurrentModificationException}s. Also, the assumption is that accessing the children
* will occur much more frequently than modifying the children. This should only be a problem if
* a direct descendent of AbstractGTreeNode creates it children by calling
* addNode many times. But in that case, the tree should be using Lazy or
* SlowLoading nodes which always load into another list first and all the children will be set
* on a node in a single operation.
* <P>
* Subclasses that need access to the children
* can call the {@link #children()} method which will ensure that the children are
* loaded (not null). Since this class uses a {@link CopyOnWriteArrayList}, subclasses that call
* the {@link #children()} method can safely operate and iterate on the list they get back without
* having to worry about getting a {@link ConcurrentModificationException}.
* <P>
* This class uses synchronization to assure that the parent/children relationship is stable across
* threads. To avoid deadlocks, the sychronization strategy is that if you have the lock on
* a parent node, you can safely acquire the lock on any of its descendants, put never its
* ancestors. To facilitate this strategy, the {@link #getParent()} is not synchronized, but it
* is made volatile to assure the current value is always used.
*/
abstract class CoreGTreeNode implements GTreeNode {
private static InProgressGTreeNode IN_PROGRESS_NODE = new InProgressGTreeNode();
private static List<GTreeNode> IN_PROGRESS_CHILDREN =
Collections.unmodifiableList(Arrays.asList(new GTreeNode[] { IN_PROGRESS_NODE }));
abstract class CoreGTreeNode implements Cloneable {
// the parent is volatile to facilitate the synchronization strategy (see comments above)
private volatile GTreeNode parent;
private List<GTreeNode> children;
private GTreeNode parent;
private List<GTreeNode> allChildrenList = null;
private List<GTreeNode> activeChildrenList = null;
@Override
public synchronized GTreeNode getParent() {
/**
* Returns the parent of this node.
*
* Note: this method is deliberately not synchronized (See comments above)
* @return the parent of this node.
*/
public final GTreeNode getParent() {
return parent;
}
@Override
public void dispose() {
parent = null;
if (allChildrenList == null) {
return;
/**
* Sets the parent of this node. This method should only be used by a parent
* node when a new child is added to that parent node.
* @param parent the node that this node is being added to.
*/
synchronized final void setParent(GTreeNode parent) {
if (this.parent != null) {
throw new IllegalStateException(
"Attempted to assign a node to a parent more than once!");
}
for (GTreeNode node : allChildrenList) {
this.parent = parent;
}
// provides direct access to the children list
protected final List<GTreeNode> children() {
synchronized (this) {
if (isLoaded()) {
return children;
}
}
// The generateChildren must be called outside the synchronized scope because
// if it is slow it will lock out other threads. Keep in mind that if this is
// called outside the swing thread then this doesn't return
// until the work is completed (even for slow loading nodes - they only offload
// the children loading in another task if called on the swing thread)
List<GTreeNode> newChildren = generateChildren();
synchronized (this) {
// null implies cancelled
if (newChildren == null) {
return Collections.emptyList();
}
// This can be tricky. If we are in the swing thread and the generate children
// is deferred to a background thread and we are about to set an in-progress node,
// then it is possible that the background thread got here first and we are about
// to overwrite the actual children with an in-progress node. Check for that case.
if (isInProgress(newChildren) && children != null) {
return children;
}
doSetChildren(newChildren);
return children;
}
}
/**
* Subclasses implement this method to initially load the children.
* @return a list of the initial children for this node.
*/
protected abstract List<GTreeNode> generateChildren();
protected synchronized void doSetChildren(List<GTreeNode> childList) {
List<GTreeNode> oldChildren = children;
children = null;
if (oldChildren != null) {
for (GTreeNode node : oldChildren) {
node.setParent(null);
}
}
if (childList != null) {
for (GTreeNode node : childList) {
node.setParent((GTreeNode) this);
}
children = new CopyOnWriteArrayList<GTreeNode>(childList);
}
if (oldChildren != null) {
for (GTreeNode node : oldChildren) {
node.dispose();
}
allChildrenList = null;
activeChildrenList = null;
}
@Override
public synchronized boolean isInProgress() {
return activeChildrenList == IN_PROGRESS_CHILDREN;
}
protected void setInProgress() {
doSetActiveChildren(IN_PROGRESS_CHILDREN);
}
public synchronized boolean isChildrenLoadedOrInProgress() {
return activeChildrenList != null;
}
protected synchronized boolean isChildrenLoaded() {
return allChildrenList != null;
}
protected synchronized int doGetChildCount() {
if (activeChildrenList != null) {
return activeChildrenList.size();
}
return 0;
}
protected synchronized int doGetAllChildCount() {
if (allChildrenList != null) {
return allChildrenList.size();
}
return 0;
}
protected synchronized List<GTreeNode> doGetAllChildren() {
if (allChildrenList == null) {
return Collections.emptyList();
}
return new ArrayList<GTreeNode>(allChildrenList);
}
protected synchronized List<GTreeNode> doGetActiveChildren() {
if (activeChildrenList == null) {
return Collections.emptyList();
}
return new ArrayList<GTreeNode>(activeChildrenList);
}
protected synchronized GTreeNode doGetChild(int index) {
if (activeChildrenList == null) {
return null;
}
if (index < 0 || index >= activeChildrenList.size()) {
return null;
}
return activeChildrenList.get(index);
}
protected synchronized int doGetIndexOfChild(GTreeNode node) {
if (activeChildrenList == null) {
return -1;
}
return activeChildrenList.indexOf(node);
}
/**
* Subclasses can override this method to perform faster lookups of a node; for
* example, if the subclass has a sorted list of children, then a binary search can
* be used.
*
* @param node the node whose index we seek
* @param children the children who contain the given node (may be null)
* @return the index of the given child in the given list
* Creates a clone of this node. The clone should contain a shallow copy of all the node's
* attributes except that the parent and children are null.
* @return the clone of this object.
* @throws CloneNotSupportedException if some implementation prevents itself from being cloned.
*/
protected synchronized int doGetIndexOfChild(GTreeNode node, List<GTreeNode> children) {
@Override
public GTreeNode clone() throws CloneNotSupportedException {
CoreGTreeNode clone = (GTreeNode) super.clone();
clone.parent = null;
clone.children = null;
return (GTreeNode) clone;
}
public void dispose() {
List<GTreeNode> oldChildren;
synchronized (this) {
oldChildren = children;
children = null;
parent = null;
}
if (oldChildren != null) {
for (GTreeNode node : oldChildren) {
node.dispose();
}
oldChildren.clear();
}
}
/**
* Returns true if the node is in the process of loading its children.
* See {@link GTreeSlowLoadingNode}
* @return true if the node is in the process of loading its children.
*/
public synchronized final boolean isInProgress() {
return isInProgress(children);
}
/**
* True if the children for this node have been loaded yet. Some GTree nodes are lazy in that they
* don't load their children until needed. Nodes that have the IN_PROGRESS node as it child
* is considered loaded if in the swing thread, otherwise they are considered not loaded.
* @return true if the children for this node have been loaded.
*/
public synchronized boolean isLoaded() {
if (children == null) {
return -1;
return false;
}
return children.indexOf(node);
}
//==================================================================================================
// Setter/Mutator Methods
//==================================================================================================
protected void doAddNode(final int index, final GTreeNode child) {
if (SwingUtilities.isEventDispatchThread()) {
swingAddNode(index, child);
return;
}
SystemUtilities.runSwingNow(new Runnable() {
@Override
public void run() {
swingAddNode(index, child);
}
});
}
private void swingAddNode(int index, GTreeNode child) {
//
// The following code is 'Swing Atomic'--it all (manipulation and notification) happens
// in the Swing thread together, which synchronizes it with other Swing operations.
//
// Synchronized so that the accessor methods do not try to read while we are writing.
synchronized (this) {
if (allChildrenList == null) {
allChildrenList = new ArrayList<GTreeNode>();
activeChildrenList = allChildrenList;
}
if (allChildrenList.contains(child)) {
return;
}
((CoreGTreeNode) child).parent = this;
if (index < 0 || index >= allChildrenList.size()) {
index = allChildrenList.size();
}
allChildrenList.add(index, child);
}
// can't be in synchronized block!
fireNodeAdded(this, child);
}
@Override
public void removeNode(final GTreeNode node) {
if (SwingUtilities.isEventDispatchThread()) {
swingRemoveNode(node);
return;
}
SystemUtilities.runSwingNow(new Runnable() {
@Override
public void run() {
swingRemoveNode(node);
}
});
}
private void swingRemoveNode(GTreeNode node) {
int index;
synchronized (this) {
((CoreGTreeNode) node).parent = null;
if (activeChildrenList == null) {
return;
}
index = activeChildrenList.indexOf(node);
if (index >= 0) {
activeChildrenList.remove(index);
}
if (allChildrenList != activeChildrenList && allChildrenList != null) {
allChildrenList.remove(node);
}
}
// can't be in synchronized block!
if (index >= 0) {
fireNodeRemoved(this, node, index);
}
}
protected void doSetChildren(final List<GTreeNode> childList, final boolean notify) {
if (SwingUtilities.isEventDispatchThread()) {
swingSetChildren(childList, notify, false);
return;
}
SystemUtilities.runSwingNow(new Runnable() {
@Override
public void run() {
swingSetChildren(childList, notify, false);
}
});
}
protected void swingSetChildren(List<GTreeNode> childList, boolean notify,
boolean onlyIfInProgress) {
//
// The following code is 'Swing Atomic'--it all (manipulation and notification) happens
// in the Swing thread together, which synchronizes it with other Swing operations.
//
// Synchronized so that the accessor methods do not try to read while we are writing.
synchronized (this) {
if (childList == null) {
allChildrenList = null;
activeChildrenList = null;
}
else {
if (onlyIfInProgress && !isInProgress()) {
return;
}
for (GTreeNode child : childList) {
((CoreGTreeNode) child).parent = this;
}
allChildrenList = new ArrayList<GTreeNode>(childList);
activeChildrenList = allChildrenList;
}
}
// can't be in synchronized block!
if (notify) {
notifyNodeStructureChanged(this);
}
}
protected void doSetActiveChildren(final List<GTreeNode> childList) {
if (SwingUtilities.isEventDispatchThread()) {
swingSetActiveChilren(childList);
return;
}
SystemUtilities.runSwingNow(new Runnable() {
@Override
public void run() {
swingSetActiveChilren(childList);
}
});
}
private void swingSetActiveChilren(List<GTreeNode> childList) {
//
// The following code is 'Swing Atomic'--it all (manipulation and notification) happens
// in the Swing thread together, which synchronizes it with other Swing operations.
//
// Synchronized so that the accessor methods do not try to read while we are writing.
synchronized (this) {
activeChildrenList = childList;
}
// can't be in synchronized block!
notifyNodeStructureChanged(this);
}
/**
* Convenience method to clear any filtered items by restoring the active children of this
* node to be the complete set of children.
*/
protected void doResetActiveChildren() {
doSetActiveChildren(allChildrenList);
}
//==================================================================================================
// Utility Methods
//==================================================================================================
@Override
public void fireNodeChanged(final GTreeNode parentNode, final GTreeNode node) {
SystemUtilities.runIfSwingOrPostSwingLater(new Runnable() {
@Override
public void run() {
notifyNodeChanged(parentNode, node);
}
});
}
private void notifyNodeChanged(GTreeNode parentNode, GTreeNode node) {
if (isAnyAncestorInProgress()) {
return;
}
GTree tree = getTree();
if (isInValidTree(tree)) {
tree.getModel().fireNodeDataChanged(parentNode, node);
}
}
private boolean isInValidTree(GTree tree) {
return tree != null && !tree.isDisposed();
}
@Override
public void fireNodeStructureChanged(final GTreeNode node) {
SystemUtilities.runIfSwingOrPostSwingLater(new Runnable() {
@Override
public void run() {
notifyNodeStructureChanged(node);
}
});
}
private void notifyNodeStructureChanged(GTreeNode node) {
if (isAnyAncestorInProgress()) {
return;
}
GTree tree = getTree();
if (isInValidTree(tree)) {
tree.getModel().fireNodeStructureChanged(node);
}
}
private void fireNodeAdded(GTreeNode parentNode, GTreeNode newNode) {
// assumption: we are always called in the Swing thread.
if (!isAnyAncestorInProgress()) {
GTree tree = getTree();
if (isInValidTree(tree)) {
tree.getModel().fireNodeAdded(parentNode, newNode);
}
}
}
private void fireNodeRemoved(GTreeNode parentNode, GTreeNode removedNode, int deletedChildIndex) {
// assumption: we are always called in the Swing thread.
if (!isAnyAncestorInProgress()) {
GTree tree = getTree();
if (isInValidTree(tree)) {
tree.getModel().fireNodeRemoved(parentNode, removedNode, deletedChildIndex);
}
}
}
private boolean isAnyAncestorInProgress() {
GTreeNode node = this;
while (node != null) {
if (node.isInProgress()) {
if (Swing.isSwingThread()) {
return true;
}
node = node.getParent();
return !isInProgress(children);
}
/**
* Returns true if the node is in the process of loading its children. For nodes
* that directly extend GTreeNode, this is always false. See {@link GTreeSlowLoadingNode}
* for information on nodes that that can be in the progress of loading.
* @param childList the list to test.
* @return true if the node is in the progress of loading its children.
*/
private boolean isInProgress(List<GTreeNode> childList) {
if (childList != null && childList.size() == 1 &&
childList.get(0) instanceof InProgressGTreeNode) {
return true;
}
return false;
}
//==================================================================================================
// End Utility Methods
//==================================================================================================
}

View file

@ -15,8 +15,8 @@
*/
package docking.widgets.tree;
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.USER_GENERATED;
import static ghidra.util.SystemUtilities.runSwingNow;
import static docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin.*;
import static ghidra.util.SystemUtilities.*;
import java.awt.*;
import java.awt.dnd.Autoscroll;
@ -62,7 +62,15 @@ public class GTree extends JPanel implements BusyListener {
* this variable around, we can give this node to clients, regardless of the root node
* visible in the tree.
*/
private GTreeRootNode realRootNode;
private GTreeNode realRootNode;
/**
* The rootParent is a node that is assigned as the parent to the realRootNode. It's primary purpose is
* to allow nodes access to the tree. It overrides the getTree() method on GTreeNode to return
* this tree. This eliminated the need for clients to create special root nodes that had
* public setTree/getTree methods.
*/
private GTreeRootParentNode rootParent = new GTreeRootParentNode(this);
private JScrollPane scrollPane;
private GTreeRenderer renderer;
@ -90,9 +98,7 @@ public class GTree extends JPanel implements BusyListener {
private GTreeFilter filter;
private GTreeFilterProvider filterProvider;
private List<GTreeNode> nodesToBeFiltered = new ArrayList<>();
private SwingUpdateManager filterUpdateManager;
private int MAX_BUFFERED_FILTERED = 10;
/**
* Creates a GTree with the given root node. The created GTree will use a threaded model
@ -100,13 +106,13 @@ public class GTree extends JPanel implements BusyListener {
*
* @param root The root node of the tree.
*/
public GTree(GTreeRootNode root) {
public GTree(GTreeNode root) {
uniquePreferenceKey = generateFilterPreferenceKey();
this.realRootNode = root;
monitor = new TaskMonitorComponent();
monitor.setShowProgressValue(false);// the tree's progress is fabricated--don't paint it
worker = new PriorityWorker("GTree Worker", monitor);
root.setGTree(this);
root.setParent(rootParent);
this.model = new GTreeModel(root);
worker.setBusyListener(this);
init();
@ -115,7 +121,7 @@ public class GTree extends JPanel implements BusyListener {
(windowManager, provider) -> filterProvider.loadFilterPreference(windowManager,
uniquePreferenceKey));
filterUpdateManager = new SwingUpdateManager(1000, 30000, () -> performNodeFiltering());
filterUpdateManager = new SwingUpdateManager(1000, 30000, () -> updateModelFilter());
}
/**
@ -155,7 +161,7 @@ public class GTree extends JPanel implements BusyListener {
return localMonitor;
}
return TaskMonitorAdapter.DUMMY_MONITOR;
return TaskMonitor.DUMMY;
}
@Override
@ -166,6 +172,14 @@ public class GTree extends JPanel implements BusyListener {
filterProvider.setEnabled(enabled);
}
/**
* Turns tree event notifications on/off
* @param b true to enable events, false to disable events
*/
public void setEventsEnabled(boolean b) {
model.setEventsEnabled(b);
}
public void setDragNDropHandler(GTreeDragNDropHandler dragNDropHandler) {
this.dragNDropHandler = dragNDropHandler;
new GTreeDragNDropAdapter(this, tree, dragNDropHandler);
@ -247,7 +261,7 @@ public class GTree extends JPanel implements BusyListener {
public void dispose() {
filterUpdateManager.dispose();
worker.dispose();
GTreeRootNode root = model.getModelRoot();
GTreeNode root = model.getModelRoot();
if (root != null) {
root.dispose();
}
@ -272,7 +286,6 @@ public class GTree extends JPanel implements BusyListener {
protected void updateModelFilter() {
filter = filterProvider.getFilter();
modificationID.incrementAndGet();
if (lastFilterTask != null) {
@ -280,7 +293,7 @@ public class GTree extends JPanel implements BusyListener {
lastFilterTask.cancel();
}
lastFilterTask = new GTreeFilterTask(this, getRootNode(), filter);
lastFilterTask = new GTreeFilterTask(this, filter);
if (isFilteringEnabled()) {
worker.schedule(lastFilterTask);
@ -351,7 +364,7 @@ public class GTree extends JPanel implements BusyListener {
}
public void expandAll() {
runTask(new GTreeExpandAllTask(this, getRootNode()));
runTask(new GTreeExpandAllTask(this, getViewRoot()));
}
public void collapseAll(GTreeNode node) {
@ -378,7 +391,7 @@ public class GTree extends JPanel implements BusyListener {
}
public void expandPaths(TreePath[] paths) {
runTask(new GTreeExpandPathsTask(this, tree, Arrays.asList(paths)));
runTask(new GTreeExpandPathsTask(this, Arrays.asList(paths)));
}
public void expandPaths(List<TreePath> pathsList) {
@ -492,7 +505,11 @@ public class GTree extends JPanel implements BusyListener {
tree.setScrollableUnitIncrement(increment);
}
protected GTreeModel getModel() {
/**
* Returns the model for this tree
* @return the model for this tree
*/
public GTreeModel getModel() {
return model;
}
@ -503,6 +520,10 @@ public class GTree extends JPanel implements BusyListener {
return tree;
}
/**
* Returns the current viewport position of the scrollable tree.
* @return the current viewport position of the scrollable tree.
*/
public Point getViewPosition() {
JViewport viewport = scrollPane.getViewport();
Point p = viewport.getViewPosition();
@ -529,35 +550,56 @@ public class GTree extends JPanel implements BusyListener {
}
/**
* Gets the node for the given path. This is useful if the node that is in the path has
* been replaced by a new node that is equal, but a different instance.
* Gets the model node for the given path. This is useful if the node that is in the path has
* been replaced by a new node that is equal, but a different instance. One way this happens
* is if the tree is filtered and therefor the displayed nodes are clones of the model nodes. This
* can also happen if the tree nodes are rebuilt for some reason.
*
* @param path the path of the node
* @return the current node in the tree
* @return the corresponding model node in the tree. If the tree is filtered the viewed node will
* be a clone of the corresponding model node.
*/
public GTreeNode getNodeForPath(TreePath path) {
if (path == null) {
public GTreeNode getModelNodeForPath(TreePath path) {
return getNodeForPath(getModelRoot(), path);
}
/**
* Gets the view node for the given path. This is useful to translate to a tree path that
* is valid for the currently displayed tree. (Remember that if the tree is filtered,
* then the displayed nodes are clones of the model nodes.)
*
* @param path the path of the node
* @return the current node in the displayed (possibly filtered) tree
*/
public GTreeNode getViewNodeForPath(TreePath path) {
return getNodeForPath(getViewRoot(), path);
}
private GTreeNode getNodeForPath(GTreeNode root, TreePath path) {
if (path == null || root == null) {
return null;
}
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (path.getPathCount() == 1) {
Object lastPathComponent = path.getLastPathComponent();
GTreeNode rootNode = getRootNode();
if (rootNode.equals(lastPathComponent)) {
return rootNode;
if (root.getId() == node.getId()) {
return root;
}
return null; // invalid path--the root of the path is not equal to our root!
}
if (node.getRoot() == root) {
return node;
}
GTreeNode parentNode = getNodeForPath(path.getParentPath());
GTreeNode parentNode = getNodeForPath(root, path.getParentPath());
if (parentNode == null) {
return null; // must be a path we don't have
}
Object lastPathComponent = path.getLastPathComponent();
GTreeNode lastPathComponent = (GTreeNode) path.getLastPathComponent();
List<GTreeNode> children = parentNode.getChildren();
for (GTreeNode child : children) {
if (child.equals(lastPathComponent)) {
if (child.getId() == lastPathComponent.getId()) {
return child;
}
}
@ -616,7 +658,7 @@ public class GTree extends JPanel implements BusyListener {
isFilteringEnabled = enabled;
setFilterFieldEnabled(enabled);
validate();
refilter();
refilterNow();
}
/**
@ -685,13 +727,20 @@ public class GTree extends JPanel implements BusyListener {
*
* @param rootNode The node to set.
*/
public void setRootNode(GTreeRootNode rootNode) {
public void setRootNode(GTreeNode rootNode) {
GTreeNode oldRoot = doSetRootNode(rootNode, true);
oldRoot.dispose();
if (filter != null) {
filterUpdateManager.update();
}
}
private GTreeNode doSetRootNode(GTreeNode rootNode, boolean waitForJobs) {
worker.clearAllJobs();
GTreeRootNode root = model.getModelRoot();
root.dispose();
GTreeNode root = model.getModelRoot();
this.realRootNode = rootNode;
rootNode.setGTree(this);
rootNode.setParent(rootParent);
//
// We need to use our standard 'worker pipeline' for mutations to the tree. This means
@ -706,23 +755,54 @@ public class GTree extends JPanel implements BusyListener {
runTask(new SetRootNodeTask(this, rootNode, model));
}
else {
if (waitForJobs) {
worker.waitUntilNoJobsScheduled(Integer.MAX_VALUE);
}
monitor.clearCanceled();
model.setRootNode(rootNode);
}
return root;
}
void setFilteredRootNode(GTreeNode filteredRootNode) {
GTreeNode currentRoot = (GTreeNode) model.getRoot();
model.setRootNode(filteredRootNode);
if (currentRoot != realRootNode) {
currentRoot.dispose();
}
}
void restoreNonFilteredRootNode() {
GTreeNode currentRoot = (GTreeNode) model.getRoot();
model.setRootNode(realRootNode);
if (currentRoot != realRootNode) {
currentRoot.dispose();
}
}
/**
* This method always returns the root node given by the client, whether from the
* constructor or from {@link #setRootNode(GTreeRootNode)}. There is a chance that the
* root node being used by the GUI is an "In Progress" node that is a placeholder used while
* this threaded tree is setting the root node.
* @return
* This method returns the root node that was provided to the tree by the client, whether from the
* constructor or from {@link #setRootNode(GTreeNode)}.
* This node represents the data model and always contains all the nodes regardless of any filter
* being applied. If a filter is applied to the tree, then this is not the actual root node being
* displayed by the {@link JTree}.
* @return the root node as provided by the client.
*/
public GTreeRootNode getRootNode() {
public GTreeNode getModelRoot() {
return realRootNode;
}
/**
* This method returns the root node currently being displayed by the {@link JTree}. If there
* are no filters applied, then this will be the same as the model root (See {@link #getModelRoot()}).
* If a filter is applied, then this will be a clone of the model root that contains clones of all
* nodes matching the filter.
* @return the root node currently being display by the {@link JTree}
*/
public GTreeNode getViewRoot() {
return (GTreeNode) model.getRoot();
}
/**
* This method is useful for debugging tree problems. Don't know where else to put it.
* @param name - Use this to indicate what tree event occurred ("node inserted" "node removed", etc.)
@ -979,8 +1059,35 @@ public class GTree extends JPanel implements BusyListener {
});
}
public void refilter() {
updateModelFilter();
/**
* Causes the tree to refilter immediately (before this method returns)
*/
public void refilterNow() {
if (isFilteringEnabled && filter != null) {
filterUpdateManager.updateNow();
}
}
/**
* Causes the tree to refilter some time later
*/
public void refilterLater() {
if (isFilteringEnabled && filter != null) {
filterUpdateManager.update();
}
}
/**
* Re-filters the tree if the newNode should be included in the current filter results. If
* the new node doesn't match the filter, there is no need to refilter the tree.
* @param newNode the node that may cause the tree to refilter.
*/
public void refilterLater(GTreeNode newNode) {
if (isFilteringEnabled && filter != null) {
if (filter.acceptsNode(newNode)) {
filterUpdateManager.updateLater();
}
}
}
public GTreeFilter getFilter() {
@ -1020,38 +1127,6 @@ public class GTree extends JPanel implements BusyListener {
});
}
public synchronized void scheduleFilterTask(GTreeNode node) {
if (!isFilteringEnabled()) {
return;
}
if (nodesToBeFiltered.size() <= MAX_BUFFERED_FILTERED) {
nodesToBeFiltered.add(node);
}
filterUpdateManager.update();
}
private synchronized void performNodeFiltering() {
if (!isFilteringEnabled()) {
return;
}
if (nodesToBeFiltered.isEmpty()) {
return;
}
if (worker.isBusy()) {
filterUpdateManager.updateLater();
return;
}
if (nodesToBeFiltered.size() >= MAX_BUFFERED_FILTERED) {
worker.schedule(new GTreeFilterTask(this, getRootNode(), filter));
}
else {
for (GTreeNode node : nodesToBeFiltered) {
worker.schedule(new GTreeFilterTask(this, node, filter));
}
}
nodesToBeFiltered.clear();
}
public void runBulkTask(GTreeBulkTask task) {
worker.schedule(task);
}
@ -1074,7 +1149,7 @@ public class GTree extends JPanel implements BusyListener {
@Override
public String toString() {
GTreeRootNode rootNode = getRootNode();
GTreeNode rootNode = getModelRoot();
if (rootNode == null) {
return "GTree - no root node";
}
@ -1091,7 +1166,7 @@ public class GTree extends JPanel implements BusyListener {
}
public void clearSizeCache() {
recurseClearSizeCache(getRootNode());
recurseClearSizeCache(getViewRoot());
}
private void recurseClearSizeCache(GTreeNode node) {
@ -1369,4 +1444,5 @@ public class GTree extends JPanel implements BusyListener {
return stackTrace[creatorIndex].getClassName();
}
}

View file

@ -13,23 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.widgets.tree.tasks;
package docking.widgets.tree;
import docking.widgets.tree.*;
import docking.widgets.tree.support.GTreeFilter;
import docking.widgets.tree.tasks.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class GTreeFilterTask extends GTreeTask {
private final GTreeNode node;
private final GTreeFilter filter;
private final GTreeState defaultRestoreState;
private boolean cancelledProgramatically;
public GTreeFilterTask(GTree tree, GTreeNode node, GTreeFilter filter) {
public GTreeFilterTask(GTree tree, GTreeFilter filter) {
super(tree);
this.node = node;
this.filter = filter;
// save this now, before we modify the tree
@ -39,31 +38,44 @@ public class GTreeFilterTask extends GTreeTask {
@Override
public void run(TaskMonitor monitor) {
if (filter == null) {
node.clearFilter();
tree.restoreNonFilteredRootNode();
restoreInSameTask(monitor);
return;
}
monitor.setMessage("Filtering...");
monitor.initialize(1000000000);
GTreeNode root = tree.getModelRoot();
try {
node.filter(filter, monitor, 0, 1000000000);
monitor.setMessage("Loading/Organizing Tree ....");
// disable tree events while loading to prevent unnecessary events from slowing
// down the operation
tree.setEventsEnabled(false);
int nodeCount = root.loadAll(monitor);
tree.setEventsEnabled(true);
monitor.setMessage("Filtering...");
monitor.initialize(nodeCount);
GTreeNode filtered = root.filter(filter, monitor);
runOnSwingThread(() -> tree.setFilteredRootNode(filtered));
if (filter.showFilterMatches()) {
expandInSameTask(monitor);
expandInSameTask(monitor, filtered);
restoreInSameTask(monitor);
}
}
catch (CloneNotSupportedException e) {
Msg.error(this, "Got Unexpected CloneNotSupportedException", e);
}
catch (CancelledException e) {
if (!cancelledProgramatically) {
tree.runTask(new GTreeClearTreeFilterTask(tree));
}
}
finally {
tree.setEventsEnabled(true);
}
}
private void expandInSameTask(TaskMonitor monitor) {
GTreeExpandAllTask expandTask = new GTreeExpandAllTask(tree, node);
private void expandInSameTask(TaskMonitor monitor, GTreeNode filtered) {
GTreeExpandAllTask expandTask = new GTreeExpandAllTask(tree, filtered);
expandTask.run(monitor);
}

View file

@ -15,53 +15,67 @@
*/
package docking.widgets.tree;
import java.util.Collections;
import java.util.List;
/**
* Base class for GTNodes that want to use a lazy loading approach. By using lazy
* nodes, you don't have to create all the nodes up front and the nodes will only
* be created as needed. If you extend this base class, you have to implement one
* additional method than if you extended AbstractGTreeNode and that is generateChildren().
* The generateChildren() method will be called automatically when needed.
* Base class for GTreeNodes that populate their children on demand (typically when expanded).
* Also, children of this node can be unloaded by calling {@link #unloadChildren()}. This
* can be used by nodes in large trees to save memory by unloading children that are no longer
* in the current tree view (collapsed). Of course, that decision would need to be balanced
* against the extra time to reload the nodes in the event that a filter is applied.
*/
public abstract class GTreeLazyNode extends GTreeNode {
public abstract class GTreeLazyNode extends AbstractGTreeNode {
/**
* Subclasses must be able to generate their children nodes on demand by implementing this method.
* @return the list of GTreeNodes that make up the children for this node.
*/
@Override
protected abstract List<GTreeNode> generateChildren();
/**
* Sets this lazy node back to the "unloaded" state such that if
* its children are accessed, it will reload its children as needed.
*/
public void unloadChildren() {
if (isLoaded()) {
doSetChildren(null);
}
}
@Override
protected final void loadChildren() {
if (isChildrenLoadedOrInProgress()) {
return;
public void addNode(GTreeNode node) {
if (isLoaded()) {
super.addNode(node);
}
List<GTreeNode> generateChildren = generateChildren();
if (isChildrenLoadedOrInProgress()) {
return;
}
doSetChildren(generateChildren, false);
}
@Override
public void addNode(int index, GTreeNode node) {
if (!isChildrenLoadedOrInProgress()) {
return;
}
if (isLoaded()) {
super.addNode(index, node);
}
/**
* A convenience method to return this node's children if they are loaded; an empty list
* if they are not loaded. This allows clients that don't care either way to use the
* list returned here without checking for null.
*
* @return the loaded children
*/
public List<GTreeNode> getAllChildrenIfLoaded() {
if (isChildrenLoadedOrInProgress()) {
return getAllChildren();
}
// not loaded; do not load
return Collections.emptyList();
@Override
public void addNodes(List<GTreeNode> nodes) {
if (isLoaded()) {
super.addNodes(nodes);
}
}
@Override
public void removeAll() {
if (isLoaded()) {
unloadChildren();
}
}
@Override
public void removeNode(GTreeNode node) {
if (!isLoaded()) {
return;
}
super.removeNode(node);
}
}

View file

@ -15,61 +15,103 @@
*/
package docking.widgets.tree;
import java.util.List;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
import javax.swing.Icon;
import javax.swing.tree.TreePath;
import docking.widgets.tree.support.GTreeFilter;
import docking.widgets.tree.support.*;
import ghidra.util.Swing;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import util.CollectionUtils;
public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
/**
* Base implementation for GTree nodes. Direct subclasses of this class are expected to have
* all their children in hand when initially constructed (either in their constructor or externally
* using {@link #addNode(GTreeNode)} or {@link #setChildren(List)}. For large trees, subclasses
* should instead extend {@link GTreeLazyNode} or {@link GTreeSlowLoadingNode}
*/
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() {
return Collections.emptyList();
}
/**
* Returns the name of the node to be displayed in the tree
* @return the name of the node.
*/
public String getName();
public abstract String getName();
/**
* Returns the Icon to be displayed for this node in the tree.
* @param expanded true if the node is expanded.
* @return the icon to be displayed for this node in the tree.
*/
public Icon getIcon(boolean expanded);
public abstract Icon getIcon(boolean expanded);
/**
* Returns the string to be displayed as a tooltip when the user
* hovers the mouse on this node in the tree.
* @return the tooltip to be displayed.
*/
public String getToolTip();
public abstract String getToolTip();
/**
* Returns true if this node never has children.
* @return true if this node is a leaf.
*/
public boolean isLeaf();
public abstract boolean isLeaf();
@Override
public int compareTo(GTreeNode node) {
return getName().compareToIgnoreCase(node.getName());
}
/**
* Adds the given node as a child to this node.
* Adds the given node as a child to this node. Note: this method may be inefficient so if you
* have many nodes to add, you should use either {@link #addNodes(List)} or {@link #setChildren(List)}
* @param node the node to add as a child.
*/
public void addNode(GTreeNode node);
public void addNode(GTreeNode node) {
children().add(node);
node.setParent(this);
fireNodeAdded(this, node);
}
/**
* Adds the given nodes as children to this node.
* @param nodes the nodes to add.
*/
public void addNodes(List<GTreeNode> nodes) {
for (GTreeNode node : nodes) {
node.setParent(this);
}
children().addAll(nodes);
fireNodeStructureChanged(this);
}
/**
* Adds the given node at the given index as a child to this node.
* @param index the index to place the node.
* @param node the node to add as a child of this node.
*/
public void addNode(int index, GTreeNode node);
/**
* Returns the list of children including those that have been filtered out.
* @return the list of all children of this node including those that are filtered out.
*/
public List<GTreeNode> getAllChildren();
public void addNode(int index, GTreeNode node) {
children().add(index, node);
node.setParent(this);
fireNodeAdded(this, node);
}
/**
* Returns all of the <b>visible</b> children of this node. If there are filtered nodes, then
@ -78,28 +120,32 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
* @return all of the <b>visible</b> children of this node. If there are filtered nodes, then
* they will not be returned.
*/
public List<GTreeNode> getChildren();
public List<GTreeNode> getChildren() {
return Collections.unmodifiableList(children());
}
/**
* Returns the number of <b>visible</b> children of this node. Does not include
* nodes that are current filtered out.
* @return the number of <b>visible</b> children of this node.
*/
public int getChildCount();
/**
* Returns the number of <b>all</b> children of this node. Includes nodes that
* are currently filtered out.
* @return the number of <b>all</b? children of this node.
*/
public int getAllChildCount();
public int getChildCount() {
return children().size();
}
/**
* Returns the child node of this node with the given name.
* @param name the name of the child to be returned.
* @return the child with the given name.
*/
public GTreeNode getChild(String name);
public GTreeNode getChild(String name) {
for (GTreeNode node : children()) {
if (name.equals(node.getName())) {
return node;
}
}
return null;
}
/**
* Returns the child node at the given index. Returns null if the index is out of
@ -107,26 +153,46 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
* @param index the index of the child to be returned.
* @return the child at the given index.
*/
public GTreeNode getChild(int index);
public GTreeNode getChild(int index) {
return children().get(index);
}
/**
* Returns the total number of nodes in the subtree rooted at this node. Leaf
* nodes return 1.
* @return the number of nodes from this node downward.
*/
public int getNodeCount();
public int getNodeCount() {
int count = 1;
for (GTreeNode node : children()) {
count += node.getNodeCount();
}
return count;
}
/**
* Returns the total number of leaf nodes in the subtree from this node.
* @return the total number of leaf nodes in the subtree from this node.
*/
public int getLeafCount();
public int getLeafCount() {
int count = 0;
for (GTreeNode node : children()) {
count += node.getLeafCount();
}
return count == 0 ? 1 : count; // if my child count == 0, return 1 since I am a leaf
}
/**
* Returns the index of this node within its parent node.
* @return the index of this node within its parent node.
*/
public int getIndexInParent();
public int getIndexInParent() {
GTreeNode parent = getParent();
if (parent == null) {
return -1;
}
return parent.getIndexOfChild(this);
}
/**
* Returns the index of the given node within this node. -1 is returned
@ -134,59 +200,74 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
* @param node whose index we want.
* @return the index of the given node within this node.
*/
public int getIndexOfChild(GTreeNode node);
public int getIndexOfChild(GTreeNode node) {
return children().indexOf(node);
}
/**
* Returns the TreePath for this node.
* @return the TreePath for this node.
*/
public TreePath getTreePath();
public TreePath getTreePath() {
return new TreePath(getPathToRoot(this, 0));
}
/**
* Removes all children from this node. The children nodes will be disposed.
*/
public void removeAll();
public void removeAll() {
if (!isLoaded()) {
return;
}
List<GTreeNode> children = children();
if (children != null) {
for (GTreeNode child : children) {
child.dispose();
}
children().clear();
fireNodeStructureChanged(this);
}
}
/**
* Remove the given node from this node.
* @param node the to be removed.
*/
public void removeNode(GTreeNode node);
public void removeNode(GTreeNode node) {
if (!isLoaded()) {
return;
}
List<GTreeNode> children = children();
if (children.remove(node)) {
node.setParent(null);
fireNodeStructureChanged(this);
}
}
/**
* Sets the children on this node. Any existing current children will be dispose.
* @param childList this list of nodes to be set as children of this node.
*/
public void setChildren(List<GTreeNode> childList);
public void setChildren(List<GTreeNode> childList) {
doSetChildren(childList);
fireNodeStructureChanged(this);
}
/**
* Returns true if the given node is a child of this node or one of its children.
* @param node the potential descendant node to check
* @return true if the given node is a child of this node or one of its children.
*/
public boolean isAncestor(GTreeNode node);
/**
* Applies the the given filter to the subtree of this node. Nodes will be
* filtered out if the node and all of its descendants are not accepted by the filter. In
* other words, a node will remain if it or any of its descendants are accepted by the filter.
* @param filter the filter being applied.
* @param monitor a TaskMonitor for tracking the progress and cancelling.
* @param min the min value to use for the progress bar for this subtree.
* @param max the max value to use for the progress bar for this subtree.
* @throws CancelledException if the operation is cancelled via the TaskMonitor.
*/
public void filter(GTreeFilter filter, TaskMonitor monitor, int min, int max)
throws CancelledException;
/**
* Removes any filtering on this subtree.
*/
public void clearFilter();
/**
* Returns true if this node is filtered and not in the current view
*/
public boolean isFilteredOut();
public boolean isAncestor(GTreeNode node) {
GTreeNode nodeParent = node.getParent();
while (nodeParent != null) {
if (nodeParent.equals(this)) {
return true;
}
nodeParent = nodeParent.getParent();
}
return false;
}
/**
* Notification method called when a cell editor completes editing to notify this
@ -195,7 +276,9 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
* @param newValue the new value provided by the cell editor.
* @see #isEditable()
*/
public void valueChanged(Object newValue);
public void valueChanged(Object newValue) {
// Overridden in subclasses
}
/**
* Returns true if this node is allowed to be edited in the tree. You must override this
@ -204,49 +287,216 @@ public interface GTreeNode extends Comparable<GTreeNode>, Iterable<GTreeNode> {
* @return true if this node is allowed to be edited in the tree.
* @see #valueChanged(Object)
*/
public boolean isEditable();
public boolean isEditable() {
return false;
}
/**
* Returns the rootNode for this tree or null if there is no parent path to a
* GTRootNode.
* @return the rootNode for this tree.
*/
public GTreeRootNode getRoot();
public GTreeNode getRoot() {
GTreeNode myParent = getParent();
if (myParent == null || myParent instanceof GTreeRootParentNode) {
return this;
}
return myParent.getRoot();
}
/**
* Returns the GTTree that contains this node.
* @return the GTTree that contains this node.
* Returns true if this is a root node
* @return true if this is a root node
*/
public GTree getTree();
public boolean isRoot() {
return getRoot() == this;
}
/**
* Disposes this node and all of its descendants.
* Returns the GTree that this node is attached to
* @return the GTree that this node is attached to
*/
public void dispose();
public GTree getTree() {
GTreeNode parent = getParent();
if (parent != null) {
return parent.getTree();
}
return null;
}
/**
* Returns true if this node is currently being modified.
* @return true if this node is currently being modified.
* Generates a filtered copy of this node and its children.
* <P>
* A node will be included if it or any of its descendants are accepted by the filter.
* NOTE: the filter will only be applied to a nodes children if they are loaded. So to
* perform a filter on all the nodes in the tree, the {@link #loadAll(TaskMonitor)} should
* be called before the filter call.
* @param filter the filter being applied.
* @param monitor a TaskMonitor for tracking the progress and cancelling.
* @return A copy of this node and its children that matches the filter or null
* if this node and none of its children match the filter.
* @throws CancelledException if the operation is cancelled via the TaskMonitor.
* @throws CloneNotSupportedException if any nodes in the tree explicitly prevents cloning.
*/
public boolean isInProgress();
public GTreeNode filter(GTreeFilter filter, TaskMonitor monitor)
throws CancelledException, CloneNotSupportedException {
List<GTreeNode> list = new ArrayList<>();
if (isLoaded()) {
for (GTreeNode child : children()) {
monitor.checkCanceled();
GTreeNode filtered = child.filter(filter, monitor);
if (filtered != null) {
list.add(filtered);
}
monitor.incrementProgress(1);
}
}
if (isRoot() || !list.isEmpty() || filter.acceptsNode(this) || getParent() == null) {
GTreeNode clone = clone();
clone.doSetChildren(list);
return clone;
}
return null;
}
/**
* Causes any lazy or slow loading nodes in the tree to load their children so that the tree
* is fully loaded. Nodes that are already loaded (including normal nodes which are always loaded)
* do nothing except recursively call {@link #loadAll(TaskMonitor)} on their children.
* @param monitor the TaskMonitor to monitor progress and provide cancel checking
* @return the total number of nodes in the subtree of this node.
* @throws CancelledException if the operation is cancelled using the monitor.
*/
public int loadAll(TaskMonitor monitor) throws CancelledException {
List<GTreeNode> children = children();
monitor = new TreeTaskMonitor(monitor, children.size());
int count = 1;
for (GTreeNode child : children) {
monitor.checkCanceled();
count += child.loadAll(monitor);
monitor.incrementProgress(1);
}
return count;
}
@Override
public int hashCode() {
return (int) id;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
GTreeNode other = (GTreeNode) obj;
return id == other.id;
}
/**
* Notifies the tree that the node has different children. This method
* @param node the node that has changed.
*/
public void fireNodeStructureChanged(GTreeNode node);
public void fireNodeStructureChanged(GTreeNode node) {
GTree tree = getTree();
if (tree != null) {
Swing.runNow(() -> tree.getModel().fireNodeStructureChanged(node));
tree.refilterLater();
}
}
/**
* Notifies the tree that a node has changed.
* @param parentNode the node that contains the node that was changed.
* @param node the that changed.
*/
public void fireNodeChanged(GTreeNode parentNode, GTreeNode node);
public void fireNodeChanged(GTreeNode parentNode, GTreeNode node) {
GTree tree = getTree();
if (tree != null) {
Swing.runNow(() -> tree.getModel().fireNodeDataChanged(parentNode, node));
tree.refilterLater();
}
}
/**
* Returns the parent of this node.
* @return the parent of this node.
* Returns a stream of the GTree nodes in the subtree of this node.
* @param depthFirst if true, the nodes will be streamed in depth-first order, otherwise breadth-first order
* @return a stream of the GTree nodes in the subtree of this node.
*/
public GTreeNode getParent();
public Stream<GTreeNode> stream(boolean depthFirst) {
return CollectionUtils.asStream(iterator(depthFirst));
}
/**
* Returns an iterator of the GTree nodes in the subtree of this node.
* @param depthFirst if true, the nodes will be returned in depth-first order, otherwise breadth-first order
* @return an iterator of the GTree nodes in the subtree of this node.
*/
public Iterator<GTreeNode> iterator(boolean depthFirst) {
if (depthFirst) {
return new DepthFirstIterator(this);
}
return new BreadthFirstIterator(this);
}
@Override
public String toString() {
return getName();
}
/**
* Returns an id for this node that is unique among all GTreeNodes in this running JVM.
* If this node is cloned, the clone will have the same id.
* @return the unique id for this node.
*/
public long getId() {
return id;
}
protected void fireNodeAdded(GTreeNode parentNode, GTreeNode newNode) {
GTree tree = getTree();
if (tree != null) {
Swing.runNow(() -> tree.getModel().fireNodeAdded(parentNode, newNode));
tree.refilterLater(newNode);
}
}
protected void fireNodeRemoved(GTreeNode parentNode, GTreeNode removedNode,
int deletedChildIndex) {
GTree tree = getTree();
if (tree != null) {
Swing.runNow(
() -> tree.getModel().fireNodeRemoved(parentNode, removedNode, deletedChildIndex));
}
}
private GTreeNode[] getPathToRoot(GTreeNode node, int depth) {
GTreeNode[] returnNodes;
/* Check for null, in case someone passed in a null node, or
they passed in an element that isn't rooted at root. */
if (node == null || node instanceof GTreeRootParentNode) {
if (depth == 0) {
return null;
}
returnNodes = new GTreeNode[depth];
}
else {
depth++;
returnNodes = getPathToRoot(node.getParent(), depth);
returnNodes[returnNodes.length - depth] = node;
}
return returnNodes;
}
}

View file

@ -15,23 +15,42 @@
*/
package docking.widgets.tree;
import javax.swing.Icon;
/**
* Simple base class for GTRootNodes. If your root node has different internal logic
* than other nodes, then extend this class. If you root node has the same internal
* logic as other nodes, then it is probably better to extend your other node class and
* implement the getGTree() and setGTree() methods yourself.
*
* Artificial node used by the GTree to set as a parent on the real root node of a GTree. It allows
* nodes to access the GTree because it overrides getTree to return the GTree. This eliminates the
* need for clients to create special root nodes that have getTree/setTree
*/
public abstract class AbstractGTreeRootNode extends AbstractGTreeNode implements GTreeRootNode {
class GTreeRootParentNode extends GTreeNode {
private GTree tree;
@Override
public void setGTree(GTree tree) {
GTreeRootParentNode(GTree tree) {
this.tree = tree;
}
@Override
public GTree getGTree() {
public GTree getTree() {
return tree;
}
@Override
public String getName() {
return null;
}
@Override
public Icon getIcon(boolean expanded) {
return null;
}
@Override
public String getToolTip() {
return null;
}
@Override
public boolean isLeaf() {
return false;
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,120 +15,102 @@
*/
package docking.widgets.tree;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
import java.util.List;
import javax.swing.SwingUtilities;
import docking.widgets.tree.tasks.GTreeLoadChildrenTask;
import docking.widgets.tree.internal.InProgressGTreeNode;
import ghidra.util.Swing;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import util.CollectionUtils;
/**
* Base class for GTNodes that want to use a lazy loading approach, but the loading may
* be slow and therefor should be done in another thread. By using SlowLoadingNode
* nodes, you don't have to create all the nodes up front and the nodes will only
* be created as needed. If you extend this base class, you have to implement one
* additional method than if you extended AbstractGTreeNode and that is
* generateChildren(TaskMonitor monitor).
* The generateChildren(TaskMonitor monitor) method will be called
* automatically from a task thread when needed. While the loading is taking place,
* An "In Progress" node will be displayed.
* Base class for nodes that generate their children on demand, but because generating their children
* is slow, that operation is moved to a background thread. While the children are being generated,
* an {@link InProgressGTreeNode} will appear in the tree until the {@link LoadChildrenTask} has completed.
*/
public abstract class GTreeSlowLoadingNode extends AbstractGTreeNode {
public abstract List<GTreeNode> generateChildren(TaskMonitor monitor) throws CancelledException;
public abstract class GTreeSlowLoadingNode extends GTreeLazyNode {
/**
* Subclass must implement this method to generate their children. This operation will always be
* performed in a background thread (i.e. Not the swing thread)
* @param monitor a TaskMonitor for reporting progress and cancel notification.
* @return the list of children for this node.
* @throws CancelledException if the monitor is cancelled
*/
public abstract List<GTreeNode> generateChildren(TaskMonitor monitor)
throws CancelledException;
@Override
protected final void loadChildren() {
protected final List<GTreeNode> generateChildren() {
final GTree tree = getTree();
if (SystemUtilities.isEventDispatchThread()) {
if (isChildrenLoadedOrInProgress()) {
return;
}
setInProgress(); // this will make isChildrenLoaded() return true for any subsequent calls.
if (tree != null) {
GTreeLoadChildrenTask loadTask = new GTreeLoadChildrenTask(tree, this);
if (Swing.isSwingThread() && tree != null) {
LoadChildrenTask loadTask = new LoadChildrenTask(tree);
tree.runTask(loadTask);
return;
return CollectionUtils.asList(new InProgressGTreeNode());
}
return generateChildrenNow(getMonitor(tree));
}
if (isChildrenLoadedOrInProgress() && !isInProgress()) {
return; // fully loaded
@Override
public int loadAll(TaskMonitor monitor) throws CancelledException {
if (!isLoaded()) {
monitor = new TreeTaskMonitor(monitor, 2);
doSetChildren(generateChildren(new TreeTaskMonitor(monitor, 0)));
monitor.incrementProgress(1);
}
return super.loadAll(monitor);
}
setInProgress();
doLoadChildren(tree, getMonitor(tree));
private List<GTreeNode> generateChildrenNow(TaskMonitor monitor) {
try {
return generateChildren(monitor);
}
catch (CancelledException e) {
return null;
}
}
private TaskMonitor getMonitor(GTree tree) {
if (tree == null) {
return TaskMonitorAdapter.DUMMY_MONITOR;
return TaskMonitor.DUMMY;
}
return tree.getThreadLocalMonitor();
}
private void doLoadChildren(final GTree tree, TaskMonitor monitor) {
if (isChildrenLoaded()) {
// Odd case where we have been told to load even though we are already loaded.
// Probably in the middle of a filter job. Need to reset the active chi
// in any case. Calling setChildren will effectively set the allChildren to its
// current contents, but will also set the active children.
setChildren(doGetAllChildren());
private class LoadChildrenTask extends GTreeTask {
LoadChildrenTask(GTree tree) {
super(tree);
}
@Override
public void run(TaskMonitor monitor) {
if (isLoaded()) {
return;
}
long progressValue = monitor.getProgress();
long maxValue = monitor.getMaximum();
monitor.setMessage("Loading children");
try {
setChildren(generateChildren(monitor));
}
catch (CancelledException e) {
SystemUtilities.runSwingNow(new Runnable() {
if (!tree.isDisposed()) {
runOnSwingThread(new Runnable() {
@Override
public void run() {
if (tree != null) {
tree.collapseAll(tree.getRootNode());
}
tree.collapseAll(tree.getViewRoot());
}
});
doSetChildren(null, true);
}
doSetChildren(null);
}
finally {
// restore monitor min/max/progress values to original state since we don't know
// where we fit into the bigger progress picture.
monitor.initialize(maxValue);
monitor.setProgress(progressValue);
}
}
@Override
protected void swingSetChildren(List<GTreeNode> childList, boolean notify,
boolean onlyIfInProgress) {
// intentionally ignore 'onlyIfInProgress'
super.swingSetChildren(childList, notify, true);
}
/**
* Note: you cannot call this method from the Swing thread, as the data may not have been
* loaded. Instead, this method should be called from a {@link GTreeTask}.
*
* @param index The index where the node should be inserted
* @param node The node to insert
*/
@Override
public void addNode(int index, GTreeNode node) {
if (SwingUtilities.isEventDispatchThread()) {
throw new AssertException(
"You may not invoke this method on a GTReeSlowLoadingNode from the Swing thread");
}
super.addNode(index, node);
}
}

View file

@ -56,11 +56,10 @@ public class GTreeState {
private GTree tree;
public GTreeState(GTree tree) {
this(tree, tree.getRootNode());
this(tree, tree.getViewRoot());
}
public GTreeState(GTree tree, GTreeNode node) {
this.tree = tree;
expandedPaths = tree.getExpandedPaths(node);
selectionPaths = getSelectionPaths(node);
@ -166,7 +165,7 @@ public class GTreeState {
private List<TreePath> getSelectionPaths(GTreeNode node) {
TreePath[] allSelectionPaths = tree.getSelectionPaths();
if (node == tree.getRootNode()) {
if (node == tree.getViewRoot()) {
return CollectionUtils.asList(allSelectionPaths);
}
if (allSelectionPaths == null) {

View file

@ -60,7 +60,7 @@ public abstract class GTreeTask extends PriorityJob {
// note: call this on the Swing thread, since the Swing thread maintains the node state
// (we have seen errors where the tree will return nodes that are in the process
// of being disposed)
GTreeNode nodeForPath = SystemUtilities.runSwingNow(() -> tree.getNodeForPath(path));
GTreeNode nodeForPath = SystemUtilities.runSwingNow(() -> tree.getViewNodeForPath(path));
if (nodeForPath != null) {
return nodeForPath.getTreePath();
}

View file

@ -0,0 +1,178 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.widgets.tree;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.CancelledListener;
import ghidra.util.task.TaskMonitor;
/**
* TaskMonitor implementation that is useful for monitor work when traversing trees.
*
* It works by subdividing the distance of the top-most progress bar (represented by the top-most
* monitor) into equal size chunks depending on how many children have to be visited. For example,
* assume the root node has 5 children, then the task bar for that node would increment 20% of
* the bar as it completed work on each of its children. Now, assume each child of the root node
* has 10 children. The task monitor for each root child will operate entirely with its 20% as
* mentioned above. So the first child of the first child will increment the progress bar
* 2% (10% of 20%) when it is complete.
*/
public class TreeTaskMonitor implements TaskMonitor {
// initialize to a huge number that can be subdivided many times
private static long MAX_VALUE = 0x1000_0000_0000_0000L;
private final TaskMonitor monitor;
// This monitor operates on a sub-range of top-most monitor. The range min/max define that range
private final long currentRangeMin;
private final long currentRangeMax;
// The amount of one increment increase the top-most monitor by this amount
private long chunkSize;
// These values are the max and progress for this sub-monitor
private long max;
private long progress;
public TreeTaskMonitor(TaskMonitor monitor, long max) {
if (monitor instanceof TreeTaskMonitor) {
TreeTaskMonitor treeTaskMonitor = (TreeTaskMonitor) monitor;
this.monitor = treeTaskMonitor.monitor;
currentRangeMin = treeTaskMonitor.getTrueProgress();
currentRangeMax = currentRangeMin + treeTaskMonitor.chunkSize;
}
else {
this.monitor = monitor;
currentRangeMin = 0;
currentRangeMax = MAX_VALUE;
monitor.initialize(MAX_VALUE);
}
setMaximum(max);
progress = 0;
}
private long getTrueProgress() {
return monitor.getProgress();
}
@Override
public boolean isCancelled() {
return monitor.isCancelled();
}
@Override
public void setShowProgressValue(boolean showProgressValue) {
monitor.setShowProgressValue(showProgressValue);
}
@Override
public void setMessage(String message) {
monitor.setMessage(message);
}
@Override
public String getMessage() {
return monitor.getMessage();
}
@Override
public void setProgress(long value) {
this.progress = value;
monitor.setProgress(currentRangeMin + value * chunkSize);
}
@Override
public void initialize(long maxValue) {
setMaximum(maxValue);
}
@Override
public void setMaximum(long maxValue) {
if (maxValue > 0) {
this.max = maxValue;
// The size of the current window/section of the overall monitor
long currentRange = currentRangeMax - currentRangeMin;
// the size of one increment within the current range
chunkSize = Math.max(currentRange / max, 1);
}
else {
this.max = 0;
chunkSize = 0;
}
}
@Override
public long getMaximum() {
return max;
}
@Override
public void setIndeterminate(boolean indeterminate) {
monitor.setIndeterminate(indeterminate);
}
@Override
public boolean isIndeterminate() {
return monitor.isIndeterminate();
}
@Override
public void checkCanceled() throws CancelledException {
monitor.checkCanceled();
}
@Override
public void incrementProgress(long incrementAmount) {
progress += incrementAmount;
monitor.setProgress(currentRangeMin + progress * chunkSize);
}
@Override
public long getProgress() {
return progress;
}
@Override
public void cancel() {
monitor.cancel();
}
@Override
public void addCancelledListener(CancelledListener listener) {
monitor.addCancelledListener(listener);
}
@Override
public void removeCancelledListener(CancelledListener listener) {
monitor.removeCancelledListener(listener);
}
@Override
public void setCancelEnabled(boolean enable) {
monitor.setCancelEnabled(enable);
}
@Override
public boolean isCancelEnabled() {
return monitor.isCancelEnabled();
}
@Override
public void clearCanceled() {
monitor.clearCanceled();
}
}

View file

@ -221,7 +221,7 @@ public class GTreeDragNDropAdapter implements DragSourceListener, DragGestureLis
}
for (int i = 0; i < selectionPaths.length; i++) {
list.add((AbstractGTreeNode) selectionPaths[i].getLastPathComponent());
list.add((GTreeNode) selectionPaths[i].getLastPathComponent());
}
return list;
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +15,6 @@
*/
package docking.widgets.tree.internal;
import ghidra.util.SystemUtilities;
import java.util.ArrayList;
import java.util.List;
@ -27,13 +24,14 @@ import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeRootNode;
import ghidra.util.SystemUtilities;
public class GTreeModel implements TreeModel {
private GTreeRootNode root;
private GTreeNode root;
private List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
private boolean isFiringNodeStructureChanged;
private volatile boolean eventsEnabled = true;
/**
* Constructs a GTreeModel with the given root node.
@ -42,64 +40,72 @@ public class GTreeModel implements TreeModel {
* @param isThreaded True signals to perform all tree tasks in a threaded environment to
* avoid hanging the swing thread.
*/
public GTreeModel(GTreeRootNode root) {
public GTreeModel(GTreeNode root) {
this.root = root;
}
public void setRootNode(GTreeRootNode root) {
public void setRootNode(GTreeNode root) {
this.root = root;
fireRootChanged();
}
@Override
public Object getRoot() {
return root;
}
public GTreeRootNode getModelRoot() {
public GTreeNode getModelRoot() {
return root;
}
@Override
public void addTreeModelListener(TreeModelListener l) {
listeners.add(l);
}
@Override
public void removeTreeModelListener(TreeModelListener l) {
listeners.remove(l);
}
@Override
public Object getChild(Object parent, int index) {
GTreeNode gTreeParent = (GTreeNode) parent;
return gTreeParent.getChild(index);
}
@Override
public int getChildCount(Object parent) {
GTreeNode gTreeParent = (GTreeNode) parent;
return gTreeParent.getChildCount();
}
@Override
public int getIndexOfChild(Object parent, Object child) {
GTreeNode gTreeParent = (GTreeNode) parent;
return gTreeParent.getIndexOfChild((GTreeNode) child);
}
@Override
public boolean isLeaf(Object node) {
GTreeNode gTreeNode = (GTreeNode) node;
return gTreeNode.isLeaf();
}
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
node.valueChanged(newValue);
}
public void fireNodeStructureChanged(final GTreeNode changedNode) {
if (isFiringNodeStructureChanged) {
if (!eventsEnabled || isFiringNodeStructureChanged) {
return;
}
try {
isFiringNodeStructureChanged = true;
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeStructuredChanged() must be "
+ "called from the AWT thread");
SystemUtilities.assertThisIsTheSwingThread(
"GTreeModel.fireNodeStructuredChanged() must be " + "called from the AWT thread");
TreeModelEvent event = new TreeModelEvent(this, changedNode.getTreePath());
for (TreeModelListener listener : listeners) {
@ -112,7 +118,11 @@ public class GTreeModel implements TreeModel {
}
public void fireRootChanged() {
if (!eventsEnabled) {
return;
}
SystemUtilities.runIfSwingOrPostSwingLater(new Runnable() {
@Override
public void run() {
GTreeNode rootNode = root;
if (rootNode != null) {
@ -123,8 +133,11 @@ public class GTreeModel implements TreeModel {
}
public void fireNodeDataChanged(final GTreeNode parentNode, final GTreeNode changedNode) {
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeChanged() must be "
+ "called from the AWT thread");
if (!eventsEnabled) {
return;
}
SystemUtilities.assertThisIsTheSwingThread(
"GTreeModel.fireNodeDataChanged() must be " + "called from the AWT thread");
TreeModelEvent event;
if (parentNode == null) { // special case when root node changes.
@ -145,8 +158,11 @@ public class GTreeModel implements TreeModel {
}
public void fireNodeAdded(final GTreeNode parentNode, final GTreeNode newNode) {
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeAdded() must be "
+ "called from the AWT thread");
if (!eventsEnabled) {
return;
}
SystemUtilities.assertThisIsTheSwingThread(
"GTreeModel.fireNodeAdded() must be " + "called from the AWT thread");
int indexInParent = newNode.getIndexInParent();
if (indexInParent < 0) {
@ -164,8 +180,8 @@ public class GTreeModel implements TreeModel {
public void fireNodeRemoved(final GTreeNode parentNode, final GTreeNode removedNode,
final int oldIndexInParent) {
SystemUtilities.assertThisIsTheSwingThread("GTreeModel.fireNodeRemoved() must be "
+ "called from the AWT thread");
SystemUtilities.assertThisIsTheSwingThread(
"GTreeModel.fireNodeRemoved() must be " + "called from the AWT thread");
TreeModelEvent event =
new TreeModelEvent(this, parentNode.getTreePath(), new int[] { oldIndexInParent },
@ -178,4 +194,8 @@ public class GTreeModel implements TreeModel {
public void dispose() {
root = null;
}
public void setEventsEnabled(boolean b) {
eventsEnabled = b;
}
}

Some files were not shown because too many files have changed in this diff Show more