mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
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:
parent
f9463e600d
commit
ec88c732d2
19 changed files with 1104 additions and 379 deletions
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue