GP-1382 - Added Find actions to the Data Types provider to allow users

to find structures by size or by offset(s).
This commit is contained in:
dragonmacher 2021-10-20 16:54:04 -04:00
parent f9463e600d
commit ec88c732d2
19 changed files with 1104 additions and 379 deletions

View File

@ -228,6 +228,7 @@ src/main/help/help/topics/DataTypeManagerPlugin/images/DisassociateDialog.png||G
src/main/help/help/topics/DataTypeManagerPlugin/images/EditPaths.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/FavoriteDts.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/FindDataTypes.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/FindDataTypesBySize.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/PreviewWindow.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/RevertDialog.png||GHIDRA||||END|
src/main/help/help/topics/DataTypeManagerPlugin/images/SearchResults.png||GHIDRA||||END|

View File

@ -650,7 +650,7 @@
types to another program.</P>
</BLOCKQUOTE>
<H3><A name="Find_Data_Types"></A> <A name="FindDataTypesDialog"></A>Find Data Types by
<H3><A name="Find_Data_Types_By_Name"></A> <A name="FindDataTypesDialog"></A>Find Data Types by
Name</H3>
<BLOCKQUOTE>
@ -667,8 +667,8 @@
<P align="center"><IMG src="images/FindDataTypes.png" alt="" border="0"><BR>
</P>
<P>Enter a value in the "Please enter the search string: " field, or choose a
previously entered value from the combo box. Either press the &lt;Enter&gt; key or
<P>Enter a value in the "Please enter the search string: " field.
Either press the &lt;Enter&gt; key or
select the <B>OK</B> button. A new <I>Find Data Type</I> window will appear with
the filter set to the entered search text.</P>
@ -681,9 +681,37 @@
<H3><A name="Find_Data_Types_By_Size"></A>Find Data Types by Size</H3>
<BLOCKQUOTE>
<P>This is similar to the <B>Find Data Types by Name</B> action (show above) except
that it lets you specify a size that will be used to show only those data types with
the same length.</P>
<P align="center"><IMG src="images/FindDataTypesBySize.png" alt="" border="0"><BR>
</P>
<P>This is similar to the <A HREF="#Find_Data_Types_By_Name">
<B>Find Data Types by Name</B></A> action (show above) except that it lets you specify
one or more sizes that will be used to show only those data types with the same length.
</P>
<P>
This dialog accepts a single integer value or multiple values separated by a ','.
You may also enter ranges in this dialog using ':' as the range separator. Values
with a leading '0x' will be treated as hex.
</P>
</BLOCKQUOTE>
<H3><A name="Find_Structues_By_Size"></A>Find Structures by Size</H3>
<BLOCKQUOTE>
<P>This is similar to the <B>Find Data Types by Size</B> action (show above) except
that it filters results to show only Structures.</P>
</BLOCKQUOTE>
<H3><A name="Find_Structures_By_Offset"></A>Find Structures by Offset</H3>
<BLOCKQUOTE>
<P>This is similar to the <B>Find Structures by Size</B> action (show above) except
that it lets you specify one or more offset values that will be used to show only those
structures that contain any of the specified offsets.</P>
</BLOCKQUOTE>
<H3><A name="Export_Data_Types"></A>Export Data Type(s)</H3>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -177,9 +177,14 @@ public class DataTypesProvider extends ComponentProviderAdapter {
addLocalAction(new ExpandAllAction(plugin)); // Tree
// VeryLast group
addLocalAction(getIncludeDataMembersInSearchAction()); // Common
addLocalAction(new FindDataTypesAction(plugin)); // Common
addLocalAction(new FindDataTypesBySizeAction(plugin)); // Common
addLocalAction(new FindDataTypesByNameAction(plugin, "1"));
addLocalAction(new FindDataTypesBySizeAction(plugin, "2"));
addLocalAction(new FindStructuresByOffsetAction(plugin, "3"));
addLocalAction(new FindStructuresBySizeAction(plugin, "4"));
includeDataMembersInSearchAction =
new IncludeDataTypesInFilterAction(plugin, this, "5");
addLocalAction(includeDataMembersInSearchAction);
addLocalAction(new ApplyFunctionDataTypesAction(plugin)); // Tree
addLocalAction(new CaptureFunctionDataTypesAction(plugin)); // Tree
addLocalAction(new SetFavoriteDataTypeAction(plugin)); // Data Type
@ -189,7 +194,6 @@ public class DataTypesProvider extends ComponentProviderAdapter {
// ZVeryLast group
addLocalAction(new FindReferencesToDataTypeAction(plugin)); // DataType
addLocalAction(new FindReferencesToFieldAction(plugin)); // DataType
// addLocalAction( new FindDataTypesContainingAction(plugin) ); // DataType
addLocalAction(new FindBaseDataTypeAction(plugin)); // DataType
addLocalAction(new DisplayTypeAsGraphAction(plugin));
@ -322,13 +326,6 @@ public class DataTypesProvider extends ComponentProviderAdapter {
return previewWindowAction;
}
private ToggleDockingAction getIncludeDataMembersInSearchAction() {
if (includeDataMembersInSearchAction == null) {
includeDataMembersInSearchAction = new IncludeDataTypesInFilterAction(plugin, this);
}
return includeDataMembersInSearchAction;
}
@Override
public ActionContext getActionContext(MouseEvent event) {
GTreeNode clickedNode = null;
@ -641,7 +638,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
getPreviewWindowAction().setSelected(previewWindowVisible);
boolean dataMembersInSearch = saveState.getBoolean(INCLUDE_DATA_MEMBERS_IN_SEARCH, false);
getIncludeDataMembersInSearchAction().setSelected(dataMembersInSearch);
includeDataMembersInSearchAction.setSelected(dataMembersInSearch);
}
void save(SaveState saveState) {
@ -651,7 +648,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
getConflictHandlerModesAction().getCurrentUserData().toString());
saveState.putBoolean(PREVIEW_WINDOW_STATE, getPreviewWindowAction().isSelected());
saveState.putBoolean(INCLUDE_DATA_MEMBERS_IN_SEARCH,
getIncludeDataMembersInSearchAction().isSelected());
includeDataMembersInSearchAction.isSelected());
}
public DataTypeArchiveGTree getGTree() {
@ -788,7 +785,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
archiveGTree.setIncludeDataTypeMembersInSearch(includeDataMembersInFilter);
// make sure the action is in sync
ToggleDockingAction action = getIncludeDataMembersInSearchAction();
ToggleDockingAction action = includeDataMembersInSearchAction;
boolean selected = action.isSelected();
if (selected != includeDataMembersInFilter) {
action.setSelected(includeDataMembersInFilter);

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,30 +15,32 @@
*/
package ghidra.app.plugin.core.datamgr.actions;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
import ghidra.framework.plugintool.PluginTool;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import docking.ActionContext;
import docking.DockingUtils;
import docking.action.*;
import docking.widgets.dialogs.InputDialog;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.HelpLocation;
public class FindDataTypesAction extends DockingAction {
public class FindDataTypesByNameAction extends DockingAction {
public static final String NAME = "Find Data Types by Name";
private final DataTypeManagerPlugin plugin;
public FindDataTypesAction(DataTypeManagerPlugin plugin) {
super("Find Data Types", plugin.getName());
public FindDataTypesByNameAction(DataTypeManagerPlugin plugin, String menuSubGroup) {
super("Find Data Types by Name", plugin.getName());
this.plugin = plugin;
setMenuBarData(new MenuData(new String[] { "Find Data Types by Name..." }, null,
"VeryLast", -1, "1"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
setEnabled(true);
setMenuBarData(new MenuData(new String[] { NAME + "..." }, null,
"VeryLast", -1, menuSubGroup));
setKeyBindingData(
new KeyBindingData(KeyEvent.VK_F, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Find_Data_Types_By_Name"));
}
@Override
@ -49,16 +50,14 @@ public class FindDataTypesAction extends DockingAction {
new InputDialog("Find Data Types", "Please enter the search string: ");
PluginTool tool = plugin.getTool();
tool.showDialog(inputDialog);
if (inputDialog.isCanceled()) {
return;
}
final String searchString = inputDialog.getValue();
String title = "Find Data Type";
final DataTypesProvider newProvider = plugin.createProvider();
String searchString = inputDialog.getValue();
DataTypesProvider newProvider = plugin.createProvider();
newProvider.setIncludeDataTypeMembersInFilter(plugin.includeDataMembersInSearch());
newProvider.setTitle(title);
newProvider.setTitle(NAME);
newProvider.setFilterText(searchString);
newProvider.setVisible(true);
}

View File

@ -18,7 +18,7 @@ package ghidra.app.plugin.core.datamgr.actions;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.dialogs.NumberInputDialog;
import docking.widgets.dialogs.NumberRangeInputDialog;
import docking.widgets.tree.*;
import docking.widgets.tree.support.CombinedGTreeFilter;
import docking.widgets.tree.support.GTreeFilter;
@ -28,40 +28,50 @@ import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.program.model.data.DataType;
import ghidra.util.HelpLocation;
import ghidra.util.datastruct.Range;
import ghidra.util.datastruct.SortedRangeList;
public class FindDataTypesBySizeAction extends DockingAction {
public static final String NAME = "Find Data Types by Size";
private DataTypeManagerPlugin plugin;
public FindDataTypesBySizeAction(DataTypeManagerPlugin plugin) {
super("Find Data Types By Size", plugin.getName());
public FindDataTypesBySizeAction(DataTypeManagerPlugin plugin, String menuSubGroup) {
this(plugin, NAME, menuSubGroup);
}
FindDataTypesBySizeAction(DataTypeManagerPlugin plugin, String name, String menuSubGroup) {
super(name, plugin.getName());
this.plugin = plugin;
setMenuBarData(
new MenuData(new String[] { "Find Data Types by Size..." }, null, "VeryLast", -1, "2"));
setEnabled(true);
new MenuData(new String[] { name + "..." }, null, "VeryLast", -1, menuSubGroup));
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Find_Data_Types_By_Size"));
}
@Override
public void actionPerformed(ActionContext context) {
NumberInputDialog numberInputDialog = new NumberInputDialog("bytes", 1, 1);
if (!numberInputDialog.show()) {
NumberRangeInputDialog inputDialog =
new NumberRangeInputDialog(getName(), "Size(s)");
if (!inputDialog.show()) {
return;
}
int value = numberInputDialog.getValue();
String title = "Find Data Types With Size";
final DataTypesProvider newProvider = plugin.createProvider();
newProvider.setTitle(title);
SortedRangeList values = inputDialog.getValue();
DataTypesProvider newProvider = plugin.createProvider();
newProvider.setTitle(getName());
DataTypeArchiveGTree tree = newProvider.getGTree();
tree.setFilterProvider(new MyTreeFilterProvider(tree, new SizeGTreeFilter(value)));
GTreeFilter filter = createFilter(values);
tree.setFilterProvider(new MyTreeFilterProvider(tree, filter));
newProvider.setVisible(true);
}
protected GTreeFilter createFilter(SortedRangeList values) {
return new SizeGTreeFilter(values);
}
private class MyTreeFilterProvider extends DefaultGTreeFilterProvider {
private GTreeFilter secondaryFilter;
@ -82,10 +92,10 @@ public class FindDataTypesBySizeAction extends DockingAction {
private class SizeGTreeFilter implements GTreeFilter {
private final int size;
private final SortedRangeList sizes;
SizeGTreeFilter(int size) {
this.size = size;
SizeGTreeFilter(SortedRangeList sizes) {
this.sizes = sizes;
}
@Override
@ -99,9 +109,15 @@ public class FindDataTypesBySizeAction extends DockingAction {
return false;
}
DataTypeNode dataTypeNode = (DataTypeNode) node;
DataType dataType = dataTypeNode.getDataType();
int length = dataType.getLength();
return length == size;
DataType dt = dataTypeNode.getDataType();
int length = dt.getLength();
for (Range range : sizes) {
if (range.contains(length)) {
return true;
}
}
return false;
}
}
}

View File

@ -1,251 +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 ghidra.app.plugin.core.datamgr.actions;
import java.util.*;
import javax.swing.tree.TreePath;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.tree.GTree;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.program.database.DatabaseObject;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.util.*;
import ghidra.util.exception.AssertException;
public class FindDataTypesContainingAction extends DockingAction {
// private final DataTypeManagerPlugin plugin;
public FindDataTypesContainingAction(DataTypeManagerPlugin plugin) {
super("Find Data Types Containing", plugin.getName());
// this.plugin = plugin;
setPopupMenuData(
new MenuData(new String[] { "Find Data Types Containing..." }, null, "ZVeryLast"));
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Data_Type_Manager_Plugin"));
setEnabled(true);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return (context instanceof DataTypesActionContext);
}
@Override
public void actionPerformed(ActionContext context) {
if (!(context instanceof DataTypesActionContext)) {
return;
}
Object contextObject = context.getContextObject();
GTree gTree = (GTree) contextObject;
TreePath[] selectionPaths = gTree.getSelectionPaths();
StringBuffer text = new StringBuffer();
if (selectionPaths.length != 1) {
Msg.showInfo(getClass(), gTree, "Find Data Types Containing",
"You must select a single data type in the tree.");
return;
}
TreePath path = gTree.getSelectionPath();
Object node = path.getLastPathComponent();
if (!(node instanceof DataTypeNode)) {
Msg.showInfo(getClass(), gTree, "Find Data Types Containing",
"You must select a single data type in the tree.");
return;
}
DataTypeNode dataTypeNode = (DataTypeNode) node;
DataType dataType = dataTypeNode.getDataType();
Iterator<DataType> parentDtIterator = getDataTypesContaining(dataType, gTree);
if (!parentDtIterator.hasNext()) {
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
ArchiveType type = dataTypeManager.getType();
String typeName = " archive";
if (type.equals(ArchiveType.PROGRAM)) {
typeName = " program";
}
else if (type.equals(ArchiveType.BUILT_IN)) {
typeName = "";
}
Msg.showInfo(getClass(), gTree,
"Find Data Types Containing \"" + dataType.getPathName() + "\"",
"<HTML><B>" + HTMLUtilities.friendlyEncodeHTML(dataType.getPathName()) +
"</B> isn't contained in any other data types in the <B>" +
HTMLUtilities.friendlyEncodeHTML(dataTypeManager.getName()) + "</B>" +
typeName + "!!!</HTML>");
return;
}
text.append("<HTML>");
text.append("<B>" + HTMLUtilities.friendlyEncodeHTML(dataType.getPathName()) +
"</B> is contained in:<BR>");
int count = 0;
while (parentDtIterator.hasNext()) {
DataType parentDt = parentDtIterator.next();
count++;
if (count >= 50) {
text.append("<BR><BR>Too many to display...");
break;
}
text.append(
"<BR> " + HTMLUtilities.friendlyEncodeHTML(parentDt.getPathName()));
}
text.append("</HTML>");
Msg.showInfo(getClass(), gTree,
"Find Data Types Containing \"" + dataType.getPathName() + "\"", text.toString());
}
protected Iterator<DataType> getDataTypesContaining(DataType dataType, GTree gTree) {
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
if (dataTypeManager == null) {
Msg.showInfo(getClass(), gTree,
"Find Data Types Containing \"" + dataType.getName() + "\"",
"\"" + dataType.getName() + "\" doesn't have a data type manager.");
return new DataTypeArrayIterator(new DataType[0]);
}
if (dataType instanceof DatabaseObject) {
return new DataTypeArrayIterator(dataType.getParents());
}
return new DataTypeContainingIterator(dataType);
}
private class DataTypeArrayIterator implements Iterator<DataType> {
DataType[] dataTypes;
int nextIndex = 0;
private DataTypeArrayIterator(DataType[] dataTypes) {
this.dataTypes = dataTypes;
}
@Override
public boolean hasNext() {
return nextIndex < dataTypes.length;
}
@Override
public DataType next() {
return dataTypes[nextIndex++];
}
@Override
public void remove() {
}
}
private class DataTypeContainingIterator implements Iterator<DataType> {
DataType dataType;
Iterator<DataType> iterator;
DataType nextDataType = null;
private DataTypeContainingIterator(DataType dataType) {
this.dataType = dataType;
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
if (dataTypeManager == null) {
iterator = new DataTypeArrayIterator(new DataType[0]);
}
else {
iterator = dataTypeManager.getAllDataTypes();
}
}
@Override
public boolean hasNext() {
if (nextDataType != null) {
return true;
}
while (iterator.hasNext()) {
DataType dt = iterator.next();
Collection<DataType> containedDataTypes = getDirectContainedDatatypes(dt);
for (DataType containedDt : containedDataTypes) {
if (containedDt == dataType) {
nextDataType = dt;
return true;
}
}
}
return false;
}
@Override
public DataType next() {
if (nextDataType == null) {
hasNext();
}
DataType tempDataType = nextDataType;
nextDataType = null;
return tempDataType;
}
@Override
public void remove() {
}
}
private static List<DataType> getDirectContainedDatatypes(DataType dt) {
List<DataType> list = new ArrayList<DataType>();
if (dt instanceof Array) {
Array array = (Array) dt;
list.add(array.getDataType());
}
else if (dt instanceof Pointer) {
Pointer ptr = (Pointer) dt;
DataType ptrDt = ptr.getDataType();
if (ptrDt != null) {
list.add(ptrDt);
}
}
else if (dt instanceof Composite) {
Composite composite = (Composite) dt;
int n = composite.getNumComponents();
for (int i = 0; i < n; i++) {
DataTypeComponent component = composite.getComponent(i);
list.add(component.getDataType());
}
}
else if (dt instanceof TypeDef) {
TypeDef typedef = (TypeDef) dt;
list.add(typedef.getDataType());
}
else if (dt instanceof Enum) {
}
else if (dt instanceof FunctionDefinition) {
FunctionDefinition funDef = (FunctionDefinition) dt;
list.add(funDef.getReturnType());
ParameterDefinition[] arguments = funDef.getArguments();
for (ParameterDefinition parameter : arguments) {
list.add(parameter.getDataType());
}
}
else if (dt instanceof BuiltInDataType) {
}
else if (dt instanceof MissingBuiltInDataType) {
}
else if (dt.equals(DataType.DEFAULT)) {
}
else {
throw new AssertException("Unknown data Type:" + dt.getDisplayName());
}
return list;
}
}

View File

@ -0,0 +1,159 @@
/* ###
* 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.actions;
import java.util.Iterator;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.dialogs.NumberRangeInputDialog;
import docking.widgets.tree.*;
import docking.widgets.tree.support.CombinedGTreeFilter;
import docking.widgets.tree.support.GTreeFilter;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.program.model.data.*;
import ghidra.util.HelpLocation;
import ghidra.util.datastruct.Range;
import ghidra.util.datastruct.SortedRangeList;
import util.CollectionUtils;
/**
* Allows the user to supply one or more offsets that are used to search for structures that have
* any of those offsets.
*/
public class FindStructuresByOffsetAction extends DockingAction {
public static final String NAME = "Find Structures by Offset";
private DataTypeManagerPlugin plugin;
public FindStructuresByOffsetAction(DataTypeManagerPlugin plugin, String menuSubGroup) {
super(NAME, plugin.getName());
this.plugin = plugin;
setMenuBarData(
new MenuData(new String[] { NAME + "..." }, null, "VeryLast", -1, menuSubGroup));
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Find_Data_Types_By_Offset"));
}
@Override
public void actionPerformed(ActionContext context) {
NumberRangeInputDialog inputDialog =
new NumberRangeInputDialog(NAME, "Offset(s)");
if (!inputDialog.show()) {
return;
}
SortedRangeList values = inputDialog.getValue();
DataTypesProvider newProvider = plugin.createProvider();
newProvider.setTitle(NAME);
DataTypeArchiveGTree tree = newProvider.getGTree();
tree.setFilterProvider(new MyTreeFilterProvider(tree, new OffsetGTreeFilter(values)));
newProvider.setVisible(true);
}
private class MyTreeFilterProvider extends DefaultGTreeFilterProvider {
private GTreeFilter secondaryFilter;
MyTreeFilterProvider(GTree tree, GTreeFilter secondaryFilter) {
super(tree);
this.secondaryFilter = secondaryFilter;
}
@Override
public GTreeFilter getFilter() {
GTreeFilter filter = super.getFilter();
if (filter == null) {
return secondaryFilter;
}
return new CombinedGTreeFilter(filter, secondaryFilter);
}
}
private class OffsetGTreeFilter implements GTreeFilter {
private final SortedRangeList offsets;
OffsetGTreeFilter(SortedRangeList offsets) {
this.offsets = offsets;
}
@Override
public boolean showFilterMatches() {
return true;
}
@Override
public boolean acceptsNode(GTreeNode node) {
if (!(node instanceof DataTypeNode)) {
return false;
}
DataTypeNode dataTypeNode = (DataTypeNode) node;
DataType dataType = dataTypeNode.getDataType();
if (!(dataType instanceof Structure)) {
return false;
}
Structure structure = (Structure) dataType;
OffsetIterator it = new OffsetIterator(structure);
for (Integer structureOffset : CollectionUtils.asIterable(it)) {
for (Range range : offsets) {
if (range.contains(structureOffset)) {
return true;
}
if (structureOffset > range.max) {
// ranges are ascending sorted order; the structure offset is already
// bigger than this range, so no more ranges can match
break;
}
}
}
return false;
}
private class OffsetIterator implements Iterator<Integer> {
private DataTypeComponent[] components;
private int index = 0;
private int length;
OffsetIterator(Structure s) {
this.components = s.getComponents();
this.length = components.length;
}
@Override
public boolean hasNext() {
if (index >= length) {
return false;
}
return true;
}
@Override
public Integer next() {
DataTypeComponent dtc = components[index++];
return dtc.getOffset();
}
}
}
}

View File

@ -0,0 +1,82 @@
/* ###
* 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.actions;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.support.GTreeFilter;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Structure;
import ghidra.util.HelpLocation;
import ghidra.util.datastruct.Range;
import ghidra.util.datastruct.SortedRangeList;
/**
* Allows the user to supply one or more sizes that are used to search for structures that have
* that size.
*/
public class FindStructuresBySizeAction extends FindDataTypesBySizeAction {
@SuppressWarnings("hiding")
public static final String NAME = "Find Structures by Size";
public FindStructuresBySizeAction(DataTypeManagerPlugin plugin, String menuSubGroup) {
super(plugin, NAME, menuSubGroup);
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Find_Structures_By_Size"));
}
@Override
protected GTreeFilter createFilter(SortedRangeList values) {
return new StructureSizeGTreeFilter(values);
}
private class StructureSizeGTreeFilter implements GTreeFilter {
private final SortedRangeList sizes;
StructureSizeGTreeFilter(SortedRangeList sizes) {
this.sizes = sizes;
}
@Override
public boolean showFilterMatches() {
return true;
}
@Override
public boolean acceptsNode(GTreeNode node) {
if (!(node instanceof DataTypeNode)) {
return false;
}
DataTypeNode dataTypeNode = (DataTypeNode) node;
DataType dataType = dataTypeNode.getDataType();
if (!(dataType instanceof Structure)) {
return false;
}
Structure structure = (Structure) dataType;
int length = structure.getLength();
for (Range range : sizes) {
if (range.contains(length)) {
return true;
}
}
return false;
}
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,23 +15,25 @@
*/
package ghidra.app.plugin.core.datamgr.actions;
import docking.action.MenuData;
import docking.action.ToggleDockingAction;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
import ghidra.util.HelpLocation;
import docking.action.MenuData;
import docking.action.ToggleDockingAction;
public class IncludeDataTypesInFilterAction extends ToggleDockingAction {
private final DataTypesProvider provider;
public IncludeDataTypesInFilterAction(DataTypeManagerPlugin plugin, DataTypesProvider provider) {
public IncludeDataTypesInFilterAction(DataTypeManagerPlugin plugin, DataTypesProvider provider,
String menuSubGroup) {
super("Include Data Members in Filter", plugin.getName());
this.provider = provider;
setMenuBarData(new MenuData(new String[] { "Include Data Members in Filter" }, null,
"VeryLast", MenuData.NO_MNEMONIC, "3"));
setDescription("Selected indicates to include member names and data types in filter operations.");
"VeryLast", MenuData.NO_MNEMONIC, menuSubGroup));
setDescription(
"Selected indicates to include member names and data types in filter operations.");
setEnabled(true);
setSelected(false); // default to off!

View File

@ -0,0 +1,178 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.widgets.dialogs;
import static org.junit.Assert.*;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JTextField;
import org.junit.*;
import docking.DialogComponentProvider;
import docking.DockingWindowManager;
import docking.test.AbstractDockingTest;
import ghidra.util.datastruct.Range;
import ghidra.util.datastruct.SortedRangeList;
public class NumberRangeInputDialogTest extends AbstractDockingTest {
private NumberRangeInputDialog dialog;
private JTextField textField;
@Before
public void setUp() throws Exception {
createAndShowDialog();
}
@After
public void tearDown() throws Exception {
close(dialog);
}
@Test
public void testRangeInput_SingleValue_Hex() {
setText("0x4");
ok();
assertValue(0x4);
}
@Test
public void testRangeInput_MultiValue_Mixed() {
setText("0x4,0x12, 100");
ok();
assertValues(0x4, 0x12, 100);
}
@Test
public void testRangeInput_Range_OneRange() {
setText("0x4:0x12");
ok();
assertValues(new Range(0x4, 0x12));
}
@Test
public void testRangeInput_MixedValues_RangeAndSingleValues() {
setText("100, -20, 0x4:0x6, -100");
ok();
assertValues(-100, -20, 0x4, 0x5, 0x6, 100);
}
@Test
public void testRangeInput_SingleValue_InvalidNumber() {
setText("0xBob");
ok();
assertStatusText("Unable to parse as a number: '0xBob'");
}
@Test
public void testRangeInput_MixedValues_InvalidNumber() {
setText("100, 0xBob");
ok();
assertStatusText("Unable to parse as a number: '0xBob'");
}
@Test
public void testRangeInput_Range_InvalidNumber() {
setText("100:0xBob");
ok();
assertStatusText("Unable to parse as a number: '100:0xBob'");
}
private void assertStatusText(String expected) {
String actual = runSwing(() -> dialog.getStatusText());
assertEquals(expected, actual);
}
private void assertValues(Range range) {
SortedRangeList ranges = dialog.getValue();
assertEquals(range.size(), ranges.getNumValues());
assertEquals(range, ranges.getRange(0));
}
private void assertValues(int... values) {
SortedRangeList ranges = dialog.getValue();
assertEquals(values.length, ranges.getNumValues());
Set<Integer> set = new HashSet<>();
for (int value : values) {
set.add(value);
}
for (Range range : ranges) {
for (int value : range) {
assertTrue("Range value not expected: " + value, set.contains(value));
}
}
}
private void assertValue(int expected) {
SortedRangeList ranges = dialog.getValue();
assertEquals(1, ranges.getNumValues());
assertEquals(expected, ranges.getRange(0).min);
assertEquals(expected, ranges.getRange(0).max);
}
private void createAndShowDialog() {
dialog = new NumberRangeInputDialog("My Dialog Title", "Offset(s)");
show(dialog);
textField = getTextField(dialog);
}
private void ok() {
pressButtonByText(dialog, "OK");
}
private void setText(String value) {
setText(textField, value);
}
private void show(DialogComponentProvider theDialog) {
runSwing(() -> {
DockingWindowManager.showDialog(theDialog);
}, false);
waitForDialogComponent(NumberRangeInputDialog.class);
}
private JTextField getTextField(NumberRangeInputDialog theDialog) {
return theDialog.getTextField();
}
}

View File

@ -24,8 +24,7 @@ import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@ -35,6 +34,7 @@ import javax.swing.tree.TreePath;
import org.junit.*;
import docking.DialogComponentProvider;
import docking.DockingUtils;
import docking.action.DockingActionIf;
import docking.action.ToggleDockingActionIf;
@ -42,11 +42,12 @@ import docking.actions.KeyBindingUtils;
import docking.tool.ToolConstants;
import docking.tool.util.DockingToolConstants;
import docking.widgets.OptionDialog;
import docking.widgets.dialogs.NumberRangeInputDialog;
import docking.widgets.tree.GTreeNode;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.plugin.core.compositeeditor.ApplyAction;
import ghidra.app.plugin.core.compositeeditor.CompositeEditorTableAction;
import ghidra.app.plugin.core.datamgr.actions.CreateTypeDefDialog;
import ghidra.app.plugin.core.datamgr.actions.*;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
import ghidra.app.plugin.core.datamgr.tree.*;
@ -62,9 +63,10 @@ import ghidra.program.database.ProgramDB;
import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.model.data.*;
import ghidra.test.*;
import ghidra.util.classfinder.ClassFilter;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.classfinder.ClassSearchTask;
import ghidra.util.task.Task;
import ghidra.util.task.TaskMonitor;
import util.CollectionUtils;
import utilities.util.FileUtilities;
/**
@ -74,10 +76,11 @@ import utilities.util.FileUtilities;
public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
private static final String BUILTIN_NAME = "BuiltInTypes";
private static final String PROGRAM_FILENAME = "notepad";
private static final String PROGRAM_FILENAME = "sample";
private TestEnv env;
private PluginTool tool;
private ProgramBuilder builder;
private ProgramDB program;
private DataTypeManagerPlugin plugin;
private DataTypeArchiveGTree tree;
@ -122,7 +125,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
}
private ProgramDB buildProgram() throws Exception {
ProgramBuilder builder = new ProgramBuilder("notepad", ProgramBuilder._TOY, this);
builder = new ProgramBuilder("sample", ProgramBuilder._TOY, this);
builder.createMemory(".text", "0x1001000", 0x100);
CategoryPath miscPath = new CategoryPath("/MISC");
@ -231,10 +234,10 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
// select "New Category" action
DataTypeTestUtils.performAction(action, tree, false);
waitForDialogComponent("Cannot Edit Tree Node");
DialogComponentProvider dialog = waitForDialogComponent("Cannot Edit Tree Node");
close(dialog);
// verify that the tree opens a new node with the default
// category name is "New Category"
// verify that the tree opens a new node with the default category name is "New Category"
assertEquals(childCount + 1, miscNode.getChildCount());
GTreeNode node = miscNode.getChild("New Category");
assertNotNull(node);
@ -620,7 +623,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
toggleDetailedSearch(true);
assertSingleFilterMatch(
new String[] { "Data Types", "notepad", "Category1", "Category2", "MyStruct" });
new String[] { "Data Types", "sample", "Category1", "Category2", "MyStruct" });
}
@Test
@ -663,48 +666,34 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
@Test
public void testRefreshBuiltins() throws Exception {
try {
doTestRefreshBuiltins();
}
finally {
cleanupTestBuiltin();
}
}
private void doTestRefreshBuiltins() throws Exception {
GTreeNode treeRoot = tree.getModelRoot();
GTreeNode builtInNode = treeRoot.getChild("BuiltInTypes");
assertNull("Test setup Error: ghidra.app.test.TestDataType was not removed!",
builtInNode.getChild("TestDataType"));
compileJavaFile();
compileJavaDataType();
DockingActionIf action = getAction(plugin, "Refresh BuiltInTypes");
assertTrue(action.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(action, tree, false);
waitForTasks();
waitForProgram();
waitForActionToBeEnabled(action);
builtInNode = treeRoot.getChild("BuiltInTypes");
assertNotNull(builtInNode.getChild("TestDataType"));
ClassFilter filter = new BuiltInDataTypeClassExclusionFilter();
ArrayList<DataType> listOne =
new ArrayList<>(ClassSearcher.getInstances(BuiltInDataType.class, filter));
DataTypeManager bdtm = plugin.getBuiltInDataTypesManager();
ArrayList<DataType> listTwo = new ArrayList<>();
Iterator<DataType> iter = bdtm.getAllDataTypes();
while (iter.hasNext()) {
DataType dt = iter.next();
listTwo.add(dt);
}
for (DataType dt : listOne) {
boolean found = false;
for (DataType dt2 : listTwo) {
if (dt.isEquivalent(dt2)) {
found = true;
break;
}
}
assertTrue(found);
}
}
@Test
@ -793,10 +782,163 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
assertNotNull(message, action);
}
@Test
public void testAction_FindStructureByOffset() {
DockingActionIf action = getAction(plugin, FindStructuresByOffsetAction.NAME);
performAction(action, false);
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
setText(dialog, "0x1");
pressButtonByText(dialog, "OK");
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
FindStructuresByOffsetAction.NAME);
assertMatchingStructures(resultsProvider, "ArrayStruct");
}
@Test
public void testAction_FindStructureByOffset_NoMatches() {
DockingActionIf action = getAction(plugin, FindStructuresByOffsetAction.NAME);
performAction(action, false);
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
setText(dialog, "0x100");
pressButtonByText(dialog, "OK");
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
FindStructuresByOffsetAction.NAME);
assertMatchingStructures(resultsProvider);
}
@Test
public void testAction_FindStructureByOffset_Range() {
DockingActionIf action = getAction(plugin, FindStructuresByOffsetAction.NAME);
performAction(action, false);
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
setText(dialog, "0x1:0x3,20");
pressButtonByText(dialog, "OK");
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
FindStructuresByOffsetAction.NAME);
assertMatchingStructures(resultsProvider, "ArrayStruct");
}
@Test
public void testAction_FindStructureByOffset_MixedInput() {
createStructureWithOffset_0x4(); // 0x4
createStructureWithOffset_0x8(); // 0x4, 0x8
createStructureWithOffset_0x10(); // 0x4, 0x8, 0x10
createStructureWithOffset_0x20(); // 0x8, 0x16, 0x20
DockingActionIf action = getAction(plugin, FindStructuresByOffsetAction.NAME);
performAction(action, false);
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
setText(dialog, "0x8:0x10, 32");
pressButtonByText(dialog, "OK");
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
FindStructuresByOffsetAction.NAME);
assertMatchingStructures(resultsProvider, "Structure_0x8", "Structure_0x10",
"Structure_0x20");
}
@Test
public void testAction_FindStructureBySize() {
createStructureWithOffset_0x4(); // 6
createStructureWithOffset_0x8(); // 10
createStructureWithOffset_0x10(); // 12
createStructureWithOffset_0x20(); // 22
DockingActionIf action = getAction(plugin, FindStructuresBySizeAction.NAME);
performAction(action, false);
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
setText(dialog, "10");
pressButtonByText(dialog, "OK");
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
FindStructuresBySizeAction.NAME);
assertMatchingStructures(resultsProvider, "Structure_0x8");
}
@Test
public void testAction_FindStructureBySize_Ranage() {
createStructureWithOffset_0x4(); // 6
createStructureWithOffset_0x8(); // 10
createStructureWithOffset_0x10(); // 12
createStructureWithOffset_0x20(); // 22
DockingActionIf action = getAction(plugin, FindStructuresBySizeAction.NAME);
performAction(action, false);
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
setText(dialog, "12:22");
pressButtonByText(dialog, "OK");
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
FindStructuresBySizeAction.NAME);
assertMatchingStructures(resultsProvider, "Structure_0x10", "Structure_0x20");
}
//==================================================================================================
// Private methods
//==================================================================================================
private void createStructureWithOffset_0x4() {
StructureDataType stuct = new StructureDataType("Structure_0x4", 0);
stuct.add(new DWordDataType());
stuct.add(new WordDataType());
builder.addDataType(stuct);
}
private void createStructureWithOffset_0x8() {
StructureDataType stuct = new StructureDataType("Structure_0x8", 0);
stuct.add(new DWordDataType());
stuct.add(new DWordDataType());
stuct.add(new WordDataType());
builder.addDataType(stuct);
}
private void createStructureWithOffset_0x10() {
StructureDataType stuct = new StructureDataType("Structure_0x10", 0);
stuct.add(new DWordDataType());
stuct.add(new DWordDataType());
stuct.add(new WordDataType());
stuct.add(new WordDataType());
builder.addDataType(stuct);
}
private void createStructureWithOffset_0x20() {
StructureDataType stuct = new StructureDataType("Structure_0x20", 0);
stuct.add(new QWordDataType());
stuct.add(new QWordDataType());
stuct.add(new DWordDataType());
stuct.add(new WordDataType());
builder.addDataType(stuct);
}
private void setText(NumberRangeInputDialog dialog, String text) {
runSwing(() -> dialog.setValue(text));
}
private void editSignature(String name, String newSignature) {
expandNode(programNode);
GTreeNode child = programNode.getChild(name);
@ -916,6 +1058,41 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
}
}
private void assertMatchingStructures(DataTypesProvider resultsProvider, String... names) {
DataTypeArchiveGTree gTree = resultsProvider.getGTree();
waitForTree(gTree);
Map<String, Structure> structures = getStructures(resultsProvider);
assertEquals("Incorrect number of matches.\n\tExpected: " + Arrays.toString(names) +
"\n\tFound: " + structures.keySet(), names.length, structures.size());
for (String name : names) {
if (!structures.containsKey(name)) {
fail("Structure not found in results: '" + name + "'.\nFound: " +
structures.keySet());
}
}
}
private Map<String, Structure> getStructures(DataTypesProvider resultsProvider) {
Map<String, Structure> map = new HashMap<>();
DataTypeArchiveGTree gTree = resultsProvider.getGTree();
GTreeNode rootNode = gTree.getViewRoot();
Iterator<GTreeNode> it = rootNode.iterator(true);
for (GTreeNode node : CollectionUtils.asIterable(it)) {
if (!(node instanceof DataTypeNode)) {
continue;
}
DataTypeNode dtNode = (DataTypeNode) node;
DataType dt = dtNode.getDataType();
if (dt instanceof Structure) {
map.put(dt.getName(), (Structure) dt);
}
}
return map;
}
private void assertEmptyTree() {
final GTreeNode rootNode = tree.getViewRoot();
final Integer[] box = new Integer[1];
@ -1061,7 +1238,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
}
}
private void compileJavaFile() throws Exception {
private void compileJavaDataType() throws Exception {
boolean success = false;
try {
@ -1072,8 +1249,8 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
Assert.fail("Could not create directory " + binDir.getAbsolutePath());
}
}
File javaFile = new File(binDir, "TestDataType.java");
File javaFile = new File(binDir, "TestDataType.java");
FileUtilities.copyFile(file, javaFile, false, TaskMonitor.DUMMY);
assertTrue(javaFile.exists());
@ -1088,6 +1265,31 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
}
}
private void cleanupTestBuiltin() throws Exception {
File binDir = getClassesDirectory();
File javaFile = new File(binDir, "TestDataType.java");
File classFile = new File(binDir, "TestDataType.class");
javaFile.delete();
classFile.delete();
// force built-ins to get restored
Task task = new ClassSearchTask();
task.run(TaskMonitor.DUMMY);
DockingActionIf action = getAction(plugin, "Refresh BuiltInTypes");
assertTrue(action.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(action, tree, false);
waitForTasks();
waitForProgram();
waitForActionToBeEnabled(action);
GTreeNode treeRoot = tree.getModelRoot();
GTreeNode builtInNode = treeRoot.getChild("BuiltInTypes");
assertNull("Test setup Error: ghidra.app.test.TestDataType was not removed!",
builtInNode.getChild("TestDataType"));
}
private File getTestDataTypeFile() {
URL url = getClass().getResource("TestDataType.txt");
try {

View File

@ -754,6 +754,26 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
return provider;
}
/**
* Allows you to find a component provider <b>with the given title</b>. Most plugins will
* only ever have a single provider. In those cases, use
* {@link #waitForComponentProvider(Class)}. This version of that method is to allow you to
* differentiate between multiple instances of a given provider that have different titles.
*
* @param clazz The class of the ComponentProvider to locate
* @param title the title of the component provider
* @return The component provider, or null if one cannot be found
*/
public static <T extends ComponentProvider> T waitForComponentProvider(Class<T> clazz,
String title) {
DockingWindowManager dwm = findActiveDockingWindowManager();
assertNotNull("Unable to find a DockingWindowManager - is there a tool showing?", dwm);
T provider = doWaitForComponentProvider(dwm, clazz, title);
return provider;
}
@SuppressWarnings("unchecked")
private static DockingWindowManager findActiveDockingWindowManager() {
DockingWindowManager activeInstance = DockingWindowManager.getActiveInstance();
@ -795,6 +815,25 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
"Timed-out waiting for ComponentProvider of class: " + clazz);
}
private static <T extends ComponentProvider> T doWaitForComponentProvider(
DockingWindowManager windowManager, Class<T> clazz, String title) {
Objects.requireNonNull(windowManager, "DockingWindowManager cannot be null");
int totalTime = 0;
while (totalTime <= DEFAULT_WAIT_TIMEOUT) {
T t = getComponentProvider(windowManager, clazz);
if (Objects.deepEquals(title, t.getTitle())) {
return t;
}
totalTime += sleep(DEFAULT_WAIT_DELAY);
}
throw new AssertionFailedError(
"Timed-out waiting for ComponentProvider of class: " + clazz);
}
/** These providers are those that appear in dialogs outside of the main frame **/
private static <T extends ComponentProvider> T getDetachedWindowProvider(
final Class<T> providerClass, final DockingWindowManager windowManager) {

View File

@ -0,0 +1,252 @@
/* ###
* 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.dialogs;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.*;
import docking.DialogComponentProvider;
import docking.DockingWindowManager;
import docking.widgets.label.GLabel;
import docking.widgets.textfield.HintTextField;
import ghidra.util.NumericUtilities;
import ghidra.util.datastruct.SortedRangeList;
import ghidra.util.layout.PairLayout;
/**
* An input dialog that accepts number input as discrete values or a range of values using
* ':' as the range separator.
*/
public class NumberRangeInputDialog extends DialogComponentProvider {
private static final String RANGE_DELIMITER = ":";
private static final String DEFAULT_VALUE = "";
private static final String HINT_TEXT = "e.g. 2,5 or 1,4:8";
private static final int MAX_SIZE = 256;
private boolean wasCancelled;
private String inputLabel;
private String initialValue = DEFAULT_VALUE;
private SortedRangeList rangeList = new SortedRangeList();
private HintTextField textField;
private KeyListener keyListener;
public NumberRangeInputDialog(String title, String label) {
super(title, true, true/* status */, true /* buttons */,
false /* no tasks */);
keyListener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_ENTER) {
okCallback();
}
}
};
this.inputLabel = label;
setTransient(true);
addOKButton();
addCancelButton();
buildMainPanel();
DocumentListener docListener = new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
clearStatusText();
}
@Override
public void insertUpdate(DocumentEvent e) {
clearStatusText();
}
@Override
public void removeUpdate(DocumentEvent e) {
clearStatusText();
}
};
textField.getDocument().addDocumentListener(docListener);
setFocusComponent(textField);
}
private void buildMainPanel() {
JPanel panel = new JPanel(new PairLayout(5, 5, 120));
textField = new MyHintTextField(HINT_TEXT);
textField.setText(initialValue);
textField.addKeyListener(keyListener);
textField.setName("number.range.input.dialog.text.field");
panel.add(new GLabel(inputLabel, SwingConstants.RIGHT));
panel.add(textField);
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
this.addWorkPanel(panel);
}
/**
* <code>show</code> displays the dialog, gets the user input
*
* @return false if the user cancelled the operation
*/
public boolean show() {
DockingWindowManager.showDialog(this);
return !wasCancelled;
}
@Override
protected void okCallback() {
wasCancelled = false;
if (!parseRanges()) {
return;
}
close();
}
JTextField getTextField() {
return textField;
}
private boolean parseRanges() {
// format: 1
// 1,4
// 1-4
// 1-4,8
// -8, 0xc, 0x10:0x20
// -0x20:-0x10
String value = textField.getText();
String[] parts = value.split(",");
for (String rangeText : parts) {
if (!addRange(rangeText)) {
return false;
}
}
return true;
}
private boolean addRange(String rangeText) {
String trimmed = rangeText.trim();
if (!trimmed.contains(RANGE_DELIMITER)) {
try {
long parsedLong = NumericUtilities.parseLong(trimmed);
int intValue = (int) parsedLong;
rangeList.addRange(intValue, intValue);
}
catch (NumberFormatException e) {
setStatusText("Unable to parse as a number: '" + trimmed + "'");
return false;
}
return true;
}
// this must be a range
String[] startAndEnd = trimmed.split(RANGE_DELIMITER);
try {
long parsedLong = NumericUtilities.parseLong(startAndEnd[0]);
int startInt = (int) parsedLong;
parsedLong = NumericUtilities.parseLong(startAndEnd[1]);
int endInt = (int) parsedLong;
rangeList.addRange(startInt, endInt);
}
catch (NumberFormatException e) {
setStatusText("Unable to parse as a number: '" + trimmed + "'");
return false;
}
return true;
}
@Override
protected void cancelCallback() {
wasCancelled = true;
rangeList.clear();
close();
}
/**
* Returns if this dialog is cancelled
* @return true if cancelled
*/
public boolean wasCancelled() {
return wasCancelled;
}
/**
* Return the value of the first (and maybe only) text field
* @return the text field value
*/
public SortedRangeList getValue() {
return rangeList;
}
/**
* Sets the text of the primary text field
* @param text the text
*/
public void setValue(String text) {
textField.setText(text);
}
private class MyHintTextField extends HintTextField {
MyHintTextField(String hintText) {
super(hintText);
setColumns(20);
}
@Override
protected Document createDefaultModel() {
return new MyDocument(this);
}
private class MyDocument extends PlainDocument {
private JTextField documentTf;
private MyDocument(JTextField textField) {
super();
this.documentTf = textField;
}
@Override
public void insertString(int offs, String str, AttributeSet a)
throws BadLocationException {
if (str == null) {
return;
}
String text = documentTf.getText();
if (text.length() + str.length() > MAX_SIZE) {
int nTooMany = text.length() + str.length() - MAX_SIZE;
int len = str.length() - nTooMany;
str = str.substring(0, len);
}
super.insertString(offs, str, a);
}
}
}
}

View File

@ -29,10 +29,10 @@ public class ClassSearchTask extends Task {
}
@Override
public void run(final TaskMonitor taskMonitor) {
public void run(final TaskMonitor monitor) {
try {
ClassSearcher.search(true, taskMonitor);
ClassSearcher.search(true, monitor);
}
catch (CancelledException e) {
// user cancelled

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,7 +29,7 @@ public class SortedRangeList implements Iterable<Range> {
* Creates a new empty sorted range list.
*/
public SortedRangeList() {
set = new TreeSet<Range>();
set = new TreeSet<>();
}
/**
@ -39,7 +38,7 @@ public class SortedRangeList implements Iterable<Range> {
* @param list the sorted range list to make an equivalent copy of.
*/
public SortedRangeList(SortedRangeList list) {
set = new TreeSet<Range>();
set = new TreeSet<>();
Iterator<Range> it = list.set.iterator();
while (it.hasNext()) {
Range r = it.next();
@ -98,7 +97,7 @@ public class SortedRangeList implements Iterable<Range> {
return set.iterator();
}
Iterator<Range> it = set.iterator();
LinkedList<Range> ll = new LinkedList<Range>();
LinkedList<Range> ll = new LinkedList<>();
while (it.hasNext()) {
ll.addFirst(it.next());
}
@ -362,4 +361,8 @@ public class SortedRangeList implements Iterable<Range> {
public Iterator<Range> iterator() {
return getRanges(true);
}
public void clear() {
set.clear();
}
}

View File

@ -41,7 +41,8 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
private ChangeListener classSearcherListener = e -> refresh();
/**
* Returns shared instance of built-in data-type manager.
* Returns shared instance of built-in data type manager.
* @return the manager
*/
public static synchronized BuiltInDataTypeManager getDataTypeManager() {
if (manager == null) {
@ -58,9 +59,6 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
return manager;
}
/**
* Constructor
*/
private BuiltInDataTypeManager() {
super(BUILT_IN_DATA_TYPES_NAME);
initialize();
@ -104,8 +102,6 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
populateBuiltInTypes();
}
/////////////////////////////////////
private void initialize() {
try {
populateBuiltInTypes();
@ -123,7 +119,8 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
protected void populateBuiltInTypes() {
int id = super.startTransaction("Populate");
try {
ArrayList<DataType> list = new ArrayList<>();
List<DataType> list = new ArrayList<>();
ClassFilter filter = new BuiltInDataTypeClassExclusionFilter();
List<BuiltInDataType> datatypes =
ClassSearcher.getInstances(BuiltInDataType.class, filter);
@ -141,7 +138,7 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
datatype.getName() + "'");
}
else if (list.size() != 1) {
throw new AssertException("Should be no duplicate named nuilt-in types");
throw new AssertException("Should be no duplicate named built-in types");
}
}
}
@ -156,7 +153,8 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
return DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID;
}
throw new IllegalArgumentException(
"Only Built-In data types can be resolved by the BuiltInTypes manager.");
"Only Built-in data types can be resolved by the " + getClass().getSimpleName() +
" manager.");
}
@Override
@ -197,7 +195,6 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
@Override
public void close() {
// do nothing - cannot close a built-in data type manager
// close performed automatically during shutdown
// cannot close a built-in data type manager; close performed automatically during shutdown
}
}

View File

@ -15,14 +15,18 @@
*/
package ghidra.util.datastruct;
import java.util.Iterator;
import java.util.stream.IntStream;
/**
* A class for holding a minimum and maximum signed int values that define a range.
*/
public class Range implements Comparable<Range> {
public class Range implements Comparable<Range>, Iterable<Integer> {
/** The range's minimum extent. */
public int min;
/** The range's maximum extent (inclusive). */
public int max;
/**
* Creates a range whose extent is from min to max.
* @param min the minimum extent.
@ -31,8 +35,8 @@ public class Range implements Comparable<Range> {
*/
public Range(int min, int max) {
if (max < min) {
throw new IllegalArgumentException("Range max (" + max
+ ") cannot be less than min (" + min + ").");
throw new IllegalArgumentException(
"Range max (" + max + ") cannot be less than min (" + min + ").");
}
this.min = min;
this.max = max;
@ -50,11 +54,11 @@ public class Range implements Comparable<Range> {
}
@Override
public boolean equals(Object obj) {
public boolean equals(Object obj) {
if (obj.getClass() != Range.class) {
return false;
}
Range other = (Range)obj;
Range other = (Range) obj;
return other.min == min && other.max == max;
}
@ -64,20 +68,29 @@ public class Range implements Comparable<Range> {
}
@Override
public String toString() {
return "("+min+","+max+")";
public String toString() {
return "(" + min + "," + max + ")";
}
/**
* Returns true if the value is within the ranges extent.
* @param value the value to check.
* @return true if the value is within the ranges extent.
*/
public boolean contains(int value) {
return value >= min && value <= max;
}
/**
* Returns the range's size.
* @return the size
*/
public long size() {
return (long)max - (long)min + 1;
return (long) max - (long) min + 1;
}
@Override
public Iterator<Integer> iterator() {
return IntStream.range(min, max + 1).iterator();
}
}

View File

@ -31,6 +31,7 @@ import docking.widgets.table.GTableCellRenderer;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.*;
import ghidra.app.plugin.core.datamgr.actions.FindStructuresBySizeAction;
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
import ghidra.app.plugin.core.datamgr.archive.InvalidFileArchive;
import ghidra.app.plugin.core.datamgr.util.ConflictDialog;
@ -155,6 +156,14 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator
pressButtonByText(d, "Cancel");
}
@Test
public void testFindDataTypesBySize() {
performAction(FindStructuresBySizeAction.NAME, "DataTypeManagerPlugin", false);
JDialog d = waitForJDialog(FindStructuresBySizeAction.NAME);
captureDialog();
pressButtonByText(d, "Cancel");
}
@Test
public void testPreviewWindow() {