GP-1743 - Added a method to GhiraScript to allow script writers to

disable reusing previously chosen values in the various 'ask' methods.
This commit is contained in:
dragonmacher 2022-02-11 18:47:10 -05:00
parent 7a5f0b4e16
commit ea52da673e
2 changed files with 90 additions and 43 deletions

View file

@ -94,8 +94,8 @@ import ghidra.util.task.TaskMonitor;
* </pre>
* <h3>Ghidra Script State</h3>
* <blockquote>
*
* <p>All scripts, when run, will be handed the current state in the form of class instance
*
* <p>All scripts, when run, will be handed the current state in the form of class instance
* variable. These variables are:
* <ol>
* <li><code>currentProgram</code>: the active program</li>
@ -140,6 +140,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
// Stores any parameters in a .properties file sharing the same base name as this GhidraScript
protected GhidraScriptProperties propertiesFileParams;
protected List<ResourceFile> potentialPropertiesFileLocs = new ArrayList<>();
private boolean reusePreviousChoices = true;
private CodeUnitFormat cuFormat;
// Stores any script-specific arguments
@ -181,7 +182,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
/**
* Set the context for this script.
*
*
* @param state state object
* @param monitor the monitor to use during run
* @param writer the target of script "print" statements
@ -193,9 +194,28 @@ public abstract class GhidraScript extends FlatProgramAPI {
loadVariablesFromState();
}
/**
* Sets whether the user's previously selected values should be used when showing the various
* {@code ask} methods. This is true by default, meaning that previous choices will be shown
* instead of any provided default value.
* @param reuse true to reuse values; false to not reuse previous values
*/
public void setReusePreviousChoices(boolean reuse) {
this.reusePreviousChoices = reuse;
}
/**
* Returns whether scripts will reuse previously selected values when showing the various
* {@code ask} methods.
* @return true to reuse values; false to not reuse previous values
*/
public boolean getReusePreviousChoices() {
return reusePreviousChoices;
}
/**
* Execute/run script and {@link #doCleanup} afterwards.
*
*
* @param runState state object
* @param runMonitor the monitor to use during run
* @param runWriter the target of script "print" statements
@ -490,7 +510,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
/**
* Set the script {@link #currentAddress}, {@link #currentLocation}, and update state object.
*
*
* @param address the new address
*/
public final void setCurrentLocation(Address address) {
@ -1707,7 +1727,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* action from a selection in the table.
* <p>
* This method is unavailable in headless mode.
*
*
* @param title the title of the dialog
* @param executor the TableChooserExecuter to be used to apply operations on table entries.
* @return a new TableChooserDialog.
@ -1725,7 +1745,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* action from a selection in the table.
* <p>
* This method is unavailable in headless mode.
*
*
* @param title of the dialog
* @param executor the TableChooserExecuter to be used to apply operations on table entries.
* @param isModal indicates whether the dialog should be modal or not
@ -1954,7 +1974,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
Map<Class<?>, Object> map = getScriptMap(key1, key2);
T mappedValue = null;
if (clazz != null) {
if (clazz != null && reusePreviousChoices) {
mappedValue = (T) map.get(clazz);
}
@ -2402,7 +2422,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* .properties value (if it exists), or throws an Exception if there is an invalid or
* missing .properties value.</li>
* </ol>
*
*
*
* @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode or when using .properties file)
@ -2475,7 +2495,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* .properties value (if it exists), or throws an Exception if there is an invalid or
* missing .properties value.</li>
* </ol>
*
*
*
* @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode or when using .properties file)
@ -2549,7 +2569,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* .properties byte pattern value (if it exists), or throws an Exception if there is
* an invalid or missing .properties value.</li>
* </ol>
*
*
*
* @param title the title of the dialog (in GUI mode) or the first part of the variable
* name (in headless mode or when using .properties file)
@ -2608,7 +2628,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* then that value is returned. Otherwise, an Exception is thrown if there is an
* invalid or missing .properties value.</li>
* </ol>
*
*
*
* @param title the title of the pop-up dialog (in GUI mode) or the variable name (in
* headless mode)
@ -2694,7 +2714,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* then that value is returned. Otherwise, an Exception is thrown if there is an invalid
* or missing .properties value.</li>
* </ol>
*
*
* @param title the title of the pop-up dialog (in GUI mode) or the variable name (in headless
* mode or when using .properties file)
* @throws IllegalArgumentException if in headless mode, there was a missing or invalid domain
@ -2831,7 +2851,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* .properties value (if it exists), or throws an Exception if there is an invalid or
* missing .properties value.</li>
* </ol>
*
*
*
* @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode or when using .properties file)
@ -2874,7 +2894,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* not null or an empty String, it is returned. In all other cases, an exception
* is thrown.</li>
* </ol>
*
*
*
* @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode or when using .properties file)
@ -2948,7 +2968,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* .properties value (if it exists and is a valid choice), or throws an Exception if
* there is an invalid or missing .properties value.</li>
* </ol>
*
*
* @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode or when using .properties file)
* @param message the message to display next to the input field (in GUI mode) or the second
@ -3088,7 +3108,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
*
* @throws CancelledException if the user hits the 'cancel' button
* @throws IllegalArgumentException if in headless mode, there was a missing or invalid set of
* choices specified in the .properties file
* choices specified in the .properties file
*/
public <T> List<T> askChoices(String title, String message, List<T> choices)
throws CancelledException {
@ -3160,8 +3180,8 @@ public abstract class GhidraScript extends FlatProgramAPI {
* @return the user-selected value(s); null if no selection was made
*
* @throws CancelledException if the user hits the 'cancel' button
* @throws IllegalArgumentException if choices is empty; if in headless mode,
* there was a missing or invalid set of choices specified in the .properties file
* @throws IllegalArgumentException if choices is empty; if in headless mode,
* there was a missing or invalid set of choices specified in the .properties file
*/
public <T> List<T> askChoices(String title, String message, List<T> choices,
List<String> choiceLabels) throws CancelledException {
@ -3232,7 +3252,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* then that value is returned. Otherwise, an Exception is thrown if there is an
* invalid or missing .properties value.</li>
* </ol>
*
*
*
* @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode)

View file

@ -123,7 +123,7 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
}
/*
* Calling askProgram() would stacktrace if the user 1) didn't select a program in the
* Calling askProgram() would stacktrace if the user 1) didn't select a program in the
* tree and then 2) pressed the OK button.
*/
@Test
@ -151,17 +151,17 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
runSwing(() -> dtd.close());
}
/*
/*
* For scripts with properties files in a different location (could be the case with subscripts),
* tests that the .properties file is found in the default location and that the default value
* for the input field is provided by the .properties file in the alternate location.
*
* tests that the .properties file is found in the default location and that the default value
* for the input field is provided by the .properties file in the alternate location.
*
* @throws Exception
*/
@Test
public void testAlternateLocationPropertiesFile() throws Exception {
// Create a temporary .properties file and set the potentialPropertiesFileLocs to look
// Create a temporary .properties file and set the potentialPropertiesFileLocs to look
// in that location
String tempDirPath = AbstractGTest.getTestDirectoryPath();
File tempDir = new File(tempDirPath);
@ -481,7 +481,7 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
/*
* Test that askInt method auto-populates dialog with value in .properties file.
*
*
* Also test that subsequent calls to the dialog show the last-used value.
*/
@Test
@ -622,13 +622,40 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
createScript();
final String defaultValue = "a default value";
String defaultValue = "a default value";
String myString = ask_TextInput(() -> {
return script.askString("Default Test", "Enter a string here:", defaultValue);
});
assertEquals(defaultValue, myString);
}
@Test
public void testAskStringDefaultValue_DoNotReusePreviousValues() throws Exception {
createScript();
String defaultValue = "a default value";
String myString = ask_TextInput(() -> {
return script.askString("Default Test", "Enter a string here:", defaultValue);
});
assertEquals(defaultValue, myString);
script.setReusePreviousChoices(false);
String secondDefaultValue = "a new default value";
String secondString = ask_TextInput(() -> {
return script.askString("Default Test", "Enter a string here:", secondDefaultValue);
});
assertEquals(secondDefaultValue, secondString);
script.setReusePreviousChoices(true);
String thirdDefaultValue = "a third default value";
String thirdString = ask_TextInput(() -> {
return script.askString("Default Test", "Enter a string here:", thirdDefaultValue);
});
assertEquals(secondString, thirdString);
}
@Test
public void testAskChoice() throws Exception {
createScript();
@ -673,37 +700,37 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
assertEquals(choices.get(choiceIndex), chosen);
}
// TODO test for askChoices()
// TODO test for askChoices()
/*
* No test for either of the two versions of 'askChoices()" because it does not use either the
* the last-selected value or a .properties file value to pre-populate the user choice in the
* GUI.
/*
* No test for either of the two versions of 'askChoices()" because it does not use either the
* the last-selected value or a .properties file value to pre-populate the user choice in the
* GUI.
*/
/*
/*
* No test for 'askYesNo()" because it does not use either the the last-selected value or
* a .properties file value to pre-populate the user choice in the GUI.
* a .properties file value to pre-populate the user choice in the GUI.
*/
/*
* No test for 'askProjectFolder()" because it does not use either the the last-selected value
* or a .properties file value to pre-populate the user choice in the GUI.
/*
* No test for 'askProjectFolder()" because it does not use either the the last-selected value
* or a .properties file value to pre-populate the user choice in the GUI.
*/
/*
/*
* No test for 'askProgram()" because it does not use either the the last-selected value or
* a .properties file value to pre-populate the user choice in the GUI.
* a .properties file value to pre-populate the user choice in the GUI.
*/
/*
/*
* No test for 'askDomainFile()" because it does not use either the the last-selected value or
* a .properties file value to pre-populate the user choice in the GUI.
* a .properties file value to pre-populate the user choice in the GUI.
*/
//==================================================================================================
// Private Methods
//==================================================================================================
//==================================================================================================
private <T> T ask_ComboInput(Callable<T> c) {
return ask_TextInput(null, c);