diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallNode.java index e621f59814..55db539d2f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallNode.java @@ -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()); - return; + return 1; } - super.filter(filter, monitor, min, max); + return super.loadAll(monitor); } private int depth() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java index 3d2b32ff4c..889c67e606 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java @@ -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 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 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) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/IncomingCallsRootNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/IncomingCallsRootNode.java index f29e371f14..3a07ff0b2d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/IncomingCallsRootNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/IncomingCallsRootNode.java @@ -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; - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/OutgoingCallsRootNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/OutgoingCallsRootNode.java index 6b47e4dbe5..21d2ac6068 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/OutgoingCallsRootNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/OutgoingCallsRootNode.java @@ -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; - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java index 32588ff96d..13684971a6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java @@ -454,8 +454,9 @@ public class DataTypesProvider extends ComponentProviderAdapter { previewScrollPane = new JScrollPane(previewPane); - DockingWindowManager.getHelpService().registerHelp(previewScrollPane, - new HelpLocation("DataTypeManagerPlugin", "Preview_Window")); + DockingWindowManager.getHelpService() + .registerHelp(previewScrollPane, + new HelpLocation("DataTypeManagerPlugin", "Preview_Window")); } private DataType locateDataType(HyperlinkEvent event) { @@ -701,8 +702,8 @@ public class DataTypesProvider extends ComponentProviderAdapter { } private ArchiveNode getProgramArchiveNode() { - GTreeNode rootNode = getGTree().getRootNode(); - List children = rootNode.getAllChildren(); + GTreeNode rootNode = getGTree().getModelRoot(); + List 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 children = rootNode.getAllChildren(); + GTreeNode rootNode = getGTree().getModelRoot(); + List 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 allChildren = rootNode.getAllChildren(); + ArchiveRootNode rootNode = (ArchiveRootNode) archiveGTree.getModelRoot(); + List allChildren = rootNode.getChildren(); for (GTreeNode node : allChildren) { ArchiveNode archiveNode = (ArchiveNode) node; if (archiveNode.getArchive() instanceof ProgramArchive) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenDomainFileTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenDomainFileTask.java index 9e8b025950..7cff45f0c7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenDomainFileTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/OpenDomainFileTask.java @@ -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) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CollapseAllArchivesAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CollapseAllArchivesAction.java index a8ff2fba0c..da37e16902 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CollapseAllArchivesAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CollapseAllArchivesAction.java @@ -154,7 +154,7 @@ public class CollapseAllArchivesAction extends DockingAction { } private void collapseAll(GTree archiveGTree) { - GTreeNode rootNode = archiveGTree.getRootNode(); + GTreeNode rootNode = archiveGTree.getViewRoot(); List children = rootNode.getChildren(); for (GTreeNode childNode : children) { archiveGTree.collapseAll(childNode); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateArchiveAction.java index 32c0504141..3329bef6f6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateArchiveAction.java @@ -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() }); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateEnumFromSelectionAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateEnumFromSelectionAction.java index 6bf6d467a8..2bd8ff0afb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateEnumFromSelectionAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateEnumFromSelectionAction.java @@ -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); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateProjectArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateProjectArchiveAction.java index f28492eaef..e1a8f90c5b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateProjectArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateProjectArchiveAction.java @@ -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,31 +34,32 @@ 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); } @Override - public void actionPerformed(ActionContext context) { + public void actionPerformed(ActionContext context) { 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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java index 9481078634..d99f66a24b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DeleteAction.java @@ -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; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DerivativeDataTypeInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DerivativeDataTypeInfo.java index 3908a826b1..bc9591ae23 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DerivativeDataTypeInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DerivativeDataTypeInfo.java @@ -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 children = rootNode.getChildren(); for (GTreeNode node : children) { ArchiveNode archiveNode = (ArchiveNode) node; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DisassociateDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DisassociateDataTypeAction.java index 3c29ae7368..3409df1983 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DisassociateDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DisassociateDataTypeAction.java @@ -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 archives = root.getAllChildren(); + GTreeNode root = tree.getViewRoot(); + List archives = root.getChildren(); archives.forEach(archive -> tree.collapseAll(archive)); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ExportToHeaderAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ExportToHeaderAction.java index 9fcc12609b..3510cdaf68 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ExportToHeaderAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/ExportToHeaderAction.java @@ -210,8 +210,9 @@ public class ExportToHeaderAction extends DockingAction { finally { writer.close(); } - plugin.getTool().setStatusInfo( - "Successfully exported data type(s) to " + file.getAbsolutePath()); + plugin.getTool() + .setStatusInfo( + "Successfully exported data type(s) to " + file.getAbsolutePath()); } catch (CancelledException e) { // user cancelled; ignore @@ -242,7 +243,7 @@ public class ExportToHeaderAction extends DockingAction { } else if (last instanceof CategoryNode) { CategoryNode node = (CategoryNode) last; - List children = node.getAllChildren(); + List children = node.getChildren(); for (GTreeNode cnode : children) { addToManager(cnode.getTreePath(), managersToDataTypesMap); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FilterArraysAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FilterArraysAction.java index c1ddc16adc..1f405edfa9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FilterArraysAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FilterArraysAction.java @@ -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 expandedPaths = gtree.getExpandedPaths(gtree.getRootNode()); + List expandedPaths = gtree.getExpandedPaths(gtree.getViewRoot()); TreePath[] selectionPaths = gtree.getSelectionPaths(); gtree.enableArrayFilter(isSelected()); gtree.expandPaths(expandedPaths); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FilterPointersAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FilterPointersAction.java index dfce9ee8cf..d1b3cfb29a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FilterPointersAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/FilterPointersAction.java @@ -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 expandedPaths = gtree.getExpandedPaths(gtree.getRootNode()); + List expandedPaths = gtree.getExpandedPaths(gtree.getViewRoot()); TreePath[] selectionPaths = gtree.getSelectionPaths(); gtree.enablePointerFilter(isSelected()); gtree.expandPaths(expandedPaths); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveAction.java index d0e536078b..35876cf5d5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/OpenArchiveAction.java @@ -91,8 +91,8 @@ public class OpenArchiveAction extends DockingAction { } private GTreeNode getNodeForArchive(GTree tree, Archive archive) { - GTreeNode rootNode = tree.getRootNode(); - List allChildren = rootNode.getAllChildren(); + GTreeNode rootNode = tree.getModelRoot(); + List allChildren = rootNode.getChildren(); for (GTreeNode node : allChildren) { ArchiveNode archiveNode = (ArchiveNode) node; if (archiveNode.getArchive() == archive) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java index edd10c26b6..001b97bab6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveNode.java @@ -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 children = node.getAllChildren(); + List children = node.getChildren(); for (GTreeNode child : children) { if (!(child instanceof CategoryNode)) { continue; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNode.java index 80771a6662..96d3c410f6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArchiveRootNode.java @@ -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 iterator = getAllChildren().iterator(); + Iterator 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 iterator = getAllChildren().iterator(); + Iterator 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 generateChildren() { - List children = new ArrayList<>(); + List list = new ArrayList<>(); Iterator 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 allChildrenList = getAllChildren(); + List 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 allChildrenList = getAllChildren(); + List allChildrenList = getChildren(); Iterator 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 allChildrenList = getAllChildren(); + if (isLoaded()) { + GTreeNode node = createArchiveNode(archive, filterState); + List 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); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArrayPointerFilterState.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArrayPointerFilterState.java new file mode 100644 index 0000000000..83962e721d --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ArrayPointerFilterState.java @@ -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; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java index e67b3b6880..67a9926a58 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/BuiltInArchiveNode.java @@ -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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/CategoryNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/CategoryNode.java index f51f932c16..b81566f8f9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/CategoryNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/CategoryNode.java @@ -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 children) { - if (children == null) { - return -1; - } - return Collections.binarySearch(children, node); - } - @Override protected List 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 children = new ArrayList<>(subCategories.length + dataTypes.length); + List 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 allChildrenList = getAllChildren(); + CategoryNode node = new CategoryNode(newCategory, filterState); + List 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 allChildrenList = getAllChildren(); + List 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; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeArchiveGTree.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeArchiveGTree.java index 98910cc8ae..246b1cf512 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeArchiveGTree.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeArchiveGTree.java @@ -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 archiveNodes = getRootNode().getChildren(); + List archiveNodes = getModelRoot().getChildren(); for (GTreeNode treeNode : archiveNodes) { if (treeNode instanceof ProjectArchiveNode) { ProjectArchiveNode projectArchiveNode = (ProjectArchiveNode) treeNode; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java index c0021a5d40..77bfe96fc6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeNode.java @@ -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 generateChildren() { + return Collections.emptyList(); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java index a2c9e1e902..182ebc471b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeTreeNode.java @@ -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 pastedNodes); + public abstract boolean canPaste(List 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(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java index 2d8a54573f..fdc506ec51 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DomainFileArchiveNode.java @@ -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,13 +109,13 @@ public class DomainFileArchiveNode extends ArchiveNode { } @Override - public boolean isSystemNode() { - return true; + public boolean canDelete() { + return false; } @Override public Icon getIcon(boolean expanded) { - + ImageIcon baseIcon = archive.getIcon(expanded); BackgroundIcon bgIcon = new BackgroundIcon(24, 16, isVersioned); MultiIcon multiIcon = new MultiIcon(bgIcon); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java index 16cb4b0340..4d12f658f8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/FileArchiveNode.java @@ -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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/InvalidArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/InvalidArchiveNode.java index 2701694147..1babc27c02 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/InvalidArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/InvalidArchiveNode.java @@ -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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java index 207801570b..57198b7a0a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProgramArchiveNode.java @@ -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]"; } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java index f1ac16311a..8763a3cf14 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/ProjectArchiveNode.java @@ -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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialog.java index 5784ad3ad3..a1d864e79d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialog.java @@ -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 dtNodes = new ArrayList<>(); getDataTypeNodes(root, dtNodes); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeDeleteTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeDeleteTask.java index 0087268387..5a98de8b3d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeDeleteTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeDeleteTask.java @@ -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 children = root.getAllChildren(); + GTreeNode root = tree.getModelRoot(); + List children = root.getChildren(); for (GTreeNode archive : children) { tree.collapseAll(archive); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterTree.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterTree.java index c6c6f0ba91..ccdddf20ce 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterTree.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/register/RegisterTree.java @@ -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 allChildren = getAllChildren(); + List 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 nodes = new ArrayList(); 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 { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java index f8a5689ae8..42b6587665 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java @@ -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 toDelete = new LinkedList<>(); - Iterator nodes = new BreadthFirstIterator(scriptCategoryTree, scriptRoot); + Iterator nodes = new BreadthFirstIterator(scriptRoot); for (GTreeNode node : CollectionUtils.asIterable(nodes)) { String[] path = getCategoryPath(node); List category = Arrays.asList(path); @@ -780,8 +781,9 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { } }); - scriptCategoryTree.getSelectionModel().setSelectionMode( - TreeSelectionModel.SINGLE_TREE_SELECTION); + 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 children = rootNode.getChildren(); for (GTreeNode node : children) { String[] path = getCategoryPath(node); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/RootNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/RootNode.java index 2c41c72944..0ec8713d25 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/RootNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/RootNode.java @@ -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 children = parent.getAllChildren(); + List 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 allChildren = parent.getAllChildren(); + List allChildren = parent.getChildren(); for (GTreeNode child : allChildren) { String nodeName = child.getName(); String newNodeName = newChild.getName(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/ScriptCategoryNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/ScriptCategoryNode.java index dfec890534..018f98657f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/ScriptCategoryNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/ScriptCategoryNode.java @@ -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; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolGTree.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolGTree.java index ba28858040..89d512ea1f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolGTree.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolGTree.java @@ -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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java index 4fde47be8b..2d89ba0bd6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java @@ -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(); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ClassCategoryNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ClassCategoryNode.java index eeb7232da9..5bcfd2e031 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ClassCategoryNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ClassCategoryNode.java @@ -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 list = new ArrayList(); + 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; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ClassSymbolNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ClassSymbolNode.java index 42740bda97..1e1e166f82 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ClassSymbolNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ClassSymbolNode.java @@ -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 = diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/CodeSymbolNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/CodeSymbolNode.java index 52b2f81ef7..f337aea651 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/CodeSymbolNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/CodeSymbolNode.java @@ -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"); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/FunctionCategoryNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/FunctionCategoryNode.java index 17055fbe62..8692b83324 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/FunctionCategoryNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/FunctionCategoryNode.java @@ -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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LabelCategoryNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LabelCategoryNode.java index 2c59923174..ad07089d75 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LabelCategoryNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LabelCategoryNode.java @@ -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; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LibrarySymbolNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LibrarySymbolNode.java index b1a5eb900b..2f189dc660 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LibrarySymbolNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LibrarySymbolNode.java @@ -121,5 +121,4 @@ public class LibrarySymbolNode extends SymbolNode { public Namespace getNamespace() { return (Library) symbol.getObject(); } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LocalVariableSymbolNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LocalVariableSymbolNode.java index 5a9d37e4fc..ea605ef8d1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LocalVariableSymbolNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/LocalVariableSymbolNode.java @@ -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,29 +15,28 @@ */ 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"); - - LocalVariableSymbolNode(Program program, Symbol symbol) { - super( program, symbol ); - } + public static final Icon LOCAL_VARIABLE_ICON = + ResourceManager.loadImage("images/LocalVariable.gif"); - @Override - public Icon getIcon(boolean expanded) { - return LOCAL_VARIABLE_ICON; - } - - @Override - public void setNodeCut(boolean isCut) { - throw new UnsupportedOperationException( "Cannot cut a local variable node" ); - } + LocalVariableSymbolNode(Program program, Symbol symbol) { + super(program, symbol); + } + + @Override + public Icon getIcon(boolean expanded) { + return LOCAL_VARIABLE_ICON; + } + + @Override + public void setNodeCut(boolean isCut) { + throw new UnsupportedOperationException("Cannot cut a local variable node"); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/NamespaceSymbolNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/NamespaceSymbolNode.java index 11141f3142..427f5d7630 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/NamespaceSymbolNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/NamespaceSymbolNode.java @@ -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 = diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/OrganizationNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/OrganizationNode.java index 884646f87f..eef44f14fb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/OrganizationNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/OrganizationNode.java @@ -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 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 list, int max, int parentLevel, GTreeNode parent, - int indexInParent, TaskMonitor monitor) throws CancelledException { + private OrganizationNode(List 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 organize(List 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 children * @param parentLevel node depth in the tree of Organization nodes. - * @return the given list subgrouped as outlined above. - * @throws CancelledException + * @return the given list sub-grouped as outlined above. + * @throws CancelledException if the operation is cancelled */ private static List computeChildren(List list, int maxNodes, GTreeNode parent, int parentLevel, TaskMonitor monitor) throws CancelledException { List children; if (list.size() <= maxNodes) { children = new ArrayList<>(list); - Iterator 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( - s2.substring(0, diffLevel + 1)) != 0; + return s1.substring(0, diffLevel + 1) + .compareToIgnoreCase( + s2.substring(0, diffLevel + 1)) != 0; } private static void addNode(List children, List 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 generateChildren(TaskMonitor monitor) throws CancelledException { + // not used, children generated in constructor + return null; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ParameterSymbolNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ParameterSymbolNode.java index c8510a0305..56ec805771 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ParameterSymbolNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/ParameterSymbolNode.java @@ -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 { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolCategoryNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolCategoryNode.java index 59d7726bfd..ac0912127f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolCategoryNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolCategoryNode.java @@ -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 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 comparator = symbolTreeNode.getChildrenComparator(); - List children = parentNode.getAllChildren(); + List 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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolNode.java index 5caf5fbdb8..fa2b687df9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolNode.java @@ -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, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolTreeNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolTreeNode.java index 9fe6c751d2..1915b28426 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolTreeNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolTreeNode.java @@ -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}. * *

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 pastedNodes); + public abstract boolean canPaste(List 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 getChildrenComparator() { + public Comparator 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. * - *

Note: This is can degenerate into a brute-force search algorithm, but works in + *

Note: This can degenerate into a brute-force search algorithm, but works in * all normal cases using a binary search. - * - *

Implementation Note: It is a bit unusual to put this logic into a default - * 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 children = getAllChildren(); + List 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 getAllChildren(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolTreeRootNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolTreeRootNode.java index 1d393093d0..f2a77cdb81 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolTreeRootNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/nodes/SymbolTreeRootNode.java @@ -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 allChildren = getAllChildren(); + public SymbolNode symbolAdded(Symbol symbol) { + SymbolNode returnNode = null; + List 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 allChildren = getAllChildren(); + List 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; - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBActionManager.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBActionManager.java index 8f2b82b94f..5a431906c6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBActionManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBActionManager.java @@ -944,8 +944,9 @@ class FSBActionManager { private void doOpenFileSystem(FSRL containerFSRL, TaskMonitor monitor) { try { monitor.setMessage("Probing " + containerFSRL.getName() + " for filesystems"); - FileSystemRef ref = FileSystemService.getInstance().probeFileForFilesystem( - containerFSRL, monitor, FileSystemProbeConflictResolver.GUI_PICKER); + FileSystemRef ref = FileSystemService.getInstance() + .probeFileForFilesystem( + containerFSRL, monitor, FileSystemProbeConflictResolver.GUI_PICKER); if (ref == null) { Msg.showWarn(this, plugin.getTool().getActiveWindow(), "Open Filesystem", "No filesystem provider for " + containerFSRL.getName()); @@ -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,16 +1175,17 @@ class FSBActionManager { gTree.runTask(monitor -> { try { FileSystemRef fsRef = - FileSystemService.getInstance().probeFileForFilesystem( - containerFSRL, monitor, - FileSystemProbeConflictResolver.GUI_PICKER); + FileSystemService.getInstance() + .probeFileForFilesystem( + containerFSRL, monitor, + FileSystemProbeConflictResolver.GUI_PICKER); if (fsRef == null) { Msg.showWarn(this, gTree, "No File System Provider", "No file system provider for " + containerFSRL.getName()); return; } - FSBRootNode nestedRootNode = new FSBRootNode(fsRef, gTree, fileNode); + FSBRootNode nestedRootNode = new FSBRootNode(fsRef, fileNode); FSBRootNode containingFSBRootNode = FSBNode.findContainingFileSystemFSBRootNode(fileNode); if (containingFSBRootNode != null) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBDirNode.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBDirNode.java index 396a4d0fdd..3fd891d507 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBDirNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBDirNode.java @@ -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; *

* Visible to just this package. */ -public class FSBDirNode extends GTreeSlowLoadingNode implements FSBNode { +public class FSBDirNode extends FSBNode { private FSRL fsrl; FSBDirNode(FSRL fsrl) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBFileNode.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBFileNode.java index ce9eee96f1..8ba8b0caeb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBFileNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBFileNode.java @@ -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. *

* 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 generateChildren(TaskMonitor monitor) throws CancelledException { + return Collections.emptyList(); + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBNode.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBNode.java index c48d261d2d..af6fc0458f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBNode.java @@ -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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBRootNode.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBRootNode.java index 1aef4e811a..c7d7a4e2f3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBRootNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBRootNode.java @@ -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; *

* 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 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(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBUtils.java index 8a29698e36..ac18d30e66 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBUtils.java @@ -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(); } diff --git a/Ghidra/Features/Base/src/test.slow/java/docking/widgets/tree/GTreeFilterTest.java b/Ghidra/Features/Base/src/test.slow/java/docking/widgets/tree/GTreeFilterTest.java index e9adecb531..1761704cd2 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/widgets/tree/GTreeFilterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/widgets/tree/GTreeFilterTest.java @@ -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 children = root.getChildren(); + List 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 children = root.getChildren(); + List 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 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; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/CallTreePluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/CallTreePluginTest.java index eee878022f..f90100563b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/CallTreePluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/calltree/CallTreePluginTest.java @@ -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 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 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 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 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 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 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 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 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 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 ref = new AtomicReference<>(); - runSwing(() -> ref.set(tree.getRootNode())); + final AtomicReference 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 nodes = getNodesByDepth(false); + private void assertOutgoingNoNode(String name, int depth, boolean filtered) { + List 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 nodes = getNodesByDepth(false); + private void assertOutgoingMaxDepth(int depth, boolean filtered) { + List 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 nodes = getNodesByDepth(true); + private void assertIncomingNoNode(String name, int depth, boolean filtered) { + List 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 nodes = getNodesByDepth(true); + private void assertIncomingMaxDepth(int depth, boolean filtered) { + List nodes = getNodesByDepth(true, filtered); NodeDepthInfo maxDepthNode = nodes.get(nodes.size() - 1); assertEquals("Node max depth does not match: " + maxDepthNode, depth, maxDepthNode.depth); } private List getNodesByDepth(boolean incoming) { + return getNodesByDepth(incoming, false); + } + + private List getNodesByDepth(boolean incoming, boolean filtered) { List 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 } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorArchiveTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorArchiveTest.java index 188830ed15..a782932b32 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorArchiveTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorArchiveTest.java @@ -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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java index 14e66d7429..1f6f476384 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java @@ -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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java index 27b35f40f3..35eb92c428 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/AbstractCreateArchiveTest.java @@ -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); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadedTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadedTest.java index 77e531847f..ef27f17e81 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadedTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/ArchiveRemappedHeadedTest.java @@ -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()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java index dd13229014..ee20012c52 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateEnumFromSelectionTest.java @@ -72,7 +72,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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateLabelsFromEnumsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateLabelsFromEnumsTest.java index fdb91aa75e..4de0a89d95 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateLabelsFromEnumsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/CreateLabelsFromEnumsTest.java @@ -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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java index ea66f74002..6cc8e88e1c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeCopyMoveDragTest.java @@ -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 findConflictHandlerActionState( DataTypeConflictHandler.ConflictResolutionPolicy conflictMode) { - for (ActionState actionState : conflictHandlerModesAction.getAllActionStates()) { + for (ActionState actionState : conflictHandlerModesAction + .getAllActionStates()) { if (actionState.getUserData() == conflictMode) { return actionState; } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java index a4f2965880..2a2d8e268c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPluginTest.java @@ -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())); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java index 4ee38b921c..5403355262 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java @@ -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( diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/FavoritesAndMiscTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/FavoritesAndMiscTest.java index 6160a77f46..b2dbadd9ae 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/FavoritesAndMiscTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/FavoritesAndMiscTest.java @@ -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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java index cdaedf7e2c..5879379c90 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java @@ -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 children = node.getAllChildren(); + List 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 children = rootNode.getAllChildren(); + GTreeNode rootNode = tree.getModelRoot(); + List children = rootNode.getChildren(); for (GTreeNode node : children) { if (node.getName().equals(oldCategory)) { Assert.fail("Category in tree when expected not to be: " + oldCategory); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreePlugin3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreePlugin3Test.java index ca58a04a63..1f633cf190 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreePlugin3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreePlugin3Test.java @@ -297,7 +297,7 @@ public class SymbolTreePlugin3Test extends AbstractGhidraHeadedIntegrationTest { } } - List children = nsParentNode.getAllChildren(); + List children = nsParentNode.getChildren(); //@formatter:off List symbolNames = @@ -342,7 +342,7 @@ public class SymbolTreePlugin3Test extends AbstractGhidraHeadedIntegrationTest { util.waitForTree(); GTreeNode cnode = rootNode.getChild(4); - List children = cnode.getAllChildren(); + List children = cnode.getChildren(); //@formatter:off List symbolNames = diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreeTestUtils.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreeTestUtils.java index 3d8dd8e9d2..f903b985a8 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreeTestUtils.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreeTestUtils.java @@ -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 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( diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java index e9e5ae5bca..2d529b1da6 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java @@ -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 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 children = rootNode.getChildren(); assertEquals(2, children.size());// two archives diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/DataTreeDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/DataTreeDialogTest.java index 832be1b82a..9a69f702e5 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/DataTreeDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/DataTreeDialogTest.java @@ -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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/FrontEndPluginActionsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/FrontEndPluginActionsTest.java index b8f442cb6f..344a68ac37 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/FrontEndPluginActionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/main/datatree/FrontEndPluginActionsTest.java @@ -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())); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java index 7c4076f5d7..29137b33aa 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/KeyBindingUtilsTest.java @@ -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 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; } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/OptionsDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/OptionsDialogTest.java index 8947da7117..7abe9bcbb3 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/OptionsDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/bean/opteditor/OptionsDialogTest.java @@ -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); } diff --git a/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/info/FunctionBitPatternsGTreeNode.java b/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/info/FunctionBitPatternsGTreeNode.java index 0df6106c37..2c82a4f9e2 100644 --- a/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/info/FunctionBitPatternsGTreeNode.java +++ b/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/info/FunctionBitPatternsGTreeNode.java @@ -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 children = getChildren(); + List children = new ArrayList<>(getChildren()); Collections.sort(children); setChildren(children); //now set isLeaf diff --git a/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/info/FunctionBitPatternsGTreeRootNode.java b/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/info/FunctionBitPatternsGTreeRootNode.java index ae10f82352..60092f8430 100644 --- a/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/info/FunctionBitPatternsGTreeRootNode.java +++ b/Ghidra/Features/BytePatterns/src/main/java/ghidra/bitpatterns/info/FunctionBitPatternsGTreeRootNode.java @@ -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 children = getChildren(); + List children = new ArrayList<>(getChildren()); Collections.sort(children); setChildren(children); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/plugins/fileformats/FileFormatsPlugin.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/plugins/fileformats/FileFormatsPlugin.java index 27f351c39a..5405e523cb 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/plugins/fileformats/FileFormatsPlugin.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/plugins/fileformats/FileFormatsPlugin.java @@ -115,8 +115,9 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable { if (outputDirectory == null) { return; } - fsbContext.getTree().runTask( - monitor -> doExportToEclipse(fsrl, outputDirectory, monitor)); + 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()); } } } diff --git a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/TreeTestUtils.java b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/TreeTestUtils.java index 185780547d..a3fba31b9d 100644 --- a/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/TreeTestUtils.java +++ b/Ghidra/Features/ProgramDiff/src/test.slow/java/ghidra/app/plugin/core/diff/TreeTestUtils.java @@ -35,7 +35,7 @@ public class TreeTestUtils { if (text.equals(node.getName())) { return node.getTreePath(); } - List allChildren = node.getAllChildren(); + List allChildren = node.getChildren(); for (GTreeNode childNode : allChildren) { TreePath treePath = findPathToText(tree, childNode, text); if (treePath != null) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/ErrLogExpandableDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/ErrLogExpandableDialog.java index edb2ecec9c..d91ade235a 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/ErrLogExpandableDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/ErrLogExpandableDialog.java @@ -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 report; protected String title; protected boolean loaded = false; @@ -435,6 +435,9 @@ public class ErrLogExpandableDialog extends DialogComponentProvider { public ReportRootNode(String title, Collection 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)); - Throwable c = exc.getCause(); - if (c != null) { - if (c instanceof MultipleCauses) { - for (Throwable t : ((MultipleCauses) c).getCauses()) { - addNode(new ReportExceptionNode(t)); - } - } - else { - addNode(new ReportCauseNode(c)); + protected List generateChildren() { + List list = new ArrayList(); + list.add(new ReportStackTraceNode(exc)); + Throwable c = exc.getCause(); + if (c != null) { + if (c instanceof MultipleCauses) { + for (Throwable t : ((MultipleCauses) c).getCauses()) { + list.add(new ReportExceptionNode(t)); } } + else { + 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; - for (StackTraceElement te : exc.getStackTrace()) { - addNode(new ReportStackFrameNode(te)); - } + protected List generateChildren() { + List list = new ArrayList<>(); + for (StackTraceElement te : exc.getStackTrace()) { + 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) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsPanel.java b/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsPanel.java index 88b3dddda9..38ed5ff74e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsPanel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsPanel.java @@ -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); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsRootTreeNode.java b/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsRootTreeNode.java index aae4212022..5716f8b2bd 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsRootTreeNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsRootTreeNode.java @@ -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; - } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsTreeNode.java b/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsTreeNode.java index be90cb611b..722abc4106 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsTreeNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/options/editor/OptionsTreeNode.java @@ -83,7 +83,7 @@ class OptionsTreeNode extends GTreeLazyNode { @Override public boolean isLeaf() { - return getAllChildCount() == 0; + return getChildCount() == 0; } @Override diff --git a/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java b/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java index 6bbe8fcfc7..f2a0e37611 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/test/AbstractDockingTest.java @@ -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( diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/AbstractGTreeNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/AbstractGTreeNode.java deleted file mode 100644 index 788d0a0c43..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/AbstractGTreeNode.java +++ /dev/null @@ -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: - - *

    - *
  • getName()
  • - *
  • getToolTip()
  • - *
  • isLeaf()
  • - *
  • getIcon()
  • - *
- * - * Usage Notes: - *
    - *
  • The equals() method: The GTree 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 GTree. Java will do this by using the equals() method. - * There is a potential problem with this usage. If nodes within the GTree 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 equals() - * method on GTreeNode performs a comparison based upon instances. To fix this problem you - * must override the equals() method that can find the same logical nodes when - * the instances in memory have changed; typically this is done by overriding equals() - * to compare by node name. - *


    - *

    - * The GTreeNode 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, - * 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 equals() method. - *


    - *

    - * 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 equals() method to compare on - * {@link #getName()}, as outlined above. - *

  • - *
- */ -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 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 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 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 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 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 allChildren = getAllChildren(); - - doSetChildren(null, true); - for (GTreeNode gTreeNode : allChildren) { - gTreeNode.dispose(); - } - } - - @Override - public void setChildren(List 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 { - private TreePath ancestry; - private TreePath nextPath; - private Iterator childIt = null; - private Iterator 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 allPaths() { - return new AllPathsIterable(new TreePath(this)); - } - - @Override - public Iterator 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 allChildren = getAllChildren(); - if (allChildren.size() == 0) { - return; - } - - List newChildren = allChildren; - try { - setInProgress(); - List filteredChildren = new ArrayList(); - 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 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 GTreeNode Usage - */ - 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 { - private TreePath path; - - public AllPathsIterable(TreePath path) { - this.path = path; - } - - @Override - public Iterator iterator() { - return new AllPathsIterator(path); - } - } - -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java index 899a4ff719..537fcc831b 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java @@ -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}. - *

- * 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. - *

- * The pattern used by this class is to create doXXX 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. + *

+ * 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. + *

+ * 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}. + *

+ * 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 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 children; - private GTreeNode parent; - private List allChildrenList = null; - private List 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; } + /** + * 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!"); + } + this.parent = parent; + } + + // provides direct access to the children list + protected final List 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 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 generateChildren(); + + protected synchronized void doSetChildren(List childList) { + List 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(childList); + } + + if (oldChildren != null) { + for (GTreeNode node : oldChildren) { + node.dispose(); + } + } + } + + /** + * 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. + */ @Override + public GTreeNode clone() throws CloneNotSupportedException { + CoreGTreeNode clone = (GTreeNode) super.clone(); + clone.parent = null; + clone.children = null; + return (GTreeNode) clone; + } + public void dispose() { - parent = null; - if (allChildrenList == null) { - return; + + List oldChildren; + synchronized (this) { + oldChildren = children; + children = null; + parent = null; } - for (GTreeNode node : allChildrenList) { - node.dispose(); + + if (oldChildren != null) { + for (GTreeNode node : oldChildren) { + node.dispose(); + } + oldChildren.clear(); } - 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 doGetAllChildren() { - if (allChildrenList == null) { - return Collections.emptyList(); - } - return new ArrayList(allChildrenList); - } - - protected synchronized List doGetActiveChildren() { - if (activeChildrenList == null) { - return Collections.emptyList(); - } - return new ArrayList(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 + * 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. */ - protected synchronized int doGetIndexOfChild(GTreeNode node, List 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; + if (Swing.isSwingThread()) { + return true; } - - 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(); - 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 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 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(childList); - activeChildrenList = allChildrenList; - } - } - - // can't be in synchronized block! - if (notify) { - notifyNodeStructureChanged(this); - } - } - - protected void doSetActiveChildren(final List childList) { - - if (SwingUtilities.isEventDispatchThread()) { - swingSetActiveChilren(childList); - return; - } - - SystemUtilities.runSwingNow(new Runnable() { - @Override - public void run() { - swingSetActiveChilren(childList); - } - }); - } - - private void swingSetActiveChilren(List 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); + return !isInProgress(children); } /** - * Convenience method to clear any filtered items by restoring the active children of this - * node to be the complete set of 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. */ - 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()) { - return true; - } - node = node.getParent(); + private boolean isInProgress(List childList) { + if (childList != null && childList.size() == 1 && + childList.get(0) instanceof InProgressGTreeNode) { + return true; } return false; } -//================================================================================================== -// End Utility Methods -//================================================================================================== - } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java index fce0d43d08..613d0b9e30 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java @@ -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 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 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 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 { - worker.waitUntilNoJobsScheduled(Integer.MAX_VALUE); + 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(); } + } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeFilterTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeFilterTask.java similarity index 63% rename from Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeFilterTask.java rename to Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeFilterTask.java index 23013071c6..4008eb15ac 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeFilterTask.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeFilterTask.java @@ -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); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeLazyNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeLazyNode.java index 0181188d87..b9f4de5fbe 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeLazyNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeLazyNode.java @@ -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 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 generateChildren = generateChildren(); - if (isChildrenLoadedOrInProgress()) { - return; - } - doSetChildren(generateChildren, false); } @Override public void addNode(int index, GTreeNode node) { - if (!isChildrenLoadedOrInProgress()) { + if (isLoaded()) { + super.addNode(index, node); + } + } + + @Override + public void addNodes(List nodes) { + if (isLoaded()) { + super.addNodes(nodes); + } + } + + @Override + public void removeAll() { + if (isLoaded()) { + unloadChildren(); + } + } + + @Override + public void removeNode(GTreeNode node) { + if (!isLoaded()) { return; } - 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 getAllChildrenIfLoaded() { - if (isChildrenLoadedOrInProgress()) { - return getAllChildren(); - } - - // not loaded; do not load - return Collections.emptyList(); + super.removeNode(node); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeNode.java index b62073ce85..1dda357271 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeNode.java @@ -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, Iterable { +/** + * 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 { + private static AtomicLong NEXT_ID = new AtomicLong(); + + private final long id; + + protected GTreeNode() { + id = NEXT_ID.incrementAndGet(); + } + + @Override + protected List 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. - * @param node the node to add as a child. + * 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 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 getAllChildren(); + public void addNode(int index, GTreeNode node) { + children().add(index, node); + node.setParent(this); + fireNodeAdded(this, node); + } /** * Returns all of the visible children of this node. If there are filtered nodes, then @@ -78,28 +120,32 @@ public interface GTreeNode extends Comparable, Iterable { * @return all of the visible children of this node. If there are filtered nodes, then * they will not be returned. */ - public List getChildren(); + public List getChildren() { + return Collections.unmodifiableList(children()); + } /** * Returns the number of visible children of this node. Does not include * nodes that are current filtered out. * @return the number of visible children of this node. */ - public int getChildCount(); - - /** - * Returns the number of all children of this node. Includes nodes that - * are currently filtered out. - * @return the number of all, Iterable { * @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, Iterable { * @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 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 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 childList); + public void setChildren(List 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, Iterable { * @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, Iterable { * @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. + *

+ * 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 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 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 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 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; + } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/AbstractGTreeRootNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeRootParentNode.java similarity index 52% rename from Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/AbstractGTreeRootNode.java rename to Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeRootParentNode.java index c8cde50584..4858f28f79 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/AbstractGTreeRootNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeRootParentNode.java @@ -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; + } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeSlowLoadingNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeSlowLoadingNode.java index a6dfab7516..73dbb0accb 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeSlowLoadingNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeSlowLoadingNode.java @@ -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 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 generateChildren(TaskMonitor monitor) + throws CancelledException; @Override - protected final void loadChildren() { - + protected final List 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); - tree.runTask(loadTask); - return; - } + if (Swing.isSwingThread() && tree != null) { + LoadChildrenTask loadTask = new LoadChildrenTask(tree); + tree.runTask(loadTask); + 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 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()); - return; + private class LoadChildrenTask extends GTreeTask { + + LoadChildrenTask(GTree tree) { + super(tree); } - long progressValue = monitor.getProgress(); - long maxValue = monitor.getMaximum(); - try { - setChildren(generateChildren(monitor)); - } - catch (CancelledException e) { - SystemUtilities.runSwingNow(new Runnable() { - @Override - public void run() { - if (tree != null) { - tree.collapseAll(tree.getRootNode()); - } + @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) { + if (!tree.isDisposed()) { + runOnSwingThread(new Runnable() { + @Override + public void run() { + tree.collapseAll(tree.getViewRoot()); + } + }); } - }); - doSetChildren(null, true); + doSetChildren(null); + } + finally { + monitor.initialize(maxValue); + monitor.setProgress(progressValue); + } } - 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 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); - } - } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeState.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeState.java index ad46b5a554..6bb1ac423e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeState.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeState.java @@ -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 getSelectionPaths(GTreeNode node) { TreePath[] allSelectionPaths = tree.getSelectionPaths(); - if (node == tree.getRootNode()) { + if (node == tree.getViewRoot()) { return CollectionUtils.asList(allSelectionPaths); } if (allSelectionPaths == null) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeTask.java index 11edf3cdaa..47131a9b59 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeTask.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeTask.java @@ -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(); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/TreeTaskMonitor.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/TreeTaskMonitor.java new file mode 100644 index 0000000000..91980c5356 --- /dev/null +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/TreeTaskMonitor.java @@ -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(); + } +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/GTreeDragNDropAdapter.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/GTreeDragNDropAdapter.java index 1f770266bf..efd7d7c7af 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/GTreeDragNDropAdapter.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/GTreeDragNDropAdapter.java @@ -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; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/GTreeModel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/GTreeModel.java index b647698d5b..95c6cd6042 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/GTreeModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/GTreeModel.java @@ -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 listeners = new ArrayList(); 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; + } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/InProgressGTreeNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/InProgressGTreeNode.java index 07b0076c48..dca009522b 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/InProgressGTreeNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/InProgressGTreeNode.java @@ -15,19 +15,18 @@ */ package docking.widgets.tree.internal; - import javax.swing.Icon; -import docking.widgets.tree.AbstractGTreeNode; +import docking.widgets.tree.GTreeNode; import docking.widgets.tree.GTreeNode; import resources.ResourceManager; - -public class InProgressGTreeNode extends AbstractGTreeNode { +public class InProgressGTreeNode extends GTreeNode { private static final Icon ICON = ResourceManager.loadImage("images/magnifier.png"); public InProgressGTreeNode() { } + @Override public Icon getIcon(boolean expanded) { return ICON; @@ -52,10 +51,5 @@ public class InProgressGTreeNode extends AbstractGTreeNode { public int compareTo(GTreeNode o) { return 0; } - @Override - protected void loadChildren() { - // TODO Auto-generated method stub - - } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/InProgressGTreeRootNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/InProgressGTreeRootNode.java index 17421ef5ad..b71f3d0331 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/InProgressGTreeRootNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/internal/InProgressGTreeRootNode.java @@ -17,10 +17,10 @@ package docking.widgets.tree.internal; import javax.swing.Icon; -import docking.widgets.tree.AbstractGTreeRootNode; +import docking.widgets.tree.GTreeNode; import resources.ResourceManager; -public class InProgressGTreeRootNode extends AbstractGTreeRootNode { +public class InProgressGTreeRootNode extends GTreeNode { private static final Icon ICON = ResourceManager.loadImage("images/magnifier.png"); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/BreadthFirstIterator.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/BreadthFirstIterator.java index 28efb01e8c..7c9af519dd 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/BreadthFirstIterator.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/BreadthFirstIterator.java @@ -16,34 +16,19 @@ package docking.widgets.tree.support; import java.util.*; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; -import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; +/** + * Implements an iterator over all GTreeNodes in some gTree (or subtree). The nodes are + * return in breadth first order. + */ public class BreadthFirstIterator implements Iterator { - Queue nodeQueue = new LinkedList(); - GTreeNode lastNode; - private boolean filtered; - private long mod; - private GTree tree; + private Queue nodeQueue = new LinkedList(); + private GTreeNode lastNode; - public BreadthFirstIterator(GTree tree, GTreeNode node) { - this(tree, node, true); - } - - public BreadthFirstIterator(GTree tree, GTreeNode node, boolean filtered) { + public BreadthFirstIterator(GTreeNode node) { nodeQueue.add(node); - mod = tree.getModificationID(); - this.tree = tree; - this.filtered = filtered; - } - - public Stream stream() { - Iterable iterable = () -> this; - Stream stream = StreamSupport.stream(iterable.spliterator(), false); - return stream; } @Override @@ -53,40 +38,21 @@ public class BreadthFirstIterator implements Iterator { @Override public GTreeNode next() { - checkForConcurrentModification(); lastNode = nodeQueue.poll(); if (lastNode != null) { - if (filtered) { - List children = lastNode.getChildren(); - nodeQueue.addAll(children); - } - else { - List children = lastNode.getAllChildren(); - nodeQueue.addAll(children); - } + List children = lastNode.getChildren(); + nodeQueue.addAll(children); } return lastNode; } @Override public void remove() { - checkForConcurrentModification(); GTreeNode parent = lastNode.getParent(); if (parent == null) { throw new IllegalArgumentException("Can't delete root node!"); } parent.removeNode(lastNode); - if (filtered) { - nodeQueue.removeAll(lastNode.getChildren()); - } - else { - nodeQueue.removeAll(lastNode.getAllChildren()); - } - } - - private void checkForConcurrentModification() { - if (tree.getModificationID() != mod) { - throw new ConcurrentModificationException(); - } + nodeQueue.removeAll(lastNode.getChildren()); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/DepthFirstIterator.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/DepthFirstIterator.java index 9ca8ba7992..a23f13ae29 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/DepthFirstIterator.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/DepthFirstIterator.java @@ -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,44 +15,51 @@ */ package docking.widgets.tree.support; -import java.util.ConcurrentModificationException; import java.util.Iterator; +import java.util.Stack; + +import org.apache.commons.collections4.IteratorUtils; -import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; +/** + * Implements an iterator over all GTreeNodes in some gTree (or subtree). The nodes are + * return in depth first order. + */ public class DepthFirstIterator implements Iterator { - private GTreeNode root; - private GTreeNode next; + private Stack> stack = new Stack<>(); + private Iterator it; private GTreeNode lastNode; - private final GTree tree; - private final long mod; - public DepthFirstIterator(GTree tree, GTreeNode node) { - this.tree = tree; - this.root = node; - this.next = node; - this.mod = tree.getModificationID(); + + public DepthFirstIterator(GTreeNode node) { + it = IteratorUtils.singletonIterator(node); } + @Override public boolean hasNext() { - return next != null; + return !stack.isEmpty() || it.hasNext(); } + @Override public GTreeNode next() { - checkForConcurrentModification(); - lastNode = next; - next = findNext(next); + if (!it.hasNext()) { + if (stack.isEmpty()) { + return null; + } + it = stack.pop(); + } + lastNode = it.next(); + if (lastNode.getChildCount() > 0) { + if (it.hasNext()) { + stack.push(it); + } + it = lastNode.getChildren().iterator(); + } return lastNode; } - private void checkForConcurrentModification() { - if (tree.getModificationID() != mod) { - throw new ConcurrentModificationException(); - } - } - + @Override public void remove() { - checkForConcurrentModification(); GTreeNode parent = lastNode.getParent(); if (parent == null) { throw new IllegalArgumentException("Can't delete root node!"); @@ -61,18 +67,4 @@ public class DepthFirstIterator implements Iterator { parent.removeNode(lastNode); } - private GTreeNode findNext(GTreeNode node) { - if (node.getChildCount() > 0) { - return node.getChild(0); - } - while(node != root) { - GTreeNode parent = node.getParent(); - int nextIndexInParent = node.getIndexInParent()+1; - if (nextIndexInParent < parent.getChildCount()) { - return parent.getChild(nextIndexInParent); - } - node = parent; - } - return null; - } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/NewTestApp.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/NewTestApp.java index 337e3beea9..f5e7a5a07a 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/NewTestApp.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/support/NewTestApp.java @@ -55,9 +55,10 @@ public class NewTestApp extends JPanel { container.add(tree, BorderLayout.CENTER); JButton button = new JButton("Push Me"); container.add(button, BorderLayout.SOUTH); - frame.setSize(400,600); + frame.setSize(400, 600); frame.setVisible(true); button.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { TreePath selectionPath = tree.getSelectionPath(); if (selectionPath != null) { @@ -68,29 +69,36 @@ public class NewTestApp extends JPanel { }); } + public static long getMemoryUsage() { Runtime rt = Runtime.getRuntime(); - return rt.totalMemory()-rt.freeMemory(); + return rt.totalMemory() - rt.freeMemory(); } } + interface FileData { File getFile(); } -class FileNode extends AbstractGTreeNode implements FileData { + +class FileNode extends GTreeNode implements FileData { protected File file; public String tempName; FileNode(File file) { this.file = file; } + @Override public File getFile() { return file; } + + @Override public Icon getIcon(boolean expanded) { return null; } + @Override public String getName() { if (tempName != null) { return tempName; @@ -102,6 +110,7 @@ class FileNode extends AbstractGTreeNode implements FileData { return file.getName(); } + @Override public String getToolTip() { return file.getAbsolutePath(); } @@ -118,22 +127,26 @@ class FileNode extends AbstractGTreeNode implements FileData { public String toString() { return file.getAbsolutePath(); } + @Override public boolean equals(Object obj) { - if ( obj instanceof FileNode ) { - return file.equals( ((FileNode) obj).file ); + if (obj instanceof FileNode) { + return file.equals(((FileNode) obj).file); } return false; } + @Override public int hashCode() { return file.getAbsolutePath().hashCode(); } + @Override public boolean isLeaf() { return true; } } + class DirectoryNode extends GTreeLazyNode implements FileData { private final File file; @@ -141,14 +154,17 @@ class DirectoryNode extends GTreeLazyNode implements FileData { this.file = file; } + @Override public boolean isLeaf() { return false; } + @Override public File getFile() { return file; } + @Override public List generateChildren() { List children = new ArrayList(); @@ -174,18 +190,22 @@ class DirectoryNode extends GTreeLazyNode implements FileData { } return getName().compareTo(o.getName()); } + @Override public Icon getIcon(boolean expanded) { return null; } + @Override public String getName() { return file.getName(); } + @Override public String getToolTip() { return file.getAbsolutePath(); } + @Override public int hashCode() { return file.getAbsolutePath().hashCode(); @@ -193,55 +213,50 @@ class DirectoryNode extends GTreeLazyNode implements FileData { } -class RootNode extends DirectoryNode implements GTreeRootNode { - GTree tree; - +class RootNode extends DirectoryNode { RootNode(File file) { super(file); - - } - - @Override - public GTree getGTree() { - return tree; - } - - @Override - public void setGTree(GTree tree) { - this.tree = tree; } } class DragNDropHandler implements GTreeDragNDropHandler { public static DataFlavor[] supportedFlavors = { - DataFlavor.stringFlavor, - DataFlavor.javaFileListFlavor + DataFlavor.stringFlavor, + DataFlavor.javaFileListFlavor }; + @Override public void drop(GTreeNode destUserData, Transferable transferable, int dropAction) { - Msg.info(this, "Dropped the following Files onto "+destUserData); + Msg.info(this, "Dropped the following Files onto " + destUserData); try { - List list = (List)transferable.getTransferData(DataFlavor.javaFileListFlavor); + List list = (List) transferable.getTransferData(DataFlavor.javaFileListFlavor); Iterator it = list.iterator(); - while(it.hasNext()) { - Msg.info(this, "\t"+it.next()); + while (it.hasNext()) { + Msg.info(this, "\t" + it.next()); } - } catch (UnsupportedFlavorException e) { - } catch (IOException e) { + } + catch (UnsupportedFlavorException e) { + } + catch (IOException e) { } } + + @Override public DataFlavor[] getSupportedDataFlavors(List dragUserData) { return supportedFlavors; } + + @Override public int getSupportedDragActions() { return DnDConstants.ACTION_COPY; } + @Override public Object getTransferData(List dragUserData, DataFlavor flavor) { if (flavor.equals(DataFlavor.javaFileListFlavor)) { List fileList = new ArrayList(); for (GTreeNode node : dragUserData) { - FileData fileData = (FileData)node; + FileData fileData = (FileData) node; fileList.add(fileData.getFile()); } return fileList; @@ -249,7 +264,7 @@ class DragNDropHandler implements GTreeDragNDropHandler { else if (flavor.equals(DataFlavor.stringFlavor)) { StringBuffer buf = new StringBuffer(); Iterator it = dragUserData.iterator(); - while(it.hasNext()) { + while (it.hasNext()) { buf.append(it.next().toString()); buf.append("\n"); } @@ -258,12 +273,15 @@ class DragNDropHandler implements GTreeDragNDropHandler { return null; } + + @Override public boolean isDropSiteOk(GTreeNode destUserData, DataFlavor[] flavors, int dropAction) { if (containsFlavor(flavors, DataFlavor.javaFileListFlavor)) { return (destUserData instanceof DirectoryNode); } return false; } + private boolean containsFlavor(DataFlavor[] flavors, DataFlavor flavor) { for (DataFlavor flavor2 : flavors) { if (flavor2.equals(flavor)) { @@ -272,6 +290,8 @@ class DragNDropHandler implements GTreeDragNDropHandler { } return false; } + + @Override public boolean isStartDragOk(List dragUserData, int dragAction) { return true; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/AbstractSelectPathsTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/AbstractSelectPathsTask.java deleted file mode 100644 index e0b6fa0781..0000000000 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/AbstractSelectPathsTask.java +++ /dev/null @@ -1,111 +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.tasks; - -import java.util.ArrayList; -import java.util.List; - -import javax.swing.JTree; -import javax.swing.tree.TreePath; - -import docking.widgets.tree.*; -import docking.widgets.tree.internal.GTreeSelectionModel; -import docking.widgets.tree.support.GTreeSelectionEvent; -import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin; -import ghidra.util.task.TaskMonitor; - -public abstract class AbstractSelectPathsTask extends GTreeTask { - - private final EventOrigin origin; - private boolean expandingDisabled; - - AbstractSelectPathsTask(GTree gTree, JTree jTree, GTreeSelectionEvent.EventOrigin origin) { - super(gTree); - this.origin = origin; - } - - abstract List getTreePaths(); - - /** - * Tells the JTree to not expand paths for each selection that is set upon it. Doing this - * will speed-up performance. However, only call this when some other task is going to - * ensure that paths are properly expanded. - * - * @param disabled true to disable - */ - void setExpandingDisabled(boolean disabled) { - this.expandingDisabled = disabled; - } - - @Override - public void run(TaskMonitor monitor) { - monitor.setMessage("Selecting paths"); - List paths = getTreePaths(); - monitor.initialize(paths.size()); - List translatedPaths = new ArrayList<>(); - for (TreePath path : paths) { - if (monitor.isCancelled()) { - return; - } - TreePath xPath = translatePath(path, monitor); - if (xPath != null && !isFiltered(xPath)) { - translatedPaths.add(xPath); - } - monitor.incrementProgress(1); - } - selectPaths(translatedPaths.toArray(new TreePath[translatedPaths.size()]), monitor); - } - - private boolean isFiltered(TreePath path) { - GTreeNode node = (GTreeNode) path.getLastPathComponent(); - return node.isFilteredOut(); - } - - private void selectPaths(TreePath[] treePaths, TaskMonitor monitor) { - - runOnSwingThread(() -> { - if (monitor.isCancelled()) { - return; // we can be cancelled while waiting for Swing to run us - } - - boolean wasEnabled = jTree.getExpandsSelectedPaths(); - if (expandingDisabled) { - jTree.setExpandsSelectedPaths(false); - } - - try { - doSelectPaths(treePaths); - } - finally { - if (expandingDisabled) { - jTree.setExpandsSelectedPaths(wasEnabled); - } - } - }); - } - - private void doSelectPaths(TreePath[] treePaths) { - GTreeSelectionModel selectionModel = tree.getGTSelectionModel(); - selectionModel.setSelectionPaths(treePaths, origin); - - if (treePaths != null && treePaths.length > 0) { - // Scroll to the last item, as the tree will make the given path appear at the - // bottom of the view. By scrolling the last item, all the selected items above - // this one will appear in the view as well. - jTree.scrollPathToVisible(treePaths[treePaths.length - 1]); - } - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeExpandAllTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeExpandAllTask.java index 1b16b2e87e..b5f929ddb8 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeExpandAllTask.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeExpandAllTask.java @@ -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,47 +15,54 @@ */ package docking.widgets.tree.tasks; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; - import java.util.List; import javax.swing.tree.TreePath; import docking.widgets.tree.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; public class GTreeExpandAllTask extends GTreeTask { - - private final GTreeNode root; + // The MAX number of nodes to expand. Expanding nodes is fairly expensive and must + // be done in the Swing thread. So to avoid hanging up the GUI, we limit it to + // a reasonable number. Also, this task is primarily used to show filter results + // and if you have too many results, you probably aren't going to look at them + // all anyway. + private static final int MAX = 1000; + private final GTreeNode node; public GTreeExpandAllTask(GTree tree, GTreeNode node) { super(tree); - this.root = node; + this.node = node; } @Override public void run(TaskMonitor monitor) { - int max = 100; // Note: this used to be root.getNonLeafCount(), but that triggered a load - monitor.initialize(max); + monitor.initialize(1000); monitor.setMessage("Expanding nodes..."); try { - expandNode(root, monitor); + expandNode(node, monitor); } catch (CancelledException e) { // Not everything expanded which is ok } } - protected void expandNode(GTreeNode node, TaskMonitor monitor) throws CancelledException { - if (node.isLeaf()) { + protected void expandNode(GTreeNode parent, TaskMonitor monitor) throws CancelledException { + // only expand MAX number of nodes. + if (monitor.getProgress() >= MAX) { + return; + } + if (parent.isLeaf()) { return; } monitor.checkCanceled(); - List allChildren = node.getChildren(); + List allChildren = parent.getChildren(); if (allChildren.size() == 0) { return; } - TreePath treePath = node.getTreePath(); + TreePath treePath = parent.getTreePath(); if (!jTree.isExpanded(treePath)) { expandPath(treePath, monitor); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeExpandPathsTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeExpandPathsTask.java index d07f792015..f999b51159 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeExpandPathsTask.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeExpandPathsTask.java @@ -26,11 +26,9 @@ import ghidra.util.task.TaskMonitor; public class GTreeExpandPathsTask extends GTreeTask { private final List paths; - private final JTree jTree; - public GTreeExpandPathsTask(GTree gTree, JTree tree, List paths) { + public GTreeExpandPathsTask(GTree gTree, List paths) { super(gTree); - this.jTree = tree; this.paths = paths; } @@ -46,13 +44,16 @@ public class GTreeExpandPathsTask extends GTreeTask { } private void ensurePathLoaded(TreePath path, TaskMonitor monitor) { - GTreeNode parent = tree.getRootNode(); + GTreeNode parent = tree.getViewRoot(); if (parent == null) { return; // disposed? } - List allChildren = parent.getAllChildren(); Object[] nodeList = path.getPath(); + if (nodeList.length < 2) { + return; // only the root is in the path + } + List allChildren = parent.getChildren(); for (int i = 1; i < nodeList.length; i++) { if (monitor.isCancelled()) { return; @@ -61,7 +62,7 @@ public class GTreeExpandPathsTask extends GTreeTask { if (node == null) { return; } - allChildren = node.getAllChildren(); + allChildren = node.getChildren(); parent = node; } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeLoadChildrenTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeLoadChildrenTask.java index 83b03a112b..44b73dbb9a 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeLoadChildrenTask.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeLoadChildrenTask.java @@ -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,9 +15,9 @@ */ package docking.widgets.tree.tasks; +import docking.widgets.tree.*; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import docking.widgets.tree.*; public class GTreeLoadChildrenTask extends GTreeTask { @@ -43,11 +42,11 @@ public class GTreeLoadChildrenTask extends GTreeTask { runOnSwingThread(new Runnable() { @Override public void run() { - tree.collapseAll(tree.getRootNode()); + tree.collapseAll(tree.getViewRoot()); } }); } - node.removeAll(); + node.unloadChildren(); } finally { monitor.initialize(maxValue); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeRestoreTreeStateTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeRestoreTreeStateTask.java index 86679ae5d3..246692b51e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeRestoreTreeStateTask.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeRestoreTreeStateTask.java @@ -99,7 +99,7 @@ public class GTreeRestoreTreeStateTask extends GTreeTask { private void expandPathsInThisTask(GTreeState treeState, TaskMonitor monitor) { List expandedPaths = treeState.getExpandedPaths(); - GTreeExpandPathsTask task = new GTreeExpandPathsTask(tree, jTree, expandedPaths); + GTreeExpandPathsTask task = new GTreeExpandPathsTask(tree, expandedPaths); task.run(monitor); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeSelectNodeByNameTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeSelectNodeByNameTask.java index 84cd50f8a9..8d7565e392 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeSelectNodeByNameTask.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeSelectNodeByNameTask.java @@ -43,7 +43,7 @@ public class GTreeSelectNodeByNameTask extends GTreeTask { @Override public void run(TaskMonitor monitor) throws CancelledException { monitor.setMessage("Selecting paths"); - GTreeNode node = tree.getRootNode(); + GTreeNode node = tree.getViewRoot(); String rootName = names[0]; if (!node.getName().equals(rootName)) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeSelectPathsTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeSelectPathsTask.java index 4719011ef2..c738ebcc83 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeSelectPathsTask.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeSelectPathsTask.java @@ -15,26 +15,94 @@ */ package docking.widgets.tree.tasks; +import java.util.ArrayList; import java.util.List; import javax.swing.JTree; import javax.swing.tree.TreePath; import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeTask; +import docking.widgets.tree.internal.GTreeSelectionModel; import docking.widgets.tree.support.GTreeSelectionEvent; +import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin; +import ghidra.util.task.TaskMonitor; -public class GTreeSelectPathsTask extends AbstractSelectPathsTask { +public class GTreeSelectPathsTask extends GTreeTask { + private final EventOrigin origin; + private boolean expandingDisabled; private final List paths; public GTreeSelectPathsTask(GTree gtree, JTree tree, List paths, GTreeSelectionEvent.EventOrigin origin) { - super(gtree, tree, origin); + super(gtree); + this.origin = origin; this.paths = paths; } - @Override - List getTreePaths() { - return paths; + /** + * Tells the JTree to not expand paths for each selection that is set upon it. Doing this + * will speed-up performance. However, only call this when some other task is going to + * ensure that paths are properly expanded. + * + * @param disabled true to disable + */ + void setExpandingDisabled(boolean disabled) { + this.expandingDisabled = disabled; } + + @Override + public void run(TaskMonitor monitor) { + monitor.setMessage("Selecting paths"); + monitor.initialize(paths.size()); + List translatedPaths = new ArrayList<>(); + for (TreePath path : paths) { + if (monitor.isCancelled()) { + return; + } + TreePath xPath = translatePath(path, monitor); + if (xPath != null) { + translatedPaths.add(xPath); + } + monitor.incrementProgress(1); + } + selectPaths(translatedPaths.toArray(new TreePath[translatedPaths.size()]), monitor); + } + + private void selectPaths(TreePath[] treePaths, TaskMonitor monitor) { + + runOnSwingThread(() -> { + if (monitor.isCancelled()) { + return; // we can be cancelled while waiting for Swing to run us + } + + boolean wasEnabled = jTree.getExpandsSelectedPaths(); + if (expandingDisabled) { + jTree.setExpandsSelectedPaths(false); + } + + try { + doSelectPaths(treePaths); + } + finally { + if (expandingDisabled) { + jTree.setExpandsSelectedPaths(wasEnabled); + } + } + }); + } + + private void doSelectPaths(TreePath[] treePaths) { + GTreeSelectionModel selectionModel = tree.getGTSelectionModel(); + selectionModel.setSelectionPaths(treePaths, origin); + + if (treePaths != null && treePaths.length > 0) { + // Scroll to the last item, as the tree will make the given path appear at the + // bottom of the view. By scrolling the last item, all the selected items above + // this one will appear in the view as well. + jTree.scrollPathToVisible(treePaths[treePaths.length - 1]); + } + } + } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeStartEditingTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeStartEditingTask.java index c884cca349..f92d9b9f25 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeStartEditingTask.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/GTreeStartEditingTask.java @@ -15,7 +15,8 @@ */ package docking.widgets.tree.tasks; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import javax.swing.CellEditor; import javax.swing.JTree; @@ -28,6 +29,7 @@ import ghidra.util.Msg; import ghidra.util.SystemUtilities; import ghidra.util.exception.AssertException; import ghidra.util.task.TaskMonitor; +import util.CollectionUtils; public class GTreeStartEditingTask extends GTreeTask { @@ -68,7 +70,7 @@ public class GTreeStartEditingTask extends GTreeTask { } TreePath path = child.getTreePath(); - final List childrenBeforeEdit = parent.getChildren(); + final Set childrenBeforeEdit = new HashSet<>(parent.getChildren()); final CellEditor cellEditor = tree.getCellEditor(); cellEditor.addCellEditorListener(new CellEditorListener() { @@ -121,7 +123,7 @@ public class GTreeStartEditingTask extends GTreeTask { } private void doReselectNodeHandlingPotentialChildChange() { - List childrenAfterEdit = parent.getChildren(); + Set childrenAfterEdit = new HashSet<>(parent.getChildren()); if (childrenAfterEdit.equals(childrenBeforeEdit)) { reselectNode(); // default re-select--the original child is still there return; @@ -133,7 +135,7 @@ public class GTreeStartEditingTask extends GTreeTask { return; // no way for us to figure out the correct child to edit } - GTreeNode newChild = childrenAfterEdit.get(0); + GTreeNode newChild = CollectionUtils.any(childrenAfterEdit); tree.setSelectedNode(newChild); } }); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/SetRootNodeTask.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/SetRootNodeTask.java index 197b359078..4dc58b801a 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/SetRootNodeTask.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/tasks/SetRootNodeTask.java @@ -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,17 @@ */ package docking.widgets.tree.tasks; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; import docking.widgets.tree.*; import docking.widgets.tree.internal.GTreeModel; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; public class SetRootNodeTask extends GTreeTask { private final GTreeModel model; - private final GTreeRootNode rootNode; + private final GTreeNode rootNode; - public SetRootNodeTask(GTree tree, GTreeRootNode rootNode, GTreeModel model) { + public SetRootNodeTask(GTree tree, GTreeNode rootNode, GTreeModel model) { super(tree); this.model = model; this.rootNode = rootNode; diff --git a/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeSlowLoadingNode1Test.java b/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeSlowLoadingNode1Test.java deleted file mode 100644 index b2f6d17b45..0000000000 --- a/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeSlowLoadingNode1Test.java +++ /dev/null @@ -1,309 +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 static org.junit.Assert.*; - -import java.util.ArrayList; -import java.util.List; - -import javax.swing.*; - -import org.junit.*; - -import docking.test.AbstractDockingTest; -import docking.widgets.OptionDialog; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; - -public class GTreeSlowLoadingNode1Test extends AbstractDockingTest { - - private static final int MAX_DEPTH = 4; - private static final int MIN_CHILD_COUNT = 3; - private static final int MAX_CHILD_COUNT = 40; - - private volatile boolean pauseChildLoading = false; - - private JFrame frame; - private GTree gTree; - - @Before - public void setUp() throws Exception { - - gTree = new GTree(new EmptyRootNode()); - - frame = new JFrame("GTree Test"); - frame.getContentPane().add(gTree); - frame.setSize(400, 400); - frame.setVisible(true); - - waitForTree(); - } - - @After - public void tearDown() throws Exception { - gTree.dispose(); - frame.dispose(); - } - - @Test - public void testBasicLoading() { - gTree.setRootNode(new TestRootNode(100)); - - waitForTree(); - - // make sure we have some children - GTreeRootNode rootNode = gTree.getRootNode(); - GTreeNode nonLeaf1 = rootNode.getChild(0); - assertNotNull(nonLeaf1); - GTreeNode leaf1 = rootNode.getChild(1); - assertNotNull(leaf1); - GTreeNode nonLeaf2 = rootNode.getChild(2); - assertNotNull(nonLeaf2); - - int childCount = nonLeaf1.getChildCount(); - assertTrue("Did not find children for: " + nonLeaf1, childCount > 1); - assertEquals("An expected leaf node has some children", 0, leaf1.getChildCount()); - childCount = nonLeaf2.getChildCount(); - assertTrue("Did not find children for: " + nonLeaf2, childCount > 1); - } - - @Test - public void testSlowNodeShowsProgressBar() { - gTree.setRootNode(new TestRootNode(5000)); - - waitForTree(); - - GTreeRootNode rootNode = gTree.getRootNode(); - GTreeNode nonLeaf1 = rootNode.getChild(0); - assertNotNull(nonLeaf1); - - gTree.expandPath(nonLeaf1); - - assertProgressPanel(true); - - assertTrue(nonLeaf1.isInProgress()); - - // Press the cancel button on the progress monitor - pressProgressPanelCancelButton(); - - waitForTree(); - - // Verify no progress component - assertProgressPanel(false); - } - -//================================================================================================== -// Private Methods -//================================================================================================== - - private void waitForTree() { - waitForTree(gTree); - } - - private void assertProgressPanel(boolean isShowing) { - JComponent panel = (JComponent) getInstanceField("progressPanel", gTree); - if (!isShowing) { - assertNull("Panel is showing when it should not be", panel); - return; - } - - if (panel == null || !panel.isShowing()) { - int maxWaits = 50;// wait a couple seconds, as the progress bar may be delayed - int tryCount = 0; - while (tryCount < maxWaits) { - panel = (JComponent) getInstanceField("progressPanel", gTree); - if (panel != null && panel.isShowing()) { - return;// finally showing! - } - tryCount++; - try { - Thread.sleep(50); - } - catch (Exception e) { - // who cares? - } - } - } - - Assert.fail("Progress panel is not showing as expected"); - } - - private void pressProgressPanelCancelButton() { - Object taskMonitorComponent = getInstanceField("monitor", gTree); - final JButton cancelButton = - (JButton) getInstanceField("cancelButton", taskMonitorComponent); - runSwing(() -> cancelButton.doClick(), false); - - OptionDialog confirDialog = waitForDialogComponent(frame, OptionDialog.class, 2000); - final JButton confirmCancelButton = findButtonByText(confirDialog, "Yes"); - runSwing(() -> confirmCancelButton.doClick()); - } - -//================================================================================================== -// Inner Classes -//================================================================================================== - - private class EmptyRootNode extends AbstractGTreeRootNode { - - EmptyRootNode() { - setChildren(new ArrayList()); - } - - @Override - public Icon getIcon(boolean expanded) { - return null; - } - - @Override - public String getName() { - return "Empty Test GTree Root Node"; - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return true; - } - } - - private class TestRootNode extends AbstractGTreeRootNode { - - TestRootNode(int loadDelayMillis) { - List children = new ArrayList<>(); - children.add(new TestSlowLoadingNode(loadDelayMillis, 1)); - children.add(new TestLeafNode()); - children.add(new TestSlowLoadingNode(loadDelayMillis, 1)); - setChildren(children); - } - - @Override - public Icon getIcon(boolean expanded) { - return null; - } - - @Override - public String getName() { - return "Test GTree Root Node"; - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return false; - } - } - - private class TestSlowLoadingNode extends GTreeSlowLoadingNode { - - private final long loadDelayMillis; - private final int depth; - - TestSlowLoadingNode(long loadDelayMillis, int depth) { - this.loadDelayMillis = loadDelayMillis; - this.depth = depth; - } - - @Override - public List generateChildren(TaskMonitor monitor) throws CancelledException { - - if (depth > MAX_DEPTH) { - return new ArrayList<>(); - } - - if (monitor.isCancelled()) { - return new ArrayList<>(); - } - - sleep(loadDelayMillis); - - while (pauseChildLoading) { - sleep(100); - } - - int childCount = getRandomInt(MIN_CHILD_COUNT, MAX_CHILD_COUNT); - List children = new ArrayList<>(); - for (int i = 0; i < childCount; i++) { - if (monitor.isCancelled()) { - return new ArrayList<>(); - } - int value = getRandomInt(0, 1); - if (value == 0) { - children.add(new TestSlowLoadingNode(loadDelayMillis, depth + 1)); - } - else { - children.add(new TestLeafNode()); - } - } - return children; - } - - @Override - public Icon getIcon(boolean expanded) { - return null; - } - - @Override - public String getName() { - return getClass().getSimpleName(); - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return false; - } - - } - - private class TestLeafNode extends AbstractGTreeNode { - - private String name = getClass().getSimpleName() + getRandomString(); - - @Override - public Icon getIcon(boolean expanded) { - return null; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return true; - } - - } -} diff --git a/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeSlowLoadingNode2Test.java b/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeSlowLoadingNode2Test.java deleted file mode 100644 index a716d59c07..0000000000 --- a/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeSlowLoadingNode2Test.java +++ /dev/null @@ -1,255 +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 static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; - -import javax.swing.*; - -import org.junit.*; - -import docking.test.AbstractDockingTest; -import docking.widgets.filter.FilterTextField; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; - -public class GTreeSlowLoadingNode2Test extends AbstractDockingTest { - - private static final int MAX_DEPTH = 4; - private static final int MIN_CHILD_COUNT = 3; - private static final int MAX_CHILD_COUNT = 3; - - private volatile boolean pauseChildLoading = false; - - private JFrame frame; - private GTree gTree; - private FilterTextField filterField; - - @Before - public void setUp() throws Exception { - - gTree = new GTree(new EmptyRootNode()); - filterField = (FilterTextField) gTree.getFilterField(); - - frame = new JFrame("GTree Test"); - frame.getContentPane().add(gTree); - frame.setSize(400, 400); - frame.setVisible(true); - - waitForTree(); - - } - - @After - public void tearDown() throws Exception { - gTree.dispose(); - frame.dispose(); - } - - @Test - public void testBasicLoading() { - gTree.setRootNode(new TestRootNode(0)); - waitForTree(); - // make sure we have some children - GTreeRootNode rootNode = gTree.getRootNode(); - List allChildren = rootNode.getAllChildren(); - typeFilterText("Many B1"); - clearFilterText(); - List allChildren2 = rootNode.getAllChildren(); - assertEquals("Children were reloaded instead of being reused", allChildren, allChildren2); - } - -//================================================================================================== -// Private Methods -//================================================================================================== - - private void typeFilterText(String text) { - JTextField textField = (JTextField) getInstanceField("textField", filterField); - triggerText(textField, text); - waitForTree(); - } - - private void setFilterText(final String text) { - runSwing(() -> filterField.setText(text)); - waitForTree(); - } - - private void clearFilterText() { - setFilterText(""); - } - - private void waitForTree() { - waitForTree(gTree); - } - -//================================================================================================== -// Inner Classes -//================================================================================================== - - private class EmptyRootNode extends AbstractGTreeRootNode { - - EmptyRootNode() { - setChildren(new ArrayList()); - } - - @Override - public Icon getIcon(boolean expanded) { - return null; - } - - @Override - public String getName() { - return "Empty Test GTree Root Node"; - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return true; - } - } - - private class TestRootNode extends TestSlowLoadingNode implements GTreeRootNode { - private GTree tree; - - TestRootNode(int loadDelayMillis) { - super(loadDelayMillis, 3); - } - - @Override - public void setGTree(GTree tree) { - this.tree = tree; - } - - @Override - public GTree getGTree() { - return tree; - } - - @Override - public Icon getIcon(boolean expanded) { - return null; - } - - @Override - public String getName() { - return "Test GTree Root Node"; - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return false; - } - } - - private class TestSlowLoadingNode extends GTreeSlowLoadingNode { - - private final long loadDelayMillis; - private final int depth; - - TestSlowLoadingNode(long loadDelayMillis, int depth) { - this.loadDelayMillis = loadDelayMillis; - this.depth = depth; - } - - @Override - public List generateChildren(TaskMonitor monitor) throws CancelledException { - - if (depth > MAX_DEPTH) { - return new ArrayList<>(); - } - - if (monitor.isCancelled()) { - return new ArrayList<>(); - } - - sleep(loadDelayMillis); - - while (pauseChildLoading) { - sleep(100); - } - - int childCount = getRandomInt(MIN_CHILD_COUNT, MAX_CHILD_COUNT); - List children = new ArrayList<>(); - for (int i = 0; i < childCount; i++) { - if (monitor.isCancelled()) { - return new ArrayList<>(); - } - children.add(new TestSlowLoadingNode(0, depth + 1)); - } - return children; - } - - @Override - public Icon getIcon(boolean expanded) { - return null; - } - - @Override - public String getName() { - return getClass().getSimpleName(); - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return false; - } - - } - - private class TestLeafNode extends AbstractGTreeNode { - - private String name = getClass().getSimpleName() + getRandomString(); - - @Override - public Icon getIcon(boolean expanded) { - return null; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return true; - } - - } -} diff --git a/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeSlowLoadingNodeTest.java b/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeSlowLoadingNodeTest.java index 23f1ee6c1c..ddd2fda6fa 100644 --- a/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeSlowLoadingNodeTest.java +++ b/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeSlowLoadingNodeTest.java @@ -26,6 +26,8 @@ import org.junit.*; import docking.test.AbstractDockingTest; import docking.widgets.OptionDialog; +import docking.widgets.tree.internal.InProgressGTreeNode; +import ghidra.util.Swing; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -39,6 +41,7 @@ public class GTreeSlowLoadingNodeTest extends AbstractDockingTest { private JFrame frame; private GTree gTree; + private List children = null; @Before public void setUp() throws Exception { @@ -66,7 +69,7 @@ public class GTreeSlowLoadingNodeTest extends AbstractDockingTest { waitForTree(); // make sure we have some children - GTreeRootNode rootNode = gTree.getRootNode(); + GTreeNode rootNode = gTree.getModelRoot(); GTreeNode nonLeaf1 = rootNode.getChild(0); assertNotNull(nonLeaf1); GTreeNode leaf1 = rootNode.getChild(1); @@ -87,7 +90,7 @@ public class GTreeSlowLoadingNodeTest extends AbstractDockingTest { waitForTree(); - GTreeRootNode rootNode = gTree.getRootNode(); + GTreeNode rootNode = gTree.getModelRoot(); GTreeNode nonLeaf1 = rootNode.getChild(0); assertNotNull(nonLeaf1); @@ -95,7 +98,7 @@ public class GTreeSlowLoadingNodeTest extends AbstractDockingTest { assertProgressPanel(true); - assertTrue(nonLeaf1.isInProgress()); + assertTrue(!nonLeaf1.isLoaded()); // Press the cancel button on the progress monitor pressProgressPanelCancelButton(); @@ -106,6 +109,51 @@ public class GTreeSlowLoadingNodeTest extends AbstractDockingTest { assertProgressPanel(false); } + @Test + public void testSlowNodeShowsProgressBarFromSwingAccess() { + gTree.setRootNode(new TestRootNode(5000)); + + waitForTree(); + + GTreeNode rootNode = gTree.getModelRoot(); + GTreeNode nonLeaf1 = rootNode.getChild(0); + assertNotNull(nonLeaf1); + + Swing.runNow(() -> children = nonLeaf1.getChildren()); + + assertEquals(1, children.size()); + assertTrue(children.get(0) instanceof InProgressGTreeNode); + + assertProgressPanel(true); + + assertTrue(!nonLeaf1.isLoaded()); + + // Press the cancel button on the progress monitor + pressProgressPanelCancelButton(); + + waitForTree(); + + // Verify no progress component + assertProgressPanel(false); + } + + @Test + public void testInProgress() { + gTree.setRootNode(new TestRootNode(100)); + + waitForTree(); + + GTreeNode rootNode = gTree.getModelRoot(); + GTreeNode nonLeaf1 = rootNode.getChild(0); + Swing.runNow(() -> children = nonLeaf1.getChildren()); + assertEquals(1, children.size()); + assertTrue(children.get(0) instanceof InProgressGTreeNode); + waitForTree(); + Swing.runNow(() -> children = nonLeaf1.getChildren()); + assertTrue("Did not find children for: " + nonLeaf1, nonLeaf1.getChildCount() > 1); + + } + //================================================================================================== // Private Methods //================================================================================================== @@ -157,7 +205,7 @@ public class GTreeSlowLoadingNodeTest extends AbstractDockingTest { // Inner Classes //================================================================================================== - private class EmptyRootNode extends AbstractGTreeRootNode { + private class EmptyRootNode extends GTreeNode { EmptyRootNode() { setChildren(new ArrayList()); @@ -184,7 +232,7 @@ public class GTreeSlowLoadingNodeTest extends AbstractDockingTest { } } - private class TestRootNode extends AbstractGTreeRootNode { + private class TestRootNode extends GTreeNode { TestRootNode(int loadDelayMillis) { List children = new ArrayList<>(); @@ -232,9 +280,7 @@ public class GTreeSlowLoadingNodeTest extends AbstractDockingTest { return new ArrayList<>(); } - if (monitor.isCancelled()) { - return new ArrayList<>(); - } + monitor.checkCanceled(); sleep(loadDelayMillis); @@ -245,9 +291,7 @@ public class GTreeSlowLoadingNodeTest extends AbstractDockingTest { int childCount = getRandomInt(MIN_CHILD_COUNT, MAX_CHILD_COUNT); List children = new ArrayList<>(); for (int i = 0; i < childCount; i++) { - if (monitor.isCancelled()) { - return new ArrayList<>(); - } + monitor.checkCanceled(); int value = getRandomInt(0, 1); if (value == 0) { children.add(new TestSlowLoadingNode(loadDelayMillis, depth + 1)); @@ -281,7 +325,7 @@ public class GTreeSlowLoadingNodeTest extends AbstractDockingTest { } - private class TestLeafNode extends AbstractGTreeNode { + private class TestLeafNode extends GTreeNode { private String name = getClass().getSimpleName() + getRandomString(); diff --git a/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeTest.java b/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeTest.java index e897506fa4..9a549063d6 100644 --- a/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeTest.java +++ b/Ghidra/Framework/Docking/src/test.slow/java/docking/widgets/tree/GTreeTest.java @@ -168,7 +168,7 @@ public class GTreeTest extends AbstractDockingTest { node2 = findNodeInTree("Leaf Child - Single B1"); assertNull("Found a node in the tree that should have been filtered out", node2); - gTree.refilter(); + gTree.refilterNow(); waitForTree(); node = findNodeInTree("Leaf Child - Many B1"); assertNotNull("Did not find existing child node in filtered tree", node); @@ -177,7 +177,7 @@ public class GTreeTest extends AbstractDockingTest { assertNull("Found a node in the tree that should have been filtered out", node2); filterEnabled = false; - gTree.refilter(); + gTree.refilterNow(); waitForTree(); node = findNodeInTree("Leaf Child - Many B1"); assertNull("Found node when filter should not have matched after a refilter", node); @@ -262,6 +262,7 @@ public class GTreeTest extends AbstractDockingTest { // // Same test, but with a selection that existed before the filter's selection change // + node = findNodeInTree("Leaf Child - Many B1"); gTree.clearSelectionPaths(); waitForTree(); @@ -385,9 +386,9 @@ public class GTreeTest extends AbstractDockingTest { @Test public void testExpandPath() { - List expandedPaths = gTree.getExpandedPaths(gTree.getRootNode()); + List expandedPaths = gTree.getExpandedPaths(gTree.getViewRoot()); assertEquals("Unexpectedly have an expanded tree by default", 1, expandedPaths.size()); - assertEquals(gTree.getRootNode(), expandedPaths.get(0).getLastPathComponent()); + assertEquals(gTree.getViewRoot(), expandedPaths.get(0).getLastPathComponent()); GTreeNode node = findNodeInTree("Leaf Child - Many B1"); assertNotNull("Did not find existing child node in non filtered tree", node); @@ -395,7 +396,7 @@ public class GTreeTest extends AbstractDockingTest { gTree.expandPath(node); waitForTree(); - expandedPaths = gTree.getExpandedPaths(gTree.getRootNode()); + expandedPaths = gTree.getExpandedPaths(gTree.getViewRoot()); assertTrue("Did not get expanded paths after calling expand paths", expandedPaths.size() > 0); assertExpaned(expandedPaths, node.getParent()); @@ -403,20 +404,20 @@ public class GTreeTest extends AbstractDockingTest { @Test public void testPreventRootCollapseExpandsRoot() { - gTree.collapseAll(gTree.getRootNode()); + gTree.collapseAll(gTree.getViewRoot()); waitForTree(); gTree.setRootNodeAllowedToCollapse(false); waitForTree(); - List expanded = gTree.getExpandedPaths(gTree.getRootNode()); + List expanded = gTree.getExpandedPaths(gTree.getViewRoot()); assertEquals("Should have only one expanded path", 1, expanded.size()); TreePath path0 = expanded.get(0); - assertEquals(path0.getLastPathComponent(), gTree.getRootNode()); + assertEquals(path0.getLastPathComponent(), gTree.getViewRoot()); assertEquals("Expanded path does not end at tree root", path0.getLastPathComponent(), - gTree.getRootNode()); + gTree.getViewRoot()); } @Test @@ -426,16 +427,16 @@ public class GTreeTest extends AbstractDockingTest { gTree.expandAll(); waitForTree(); - gTree.collapseAll(gTree.getRootNode()); + gTree.collapseAll(gTree.getViewRoot()); waitForTree(); - List expanded = gTree.getExpandedPaths(gTree.getRootNode()); + List expanded = gTree.getExpandedPaths(gTree.getViewRoot()); assertEquals("Should have only one expanded path", 1, expanded.size()); TreePath path0 = expanded.get(0); assertEquals("Expanded path does not end at tree root", path0.getLastPathComponent(), - gTree.getRootNode()); + gTree.getViewRoot()); } @Test @@ -844,7 +845,7 @@ public class GTreeTest extends AbstractDockingTest { } private GTreeNode findNodeInTree(String name) { - GTreeRootNode rootNode = gTree.getRootNode(); + GTreeNode rootNode = gTree.getViewRoot(); return findNodeInTree(rootNode, name); } @@ -884,7 +885,7 @@ public class GTreeTest extends AbstractDockingTest { private class TestGTree extends GTree { - public TestGTree(GTreeRootNode root) { + public TestGTree(GTreeNode root) { super(root); } @@ -909,7 +910,7 @@ public class GTreeTest extends AbstractDockingTest { } } - private class TestRootNode extends AbstractGTreeRootNode { + private class TestRootNode extends GTreeNode { TestRootNode() { List children = new ArrayList<>(); @@ -940,7 +941,7 @@ public class GTreeTest extends AbstractDockingTest { } } - private class ManyLeafChildrenRootNode extends AbstractGTreeRootNode { + private class ManyLeafChildrenRootNode extends GTreeNode { ManyLeafChildrenRootNode() { List children = new ArrayList<>(); @@ -969,7 +970,7 @@ public class GTreeTest extends AbstractDockingTest { } } - private class ManyNonLeafChildrenRootNode extends AbstractGTreeRootNode { + private class ManyNonLeafChildrenRootNode extends GTreeNode { ManyNonLeafChildrenRootNode() { List children = new ArrayList<>(); @@ -1001,7 +1002,7 @@ public class GTreeTest extends AbstractDockingTest { /** * A basic node with some children. */ - private class NonLeafWithOneLevelOfChildrenNodeA extends AbstractGTreeNode { + private class NonLeafWithOneLevelOfChildrenNodeA extends GTreeNode { private String name = Integer.toString(++nodeIdCounter); @@ -1035,7 +1036,7 @@ public class GTreeTest extends AbstractDockingTest { /** * A basic node with some children. */ - private class NonLeafWithOneLevelOfChildrenNodeB extends AbstractGTreeNode { + private class NonLeafWithOneLevelOfChildrenNodeB extends GTreeNode { private String name = Integer.toString(++nodeIdCounter); NonLeafWithOneLevelOfChildrenNodeB() { @@ -1073,7 +1074,7 @@ public class GTreeTest extends AbstractDockingTest { /** * A basic leaf node */ - private class LeafNode extends AbstractGTreeNode { + private class LeafNode extends GTreeNode { private final String name; @@ -1105,7 +1106,7 @@ public class GTreeTest extends AbstractDockingTest { /** * A node with children that may have children */ - private class NonLeafWithManyLevelOfChildrenNodeA extends AbstractGTreeNode { + private class NonLeafWithManyLevelOfChildrenNodeA extends GTreeNode { private String name = Integer.toString(++nodeIdCounter); @@ -1139,7 +1140,7 @@ public class GTreeTest extends AbstractDockingTest { /** * A node with children that may have children */ - private class NonLeafWithManyLevelOfChildrenNodeB extends AbstractGTreeNode { + private class NonLeafWithManyLevelOfChildrenNodeB extends GTreeNode { private String name = Integer.toString(++nodeIdCounter); @@ -1174,7 +1175,7 @@ public class GTreeTest extends AbstractDockingTest { /** * A basic node with some children. */ - private class NonLeafWithMoreChildrenThanFitInTheView extends AbstractGTreeNode { + private class NonLeafWithMoreChildrenThanFitInTheView extends GTreeNode { private String name = Integer.toString(++nodeIdCounter); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeRootNode.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTestNode.java similarity index 57% rename from Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeRootNode.java rename to Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTestNode.java index 5b70a821eb..a44884b994 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTreeRootNode.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTestNode.java @@ -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,36 @@ */ package docking.widgets.tree; +import javax.swing.Icon; + /** - * Root nodes must implement this interface. They only have one additional responsibility - * and that is they must support saving off the tree that they belong to. - * + * GTreeNode implementation used for testing the GTree */ -public interface GTreeRootNode extends GTreeNode { - public void setGTree(GTree tree); - public GTree getGTree(); +public class GTestNode extends GTreeNode { + private String name; + + public GTestNode(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public Icon getIcon(boolean expanded) { + return null; + } + + @Override + public String getToolTip() { + return "tooltip: " + name; + } + + @Override + public boolean isLeaf() { + return false; + } + } diff --git a/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTreeAllPathsIteratorTest.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTreeAllPathsIteratorTest.java deleted file mode 100644 index fdc0215cad..0000000000 --- a/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTreeAllPathsIteratorTest.java +++ /dev/null @@ -1,169 +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 static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import java.util.*; - -import javax.swing.Icon; -import javax.swing.tree.TreePath; - -import org.junit.Before; -import org.junit.Test; - -import docking.test.AbstractDockingTest; - -public class GTreeAllPathsIteratorTest extends AbstractDockingTest { - - private static class TestRootNode extends AbstractGTreeRootNode { - protected String name; - - public TestRootNode(String name, List children) { - this.name = name; - setChildren(children); - } - - @Override - public String getName() { - return name; - } - - @Override - public Icon getIcon(boolean expanded) { - return null; - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return false; - } - } - - private static class TestNode extends AbstractGTreeNode { - protected String name; - - public TestNode(String name, List children) { - this.name = name; - setChildren(children); - } - - @Override - public String getName() { - return name; - } - - @Override - public Icon getIcon(boolean expanded) { - return null; - } - - @Override - public String getToolTip() { - return null; - } - - @Override - public boolean isLeaf() { - return false; - } - } - - @Before - public void setUp() throws Exception { - - } - - /** - * R - */ - @Test - public void testRootOnly() { - List children = Collections.emptyList(); - AbstractGTreeNode root = new TestRootNode("Root", children); - - TreePath path; - Iterator it = root.allPaths().iterator(); - - path = it.next(); - assertEquals("The path should consist only of the root", "[Root]", path.toString()); - - path = it.next(); - assertNull("There should only be one path", path); - } - - /** - * A - * `-B - * `-C - */ - @Test - public void testChainOfThree() { - List childrenC = Collections.emptyList(); - AbstractGTreeNode nodeC = new TestNode("C", childrenC); - AbstractGTreeNode nodeB = new TestNode("B", Collections.singletonList((GTreeNode) nodeC)); - AbstractGTreeNode rootA = - new TestRootNode("A", Collections.singletonList((GTreeNode) nodeB)); - - Iterator it = rootA.allPaths().iterator(); - - assertEquals("Path 1 incorrect", "[A]", it.next().toString()); - assertEquals("Path 2 incorrect", "[A, B]", it.next().toString()); - assertEquals("Path 3 incorrect", "[A, B, C]", it.next().toString()); - assertNull("There should be only 3 paths", it.next()); - } - - /** - * A - * |-B - * | |-C - * | `-D - * `-E - * |-F - * `-G - */ - @Test - public void testTwoByTwo() { - List empty = Collections.emptyList(); - AbstractGTreeNode nodeG = new TestNode("G", empty); - AbstractGTreeNode nodeF = new TestNode("F", empty); - AbstractGTreeNode nodeE = - new TestNode("E", Arrays.asList(new GTreeNode[] { nodeF, nodeG })); - AbstractGTreeNode nodeD = new TestNode("D", empty); - AbstractGTreeNode nodeC = new TestNode("C", empty); - AbstractGTreeNode nodeB = - new TestNode("B", Arrays.asList(new GTreeNode[] { nodeC, nodeD })); - AbstractGTreeNode rootA = - new TestRootNode("A", Arrays.asList(new GTreeNode[] { nodeB, nodeE })); - - Iterator it = rootA.allPaths().iterator(); - - assertEquals("Path 1 incorrect", "[A]", it.next().toString()); - assertEquals("Path 2 incorrect", "[A, B]", it.next().toString()); - assertEquals("Path 3 incorrect", "[A, B, C]", it.next().toString()); - assertEquals("Path 4 incorrect", "[A, B, D]", it.next().toString()); - assertEquals("Path 5 incorrect", "[A, E]", it.next().toString()); - assertEquals("Path 6 incorrect", "[A, E, F]", it.next().toString()); - assertEquals("Path 7 incorrect", "[A, E, G]", it.next().toString()); - assertNull("There should be only 7 paths", it.next()); - } -} diff --git a/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTreeNodeTest.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTreeNodeTest.java new file mode 100644 index 0000000000..a7b3974944 --- /dev/null +++ b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTreeNodeTest.java @@ -0,0 +1,470 @@ +/* ### + * 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 static org.junit.Assert.*; + +import java.util.*; +import java.util.stream.Collectors; + +import javax.swing.tree.TreePath; + +import org.junit.Before; +import org.junit.Test; + +import docking.widgets.tree.support.GTreeFilter; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +public class GTreeNodeTest { + private List events = new ArrayList<>(); + private GTreeNode root; + private GTreeNode node0; + private GTreeNode node1; + private GTreeNode node2; + private GTreeNode node0_0; + private GTreeNode node0_1; + private GTreeNode node1_0; + + @Before + public void setUp() { + root = new TestNode("root"); + node0 = new TestNode("Node0"); + node1 = new TestNode("Node1"); + node2 = new TestNode("Node2"); + node0_0 = new TestNode("Node0_0"); + node0_1 = new TestNode("Node0_1"); + node1_0 = new TestNode("Node1_0"); + root.addNode(node0); + root.addNode(node1); + root.addNode(node2); + node0.addNode(node0_0); + node0.addNode(node0_1); + node1.addNode(node1_0); + events.clear(); + } + + @Test + public void testClone() throws CloneNotSupportedException { + GTreeNode clone = node0.clone(); + + assertEquals(node0.getId(), clone.getId()); + assertNull(clone.getParent()); + assertFalse(clone.isLoaded()); + } + + @Test + public void testGetParent() { + assertEquals(root, node0.getParent()); + assertEquals(node0, node0_0.getParent()); + } + + @Test + public void testRemovingNodeClearsParent() { + root.removeNode(node0); + assertNull(node0.getParent()); + assertNotNull(node0_0.getParent()); + } + + @Test + public void testRemovingAllClearsParent() { + root.removeAll(); + assertNull(node0.getParent()); + assertNull(node1.getParent()); + assertNull(node2.getParent()); + } + + @Test + public void testCompareTo() { + TestNode nodeFoo = new TestNode("Foo"); + TestNode nodefoo = new TestNode("foo"); + TestNode nodeBar = new TestNode("Bar"); + assertEquals(0, nodeFoo.compareTo(nodefoo)); + assertEquals(0, nodefoo.compareTo(nodeFoo)); + assertEquals(0, nodeFoo.compareTo(nodeFoo)); + assertTrue(nodeFoo.compareTo(nodeBar) > 0); + assertTrue(nodeBar.compareTo(nodeFoo) < 0); + assertTrue(nodefoo.compareTo(nodeBar) > 0); + } + + @Test + public void testIterator() { + + Iterator iterator = root.getChildren().iterator(); + assertTrue(iterator.hasNext()); + assertEquals(node0, iterator.next()); + assertTrue(iterator.hasNext()); + assertEquals(node1, iterator.next()); + assertTrue(iterator.hasNext()); + assertEquals(node2, iterator.next()); + assertFalse(iterator.hasNext()); + } + + @Test + public void testAddNode() { + int originalRootCount = root.getChildCount(); + + TestNode child = new TestNode("child"); + root.addNode(child); + + assertEquals(originalRootCount + 1, root.getChildCount()); + + assertEquals(root, child.getParent()); + assertEquals(child, root.getChild(originalRootCount)); + + assertEquals(1, events.size()); + TestEvent event = events.get(0); + assertEquals(EventType.NODE_ADDED, event.type); + assertEquals(root, event.parent); + assertEquals(child, event.node); + } + + @Test + public void testAddNodeAtIndex() { + int originalRootCount = root.getChildCount(); + + TestNode child = new TestNode("child"); + + root.addNode(1, child); + + assertEquals(originalRootCount + 1, root.getChildCount()); + assertEquals(root, child.getParent()); + assertEquals(child, root.getChild(1)); + assertEquals(node0, root.getChild(0)); + assertEquals(node1, root.getChild(2)); + + assertEquals(1, events.size()); + + TestEvent event = events.get(0); + assertEquals(EventType.NODE_ADDED, event.type); + assertEquals(root, event.parent); + assertEquals(child, event.node); + } + + @Test + public void testAddNodes() { + int originalRootCount = root.getChildCount(); + + TestNode child1 = new TestNode("child1"); + TestNode child2 = new TestNode("child2"); + List list = new ArrayList(); + list.add(child1); + list.add(child2); + + root.addNodes(list); + + assertEquals(root, child1.getParent()); + assertEquals(root, child2.getParent()); + + assertEquals(originalRootCount + 2, root.getChildCount()); + + assertEquals(child1, root.getChild(originalRootCount)); + assertEquals(child2, root.getChild(originalRootCount + 1)); + + assertEquals(1, events.size()); + TestEvent event = events.get(0); + assertEquals(EventType.STRUCTURE_CHANGED, event.type); + assertEquals(root, event.node); + } + + @Test + public void testGetChildren() { + List children = root.getChildren(); + assertEquals(3, children.size()); + assertEquals(node0, children.get(0)); + assertEquals(node1, children.get(1)); + assertEquals(node2, children.get(2)); + } + + @Test + public void testGetChildrenIsImmutable() { + List children = root.getChildren(); + try { + children.add(new TestNode("test")); + fail("Should not have been able to modify list return from getChildren()"); + } + catch (Exception e) { + // expected + } + } + + @Test + public void testGetChildCount() { + assertEquals(3, root.getChildCount()); + } + + @Test + public void testGetChild() { + assertEquals(node0, root.getChild(0)); + } + + @Test + public void testGetChildByName() { + assertEquals(node1, root.getChild("Node1")); + assertNull(root.getChild("foo")); + } + + @Test + public void testGetNodeCount() { + assertEquals(3, node0.getNodeCount()); // node0 has 2 children + assertEquals(2, node1.getNodeCount()); // node1 has 1 child + assertEquals(1, node2.getNodeCount()); // node2 has no children + assertEquals(7, root.getNodeCount()); // one more that the total of the children count + } + + @Test + public void testGetLeafCount() { + assertEquals(2, node0.getLeafCount()); // node0 has 2 children + assertEquals(1, node1.getLeafCount()); // node1 has 1 child + assertEquals(1, node2.getLeafCount()); // node2 so it is a leaf + assertEquals(4, root.getLeafCount()); // the total of all its children + } + + @Test + public void testGetIndexInParent() { + assertEquals(0, node0.getIndexInParent()); + assertEquals(1, node1.getIndexInParent()); + assertEquals(2, node2.getIndexInParent()); + assertEquals(0, node0_0.getIndexInParent()); + + // test that unattached node doesn't blow up. + assertEquals(-1, new TestNode("test").getIndexInParent()); + } + + @Test + public void testGetIndexOfChild() throws CloneNotSupportedException { + assertEquals(0, root.getIndexOfChild(node0)); + assertEquals(1, root.getIndexOfChild(node1)); + assertEquals(2, root.getIndexOfChild(node2)); + + assertEquals(-1, root.getIndexOfChild(node0_0)); + + assertEquals(0, root.getIndexOfChild(node0.clone())); + } + + @Test + public void testGetTreePath() { + TreePath path = node0_0.getTreePath(); + assertEquals(node0_0, path.getLastPathComponent()); + assertEquals(node0, path.getParentPath().getLastPathComponent()); + assertEquals(root, path.getParentPath().getParentPath().getLastPathComponent()); + assertNull(path.getParentPath().getParentPath().getParentPath()); + } + + @Test + public void testRemoveAll() { + root.removeAll(); + assertEquals(0, root.getChildCount()); + assertEquals(1, events.size()); + TestEvent event = events.get(0); + assertEquals(EventType.STRUCTURE_CHANGED, event.type); + assertEquals(root, event.node); + } + + @Test + public void testRemoveNode() { + root.removeNode(node1); + assertEquals(2, root.getChildCount()); + assertNull(node1.getParent()); + assertEquals(1, events.size()); + TestEvent event = events.get(0); + assertEquals(EventType.STRUCTURE_CHANGED, event.type); + assertEquals(root, event.node); + } + + @Test + public void testRemoveNonExistingNode() { + root.removeNode(new TestNode("Test")); + assertEquals(3, root.getChildCount()); + assertTrue(events.isEmpty()); + } + + @Test + public void testSetChildren() { + List list = new ArrayList(); + TestNode child1 = new TestNode("child1"); + TestNode child2 = new TestNode("child2"); + list.add(child1); + list.add(child2); + + root.setChildren(list); + assertEquals(2, root.getChildCount()); + assertEquals(child1, root.getChild(0)); + assertEquals(child2, root.getChild(1)); + assertNull(node0.getParent()); + assertNull(node1.getParent()); + assertNull(node2.getParent()); + } + + @Test + public void testSetChildrenNull() { + root.setChildren(null); + assertFalse(root.isLoaded()); + assertEquals(0, root.getChildCount()); + assertTrue(root.isLoaded()); + } + + @Test + public void isAncestor() { + assertTrue(root.isAncestor(node0_0)); + assertFalse(node0_0.isAncestor(root)); + } + + @Test + public void testGetRoot() { + assertEquals(root, node0_1.getRoot()); + assertEquals(root, root.getRoot()); + TestNode testNode = new TestNode("test"); + assertEquals(testNode, testNode.getRoot()); + } + + @Test + public void testIsRoot() { + assertTrue(root.isRoot()); + assertFalse(node0_1.isRoot()); + } + + @Test + public void testDispose() { + root.dispose(); + assertTrue(!root.isLoaded()); + assertNull(node1.getParent()); + assertNull(node1_0.getParent()); + assertTrue(!node1.isLoaded()); + } + + @Test + public void testFilter() throws CancelledException, CloneNotSupportedException { + GTreeNode filtered = root.filter(new TestFilter("0_0"), TaskMonitor.DUMMY); + assertEquals(3, filtered.getNodeCount()); + assertEquals(root, filtered); + assertEquals(1, filtered.getChildCount()); + GTreeNode node = filtered.getChild(0); + assertEquals(node0, node); + assertEquals(1, node.getChildCount()); + assertEquals(node0_0, node.getChild(0)); + } + + @Test + public void testFilterNoMatch() throws CancelledException, CloneNotSupportedException { + GTreeNode filtered = root.filter(new TestFilter("xxxxxxx"), TaskMonitor.DUMMY); + assertNotNull(filtered); + assertEquals(root, filtered); + assertEquals(0, filtered.getChildCount()); + } + + @Test + public void testLoadAllOnSimpleTree() throws CancelledException { + assertEquals(7, root.loadAll(TaskMonitor.DUMMY)); + } + + @Test + public void testLoadAllOnLazyTre() throws CancelledException { + GTreeNode node = new LazyGTestNode("test", 2); + assertEquals(13, node.loadAll(TaskMonitor.DUMMY)); + } + + @Test + public void testStreamDepthFirst() { + List collect = root.stream(true).collect(Collectors.toList()); + assertEquals(7, collect.size()); + assertEquals(root, collect.get(0)); + assertEquals(node0, collect.get(1)); + assertEquals(node0_0, collect.get(2)); + assertEquals(node0_1, collect.get(3)); + assertEquals(node1, collect.get(4)); + assertEquals(node1_0, collect.get(5)); + assertEquals(node2, collect.get(6)); + } + + @Test + public void testStreamBreadthFirst() { + List collect = root.stream(false).collect(Collectors.toList()); + assertEquals(7, collect.size()); + assertEquals(root, collect.get(0)); + assertEquals(node0, collect.get(1)); + assertEquals(node1, collect.get(2)); + assertEquals(node2, collect.get(3)); + assertEquals(node0_0, collect.get(4)); + assertEquals(node0_1, collect.get(5)); + assertEquals(node1_0, collect.get(6)); + } + + private class TestFilter implements GTreeFilter { + + private String text; + + TestFilter(String text) { + this.text = text; + } + + @Override + public boolean acceptsNode(GTreeNode node) { + return node.getName().contains(text); + } + + @Override + public boolean showFilterMatches() { + return false; + } + + } + + private class TestNode extends GTestNode { + TestNode(String name) { + super(name); + } + + @Override + public void fireNodeStructureChanged(GTreeNode node) { + events.add(new TestEvent(EventType.STRUCTURE_CHANGED, null, node, -1)); + } + + @Override + public void fireNodeChanged(GTreeNode parent, GTreeNode node) { + events.add(new TestEvent(EventType.NODE_CHANGED, parent, node, -1)); + } + + @Override + protected void fireNodeAdded(GTreeNode parentNode, GTreeNode newNode) { + events.add(new TestEvent(EventType.NODE_ADDED, parentNode, newNode, -1)); + } + + @Override + protected void fireNodeRemoved(GTreeNode parentNode, GTreeNode removedNode, int index) { + events.add(new TestEvent(EventType.NODE_REMOVED, parentNode, removedNode, index)); + } + } + + enum EventType { + STRUCTURE_CHANGED, NODE_CHANGED, NODE_ADDED, NODE_REMOVED + } + + private class TestEvent { + EventType type; + GTreeNode parent; + GTreeNode node; + int index; + + TestEvent(EventType type, GTreeNode parent, GTreeNode node, int index) { + this.type = type; + this.parent = parent; + this.node = node; + this.index = index; + } + } + +} diff --git a/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/LazyGTestNode.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/LazyGTestNode.java new file mode 100644 index 0000000000..1a532b55dc --- /dev/null +++ b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/LazyGTestNode.java @@ -0,0 +1,69 @@ +/* ### + * 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.ArrayList; +import java.util.List; + +import javax.swing.Icon; + +/** + * Class for testing {@link GTreeLazyNode} + */ +public class LazyGTestNode extends GTreeLazyNode { + private String name; + private int depth; + + LazyGTestNode(String name, int depth) { + this.name = name; + this.depth = depth; + } + + @Override + public String getName() { + return name; + } + + @Override + public Icon getIcon(boolean expanded) { + return null; + } + + @Override + public String getToolTip() { + return "tooltip: " + name; + } + + @Override + public boolean isLeaf() { + return depth == 0; + } + + @Override + protected List generateChildren() { + List list = new ArrayList<>(); + if (depth == 0) { + return list; + } + + for (int i = 0; i < 3; i++) { + String childName = name + "_" + i; + list.add(new LazyGTestNode(childName, depth - 1)); + } + return list; + } + +} diff --git a/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/support/TreeIteratorTest.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/support/TreeIteratorTest.java new file mode 100644 index 0000000000..80bd922f24 --- /dev/null +++ b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/support/TreeIteratorTest.java @@ -0,0 +1,98 @@ +/* ### + * 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.support; + +import static org.junit.Assert.*; + +import java.util.Iterator; + +import org.junit.Before; +import org.junit.Test; + +import docking.widgets.tree.*; + +/** + * Class to test the {@link DepthFirstIterator} and {@link BreadthFirstIterator} classes for + * iterator through the {@link GTree} nodes. + */ +public class TreeIteratorTest { + private GTreeNode root; + private GTreeNode node0; + private GTreeNode node1; + private GTreeNode node2; + private GTreeNode node0_0; + private GTreeNode node0_1; + private GTreeNode node1_0; + + @Before + public void setUp() { + root = new GTestNode("root"); + node0 = new GTestNode("Node0"); + node1 = new GTestNode("Node1"); + node2 = new GTestNode("Node2"); + node0_0 = new GTestNode("Node0_0"); + node0_1 = new GTestNode("Node0_1"); + node1_0 = new GTestNode("Node1_0"); + root.addNode(node0); + root.addNode(node1); + root.addNode(node2); + node0.addNode(node0_0); + node0.addNode(node0_1); + node1.addNode(node1_0); + } + + @Test + public void testDepthFirst() { + Iterator it = new DepthFirstIterator(root); + assertTrue(it.hasNext()); + assertEquals(root, it.next()); + assertTrue(it.hasNext()); + assertEquals(node0, it.next()); + assertTrue(it.hasNext()); + assertEquals(node0_0, it.next()); + assertTrue(it.hasNext()); + assertEquals(node0_1, it.next()); + assertTrue(it.hasNext()); + assertEquals(node1, it.next()); + assertTrue(it.hasNext()); + assertEquals(node1_0, it.next()); + assertTrue(it.hasNext()); + assertEquals(node2, it.next()); + + assertFalse(it.hasNext()); + } + + @Test + public void testBreadthFirst() { + Iterator it = new BreadthFirstIterator(root); + assertTrue(it.hasNext()); + assertEquals(root, it.next()); + assertTrue(it.hasNext()); + assertEquals(node0, it.next()); + assertTrue(it.hasNext()); + assertEquals(node1, it.next()); + assertTrue(it.hasNext()); + assertEquals(node2, it.next()); + assertTrue(it.hasNext()); + assertEquals(node0_0, it.next()); + assertTrue(it.hasNext()); + assertEquals(node0_1, it.next()); + assertTrue(it.hasNext()); + assertEquals(node1_0, it.next()); + + assertFalse(it.hasNext()); + } +} diff --git a/Ghidra/Framework/Generic/src/main/java/generic/concurrent/FutureTaskMonitor.java b/Ghidra/Framework/Generic/src/main/java/generic/concurrent/FutureTaskMonitor.java index d6bc6dd790..fdb77455f4 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/concurrent/FutureTaskMonitor.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/concurrent/FutureTaskMonitor.java @@ -18,6 +18,7 @@ package generic.concurrent; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; +import ghidra.util.Msg; import ghidra.util.exception.CancelledException; import ghidra.util.task.CancelledListener; import ghidra.util.task.TaskMonitor; diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/ChangeManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/ChangeManager.java index 8c6165dd75..e8c60ab6b7 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/ChangeManager.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/ChangeManager.java @@ -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.framework.main.datatree; -import ghidra.framework.model.*; - import java.util.*; import docking.widgets.tree.GTreeNode; +import ghidra.framework.model.*; /** * Class to handle changes when a domain folder changes; updates the @@ -35,7 +33,7 @@ class ChangeManager implements DomainFolderChangeListener { ChangeManager(ProjectDataTreePanel treePanel) { this.treePanel = treePanel; tree = treePanel.getDataTree(); - root = (DomainFolderRootNode) tree.getRootNode(); + root = (DomainFolderRootNode) tree.getModelRoot(); } @Override @@ -46,7 +44,7 @@ class ChangeManager implements DomainFolderChangeListener { return; } - List children = folderNode.getAllChildren(); + List children = folderNode.getChildren(); for (GTreeNode child : children) { if (child instanceof DomainFileNode) { if (child.getName().equals(name)) { @@ -103,7 +101,7 @@ class ChangeManager implements DomainFolderChangeListener { DomainFolder parent = file.getParent(); DomainFolderNode folderNode = findDomainFolderNode(parent, true); if (folderNode != null) { - if (folderNode.isChildrenLoadedOrInProgress()) { + if (folderNode.isLoaded()) { DomainFileNode newNode = new DomainFileNode(file); addNode(folderNode, newNode); } @@ -111,7 +109,7 @@ class ChangeManager implements DomainFolderChangeListener { } static void addNode(GTreeNode parentNode, GTreeNode newNode) { - List allChildren = parentNode.getAllChildren(); + List allChildren = parentNode.getChildren(); int index = Collections.binarySearch(allChildren, newNode); if (index < 0) { index = -index - 1; @@ -127,7 +125,7 @@ class ChangeManager implements DomainFolderChangeListener { } DomainFolder parentFolder = folder.getParent(); DomainFolderNode folderNode = findDomainFolderNode(parentFolder, true); - if (folderNode != null && folderNode.isChildrenLoadedOrInProgress()) { + if (folderNode != null && folderNode.isLoaded()) { DomainFolderNode newNode = new DomainFolderNode(folder, folderNode.getDomainFileFilter()); addNode(folderNode, newNode); @@ -175,12 +173,12 @@ class ChangeManager implements DomainFolderChangeListener { DomainFolderNode folderNode = root; for (String name : folderPath) { - if (lazy && !folderNode.isChildrenLoadedOrInProgress()) { + if (lazy && !folderNode.isLoaded()) { return null; // not visited } // must look at all children since a folder and file may have the same name boolean found = false; - for (GTreeNode node : folderNode.getAllChildren()) { + for (GTreeNode node : folderNode.getChildren()) { if (!(node instanceof DomainFolderNode)) { continue; } @@ -217,7 +215,7 @@ class ChangeManager implements DomainFolderChangeListener { if (folderNode == null) { return null; } - if (lazy && !folderNode.isChildrenLoadedOrInProgress()) { + if (lazy && !folderNode.isLoaded()) { return null; // not visited } @@ -236,7 +234,7 @@ class ChangeManager implements DomainFolderChangeListener { DomainFolder folder = folderNode.getDomainFolder(); // loop through children looking for nodes whose underlying model object // does not have this folder as its parent; - List children = folderNode.getAllChildren(); + List children = folderNode.getChildren(); for (GTreeNode child : children) { if (child instanceof DomainFileNode) { if (folder.getFile(child.getName()) == null) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DataTree.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DataTree.java index e345ccc3ec..65f557e398 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DataTree.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DataTree.java @@ -23,7 +23,8 @@ import javax.swing.tree.TreePath; import docking.DockingUtils; import docking.actions.KeyBindingUtils; -import docking.widgets.tree.*; +import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeNode; import docking.widgets.tree.support.GTreeRenderer; import ghidra.framework.main.FrontEndTool; @@ -35,7 +36,7 @@ public class DataTree extends GTree { private boolean isActive; private DataTreeDragNDropHandler dragNDropHandler; - DataTree(FrontEndTool tool, GTreeRootNode root) { + DataTree(FrontEndTool tool, GTreeNode root) { super(root); setName("Data Tree"); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DomainFileNode.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DomainFileNode.java index 51fa35fc56..0aab77774e 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DomainFileNode.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DomainFileNode.java @@ -22,7 +22,7 @@ import java.util.Date; import javax.swing.Icon; import javax.swing.SwingWorker; -import docking.widgets.tree.AbstractGTreeNode; +import docking.widgets.tree.GTreeNode; import docking.widgets.tree.GTreeNode; import ghidra.framework.model.DomainFile; import ghidra.framework.store.ItemCheckoutStatus; @@ -33,7 +33,7 @@ import resources.ResourceManager; /** * Class to represent a node in the Data tree. */ -public class DomainFileNode extends AbstractGTreeNode implements Cuttable { +public class DomainFileNode extends GTreeNode implements Cuttable { private static final Icon UNKNOWN_FILE_ICON = ResourceManager.loadImage("images/unknownFile.gif"); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DomainFolderRootNode.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DomainFolderRootNode.java index c008687de3..ee4aabbadc 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DomainFolderRootNode.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/DomainFolderRootNode.java @@ -21,19 +21,16 @@ import javax.swing.Icon; import javax.swing.ImageIcon; import docking.tool.ToolConstants; -import docking.widgets.tree.GTree; -import docking.widgets.tree.GTreeRootNode; import ghidra.framework.client.RepositoryAdapter; import ghidra.framework.model.*; import resources.ResourceManager; -public class DomainFolderRootNode extends DomainFolderNode implements GTreeRootNode { +public class DomainFolderRootNode extends DomainFolderNode { private static final ImageIcon CLOSED_PROJECT = ResourceManager.loadImage("images/closedSmallFolder.png"); private static final ImageIcon OPEN_PROJECT = ResourceManager.loadImage("images/openSmallFolder.png"); - private GTree tree; private String projectName; private String toolTipText; @@ -57,20 +54,9 @@ public class DomainFolderRootNode extends DomainFolderNode implements GTreeRootN fireNodeChanged(null, this); } - @Override - public GTree getGTree() { - return tree; - } - - @Override - public void setGTree(GTree tree) { - this.tree = tree; - } - @Override public void dispose() { super.dispose(); - tree = null; } @Override diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/NoProjectNode.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/NoProjectNode.java index 55cbfce337..916440718e 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/NoProjectNode.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/NoProjectNode.java @@ -19,9 +19,9 @@ import javax.swing.Icon; import javax.swing.UIManager; import docking.tool.ToolConstants; -import docking.widgets.tree.AbstractGTreeRootNode; +import docking.widgets.tree.GTreeNode; -public class NoProjectNode extends AbstractGTreeRootNode { +public class NoProjectNode extends GTreeNode { @Override public Icon getIcon(boolean expanded) { @@ -30,7 +30,8 @@ public class NoProjectNode extends AbstractGTreeRootNode { @Override public String getName() { - return ToolConstants.NO_ACTIVE_PROJECT; } + return ToolConstants.NO_ACTIVE_PROJECT; + } @Override public String getToolTip() { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/ProjectDataTreePanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/ProjectDataTreePanel.java index a3514f5748..e6427437e2 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/ProjectDataTreePanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/ProjectDataTreePanel.java @@ -50,7 +50,7 @@ public class ProjectDataTreePanel extends JPanel { private DataTree tree; private ProjectData projectData; - private GTreeRootNode root; + private GTreeNode root; private DomainFileFilter filter; private ChangeManager changeMgr; private boolean isActiveProject; @@ -107,9 +107,10 @@ public class ProjectDataTreePanel extends JPanel { } this.projectData = projectData; - root.removeAll(); + GTreeNode oldRoot = root; root = createRootNode(projectName); tree.setRootNode(root); + oldRoot.dispose(); changeMgr = new ChangeManager(this); projectData.addDomainFolderChangeListener(changeMgr); @@ -133,7 +134,7 @@ public class ProjectDataTreePanel extends JPanel { public void closeRootFolder() { isActiveProject = false; tree.setProjectActive(false); - GTreeRootNode oldRoot = root; + GTreeNode oldRoot = root; root = new NoProjectNode(); tree.setRootNode(root); oldRoot.removeAll(); @@ -148,7 +149,7 @@ public class ProjectDataTreePanel extends JPanel { } public void selectDomainFolder(DomainFolder domainFolder) { - DepthFirstIterator it = new DepthFirstIterator(tree, root); + Iterator it = root.iterator(true); while (it.hasNext()) { GTreeNode child = it.next(); if (child instanceof DomainFolderNode) { @@ -174,7 +175,7 @@ public class ProjectDataTreePanel extends JPanel { private List getNodesForFiles(Set files) { List nodes = new ArrayList<>(); - DepthFirstIterator it = new DepthFirstIterator(tree, root); + DepthFirstIterator it = new DepthFirstIterator(root); while (it.hasNext()) { GTreeNode node = it.next(); if (node instanceof DomainFileNode) { @@ -190,7 +191,7 @@ public class ProjectDataTreePanel extends JPanel { } public void selectDomainFile(DomainFile domainFile) { - DepthFirstIterator it = new DepthFirstIterator(tree, root); + Iterator it = root.iterator(true); while (it.hasNext()) { GTreeNode child = it.next(); if (child instanceof DomainFileNode) { @@ -463,7 +464,7 @@ public class ProjectDataTreePanel extends JPanel { /** * Create the root node for this data tree. */ - private GTreeRootNode createRootNode(String projectName) { + private GTreeNode createRootNode(String projectName) { if (projectData == null) { return new NoProjectNode(); } @@ -501,7 +502,7 @@ public class ProjectDataTreePanel extends JPanel { */ public void findAndSelect(String s) { tree.expandTree(root); - DepthFirstIterator it = new DepthFirstIterator(tree, root); + Iterator it = root.iterator(true); while (it.hasNext()) { GTreeNode node = it.next(); if (node.getName().equals(s)) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/ProjectDataNewFolderAction.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/ProjectDataNewFolderAction.java index 44d8dddf06..1274f4cedc 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/ProjectDataNewFolderAction.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/projectdata/actions/ProjectDataNewFolderAction.java @@ -105,7 +105,7 @@ public class ProjectDataNewFolderAction extends ProjectDataContextAction { private GTreeNode findNodeForFolder(DataTree tree, DomainFolder newFolder) { List folderPathList = new ArrayList<>(); getFolderPath(newFolder, folderPathList); - GTreeNode node = tree.getRootNode(); + GTreeNode node = tree.getModelRoot(); for (int i = 0; node != null && i < folderPathList.size(); i++) { node = node.getChild(folderPathList.get(i)); } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java index 08c4736744..7947f16ba2 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeManagerPluginScreenShots.java @@ -28,7 +28,8 @@ import docking.*; import docking.action.DockingActionIf; import docking.widgets.table.GTable; import docking.widgets.table.GTableCellRenderer; -import docking.widgets.tree.*; +import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.datamgr.*; import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.archive.InvalidFileArchive; @@ -88,7 +89,7 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator public void testDataTypeTreeWithAssociations() { DataTypesProvider provider = getProvider(DataTypesProvider.class); GTree tree = (GTree) getInstanceField("archiveGTree", provider); - GTreeRootNode rootNode = tree.getRootNode(); + GTreeNode rootNode = tree.getViewRoot(); GTreeNode child = rootNode.getChild("WinHelloCPP.exe"); child = child.getChild("basetsd.h"); tree.expandPath(child); @@ -140,7 +141,7 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator public void testFavoriteDts() { DataTypesProvider provider = getProvider(DataTypesProvider.class); GTree tree = (GTree) getInstanceField("archiveGTree", provider); - GTreeRootNode rootNode = tree.getRootNode(); + GTreeNode rootNode = tree.getViewRoot(); GTreeNode child = rootNode.getChild("BuiltInTypes"); tree.expandPath(child); captureIsolatedProvider(DataTypesProvider.class, 500, 400); @@ -162,7 +163,7 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator DataTypesProvider provider = getProvider(DataTypesProvider.class); GTree tree = (GTree) getInstanceField("archiveGTree", provider); - GTreeRootNode rootNode = tree.getRootNode(); + GTreeNode rootNode = tree.getViewRoot(); GTreeNode child = rootNode.getChild("WinHelloCPP.exe"); child = child.getChild("DOS"); tree.expandPath(child); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java index 0d6f4a991a..dec2596397 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java @@ -25,7 +25,8 @@ import org.junit.Test; import docking.ComponentProvider; import docking.DockingWindowManager; import docking.widgets.pathmanager.PathManager; -import docking.widgets.tree.*; +import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeNode; import generic.jar.ResourceFile; import generic.util.Path; import ghidra.app.plugin.core.console.ConsoleComponentProvider; @@ -244,7 +245,7 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator private void collapse(final GTree tree, final String nodeName) { runSwing(() -> { - GTreeRootNode rootNode = tree.getRootNode(); + GTreeNode rootNode = tree.getViewRoot(); GTreeNode exmaplesNode = rootNode.getChild(nodeName); tree.collapseAll(exmaplesNode); }); @@ -255,7 +256,7 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator Arrays.asList("Examples", "Data Types", "Binary", "Functions", "Import", "Analysis")); List toRemove = new ArrayList<>(); - final GTreeRootNode rootNode = scriptCategoryTree.getRootNode(); + final GTreeNode rootNode = scriptCategoryTree.getViewRoot(); List children = rootNode.getChildren(); for (GTreeNode child : children) { String name = child.getName(); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramManagerPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramManagerPluginScreenShots.java index 9da1fce4ab..26dd3b18f9 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramManagerPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/ProgramManagerPluginScreenShots.java @@ -15,8 +15,7 @@ */ package help.screenshot; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.awt.*; import java.io.IOException; @@ -33,7 +32,8 @@ import docking.DialogComponentProvider; import docking.options.editor.DateEditor; import docking.options.editor.OptionsDialog; import docking.widgets.OptionDialog; -import docking.widgets.tree.*; +import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeNode; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.datamgr.DataTypesProvider; import ghidra.app.plugin.core.progmgr.MultiTabPlugin; @@ -111,8 +111,9 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator public void testOpenHistory() throws Exception { Project project = env.getProject(); ProjectData projectData = project.getProjectData(); - projectData.getRootFolder().createFile("WinHelloCpp.exe", program, - TaskMonitorAdapter.DUMMY_MONITOR); + projectData.getRootFolder() + .createFile("WinHelloCpp.exe", program, + TaskMonitorAdapter.DUMMY_MONITOR); DomainFile df = program.getDomainFile(); @@ -138,7 +139,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator waitForSwing(); Object treePanel = getInstanceField("treePanel", dialog); final GTree tree = (GTree) getInstanceField("tree", treePanel); - GTreeRootNode rootNode = tree.getRootNode(); + GTreeNode rootNode = tree.getViewRoot(); GTreeNode child = rootNode.getChild(0); tree.setSelectedNode(child); assertNotNull(dialog); @@ -184,7 +185,7 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator OptionsDialog dialog = (OptionsDialog) getDialog(); Object optionsPanel = getInstanceField("panel", dialog); GTree tree = (GTree) getInstanceField("gTree", optionsPanel); - GTreeRootNode rootNode = tree.getRootNode(); + GTreeNode rootNode = tree.getViewRoot(); GTreeNode child = rootNode.getChild("Program Information"); tree.setSelectedNode(child); waitForTree(tree); @@ -307,8 +308,9 @@ public class ProgramManagerPluginScreenShots extends GhidraScreenShotGenerator List list = new ArrayList<>(); for (int i = 0; i < count; i++) { String programName = "Program" + (i + 1) + ".exe"; - list.add(projectData.getRootFolder().createFile(programName, program, - TaskMonitorAdapter.DUMMY_MONITOR)); + list.add(projectData.getRootFolder() + .createFile(programName, program, + TaskMonitorAdapter.DUMMY_MONITOR)); } program.flushEvents(); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/core/processors/SetLanguageTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/core/processors/SetLanguageTest.java index f3bafcc6e9..44bab4b514 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/core/processors/SetLanguageTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/plugin/core/processors/SetLanguageTest.java @@ -82,7 +82,7 @@ public class SetLanguageTest extends AbstractGhidraHeadedIntegrationTest { GTree tree = findComponent(frontEndTool.getToolFrame(), GTree.class); waitForTree(tree); - GTreeNode rootNode = tree.getRootNode(); + GTreeNode rootNode = tree.getViewRoot(); xyzFolderNode = rootNode.getChild(0); notepadNode = rootNode.getChild(1); notepadFile = ((DomainFileNode) notepadNode).getDomainFile(); @@ -254,8 +254,10 @@ public class SetLanguageTest extends AbstractGhidraHeadedIntegrationTest { try { int txId = p.startTransaction("set Language"); - Function f = p.getListing().createFunction("BOB", addr("0x10041a8"), - new AddressSet(addr("0x10041a8"), addr("0x10041c0")), SourceType.USER_DEFINED); + Function f = p.getListing() + .createFunction("BOB", addr("0x10041a8"), + new AddressSet(addr("0x10041a8"), addr("0x10041c0")), + SourceType.USER_DEFINED); f.setCustomVariableStorage(true); ParameterImpl param = new ParameterImpl("PARAM_ONE", null, eax, p); f.addParameter(param, SourceType.USER_DEFINED); @@ -265,10 +267,12 @@ public class SetLanguageTest extends AbstractGhidraHeadedIntegrationTest { f.addLocalVariable(local1, SourceType.USER_DEFINED); f.addLocalVariable(local2, SourceType.USER_DEFINED); - p.getReferenceManager().addRegisterReference(addr("0x10041b2"), 0, esi, RefType.DATA, - SourceType.USER_DEFINED); - p.getReferenceManager().addRegisterReference(addr("0x10041b3"), 0, edi, RefType.DATA, - SourceType.USER_DEFINED); + p.getReferenceManager() + .addRegisterReference(addr("0x10041b2"), 0, esi, RefType.DATA, + SourceType.USER_DEFINED); + p.getReferenceManager() + .addRegisterReference(addr("0x10041b3"), 0, edi, RefType.DATA, + SourceType.USER_DEFINED); p.endTransaction(txId, true); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ToolActionManagerTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ToolActionManagerTest.java index 4d25850260..fd2a898f75 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ToolActionManagerTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/framework/main/ToolActionManagerTest.java @@ -35,7 +35,6 @@ import docking.widgets.OptionDialog; import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.table.GTable; import docking.widgets.tree.GTreeNode; -import docking.widgets.tree.GTreeRootNode; import docking.wizard.WizardManager; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.framework.GenericRunInfo; @@ -225,7 +224,7 @@ public class ToolActionManagerTest extends AbstractGhidraHeadedIntegrationTest { rootFolder.createFile("notepad", p, TaskMonitor.DUMMY); env.release(p); - GTreeRootNode rootNode = tree.getRootNode(); + GTreeNode rootNode = tree.getViewRoot(); waitForTree(tree); waitForSwing(); diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java index a98f5e8b98..f0a42c214f 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/test/FrontEndTestEnv.java @@ -27,7 +27,8 @@ import docking.*; import docking.action.DockingActionIf; import docking.test.AbstractDockingTest; import docking.widgets.OptionDialog; -import docking.widgets.tree.*; +import docking.widgets.tree.GTree; +import docking.widgets.tree.GTreeNode; import generic.test.AbstractGTest; import generic.test.AbstractGenericTest; import ghidra.framework.main.FrontEndTool; @@ -62,7 +63,7 @@ public class FrontEndTestEnv { protected FrontEndTool frontEndTool; protected DataTree tree; protected DomainFolder rootFolder; - protected GTreeRootNode rootNode; + protected GTreeNode rootNode; public FrontEndTestEnv() throws Exception { this(false); @@ -86,7 +87,7 @@ public class FrontEndTestEnv { rootFolder.createFile(PROGRAM_A, p, TaskMonitor.DUMMY); p.release(this); - rootNode = tree.getRootNode(); + rootNode = tree.getViewRoot(); waitForTree(); } @@ -133,7 +134,7 @@ public class FrontEndTestEnv { } public GTreeNode getRootNode() { - return tree.getRootNode(); + return tree.getModelRoot(); } /**